UNPKG

typia

Version:

Superfast runtime validators with only one line

151 lines (137 loc) 4.71 kB
import ts from "typescript"; import { RandomGenerator } from "../utils/RandomGenerator"; export namespace ExpressionFactory { export const number = (value: number) => value < 0 ? ts.factory.createPrefixUnaryExpression( ts.SyntaxKind.MinusToken, ts.factory.createNumericLiteral(Math.abs(value)), ) : ts.factory.createNumericLiteral(value); export const bigint = (value: number | bigint) => ts.factory.createCallExpression( ts.factory.createIdentifier("BigInt"), undefined, [ts.factory.createIdentifier(value.toString())], ); export const isRequired = (input: ts.Expression): ts.Expression => ts.factory.createStrictInequality( ts.factory.createIdentifier("undefined"), input, ); export const isArray = (input: ts.Expression): ts.Expression => ts.factory.createCallExpression( ts.factory.createIdentifier("Array.isArray"), undefined, [input], ); export const isObject = (options: { checkNull: boolean; checkArray: boolean }) => (input: ts.Expression): ts.Expression => { const conditions: ts.Expression[] = [ ts.factory.createStrictEquality( ts.factory.createStringLiteral("object"), ts.factory.createTypeOfExpression(input), ), ]; if (options.checkNull === true) conditions.push( ts.factory.createStrictInequality(ts.factory.createNull(), input), ); if (options.checkArray === true) conditions.push( ts.factory.createStrictEquality( ts.factory.createFalse(), ts.factory.createCallExpression( ts.factory.createIdentifier("Array.isArray"), undefined, [input], ), ), ); return conditions.length === 1 ? conditions[0]! : conditions.reduce((x, y) => ts.factory.createLogicalAnd(x, y)); }; export const isInstanceOf = (type: string) => (input: ts.Expression): ts.Expression => { return ts.factory.createBinaryExpression( input, ts.factory.createToken(ts.SyntaxKind.InstanceOfKeyword), ts.factory.createIdentifier(type), ); }; export const coalesce = (x: ts.Expression) => (y: ts.Expression): ts.Expression => ts.factory.createBinaryExpression( x, ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken), y, ); export const currying = (target: ts.Expression) => (parameters: ts.Expression[]) => { if (parameters.length === 0) return ts.factory.createCallExpression(target, undefined, undefined); let prev: ts.CallExpression = ts.factory.createCallExpression( target, undefined, [parameters[0]!], ); for (const param of parameters.slice(1)) prev = ts.factory.createCallExpression(prev, undefined, [param]); return prev; }; export const selfCall = (body: ts.ConciseBody) => ts.isCallExpression(body) ? body : ts.factory.createCallExpression( ts.factory.createParenthesizedExpression( ts.factory.createArrowFunction( undefined, undefined, [], undefined, undefined, body, ), ), undefined, undefined, ); export const getEscapedText = (printer: ts.Printer) => (input: ts.Expression): string => printer.printNode(ts.EmitHint.Expression, input, input.getSourceFile()); export const transpile = (context: ts.TransformationContext) => (script: string) => { const file: ts.SourceFile = ts.createSourceFile( `${RandomGenerator.uuid()}.ts`, script, ts.ScriptTarget.ESNext, true, ts.ScriptKind.TS, ); const statement: ts.Statement | undefined = file.statements[0]; if (statement === undefined) throw new ReferenceError( "Error on ExpressionFactory.transpile(): no statement exists.", ); else if (!ts.isExpressionStatement(statement)) throw new TypeError( "Error on ExpressionFactory.transpile(): statement is not an expression statement.", ); return (input: ts.Expression): ts.Expression => { const visitor = (node: ts.Node): ts.Node => { if (ts.isIdentifier(node) && node.text === "$input") return input; return ts.visitEachChild( (ts.factory as any).cloneNode(node), visitor, context, ); }; return visitor(statement.expression) as ts.Expression; }; }; }