typedconverter
Version:
Convert object into classes match with TypeScript type annotation
98 lines (97 loc) • 4.26 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Result = exports.pipeline = void 0;
const tslib_1 = require("tslib");
const tinspector_1 = tslib_1.__importDefault(require("tinspector"));
const converter_1 = require("./converter");
const invocation_1 = require("./invocation");
var Result;
(function (Result) {
function create(value) {
return { value };
}
Result.create = create;
function error(value, path, message) {
return { value, issues: [{ path, messages: Array.isArray(message) ? message : [message] }] };
}
Result.error = error;
})(Result || (Result = {}));
exports.Result = Result;
// --------------------------------------------------------------------- //
// ------------------------------ HELPER ------------------------------- //
// --------------------------------------------------------------------- //
function getPath(path, name) {
return path.length === 0 ? name : `${path}.${name}`;
}
const visitorMap = {
"Primitive": primitiveVisitor,
"Array": arrayVisitor,
"Object": objectVisitor,
};
function unableToConvert(value, type) {
return `Unable to convert "${converter_1.safeToString(value)}" into ${type}`;
}
// --------------------------------------------------------------------- //
// ------------------------------ VISITORS ----------------------------- //
// --------------------------------------------------------------------- //
function primitiveVisitor(value, ast, opt) {
const result = ast.converter(value);
if (result === undefined)
return Result.error(value, opt.path, unableToConvert(value, ast.type.name));
else
return Result.create(result);
}
function arrayVisitor(value, ast, opt) {
const newValues = opt.guessArrayElement && !Array.isArray(value) ? [value] : value;
if (!Array.isArray(newValues))
return Result.error(value, opt.path, unableToConvert(value, `Array<${ast.type.name}>`));
const result = [];
const errors = [];
for (let i = 0; i < newValues.length; i++) {
const val = newValues[i];
const option = {
path: getPath(opt.path, i.toString()), extension: opt.extension,
decorators: opt.decorators, parent: opt.parent,
guessArrayElement: opt.guessArrayElement
};
const elValue = pipeline(val, ast.element, option);
result.push(elValue.value);
if (elValue.issues)
errors.push(...elValue.issues);
}
return { value: result, issues: errors.length === 0 ? undefined : errors };
}
function objectVisitor(value, ast, opt) {
if (typeof value === "number" || typeof value === "string" || typeof value === "boolean")
return Result.error(value, opt.path, unableToConvert(value, ast.type.name));
const instance = Object.create(ast.type.prototype);
const meta = tinspector_1.default(ast.type);
const errors = [];
for (const property of meta.properties) {
const node = ast.properties.get(property.name);
const option = {
path: getPath(opt.path, property.name), extension: opt.extension,
decorators: property.decorators, parent: { value, type: ast.type, decorators: opt.decorators },
guessArrayElement: opt.guessArrayElement
};
const propValue = pipeline(value[property.name], node, option);
if (propValue.issues)
errors.push(...propValue.issues);
if (propValue.value == undefined || propValue.value === null)
continue;
instance[property.name] = propValue.value;
}
return { value: instance, issues: errors.length === 0 ? undefined : errors };
}
function visitor(value, ast, opt) {
if (value === undefined || value === null)
return { value };
if (value.constructor === ast.type || ast.type === Object)
return { value: ast.kind === "Array" && opt.guessArrayElement && !Array.isArray(value) ? [value] : value };
return visitorMap[ast.kind](value, ast, opt);
}
function pipeline(value, ast, opt) {
const visitors = invocation_1.pipe(value, opt.path, ast, opt.decorators, opt.extension, () => visitor(value, ast, opt), opt.parent);
return visitors.proceed();
}
exports.pipeline = pipeline;