You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

97 lines
2.8 KiB
JavaScript

"use strict";
const objectHash = require("object-hash");
const mapObj = require("map-obj");
const typeRules = require("./type-rules");
const generateDescriptor = require("./generate-descriptor");
const getSchemaKeys = require("./util/get-schema-keys");
const nullMissingFields = require("./util/null-missing-fields");
module.exports = function createType(name, schema, options = {}) {
if (schema._isTypeRule === true) {
return typeRules._createTypeRule({
_typeName: name,
_isTypeAlias: true,
_alias: schema,
_registry: options._registry
});
} else {
let propertyDescriptors = mapObj(schema, (key, rule) => {
return generateDescriptor(rule, key, false, options._registry);
});
let protoProperties = {
_typeName: name,
/* We pre-define this here so that instances can be monomorphic; this provides a 4x performance improvement. */
_data: mapObj(schema, (key, _value) => {
return [key, undefined];
})
};
let proto = Object.create(protoProperties, propertyDescriptors);
let schemaKeys = getSchemaKeys(schema);
let factory = function createInstance(instanceProps) {
factory._isUsed = true;
let instance = Object.create(proto);
instance._data = {};
// for (let key of factory._schemaKeys) {
// if (instanceProps[key] != null) {
// instance[key] = instanceProps[key];
// } else {
// instance[key] = null;
// }
// }
/* FIXME: Investigate ways to optimize this */
let nullFields = nullMissingFields(instanceProps, factory._schemaKeys);
Object.assign(instance, instanceProps, nullFields);
return instance;
};
proto._type = factory;
factory._registry = options._registry;
factory._schemaKeys = schemaKeys;
factory._isCustomType = true;
factory._name = name;
factory.prototype = proto;
factory.description = name;
factory._validator = function (value) {
return (value instanceof factory);
};
/* FIXME: Is the below actually necessary? */
proto._selfValidator = factory._validator;
factory._isUsed = false;
factory._implementedTraits = new Set();
factory.implements = function (trait, implementation) {
if (factory._isUsed !== false) {
throw new Error("Cannot modify a custom type that has already been instantiated");
} else {
let {implementationDescriptors, slotSchemaKeys} = trait.applyImplementation(implementation);
// FIXME: Test this
Object.keys(implementationDescriptors).forEach((key) => {
if (proto.key != null) {
throw new Error(`Trait implementation attempted to define a property '${key}' on the '${name}' type, but that property already exists`);
}
});
Object.defineProperties(proto, implementationDescriptors);
factory._implementedTraits.add(trait);
factory._schemaKeys = factory._schemaKeys.concat(slotSchemaKeys);
return this;
}
};
return typeRules._createTypeRule(factory);
}
};