@matatbread/typia
Version:
Superfast runtime validators with only one line
238 lines (235 loc) • 12.2 kB
JavaScript
import ts from 'typescript';
import { ExpressionFactory } from '../factories/ExpressionFactory.mjs';
import { IdentifierFactory } from '../factories/IdentifierFactory.mjs';
import { StatementFactory } from '../factories/StatementFactory.mjs';
import { TypeFactory } from '../factories/TypeFactory.mjs';
import { CheckerProgrammer } from './CheckerProgrammer.mjs';
import { FeatureProgrammer } from './FeatureProgrammer.mjs';
import { IsProgrammer } from './IsProgrammer.mjs';
import { FunctionProgrammer } from './helpers/FunctionProgrammer.mjs';
import { OptionPredicator } from './helpers/OptionPredicator.mjs';
import { check_everything } from './internal/check_everything.mjs';
import { check_object } from './internal/check_object.mjs';
var ValidateProgrammer;
(function (ValidateProgrammer) {
ValidateProgrammer.decompose = (props) => {
const is = IsProgrammer.decompose(props);
const composed = CheckerProgrammer.compose({
...props,
config: {
prefix: "_v",
path: true,
trace: true,
numeric: OptionPredicator.numeric(props.context.options),
equals: props.config.equals,
atomist: (next) => [
...(next.entry.expression ? [next.entry.expression] : []),
...(next.entry.conditions.length === 0
? []
: next.entry.conditions.length === 1
? next.entry.conditions[0].map((cond) => ts.factory.createLogicalOr(cond.expression, create_report_call({
exceptionable: next.explore.from === "top"
? ts.factory.createTrue()
: ts.factory.createIdentifier("_exceptionable"),
path: ts.factory.createIdentifier(next.explore.postfix
? `_path + ${next.explore.postfix}`
: "_path"),
expected: cond.expected,
value: next.input,
})))
: [
ts.factory.createLogicalOr(next.entry.conditions
.map((set) => set
.map((s) => s.expression)
.reduce((a, b) => ts.factory.createLogicalAnd(a, b)))
.reduce((a, b) => ts.factory.createLogicalOr(a, b)), create_report_call({
exceptionable: next.explore.from === "top"
? ts.factory.createTrue()
: ts.factory.createIdentifier("_exceptionable"),
path: ts.factory.createIdentifier(next.explore.postfix
? `_path + ${next.explore.postfix}`
: "_path"),
expected: next.entry.expected,
value: next.input,
})),
]),
].reduce((x, y) => ts.factory.createLogicalAnd(x, y)),
combiner: combine(props),
joiner: joiner(props),
success: ts.factory.createTrue(),
},
});
const arrow = ts.factory.createArrowFunction(undefined, undefined, [IdentifierFactory.parameter("input", TypeFactory.keyword("any"))], props.context.importer.type({
file: "typia",
name: "IValidation",
arguments: [
ts.factory.createTypeReferenceNode(props.name ??
TypeFactory.getFullName({
checker: props.context.checker,
type: props.type,
})),
],
}), undefined, ts.factory.createBlock([
// validate when false === is<T>(input)
ts.factory.createIfStatement(ts.factory.createStrictEquality(ts.factory.createFalse(), ts.factory.createCallExpression(ts.factory.createIdentifier("__is"), undefined, [ts.factory.createIdentifier("input")])), ts.factory.createBlock([
// prepare errors
ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(ts.factory.createIdentifier("errors"), ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createArrayLiteralExpression([]))),
ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(ts.factory.createIdentifier("_report"), ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createCallExpression(ts.factory.createAsExpression(props.context.importer.internal("validateReport"), TypeFactory.keyword("any")), [], [ts.factory.createIdentifier("errors")]))),
ts.factory.createExpressionStatement(ts.factory.createCallExpression(ts.factory.createArrowFunction(undefined, undefined, composed.parameters, undefined, undefined, composed.body), undefined, [
ts.factory.createIdentifier("input"),
ts.factory.createStringLiteral("$input"),
ts.factory.createTrue(),
])),
StatementFactory.constant({
name: "success",
value: ts.factory.createStrictEquality(ExpressionFactory.number(0), ts.factory.createIdentifier("errors.length")),
}),
ts.factory.createReturnStatement(ts.factory.createAsExpression(create_output(), TypeFactory.keyword("any"))),
])),
ts.factory.createReturnStatement(ts.factory.createAsExpression(ts.factory.createObjectLiteralExpression([
ts.factory.createPropertyAssignment("success", ts.factory.createTrue()),
ts.factory.createPropertyAssignment("data", ts.factory.createIdentifier("input")),
], true), TypeFactory.keyword("any"))),
], true));
return {
functions: {
...is.functions,
...composed.functions,
},
statements: [
...is.statements,
...composed.statements,
StatementFactory.constant({
name: "__is",
value: is.arrow,
}),
StatementFactory.mut({ name: "errors" }),
StatementFactory.mut({ name: "_report" }),
],
arrow,
};
};
ValidateProgrammer.write = (props) => {
const functor = new FunctionProgrammer(props.modulo.getText());
const result = ValidateProgrammer.decompose({
config: props.config,
context: props.context,
modulo: props.modulo,
functor,
type: props.type,
name: props.name,
});
return FeatureProgrammer.writeDecomposed({
modulo: props.modulo,
functor,
result,
returnWrapper: props.config.standardSchema
? (arrow) => ts.factory.createCallExpression(props.context.importer.internal("createStandardSchema"), undefined, [arrow])
: undefined,
});
};
})(ValidateProgrammer || (ValidateProgrammer = {}));
const combine = (props) => (next) => {
if (next.explore.tracable === false)
return IsProgrammer.configure({
options: {
object: (v) => validate_object({
context: props.context,
functor: props.functor,
config: props.config,
entries: v.entries,
input: v.input,
}),
numeric: true,
},
context: props.context,
functor: props.functor,
}).combiner(next);
const path = next.explore.postfix
? `_path + ${next.explore.postfix}`
: "_path";
return next.logic === "and"
? next.binaries
.map((binary) => binary.combined
? binary.expression
: ts.factory.createLogicalOr(binary.expression, create_report_call({
exceptionable: next.explore.source === "top"
? ts.factory.createTrue()
: ts.factory.createIdentifier("_exceptionable"),
path: ts.factory.createIdentifier(path),
expected: next.expected,
value: next.input,
})))
.reduce(ts.factory.createLogicalAnd)
: ts.factory.createLogicalOr(next.binaries
.map((binary) => binary.expression)
.reduce(ts.factory.createLogicalOr), create_report_call({
exceptionable: next.explore.source === "top"
? ts.factory.createTrue()
: ts.factory.createIdentifier("_exceptionable"),
path: ts.factory.createIdentifier(path),
expected: next.expected,
value: next.input,
}));
};
const validate_object = (props) => check_object({
config: {
equals: props.config.equals,
undefined: true,
assert: false,
reduce: ts.factory.createLogicalAnd,
positive: ts.factory.createTrue(),
superfluous: (input, description) => create_report_call({
path: ts.factory.createAdd(ts.factory.createIdentifier("_path"), ts.factory.createCallExpression(props.context.importer.internal("accessExpressionAsString"), undefined, [ts.factory.createIdentifier("key")])),
expected: "undefined",
value: input,
description,
}),
halt: (expr) => ts.factory.createLogicalOr(ts.factory.createStrictEquality(ts.factory.createFalse(), ts.factory.createIdentifier("_exceptionable")), expr),
},
context: props.context,
entries: props.entries,
input: props.input,
});
const joiner = (props) => ({
object: (v) => validate_object({
context: props.context,
functor: props.functor,
config: props.config,
entries: v.entries,
input: v.input,
}),
array: (props) => check_everything(ts.factory.createCallExpression(IdentifierFactory.access(props.input, "map"), undefined, [props.arrow])),
failure: (next) => create_report_call({
exceptionable: next.explore?.from === "top"
? ts.factory.createTrue()
: ts.factory.createIdentifier("_exceptionable"),
path: ts.factory.createIdentifier(next.explore?.postfix ? `_path + ${next.explore.postfix}` : "_path"),
expected: next.expected,
value: next.input,
}),
tuple: (binaries) => check_everything(ts.factory.createArrayLiteralExpression(binaries, true)),
});
const create_output = () => ts.factory.createConditionalExpression(ts.factory.createIdentifier("success"), undefined, ts.factory.createObjectLiteralExpression([
ts.factory.createShorthandPropertyAssignment("success"),
ts.factory.createPropertyAssignment("data", ts.factory.createIdentifier("input")),
], true), undefined, ts.factory.createObjectLiteralExpression([
ts.factory.createShorthandPropertyAssignment("success"),
ts.factory.createShorthandPropertyAssignment("errors"),
ts.factory.createPropertyAssignment("data", ts.factory.createIdentifier("input")),
], true));
const create_report_call = (props) => ts.factory.createCallExpression(ts.factory.createIdentifier("_report"), undefined, [
props.exceptionable ?? ts.factory.createIdentifier("_exceptionable"),
ts.factory.createObjectLiteralExpression([
ts.factory.createPropertyAssignment("path", props.path),
ts.factory.createPropertyAssignment("expected", ts.factory.createStringLiteral(props.expected)),
ts.factory.createPropertyAssignment("value", props.value),
...(props.description
? [
ts.factory.createPropertyAssignment("description", props.description),
]
: []),
], true),
]);
export { ValidateProgrammer };
//# sourceMappingURL=ValidateProgrammer.mjs.map