@backland/schema
Version:
TypeScript schema declaration and validation library with static type inference
447 lines (442 loc) • 13.3 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.CACHED_FIELD_INSTANCE_KEY = void 0;
exports.__getCachedFieldInstance = __getCachedFieldInstance;
exports._parserHooks = void 0;
exports.deleteCachedFieldInstance = deleteCachedFieldInstance;
exports.isObjectAsTypeDefinition = isObjectAsTypeDefinition;
exports.parseField = parseField;
exports.parseFieldDefinitionConfig = parseFieldDefinitionConfig;
exports.parseFlattenFieldDefinition = parseFlattenFieldDefinition;
exports.parseObjectDefinition = parseObjectDefinition;
exports.parseObjectField = parseObjectField;
exports.setParserHook = setParserHook;
var _utils = require("@backland/utils");
var _GraphType = require("./GraphType/GraphType");
var _ObjectType = require("./ObjectType");
var _fieldInstanceFromDef = require("./fieldInstanceFromDef");
var _FieldType = require("./fields/FieldType");
var _LiteralField = require("./fields/LiteralField");
var _MetaFieldField = require("./fields/MetaFieldField");
var _fieldTypes = require("./fields/fieldTypes");
var _parseStringDefinition = require("./parseStringDefinition");
const _parserHooks = [];
exports._parserHooks = _parserHooks;
function setParserHook(hook) {
_parserHooks.push(hook);
function remove() {
_parserHooks.find((el, index) => {
if (el !== hook) return false;
delete _parserHooks[index];
return true;
});
return _parserHooks;
}
return remove;
}
function parseObjectField(fieldName, definition, options = {}) {
let {
returnInstance,
asString,
deep,
omitMeta
} = options === true ? {
returnInstance: true
} : options;
if (deep !== null && deep !== void 0 && deep.omitMeta) omitMeta = true;
if (deep !== null && deep !== void 0 && deep.asString) asString = true;
let parsed = parseFieldDefinitionConfig(definition, {
deep,
omitMeta
});
if (_parserHooks.length) {
_parserHooks.forEach(cb => cb(parsed));
}
if (typeof parsed === 'string') {
return parsed;
} else {
if (asString) return parsed;
}
const instanceFromDef = (0, _fieldInstanceFromDef.fieldInstanceFromDef)(parsed);
setCachedFieldInstance(parsed, instanceFromDef);
if (instanceFromDef.def) {
parsed.def = instanceFromDef.def;
}
if (returnInstance) {
return instanceFromDef;
}
if (parsed) return parsed;
throw new _utils.RuntimeError(`field "${fieldName}": invalid definition.`, {
definition,
parsed
});
}
function parseField(definition) {
return parseObjectField('__parseField__', definition);
}
const stringifiableDefKeys = new Set(['type', 'list', 'optional' //
]);
function parseFieldDefinitionConfig(definition, options) {
let {
deep,
asString
} = options || {};
if (deep !== null && deep !== void 0 && deep.asString) {
asString = true;
}
function _parseField() {
if (_LiteralField.LiteralField.isFinalTypeDef(definition)) {
return {
def: definition.def,
defaultValue: definition.defaultValue,
description: definition.description,
hidden: definition.hidden,
list: !!definition.list,
optional: !!definition.optional,
type: 'literal'
};
}
if (_GraphType.GraphType.is(definition)) {
const def = parseFieldDefinitionConfig(definition.definition, {
deep
});
def.hidden = def.hidden || definition.hidden;
return def;
}
if (_GraphType.GraphType.isTypeDefinition(definition)) {
const {
list = false,
optional = false,
description,
defaultValue,
hidden,
type: {
definition: {
type,
def,
defaultValue: _defaultValue
}
}
} = definition;
return {
def,
defaultValue: defaultValue === undefined ? _defaultValue : defaultValue,
description,
hidden,
list,
optional,
type
};
}
if ((0, _parseStringDefinition.isStringFieldDefinition)(definition)) {
return (0, _parseStringDefinition.parseStringDefinition)(definition);
}
if ((0, _FieldType.isFieldInstance)(definition)) {
return definition.asFinalFieldDef;
}
if (isFinalFieldDefinition(definition)) {
if (definition.type === 'object') {
if (typeof definition.def !== 'object' || !definition.def) {
throw new _utils.RuntimeError(`Missing def for object field.`, {
definition
});
}
if ((0, _ObjectType.isObject)(definition.def)) {
definition.def = definition.def.definition;
} else {
definition.def = parseObjectDefinition(definition.def, {
deep
}).definition;
}
}
if (definition.type === 'union') {
let isOptionalUnion = definition.optional;
definition.def = definition.def.map(el => {
const parsed = parseFieldDefinitionConfig(el, {
deep
});
if (parsed.optional) isOptionalUnion = true;
return parsed;
});
definition.optional = isOptionalUnion;
}
if (definition.type === 'alias') {
if (typeof definition.def === 'object') {
definition.def.type = parseFieldDefinitionConfig(definition.def.type, {
deep
});
validFlattenDefinitionKeysList.forEach(k => {
if (definition[k] !== undefined) {
// @ts-ignore
definition.def.type[k] = definition[k];
}
});
}
}
return definition;
}
if (isListDefinition(definition)) {
const parsed = parseFieldDefinitionConfig(definition[0], {
deep
});
parsed.list = true;
parsed.optional = false;
return parsed;
}
if ((0, _ObjectType.isObject)(definition)) {
return {
def: deep ? parseObjectDefinition(definition.definition, {
deep
}) : definition.definition,
defaultValue: undefined,
description: definition.description,
hidden: definition.hidden,
type: 'object'
};
}
if (isObjectAsTypeDefinition(definition)) {
return {
def: deep ? parseObjectDefinition(definition.type.definition, {
deep
}) : definition.type.definition,
defaultValue: undefined,
description: definition.type.description,
hidden: definition.hidden || definition.type.hidden,
list: !!definition.list,
name: definition.name,
optional: !!definition.optional,
type: 'object'
};
}
const keyObjectDefinition = parseFlattenFieldDefinition(definition, {
deep
});
if (keyObjectDefinition) {
return keyObjectDefinition;
}
throw new Error(`Unexpected field definition: ${(0, _utils.inspectObject)(definition)}`);
}
try {
const result = _parseField();
if (definition && typeof definition === 'object') {
if ('name' in definition && definition.name && typeof definition.name === 'string') {
result.name = definition.name;
}
}
let hasNotStringifiableKeys = false;
let hasDef = false;
Object.entries(result).forEach(([k, v]) => {
if (v === undefined || v === false) {
delete result[k]; // deleting nullish values
return;
}
if (k === 'def') {
hasDef = true;
return;
}
if (hasNotStringifiableKeys || !stringifiableDefKeys.has(k)) {
hasNotStringifiableKeys = true;
}
});
if (asString && !hasNotStringifiableKeys) {
const {
type,
list,
optional,
def
} = result;
let _type = type;
if (list) _type = `[${_type}]`;
if (optional) _type = `${_type}?`;
if (hasDef) {
// @ts-ignore
return {
[_type]: def
};
} else {
// @ts-ignore
return _type;
}
}
// return simpleObjectClone(result);
return result;
} catch (e) {
debugger;
console.error(e, definition);
throw e;
}
}
function parseObjectDefinition(input, options = {}) {
let {
deep,
omitMeta
} = options;
if (deep !== null && deep !== void 0 && deep.omitMeta) {
omitMeta = true;
}
const result = {};
let meta = undefined;
const keys = (0, _utils.getKeys)(input);
keys.forEach(function (fieldName) {
try {
let field = input[fieldName];
if ((0, _MetaFieldField.isMetaField)(field, fieldName)) {
return meta = field;
}
const cached = hasCachedFieldInstance(field);
if (cached) {
return result[fieldName] = cached;
}
return result[fieldName] = parseObjectField(fieldName, field, {
deep
});
} catch (err) {
debugger;
throw new _utils.RuntimeError(`Failed to process object field "${fieldName}":\n${err.message}`, {
err: err.stack,
input
});
}
});
meta = meta || (0, _MetaFieldField.createEmptyMetaField)();
if (!omitMeta) {
result[_MetaFieldField.objectMetaFieldKey] = meta;
}
return {
definition: result,
meta
};
}
function isFinalFieldDefinition(input) {
return typeof (input === null || input === void 0 ? void 0 : input.type) === 'string';
}
function isListDefinition(input) {
if (Array.isArray(input) && input.length === 1) return true;
if (!(0, _utils.isProduction)()) {
var _input$forEach;
// verify against old enum definition
input === null || input === void 0 ? void 0 : (_input$forEach = input.forEach) === null || _input$forEach === void 0 ? void 0 : _input$forEach.call(input, el => {
if (typeof el === 'string' && !(0, _parseStringDefinition.isStringFieldDefinition)(el)) {
throw new Error(`Plain array is used only for union definitions.\n` + ` "${el}" is not valid as union item.\n` + ` You can use { enum: ['${el}'] } instead of ['${el}'].`);
}
});
}
return false;
}
/**
* Object as field['type'] is deprecated
* @param input
*/
function isObjectAsTypeDefinition(input) {
return input && typeof input === 'object' && (0, _ObjectType.isObject)(input.type);
}
const validFlattenDefinitionKeys = {
defaultValue: 'any',
description: 'string',
hidden: 'boolean',
list: 'boolean',
name: 'string',
optional: 'boolean'
};
const validFlattenDefinitionKeysList = (0, _utils.getKeys)(validFlattenDefinitionKeys);
function parseFlattenFieldDefinition(input, options = {}) {
const {
deep
} = options;
if ((0, _utils.getTypeName)(input) !== 'Object') return false;
if (input.type !== undefined) return false;
let type;
let def;
for (let k in input) {
const valueOfDefOrOptionalOrListOrDescription = input[k];
if (_fieldTypes.types[k]) {
type = k;
def = valueOfDefOrOptionalOrListOrDescription;
if (k !== 'object' && def && typeof def === 'object') {
for (let defKey in def) {
if (defKey === 'def' || validFlattenDefinitionKeys[defKey]) {
console.warn(`using field def as type definition?\n`, {
def,
type: k
});
return false;
}
}
}
} else {
if (valueOfDefOrOptionalOrListOrDescription !== undefined) {
const acceptAny = validFlattenDefinitionKeys[k] === 'any';
if (!acceptAny &&
// checking if the de `optional` or `list` or `description`
// has the expected types
typeof valueOfDefOrOptionalOrListOrDescription !== validFlattenDefinitionKeys[k]) {
return false;
}
}
}
}
let {
description,
optional = false,
list = false,
defaultValue,
name,
hidden
} = input;
return parseFieldDefinitionConfig({
def,
defaultValue,
description,
hidden,
list,
name,
optional,
type
}, {
deep
});
}
const CACHED_FIELD_INSTANCE_KEY = '__cachedFieldInstance';
exports.CACHED_FIELD_INSTANCE_KEY = CACHED_FIELD_INSTANCE_KEY;
function __getCachedFieldInstance(field) {
if (field !== null && field !== void 0 && field[CACHED_FIELD_INSTANCE_KEY]) {
return field[CACHED_FIELD_INSTANCE_KEY];
}
const parsed = parseFieldDefinitionConfig(field);
const instanceFromDef = (0, _fieldInstanceFromDef.fieldInstanceFromDef)(parsed);
setCachedFieldInstance(parsed, instanceFromDef);
return instanceFromDef;
}
function hasCachedFieldInstance(field) {
return !!(field !== null && field !== void 0 && field[CACHED_FIELD_INSTANCE_KEY]) ? field : null;
}
function setCachedFieldInstance(field, instanceFromDef) {
if (hasCachedFieldInstance(field)) return;
Object.defineProperty(field, CACHED_FIELD_INSTANCE_KEY, {
configurable: false,
enumerable: false,
value: instanceFromDef,
writable: false
});
}
function deleteCachedFieldInstance(def) {
if (!def || typeof def !== 'object') return def;
const {
[CACHED_FIELD_INSTANCE_KEY]: _,
...rest
} = def;
return rest;
// if (currentDepth > depth) return def;
//
// Object.entries(rest).reduce((acc, [key, value]) => {
// return {
// ...acc,
// [key]: value,
// };
// }, rest);
//
// return rest;
}
//# sourceMappingURL=parseObjectDefinition.js.map