diff --git a/benchmark/package.json b/benchmark/package.json index 57a75b14a8..87247df650 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -72,6 +72,6 @@ "suppress-warnings": "^1.0.2", "tstl": "^3.0.0", "uuid": "^9.0.1", - "typia": "../typia-6.7.0-dev.20240803-2.tgz" + "typia": "../typia-6.7.0-dev.20240803-5.tgz" } } \ No newline at end of file diff --git a/debug/package.json b/debug/package.json index 382749a86c..3ff457f542 100644 --- a/debug/package.json +++ b/debug/package.json @@ -17,7 +17,7 @@ }, "dependencies": { "tstl": "^3.0.0", - "typia": "../typia-6.7.0-dev.20240802-2.tgz", + "typia": "../typia-6.7.0-dev.20240803-4.tgz", "uuid": "^10.0.0" } } \ No newline at end of file diff --git a/debug/src/decode.ts b/debug/src/decode.ts deleted file mode 100644 index 126609cb38..0000000000 --- a/debug/src/decode.ts +++ /dev/null @@ -1,15 +0,0 @@ -import typia from "typia"; - -interface IBox3D { - scale: IPoint3D; - position: IPoint3D; - rotate: IPoint3D; - pivot: IPoint3D; -} -interface IPoint3D { - x: number; - y: number; - z: number; -} - -() => typia.protobuf.decode(new Uint8Array()); diff --git a/debug/src/form.ts b/debug/src/form.ts deleted file mode 100644 index f4b5e4411f..0000000000 --- a/debug/src/form.ts +++ /dev/null @@ -1,25 +0,0 @@ -import typia from "typia"; - -import { ObjectHttpFormData } from "./internal/ObjectHttpFormData"; -import { create_form_data } from "./internal/create_form_data"; - -const data: ObjectHttpFormData = ObjectHttpFormData.generate(); -const assert = (form: FormData) => - typia.http.assertFormData(form); -const is = (elem: ObjectHttpFormData): boolean => { - const form: FormData = create_form_data(elem); - try { - assert(form); - return true; - } catch { - return false; - } -}; - -console.log(is(data)); - -for (const s of ObjectHttpFormData.SPOILERS) { - const elem: ObjectHttpFormData = ObjectHttpFormData.generate(); - const fields = s(elem); - console.log(fields, is(elem)); -} diff --git a/debug/src/functional.ts b/debug/src/functional.ts new file mode 100644 index 0000000000..db4669f84e --- /dev/null +++ b/debug/src/functional.ts @@ -0,0 +1,39 @@ +import typia from "typia"; + +const sum = (x: number, y: number): number => x + y; + +//---- +// ASSERT +//---- +// ASSERT-PARAMETERS +typia.functional.assertParameters(sum); + +// ASSERT-RETURN +typia.functional.assertReturn(sum); + +// ASSERT-FUNCTION +typia.functional.assertFunction(sum); + +//---- +// IS +//---- +// IS-PARAMETERS +typia.functional.isParameters(sum); + +// IS-RETURN +typia.functional.isReturn(sum); + +// IS-FUNCTION +typia.functional.isFunction(sum); + +//---- +// VALIDATE +//---- +// VALIDATE-PARAMETERS +typia.functional.validateParameters(sum); + +// VALIDATE-RETURN +typia.functional.validateReturn(sum); + +// VALIDATE-FUNCTION +typia.functional.validateFunction(sum); diff --git a/debug/src/internal/DynamicTag.ts b/debug/src/internal/DynamicTag.ts deleted file mode 100644 index 43a66173d7..0000000000 --- a/debug/src/internal/DynamicTag.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { tags } from "typia"; - -import { ArrayUtil } from "typia/lib/utils/ArrayUtil"; - -import { Spoiler } from "./Spoiler"; -import { TestRandomGenerator } from "./TestRandomGenerator"; - -export interface DynamicTag { - [key: number & tags.Minimum<0> & tags.ExclusiveMaximum<10>]: bigint & - tags.Type<"uint64">; - [key: string & tags.Format<"uuid">]: string & tags.Format<"email">; -} -export namespace DynamicTag { - export const BINARABLE = false; - export const JSONABLE = false; - export const PRIMITIVE = false; - export const ADDABLE = false; - - export function generate(): DynamicTag { - const dict: DynamicTag = {}; - ArrayUtil.repeat(10, (i) => { - dict[i] = TestRandomGenerator.bigint(0n, 1_000_000n); - dict[TestRandomGenerator.uuid()] = TestRandomGenerator.email(); - }); - return dict; - } - - export const SPOILERS: Spoiler[] = [ - (input) => { - input[0] = false as any; - return [`$input["0"]`]; - }, - (input) => { - input[9] = -1n; - return [`$input["9"]`]; - }, - (input) => { - const uuid: string = TestRandomGenerator.uuid(); - input[uuid] = "not-email-address"; - return [`$input["${uuid}"]`]; - }, - ]; -} diff --git a/debug/src/internal/ObjectHttpFormData.ts b/debug/src/internal/ObjectHttpFormData.ts deleted file mode 100644 index 98e927ff27..0000000000 --- a/debug/src/internal/ObjectHttpFormData.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { tags } from "typia"; -import { v4 } from "uuid"; - -import { Spoiler } from "./Spoiler"; -import { TestRandomGenerator } from "./TestRandomGenerator"; - -export interface ObjectHttpFormData { - id: string & tags.Format<"uuid">; - number: number; - integers: Array>; - blob: Blob; - blobs: Blob[]; - file: File; - files: File[]; -} -export namespace ObjectHttpFormData { - export const ADDABLE = false; - export const BINARABLE = false; - export const FORMDATA = true; - export const JSONABLE = false; - - export function generate(): ObjectHttpFormData { - return { - id: v4(), - number: TestRandomGenerator.number(), - integers: TestRandomGenerator.array(() => TestRandomGenerator.integer()), - blob: new Blob(), - blobs: TestRandomGenerator.array(() => new Blob()), - file: new File([], "file"), - files: TestRandomGenerator.array(() => new File([], "file")), - }; - } - - export const SPOILERS: Spoiler[] = [ - (input) => { - input.id = "something"; - return ["$input.id"]; - }, - (input) => { - input.number = "abcd" as any; - return ["$input.number"]; - }, - (input) => { - input.integers = [3, 3.14, 3.141592]; - return ["$input.integers[1]", "$input.integers[2]"]; - }, - (input) => { - input.blob = new Uint8Array() as any; - return ["$input.blob"]; - }, - (input) => { - input.blobs = ["string"] as any; - return ["$input.blobs[0]"]; - }, - (input) => { - input.file = new Uint8Array() as any; - return ["$input.file"]; - }, - (input) => { - input.files = [new Uint8Array()] as any; - return ["$input.files[0]"]; - }, - ]; -} diff --git a/debug/src/internal/Spoiler.ts b/debug/src/internal/Spoiler.ts deleted file mode 100644 index 447cd7cbb6..0000000000 --- a/debug/src/internal/Spoiler.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface Spoiler { - (input: T): string[]; -} diff --git a/debug/src/internal/TestRandomGenerator.ts b/debug/src/internal/TestRandomGenerator.ts deleted file mode 100644 index fb2a015cdf..0000000000 --- a/debug/src/internal/TestRandomGenerator.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { back_inserter, ranges } from "tstl"; -import { RandomGenerator } from "typia/lib/utils/RandomGenerator"; - -export const TestRandomGenerator = { - ...RandomGenerator, - array: (closure: (index: number) => T, count?: number): T[] => - new Array(count ?? RandomGenerator.integer(3, 10)) - .fill(0) - .map((_e, index) => closure(index)), - - sample: - (array: T[]) => - (count: number): T[] => { - const ret: T[] = []; - ranges.sample(array, back_inserter(ret), count); - return ret; - }, -}; diff --git a/debug/src/internal/create_form_data.ts b/debug/src/internal/create_form_data.ts deleted file mode 100644 index 6fe69f4e3f..0000000000 --- a/debug/src/internal/create_form_data.ts +++ /dev/null @@ -1,15 +0,0 @@ -export const create_form_data = (input: Record): FormData => { - const encoded: FormData = new FormData(); - for (const [key, value] of Object.entries(input)) - if (Array.isArray(value)) value.map(append(encoded)(key)); - else append(encoded)(key)(value); - return encoded; -}; - -const append = (data: FormData) => (key: string) => (value: any) => { - if (value === undefined) return; - else if (value instanceof Blob) - if (value instanceof File) data.append(key, value, value.name); - else data.append(key, value); - else data.append(key, String(value)); -}; diff --git a/debug/src/meta.ts b/debug/src/meta.ts deleted file mode 100644 index f23823a03f..0000000000 --- a/debug/src/meta.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { tags } from "typia"; - -export interface DynamicTag { - [key: string & tags.Format<"uuid">]: string & tags.Format<"email">; -} diff --git a/debug/src/prune.ts b/debug/src/prune.ts deleted file mode 100644 index 20e0ba1e32..0000000000 --- a/debug/src/prune.ts +++ /dev/null @@ -1,9 +0,0 @@ -import typia from "typia"; - -import { DynamicTag } from "./internal/DynamicTag"; - -const data = DynamicTag.generate(); -data["__non_regular_type__0"] = "vulnerable"; -typia.misc.prune(data); - -console.log(data); diff --git a/errors/package.json b/errors/package.json index fdb2d1ee01..9becbdb1a2 100644 --- a/errors/package.json +++ b/errors/package.json @@ -32,6 +32,6 @@ "typescript": "^5.3.2" }, "dependencies": { - "typia": "../typia-6.7.0-dev.20240803-2.tgz" + "typia": "../typia-6.7.0-dev.20240803-5.tgz" } } \ No newline at end of file diff --git a/package.json b/package.json index bfe00ed04a..92e54efd90 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "typia", - "version": "6.7.0-dev.20240803-2", + "version": "6.7.0-dev.20240803-5", "description": "Superfast runtime validators with only one line", "main": "lib/index.js", "typings": "lib/index.d.ts", diff --git a/packages/typescript-json/package.json b/packages/typescript-json/package.json index 3152ca3b57..b79dd5aa85 100644 --- a/packages/typescript-json/package.json +++ b/packages/typescript-json/package.json @@ -1,6 +1,6 @@ { "name": "typescript-json", - "version": "6.7.0-dev.20240803-2", + "version": "6.7.0-dev.20240803-5", "description": "Superfast runtime validators with only one line", "main": "lib/index.js", "typings": "lib/index.d.ts", @@ -63,7 +63,7 @@ }, "homepage": "https://typia.io", "dependencies": { - "typia": "6.7.0-dev.20240803-2" + "typia": "6.7.0-dev.20240803-5" }, "peerDependencies": { "typescript": ">=4.8.0 <5.6.0" diff --git a/src/programmers/AssertProgrammer.ts b/src/programmers/AssertProgrammer.ts index 0c32e87a87..ceca972a38 100644 --- a/src/programmers/AssertProgrammer.ts +++ b/src/programmers/AssertProgrammer.ts @@ -22,6 +22,7 @@ export namespace AssertProgrammer { type: ts.Type; name: string | undefined; init: ts.Expression | undefined; + inline?: boolean; }): FeatureProgrammer.IDecomposed => { const is: FeatureProgrammer.IDecomposed = IsProgrammer.decompose(props); const composed: FeatureProgrammer.IComposed = CheckerProgrammer.compose({ diff --git a/src/programmers/IsProgrammer.ts b/src/programmers/IsProgrammer.ts index 649e1d9663..c4a9debdc4 100644 --- a/src/programmers/IsProgrammer.ts +++ b/src/programmers/IsProgrammer.ts @@ -3,10 +3,8 @@ import ts from "typescript"; import { ExpressionFactory } from "../factories/ExpressionFactory"; import { IdentifierFactory } from "../factories/IdentifierFactory"; import { MetadataCollection } from "../factories/MetadataCollection"; -// import { TypeFactory } from "../factories/TypeFactory"; import { ValueFactory } from "../factories/ValueFactory"; -// import { MetadataObject } from "../schemas/metadata/MetadataObject"; import { IProject } from "../transformers/IProject"; import { CheckerProgrammer } from "./CheckerProgrammer"; @@ -16,8 +14,6 @@ import { IExpressionEntry } from "./helpers/IExpressionEntry"; import { OptionPredicator } from "./helpers/OptionPredicator"; import { check_object } from "./internal/check_object"; -// import { feature_object_entries } from "./internal/feature_object_entries"; - export namespace IsProgrammer { export const configure = (options?: Partial) => @@ -122,43 +118,6 @@ export namespace IsProgrammer { })(props.project)(props.importer), trace: props.equals, }; - // config.decoder = () => (input, target, explore) => { - // if ( - // target.size() === 1 && - // target.objects.length === 1 && - // target.isRequired() === true && - // target.nullable === false - // ) { - // // ONLY WHEN OBJECT WITH SOME ATOMIC PROPERTIES - // const obj: MetadataObject = target.objects[0]!; - // if ( - // obj.isPlain(explore.from === "top" ? 0 : 1) && - // (props.equals === false || - // OptionPredicator.undefined(props.project.options) === false) - // ) - // return ts.factory.createLogicalAnd( - // ExpressionFactory.isObject({ - // checkNull: true, - // checkArray: false, - // })(input), - // config.joiner.object( - // ts.factory.createAsExpression(input, TypeFactory.keyword("any")), - // feature_object_entries(config as any)(props.importer)(obj)( - // ts.factory.createAsExpression( - // input, - // TypeFactory.keyword("any"), - // ), - // "top", - // ), - // ), - // ); - // } - // return CheckerProgrammer.decode(props.project)(config)(props.importer)( - // input, - // target, - // explore, - // ); - // }; // COMPOSITION const composed: FeatureProgrammer.IComposed = CheckerProgrammer.compose({ diff --git a/src/programmers/functional/FunctionalAssertFunctionProgrammer.ts b/src/programmers/functional/FunctionalAssertFunctionProgrammer.ts index 5dba56c1f7..fbf60276dc 100644 --- a/src/programmers/functional/FunctionalAssertFunctionProgrammer.ts +++ b/src/programmers/functional/FunctionalAssertFunctionProgrammer.ts @@ -1,5 +1,6 @@ import ts from "typescript"; +import { ExpressionFactory } from "../../factories/ExpressionFactory"; import { IdentifierFactory } from "../../factories/IdentifierFactory"; import { TypeFactory } from "../../factories/TypeFactory"; @@ -20,28 +21,38 @@ export namespace FunctionalAssertFunctionProgrammer { expression: ts.Expression, declaration: ts.FunctionDeclaration, init?: ts.Expression, - ): ts.ArrowFunction => { + ): ts.CallExpression => { const wrapper = errorFactoryWrapper(modulo)(declaration.parameters)(init); - const { async, returns } = FunctionAssertReturnProgrammer.returnStatement( - project, - )(modulo)(equals)(expression, declaration, wrapper.name); - return ts.factory.createArrowFunction( - async - ? [ts.factory.createModifier(ts.SyntaxKind.AsyncKeyword)] - : undefined, - undefined, - declaration.parameters, - declaration.type, - undefined, - ts.factory.createBlock([ - wrapper.variable, - ...FunctionalAssertParametersProgrammer.argumentExpressions(project)( - modulo, - )(equals)(declaration.parameters, wrapper.name).map( - ts.factory.createExpressionStatement, - ), - returns, - ]), + const p = FunctionalAssertParametersProgrammer.decompose(project)(modulo)( + equals, + )(declaration.parameters, wrapper.name); + const r = FunctionAssertReturnProgrammer.decompose(project)(modulo)( + equals, + )(expression, declaration, wrapper.name); + return ExpressionFactory.selfCall( + ts.factory.createBlock( + [ + wrapper.variable, + ...p.functions, + ...r.functions, + ts.factory.createReturnStatement( + ts.factory.createArrowFunction( + r.async + ? [ts.factory.createModifier(ts.SyntaxKind.AsyncKeyword)] + : undefined, + undefined, + declaration.parameters, + declaration.type, + undefined, + ts.factory.createBlock([ + ...p.expressions.map(ts.factory.createExpressionStatement), + ts.factory.createReturnStatement(r.value), + ]), + ), + ), + ], + true, + ), ); }; diff --git a/src/programmers/functional/FunctionalAssertParametersProgrammer.ts b/src/programmers/functional/FunctionalAssertParametersProgrammer.ts index 03e0e32b11..dd6e454935 100644 --- a/src/programmers/functional/FunctionalAssertParametersProgrammer.ts +++ b/src/programmers/functional/FunctionalAssertParametersProgrammer.ts @@ -1,5 +1,7 @@ import ts from "typescript"; +import { ExpressionFactory } from "../../factories/ExpressionFactory"; +import { StatementFactory } from "../../factories/StatementFactory"; import { TypeFactory } from "../../factories/TypeFactory"; import { IProject } from "../../transformers/IProject"; @@ -17,34 +19,47 @@ export namespace FunctionalAssertParametersProgrammer { expression: ts.Expression, declaration: ts.FunctionDeclaration, init?: ts.Expression, - ): ts.ArrowFunction => { + ): ts.CallExpression => { const wrapper = FunctionalAssertFunctionProgrammer.errorFactoryWrapper( modulo, )(declaration.parameters)(init); const { async } = FunctionalGeneralProgrammer.getReturnType( project.checker, )(declaration); - return ts.factory.createArrowFunction( - async - ? [ts.factory.createModifier(ts.SyntaxKind.AsyncKeyword)] - : undefined, - undefined, + const result = decompose(project)(modulo)(equals)( declaration.parameters, - declaration.type, - undefined, + wrapper.name, + ); + return ExpressionFactory.selfCall( ts.factory.createBlock( [ wrapper.variable, - ...argumentExpressions(project)(modulo)(equals)( - declaration.parameters, - wrapper.name, - ).map(ts.factory.createExpressionStatement), + ...result.functions, ts.factory.createReturnStatement( - ts.factory.createCallExpression( - expression, + ts.factory.createArrowFunction( + async + ? [ts.factory.createModifier(ts.SyntaxKind.AsyncKeyword)] + : undefined, + undefined, + declaration.parameters, + declaration.type, undefined, - declaration.parameters.map((p) => - ts.factory.createIdentifier(p.name.getText()), + ts.factory.createBlock( + [ + ...result.expressions.map( + ts.factory.createExpressionStatement, + ), + ts.factory.createReturnStatement( + ts.factory.createCallExpression( + expression, + undefined, + declaration.parameters.map((p) => + ts.factory.createIdentifier(p.name.getText()), + ), + ), + ), + ], + true, ), ), ), @@ -54,16 +69,20 @@ export namespace FunctionalAssertParametersProgrammer { ); }; - export const argumentExpressions = + export const decompose = (project: IProject) => (modulo: ts.LeftHandSideExpression) => (equals: boolean) => ( parameters: readonly ts.ParameterDeclaration[], wrapper: string, - ): ts.CallExpression[] => - parameters.map((p, i) => - ts.factory.createCallExpression( + ): { + functions: ts.Statement[]; + expressions: ts.Expression[]; + } => ({ + functions: parameters.map((p, i) => + StatementFactory.constant( + `__assert_param_${i}`, AssertProgrammer.write(project)(modulo)(equals)( p.type ? project.checker.getTypeFromTypeNode(p.type) @@ -74,8 +93,16 @@ export namespace FunctionalAssertParametersProgrammer { replacer: `$input.parameters[${i}]`, }), ), + // undefined, + // [ts.factory.createIdentifier(p.name.getText())], + ), + ), + expressions: parameters.map((p, i) => + ts.factory.createCallExpression( + ts.factory.createIdentifier(`__assert_param_${i}`), undefined, [ts.factory.createIdentifier(p.name.getText())], ), - ); + ), + }); } diff --git a/src/programmers/functional/FunctionalAssertReturnProgrammer.ts b/src/programmers/functional/FunctionalAssertReturnProgrammer.ts index 6555fa50d9..4d6d5fd958 100644 --- a/src/programmers/functional/FunctionalAssertReturnProgrammer.ts +++ b/src/programmers/functional/FunctionalAssertReturnProgrammer.ts @@ -1,5 +1,8 @@ import ts from "typescript"; +import { ExpressionFactory } from "../../factories/ExpressionFactory"; +import { StatementFactory } from "../../factories/StatementFactory"; + import { IProject } from "../../transformers/IProject"; import { AssertProgrammer } from "../AssertProgrammer"; @@ -15,26 +18,39 @@ export namespace FunctionAssertReturnProgrammer { expression: ts.Expression, declaration: ts.FunctionDeclaration, init?: ts.Expression, - ): ts.ArrowFunction => { + ): ts.CallExpression => { const wrapper = FunctionalAssertFunctionProgrammer.errorFactoryWrapper( modulo, )(declaration.parameters)(init); - const { async, returns: statement } = returnStatement(project)(modulo)( - equals, - )(expression, declaration, wrapper.name); - return ts.factory.createArrowFunction( - async - ? [ts.factory.createModifier(ts.SyntaxKind.AsyncKeyword)] - : undefined, - undefined, - declaration.parameters, - declaration.type, - undefined, - ts.factory.createBlock([wrapper.variable, statement], true), + const result = decompose(project)(modulo)(equals)( + expression, + declaration, + wrapper.name, + ); + return ExpressionFactory.selfCall( + ts.factory.createBlock( + [ + wrapper.variable, + ...result.functions, + ts.factory.createReturnStatement( + ts.factory.createArrowFunction( + result.async + ? [ts.factory.createModifier(ts.SyntaxKind.AsyncKeyword)] + : undefined, + undefined, + declaration.parameters, + declaration.type, + undefined, + result.value, + ), + ), + ], + true, + ), ); }; - export const returnStatement = + export const decompose = (project: IProject) => (modulo: ts.LeftHandSideExpression) => (equals: boolean) => @@ -44,7 +60,8 @@ export namespace FunctionAssertReturnProgrammer { wrapper: string, ): { async: boolean; - returns: ts.ReturnStatement; + functions: ts.Statement[]; + value: ts.Expression; } => { const { type, async } = FunctionalGeneralProgrammer.getReturnType( project.checker, @@ -58,8 +75,9 @@ export namespace FunctionAssertReturnProgrammer { ); return { async, - returns: ts.factory.createReturnStatement( - ts.factory.createCallExpression( + functions: [ + StatementFactory.constant( + "__assert_return", AssertProgrammer.write(project)(modulo)(equals)( type, undefined, @@ -68,9 +86,12 @@ export namespace FunctionAssertReturnProgrammer { replacer: "$input.return", }), ), - undefined, - [async ? ts.factory.createAwaitExpression(caller) : caller], ), + ], + value: ts.factory.createCallExpression( + ts.factory.createIdentifier("__assert_return"), + undefined, + [async ? ts.factory.createAwaitExpression(caller) : caller], ), }; }; diff --git a/src/programmers/functional/FunctionalIsFunctionProgrammer.ts b/src/programmers/functional/FunctionalIsFunctionProgrammer.ts index b004249e6d..66aef151f4 100644 --- a/src/programmers/functional/FunctionalIsFunctionProgrammer.ts +++ b/src/programmers/functional/FunctionalIsFunctionProgrammer.ts @@ -1,5 +1,7 @@ import ts from "typescript"; +import { ExpressionFactory } from "../../factories/ExpressionFactory"; + import { IProject } from "../../transformers/IProject"; import { FunctionalIsParametersProgrammer } from "./FunctionalIsParametersProgrammer"; @@ -13,26 +15,35 @@ export namespace FunctionalIsFunctionProgrammer { ( expression: ts.Expression, declaration: ts.FunctionDeclaration, - ): ts.ArrowFunction => { - const { async, statements } = - FunctionalIsReturnProgrammer.writeStatements(project)(modulo)(equals)( - expression, + ): ts.CallExpression => { + const p = + FunctionalIsParametersProgrammer.decompose(project)(modulo)(equals)( declaration, ); - return ts.factory.createArrowFunction( - async - ? [ts.factory.createModifier(ts.SyntaxKind.AsyncKeyword)] - : undefined, - undefined, - declaration.parameters, - getReturnTypeNode(declaration, async), - undefined, + const r = FunctionalIsReturnProgrammer.decompose(project)(modulo)(equals)( + expression, + declaration, + ); + return ExpressionFactory.selfCall( ts.factory.createBlock( [ - ...FunctionalIsParametersProgrammer.writeStatements(project)( - modulo, - )(equals)(declaration), - ...statements, + ...p.functions, + ...r.functions, + ts.factory.createReturnStatement( + ts.factory.createArrowFunction( + r.async + ? [ts.factory.createModifier(ts.SyntaxKind.AsyncKeyword)] + : undefined, + undefined, + declaration.parameters, + getReturnTypeNode(declaration, r.async), + undefined, + ts.factory.createBlock( + [...p.statements, ...r.statements], + true, + ), + ), + ), ], true, ), diff --git a/src/programmers/functional/FunctionalIsParametersProgrammer.ts b/src/programmers/functional/FunctionalIsParametersProgrammer.ts index 650828364b..2ed0650166 100644 --- a/src/programmers/functional/FunctionalIsParametersProgrammer.ts +++ b/src/programmers/functional/FunctionalIsParametersProgrammer.ts @@ -1,5 +1,7 @@ import ts from "typescript"; +import { ExpressionFactory } from "../../factories/ExpressionFactory"; +import { StatementFactory } from "../../factories/StatementFactory"; import { TypeFactory } from "../../factories/TypeFactory"; import { IProject } from "../../transformers/IProject"; @@ -16,27 +18,41 @@ export namespace FunctionalIsParametersProgrammer { ( expression: ts.Expression, declaration: ts.FunctionDeclaration, - ): ts.ArrowFunction => { + ): ts.CallExpression => { const { async } = FunctionalGeneralProgrammer.getReturnType( project.checker, )(declaration); - return ts.factory.createArrowFunction( - async - ? [ts.factory.createModifier(ts.SyntaxKind.AsyncKeyword)] - : undefined, - undefined, - declaration.parameters, - FunctionalIsFunctionProgrammer.getReturnTypeNode(declaration, async), - undefined, + const result = decompose(project)(modulo)(equals)(declaration); + return ExpressionFactory.selfCall( ts.factory.createBlock( [ - ...writeStatements(project)(modulo)(equals)(declaration), + ...result.functions, ts.factory.createReturnStatement( - ts.factory.createCallExpression( - expression, + ts.factory.createArrowFunction( + async + ? [ts.factory.createModifier(ts.SyntaxKind.AsyncKeyword)] + : undefined, + undefined, + declaration.parameters, + FunctionalIsFunctionProgrammer.getReturnTypeNode( + declaration, + async, + ), undefined, - declaration.parameters.map((p) => - ts.factory.createIdentifier(p.name.getText()), + ts.factory.createBlock( + [ + ...result.statements, + ts.factory.createReturnStatement( + ts.factory.createCallExpression( + expression, + undefined, + declaration.parameters.map((p) => + ts.factory.createIdentifier(p.name.getText()), + ), + ), + ), + ], + true, ), ), ), @@ -46,22 +62,33 @@ export namespace FunctionalIsParametersProgrammer { ); }; - export const writeStatements = + export const decompose = (project: IProject) => (modulo: ts.LeftHandSideExpression) => (equals: boolean) => - (declaration: ts.FunctionDeclaration): ts.Statement[] => - declaration.parameters - .map((p) => [ + ( + declaration: ts.FunctionDeclaration, + ): { + functions: ts.Statement[]; + statements: ts.Statement[]; + } => ({ + functions: declaration.parameters.map((p, i) => + StatementFactory.constant( + `__is_param_${i}`, + IsProgrammer.write(project)(modulo)(equals)( + project.checker.getTypeFromTypeNode( + p.type ?? TypeFactory.keyword("any"), + ), + ), + ), + ), + statements: declaration.parameters + .map((p, i) => [ ts.factory.createIfStatement( ts.factory.createStrictEquality( ts.factory.createFalse(), ts.factory.createCallExpression( - IsProgrammer.write(project)(modulo)(equals)( - project.checker.getTypeFromTypeNode( - p.type ?? TypeFactory.keyword("any"), - ), - ), + ts.factory.createIdentifier(`__is_param_${i}`), undefined, [ts.factory.createIdentifier(p.name.getText())], ), @@ -69,5 +96,6 @@ export namespace FunctionalIsParametersProgrammer { ts.factory.createReturnStatement(ts.factory.createNull()), ), ]) - .flat(); + .flat(), + }); } diff --git a/src/programmers/functional/FunctionalIsReturnProgrammer.ts b/src/programmers/functional/FunctionalIsReturnProgrammer.ts index 1e5a5fcda2..d68f8737cb 100644 --- a/src/programmers/functional/FunctionalIsReturnProgrammer.ts +++ b/src/programmers/functional/FunctionalIsReturnProgrammer.ts @@ -1,5 +1,6 @@ import ts from "typescript"; +import { ExpressionFactory } from "../../factories/ExpressionFactory"; import { StatementFactory } from "../../factories/StatementFactory"; import { IProject } from "../../transformers/IProject"; @@ -18,24 +19,37 @@ export namespace FunctionalIsReturnProgrammer { ( expression: ts.Expression, declaration: ts.FunctionDeclaration, - ): ts.ArrowFunction => { - const { async, statements } = writeStatements(project)(modulo)(equals)( + ): ts.CallExpression => { + const result = decompose(project)(modulo)(equals)( expression, declaration, ); - return ts.factory.createArrowFunction( - async - ? [ts.factory.createModifier(ts.SyntaxKind.AsyncKeyword)] - : undefined, - undefined, - declaration.parameters, - FunctionalIsFunctionProgrammer.getReturnTypeNode(declaration, async), - undefined, - ts.factory.createBlock(statements, true), + return ExpressionFactory.selfCall( + ts.factory.createBlock( + [ + ...result.functions, + ts.factory.createReturnStatement( + ts.factory.createArrowFunction( + result.async + ? [ts.factory.createModifier(ts.SyntaxKind.AsyncKeyword)] + : undefined, + undefined, + declaration.parameters, + FunctionalIsFunctionProgrammer.getReturnTypeNode( + declaration, + result.async, + ), + undefined, + ts.factory.createBlock(result.statements, true), + ), + ), + ], + true, + ), ); }; - export const writeStatements = + export const decompose = (project: IProject) => (modulo: ts.LeftHandSideExpression) => (equals: boolean) => @@ -44,6 +58,7 @@ export namespace FunctionalIsReturnProgrammer { declaration: ts.FunctionDeclaration, ): { async: boolean; + functions: ts.Statement[]; statements: ts.Statement[]; } => { const { type, async } = FunctionalGeneralProgrammer.getReturnType( @@ -56,29 +71,36 @@ export namespace FunctionalIsReturnProgrammer { ts.factory.createIdentifier(p.name.getText()), ), ); - const name: string = StringUtil.escapeDuplicate( declaration.parameters.map((p) => p.name.getText()), )("result"); - const statements: ts.Statement[] = [ - StatementFactory.constant( - name, - async ? ts.factory.createAwaitExpression(caller) : caller, - ), - ts.factory.createReturnStatement( - ts.factory.createConditionalExpression( - ts.factory.createCallExpression( - IsProgrammer.write(project)(modulo)(equals)(type), + return { + async, + functions: [ + StatementFactory.constant( + "__is_return", + IsProgrammer.write(project)(modulo)(equals)(type), + ), + ], + statements: [ + StatementFactory.constant( + name, + async ? ts.factory.createAwaitExpression(caller) : caller, + ), + ts.factory.createReturnStatement( + ts.factory.createConditionalExpression( + ts.factory.createCallExpression( + ts.factory.createIdentifier("__is_return"), + undefined, + [ts.factory.createIdentifier(name)], + ), undefined, - [ts.factory.createIdentifier(name)], + ts.factory.createIdentifier(name), + undefined, + ts.factory.createNull(), ), - undefined, - ts.factory.createIdentifier(name), - undefined, - ts.factory.createNull(), ), - ), - ]; - return { async, statements }; + ], + }; }; } diff --git a/src/programmers/functional/FunctionalValidateFunctionProgrammer.ts b/src/programmers/functional/FunctionalValidateFunctionProgrammer.ts index 68fcc0ad6e..cbf4a38ed0 100644 --- a/src/programmers/functional/FunctionalValidateFunctionProgrammer.ts +++ b/src/programmers/functional/FunctionalValidateFunctionProgrammer.ts @@ -1,5 +1,6 @@ import ts from "typescript"; +import { ExpressionFactory } from "../../factories/ExpressionFactory"; import { IdentifierFactory } from "../../factories/IdentifierFactory"; import { IProject } from "../../transformers/IProject"; @@ -15,25 +16,34 @@ export namespace FunctionalValidateFunctionProgrammer { ( expression: ts.Expression, declaration: ts.FunctionDeclaration, - ): ts.ArrowFunction => { - const { async, statements } = - FunctionalValidateReturnProgrammer.writeStatements(project)(modulo)( + ): ts.CallExpression => { + const p = + FunctionalValidateParametersProgrammer.decompose(project)(modulo)( equals, - )(expression, declaration); - return ts.factory.createArrowFunction( - async - ? [ts.factory.createModifier(ts.SyntaxKind.AsyncKeyword)] - : undefined, - undefined, - declaration.parameters, - getReturnTypeNode(declaration, async), - undefined, + )(declaration); + const r = FunctionalValidateReturnProgrammer.decompose(project)(modulo)( + equals, + )(expression, declaration); + return ExpressionFactory.selfCall( ts.factory.createBlock( [ - ...FunctionalValidateParametersProgrammer.writeStatements(project)( - modulo, - )(equals)(declaration), - ...statements, + ...p.functions, + ...r.functions, + ts.factory.createReturnStatement( + ts.factory.createArrowFunction( + r.async + ? [ts.factory.createModifier(ts.SyntaxKind.AsyncKeyword)] + : undefined, + undefined, + declaration.parameters, + getReturnTypeNode(declaration, r.async), + undefined, + ts.factory.createBlock( + [...p.statements, ...r.statements], + true, + ), + ), + ), ], true, ), diff --git a/src/programmers/functional/FunctionalValidateParametersProgrammer.ts b/src/programmers/functional/FunctionalValidateParametersProgrammer.ts index 0ee08904bb..316b9dbeab 100644 --- a/src/programmers/functional/FunctionalValidateParametersProgrammer.ts +++ b/src/programmers/functional/FunctionalValidateParametersProgrammer.ts @@ -1,5 +1,6 @@ import ts from "typescript"; +import { ExpressionFactory } from "../../factories/ExpressionFactory"; import { IdentifierFactory } from "../../factories/IdentifierFactory"; import { StatementFactory } from "../../factories/StatementFactory"; import { TypeFactory } from "../../factories/TypeFactory"; @@ -20,10 +21,11 @@ export namespace FunctionalValidateParametersProgrammer { ( expression: ts.Expression, declaration: ts.FunctionDeclaration, - ): ts.ArrowFunction => { + ): ts.CallExpression => { const { async } = FunctionalGeneralProgrammer.getReturnType( project.checker, )(declaration); + const result = decompose(project)(modulo)(equals)(declaration); const caller: ts.CallExpression = ts.factory.createCallExpression( expression, undefined, @@ -31,37 +33,49 @@ export namespace FunctionalValidateParametersProgrammer { ts.factory.createIdentifier(p.name.getText()), ), ); - return ts.factory.createArrowFunction( - async - ? [ts.factory.createModifier(ts.SyntaxKind.AsyncKeyword)] - : undefined, - undefined, - declaration.parameters, - FunctionalValidateFunctionProgrammer.getReturnTypeNode( - declaration, - async, - ), - undefined, + return ExpressionFactory.selfCall( ts.factory.createBlock( [ - ...writeStatements(project)(modulo)(equals)(declaration), + ...result.functions, ts.factory.createReturnStatement( - ts.factory.createObjectLiteralExpression( - [ - ts.factory.createPropertyAssignment( - "success", - ts.factory.createTrue(), - ), - ts.factory.createPropertyAssignment( - "data", - async ? ts.factory.createAwaitExpression(caller) : caller, - ), - ts.factory.createPropertyAssignment( - "errors", - ts.factory.createArrayLiteralExpression([]), - ), - ], - true, + ts.factory.createArrowFunction( + async + ? [ts.factory.createModifier(ts.SyntaxKind.AsyncKeyword)] + : undefined, + undefined, + declaration.parameters, + FunctionalValidateFunctionProgrammer.getReturnTypeNode( + declaration, + async, + ), + undefined, + ts.factory.createBlock( + [ + ...result.statements, + ts.factory.createReturnStatement( + ts.factory.createObjectLiteralExpression( + [ + ts.factory.createPropertyAssignment( + "success", + ts.factory.createTrue(), + ), + ts.factory.createPropertyAssignment( + "data", + async + ? ts.factory.createAwaitExpression(caller) + : caller, + ), + ts.factory.createPropertyAssignment( + "errors", + ts.factory.createArrayLiteralExpression([]), + ), + ], + true, + ), + ), + ], + true, + ), ), ), ], @@ -70,24 +84,25 @@ export namespace FunctionalValidateParametersProgrammer { ); }; - export const writeStatements = + export const decompose = (project: IProject) => (modulo: ts.LeftHandSideExpression) => (equals: boolean) => - (declaration: ts.FunctionDeclaration): ts.Statement[] => { + ( + declaration: ts.FunctionDeclaration, + ): { + functions: ts.Statement[]; + statements: ts.Statement[]; + } => { const resultName: string = StringUtil.escapeDuplicate( declaration.parameters.map((p) => p.name.getText()), - )("paramResults"); + )("paramErrorResults"); const validationResultArray: ts.ArrayLiteralExpression = ts.factory.createArrayLiteralExpression( - declaration.parameters.map((p) => + declaration.parameters.map((p, i) => ts.factory.createAsExpression( ts.factory.createCallExpression( - ValidateProgrammer.write(project)(modulo)(equals)( - project.checker.getTypeFromTypeNode( - p.type ?? TypeFactory.keyword("any"), - ), - ), + ts.factory.createIdentifier(`__validate_param_${i}`), undefined, [ts.factory.createIdentifier(p.name.getText())], ), @@ -107,97 +122,146 @@ export namespace FunctionalValidateParametersProgrammer { ), true, ); - const failures = ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression( - validationResultArray, - "filter", - ), + const errorMatrix = ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression(validationResultArray, "map"), undefined, [ ts.factory.createArrowFunction( undefined, undefined, - [IdentifierFactory.parameter("r")], + [ + IdentifierFactory.parameter("r"), + IdentifierFactory.parameter("i"), + ], undefined, undefined, - ts.factory.createStrictEquality( - ts.factory.createFalse(), - ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier("r"), - "success", + ts.factory.createConditionalExpression( + ts.factory.createStrictEquality( + ts.factory.createTrue(), + ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier("r"), + "success", + ), + ), + undefined, + ts.factory.createIdentifier("r"), + undefined, + ts.factory.createObjectLiteralExpression( + [ + ts.factory.createSpreadAssignment( + ts.factory.createIdentifier("r"), + ), + ts.factory.createPropertyAssignment( + "errors", + FunctionalValidateFunctionProgrammer.hookErrors({ + expression: ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier("r"), + "errors", + ), + replacer: ts.factory.createTemplateExpression( + ts.factory.createTemplateHead("$input.parameters["), + [ + ts.factory.createTemplateSpan( + ts.factory.createIdentifier("i"), + ts.factory.createTemplateTail("]"), + ), + ], + ), + }), + ), + ], + true, ), ), ), ], ); - const errorMatrix = ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier(resultName), - "map", - ), + const failures = ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression(errorMatrix, "filter"), undefined, [ ts.factory.createArrowFunction( undefined, undefined, - [ - IdentifierFactory.parameter("r"), - IdentifierFactory.parameter("i"), - ], + [IdentifierFactory.parameter("r")], undefined, undefined, - FunctionalValidateFunctionProgrammer.hookErrors({ - expression: ts.factory.createPropertyAccessExpression( + ts.factory.createStrictEquality( + ts.factory.createFalse(), + ts.factory.createPropertyAccessExpression( ts.factory.createIdentifier("r"), - "errors", - ), - replacer: ts.factory.createTemplateExpression( - ts.factory.createTemplateHead("$input.parameters["), - [ - ts.factory.createTemplateSpan( - ts.factory.createIdentifier("i"), - ts.factory.createTemplateTail("]"), - ), - ], + "success", ), - }), + ), ), ], ); - return [ - StatementFactory.constant(resultName, failures), - ts.factory.createIfStatement( - ts.factory.createBinaryExpression( - ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier(resultName), - "length", + return { + functions: declaration.parameters.map((p, i) => + StatementFactory.constant( + `__validate_param_${i}`, + ValidateProgrammer.write(project)(modulo)(equals)( + project.checker.getTypeFromTypeNode( + p.type ?? TypeFactory.keyword("any"), + ), ), - ts.SyntaxKind.GreaterThanToken, - ts.factory.createNumericLiteral("0"), ), - ts.factory.createReturnStatement( - ts.factory.createObjectLiteralExpression( - [ - ts.factory.createPropertyAssignment( - "success", - ts.factory.createFalse(), - ), - ts.factory.createPropertyAssignment( - "errors", - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression( - errorMatrix, - "flat", + ), + statements: [ + StatementFactory.constant(resultName, failures), + ts.factory.createIfStatement( + ts.factory.createStrictInequality( + ts.factory.createNumericLiteral("0"), + ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier(resultName), + "length", + ), + ), + ts.factory.createReturnStatement( + ts.factory.createObjectLiteralExpression( + [ + ts.factory.createPropertyAssignment( + "success", + ts.factory.createFalse(), + ), + ts.factory.createPropertyAssignment( + "errors", + ts.factory.createCallExpression( + IdentifierFactory.access( + ts.factory.createCallExpression( + IdentifierFactory.access( + ts.factory.createIdentifier(resultName), + )("map"), + undefined, + [ + ts.factory.createArrowFunction( + undefined, + undefined, + [ + IdentifierFactory.parameter( + "r", + TypeFactory.keyword("any"), + ), + ], + undefined, + undefined, + IdentifierFactory.access( + ts.factory.createIdentifier("r"), + )("errors"), + ), + ], + ), + )("flat"), + undefined, + undefined, ), - undefined, - undefined, ), - ), - ], - true, + ], + true, + ), ), ), - ), - ]; + ], + }; }; } diff --git a/src/programmers/functional/FunctionalValidateReturnProgrammer.ts b/src/programmers/functional/FunctionalValidateReturnProgrammer.ts index 479c8035a7..6f33d8d453 100644 --- a/src/programmers/functional/FunctionalValidateReturnProgrammer.ts +++ b/src/programmers/functional/FunctionalValidateReturnProgrammer.ts @@ -1,5 +1,6 @@ import ts from "typescript"; +import { ExpressionFactory } from "../../factories/ExpressionFactory"; import { StatementFactory } from "../../factories/StatementFactory"; import { IProject } from "../../transformers/IProject"; @@ -18,27 +19,37 @@ export namespace FunctionalValidateReturnProgrammer { ( expression: ts.Expression, declaration: ts.FunctionDeclaration, - ): ts.ArrowFunction => { - const { async, statements } = writeStatements(project)(modulo)(equals)( + ): ts.CallExpression => { + const result = decompose(project)(modulo)(equals)( expression, declaration, ); - return ts.factory.createArrowFunction( - async - ? [ts.factory.createModifier(ts.SyntaxKind.AsyncKeyword)] - : undefined, - undefined, - declaration.parameters, - FunctionalValidateFunctionProgrammer.getReturnTypeNode( - declaration, - async, + return ExpressionFactory.selfCall( + ts.factory.createBlock( + [ + ...result.functions, + ts.factory.createReturnStatement( + ts.factory.createArrowFunction( + result.async + ? [ts.factory.createModifier(ts.SyntaxKind.AsyncKeyword)] + : undefined, + undefined, + declaration.parameters, + FunctionalValidateFunctionProgrammer.getReturnTypeNode( + declaration, + result.async, + ), + undefined, + ts.factory.createBlock(result.statements, true), + ), + ), + ], + true, ), - undefined, - ts.factory.createBlock(statements, true), ); }; - export const writeStatements = + export const decompose = (project: IProject) => (modulo: ts.LeftHandSideExpression) => (equals: boolean) => @@ -47,6 +58,7 @@ export namespace FunctionalValidateReturnProgrammer { declaration: ts.FunctionDeclaration, ): { async: boolean; + functions: ts.Statement[]; statements: ts.Statement[]; } => { const { type, async } = FunctionalGeneralProgrammer.getReturnType( @@ -63,45 +75,52 @@ export namespace FunctionalValidateReturnProgrammer { const name: string = StringUtil.escapeDuplicate( declaration.parameters.map((p) => p.name.getText()), )("result"); - const statements: ts.Statement[] = [ - StatementFactory.constant( - name, - ts.factory.createCallExpression( + return { + async, + functions: [ + StatementFactory.constant( + "__validate_return", ValidateProgrammer.write(project)(modulo)(equals)(type), - undefined, - [async ? ts.factory.createAwaitExpression(caller) : caller], ), - ), - ts.factory.createIfStatement( - ts.factory.createPrefixUnaryExpression( - ts.SyntaxKind.ExclamationToken, - ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier(name), - ts.factory.createIdentifier("success"), + ], + statements: [ + StatementFactory.constant( + name, + ts.factory.createCallExpression( + ts.factory.createIdentifier("__validate_return"), + undefined, + [async ? ts.factory.createAwaitExpression(caller) : caller], ), ), - ts.factory.createExpressionStatement( - ts.factory.createBinaryExpression( + ts.factory.createIfStatement( + ts.factory.createStrictEquality( + ts.factory.createFalse(), ts.factory.createPropertyAccessExpression( ts.factory.createIdentifier(name), - ts.factory.createIdentifier("errors"), + ts.factory.createIdentifier("success"), ), - ts.factory.createToken(ts.SyntaxKind.EqualsToken), - FunctionalValidateFunctionProgrammer.hookErrors({ - expression: ts.factory.createPropertyAccessExpression( + ), + ts.factory.createExpressionStatement( + ts.factory.createBinaryExpression( + ts.factory.createPropertyAccessExpression( ts.factory.createIdentifier(name), ts.factory.createIdentifier("errors"), ), - replacer: ts.factory.createStringLiteral("$input.return"), - }), + ts.factory.createToken(ts.SyntaxKind.EqualsToken), + FunctionalValidateFunctionProgrammer.hookErrors({ + expression: ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier(name), + ts.factory.createIdentifier("errors"), + ), + replacer: ts.factory.createStringLiteral("$input.return"), + }), + ), ), ), - ), - ts.factory.createReturnStatement(ts.factory.createIdentifier("result")), - ]; - return { - async, - statements, + ts.factory.createReturnStatement( + ts.factory.createIdentifier("result"), + ), + ], }; }; } diff --git a/src/programmers/http/HttpAssertHeadersProgrammer.ts b/src/programmers/http/HttpAssertHeadersProgrammer.ts index 1ed196a45d..66f40f54a7 100644 --- a/src/programmers/http/HttpAssertHeadersProgrammer.ts +++ b/src/programmers/http/HttpAssertHeadersProgrammer.ts @@ -7,83 +7,85 @@ import { TypeFactory } from "../../factories/TypeFactory"; import { IProject } from "../../transformers/IProject"; import { AssertProgrammer } from "../AssertProgrammer"; +import { FeatureProgrammer } from "../FeatureProgrammer"; +import { FunctionImporter } from "../helpers/FunctionImporter"; import { HttpHeadersProgrammer } from "./HttpHeadersProgrammer"; export namespace HttpAssertHeadersProgrammer { - export const write = - (project: IProject) => - (modulo: ts.LeftHandSideExpression) => - (type: ts.Type, name?: string, init?: ts.Expression): ts.ArrowFunction => - ts.factory.createArrowFunction( + export const decompose = (props: { + project: IProject; + importer: FunctionImporter; + type: ts.Type; + name: string | undefined; + init: ts.Expression | undefined; + }): FeatureProgrammer.IDecomposed => { + const assert: FeatureProgrammer.IDecomposed = AssertProgrammer.decompose({ + ...props, + project: { + ...props.project, + options: { + ...props.project.options, + functional: false, + numeric: false, + }, + }, + equals: false, + guard: false, + }); + const decode: FeatureProgrammer.IDecomposed = + HttpHeadersProgrammer.decompose(props); + return { + functions: { + ...assert.functions, + ...decode.functions, + }, + statements: [ + ...assert.statements, + ...decode.statements, + StatementFactory.constant("__assert", assert.arrow), + StatementFactory.constant("__decode", decode.arrow), + ], + arrow: ts.factory.createArrowFunction( undefined, undefined, [ - IdentifierFactory.parameter( - "input", - ts.factory.createTypeReferenceNode( - HttpHeadersProgrammer.INPUT_TYPE, - ), - ), - AssertProgrammer.Guardian.parameter(init), + IdentifierFactory.parameter("input", TypeFactory.keyword("any")), + AssertProgrammer.Guardian.parameter(props.init), ], - ts.factory.createImportTypeNode( - ts.factory.createLiteralTypeNode( - ts.factory.createStringLiteral("typia"), - ), + decode.arrow.type, + undefined, + ts.factory.createCallExpression( + ts.factory.createIdentifier("__assert"), undefined, - ts.factory.createIdentifier("Resolved"), [ - ts.factory.createTypeReferenceNode( - name ?? TypeFactory.getFullName(project.checker)(type), - ), - ], - false, - ), - undefined, - ts.factory.createBlock([ - StatementFactory.constant( - "decode", - HttpHeadersProgrammer.write({ - ...project, - options: { - ...project.options, - functional: false, - numeric: false, - }, - })(modulo)(type, name), - ), - StatementFactory.constant( - "assert", - AssertProgrammer.write({ - ...project, - options: { - ...project.options, - functional: false, - numeric: false, - }, - })(modulo)(false)(type, name), - ), - StatementFactory.constant( - "output", ts.factory.createCallExpression( - ts.factory.createIdentifier("decode"), + ts.factory.createIdentifier("__decode"), undefined, [ts.factory.createIdentifier("input")], ), - ), - ts.factory.createReturnStatement( - ts.factory.createAsExpression( - ts.factory.createCallExpression( - ts.factory.createIdentifier("assert"), - undefined, - [ - ts.factory.createIdentifier("output"), - AssertProgrammer.Guardian.identifier(), - ], - ), - TypeFactory.keyword("any"), - ), - ), - ]), - ); + AssertProgrammer.Guardian.identifier(), + ], + ), + ), + }; + }; + + export const write = + (project: IProject) => + (modulo: ts.LeftHandSideExpression) => + (type: ts.Type, name?: string, init?: ts.Expression): ts.CallExpression => { + const importer: FunctionImporter = new FunctionImporter(modulo.getText()); + const result: FeatureProgrammer.IDecomposed = decompose({ + project, + importer, + type, + name, + init, + }); + return FeatureProgrammer.writeDecomposed({ + modulo, + importer, + result, + }); + }; } diff --git a/src/programmers/http/HttpAssertQueryProgrammer.ts b/src/programmers/http/HttpAssertQueryProgrammer.ts index 60a8192fb6..8b3ee25e4c 100644 --- a/src/programmers/http/HttpAssertQueryProgrammer.ts +++ b/src/programmers/http/HttpAssertQueryProgrammer.ts @@ -7,81 +7,87 @@ import { TypeFactory } from "../../factories/TypeFactory"; import { IProject } from "../../transformers/IProject"; import { AssertProgrammer } from "../AssertProgrammer"; +import { FeatureProgrammer } from "../FeatureProgrammer"; +import { FunctionImporter } from "../helpers/FunctionImporter"; import { HttpQueryProgrammer } from "./HttpQueryProgrammer"; export namespace HttpAssertQueryProgrammer { - export const write = - (project: IProject) => - (modulo: ts.LeftHandSideExpression, allowOptional: boolean = false) => - (type: ts.Type, name?: string, init?: ts.Expression): ts.ArrowFunction => - ts.factory.createArrowFunction( + export const decompose = (props: { + project: IProject; + importer: FunctionImporter; + type: ts.Type; + name: string | undefined; + init: ts.Expression | undefined; + allowOptional: boolean; + }): FeatureProgrammer.IDecomposed => { + const assert: FeatureProgrammer.IDecomposed = AssertProgrammer.decompose({ + ...props, + project: { + ...props.project, + options: { + ...props.project.options, + functional: false, + numeric: false, + }, + }, + equals: false, + guard: false, + }); + const decode: FeatureProgrammer.IDecomposed = + HttpQueryProgrammer.decompose(props); + return { + functions: { + ...assert.functions, + ...decode.functions, + }, + statements: [ + ...assert.statements, + ...decode.statements, + StatementFactory.constant("__assert", assert.arrow), + StatementFactory.constant("__decode", decode.arrow), + ], + arrow: ts.factory.createArrowFunction( undefined, undefined, [ - IdentifierFactory.parameter( - "input", - ts.factory.createTypeReferenceNode(HttpQueryProgrammer.INPUT_TYPE), - ), - AssertProgrammer.Guardian.parameter(init), + IdentifierFactory.parameter("input", TypeFactory.keyword("any")), + AssertProgrammer.Guardian.parameter(props.init), ], - ts.factory.createImportTypeNode( - ts.factory.createLiteralTypeNode( - ts.factory.createStringLiteral("typia"), - ), + decode.arrow.type, + undefined, + ts.factory.createCallExpression( + ts.factory.createIdentifier("__assert"), undefined, - ts.factory.createIdentifier("Resolved"), [ - ts.factory.createTypeReferenceNode( - name ?? TypeFactory.getFullName(project.checker)(type), - ), - ], - false, - ), - undefined, - ts.factory.createBlock([ - StatementFactory.constant( - "decode", - HttpQueryProgrammer.write({ - ...project, - options: { - ...project.options, - functional: false, - numeric: false, - }, - })(modulo, allowOptional)(type, name), - ), - StatementFactory.constant( - "assert", - AssertProgrammer.write({ - ...project, - options: { - ...project.options, - functional: false, - numeric: false, - }, - })(modulo)(false)(type, name), - ), - StatementFactory.constant( - "output", ts.factory.createCallExpression( - ts.factory.createIdentifier("decode"), + ts.factory.createIdentifier("__decode"), undefined, [ts.factory.createIdentifier("input")], ), - ), - ts.factory.createReturnStatement( - ts.factory.createAsExpression( - ts.factory.createCallExpression( - ts.factory.createIdentifier("assert"), - undefined, - [ - ts.factory.createIdentifier("output"), - AssertProgrammer.Guardian.identifier(), - ], - ), - TypeFactory.keyword("any"), - ), - ), - ]), - ); + AssertProgrammer.Guardian.identifier(), + ], + ), + ), + }; + }; + + export const write = + (project: IProject) => + (modulo: ts.LeftHandSideExpression, allowOptional: boolean = false) => + (type: ts.Type, name?: string, init?: ts.Expression): ts.CallExpression => { + const importer: FunctionImporter = new FunctionImporter(modulo.getText()); + const result: FeatureProgrammer.IDecomposed = decompose({ + project, + importer, + type, + name, + init, + allowOptional, + }); + return FeatureProgrammer.writeDecomposed({ + modulo, + importer, + result, + }); + }; } diff --git a/src/programmers/http/HttpFormDataProgrammer.ts b/src/programmers/http/HttpFormDataProgrammer.ts index 4c18d036b5..cbdd66f75e 100644 --- a/src/programmers/http/HttpFormDataProgrammer.ts +++ b/src/programmers/http/HttpFormDataProgrammer.ts @@ -29,6 +29,7 @@ export namespace HttpFormDataProgrammer { type: ts.Type; name: string | undefined; }): FeatureProgrammer.IDecomposed => { + // ANALYZE TYPE const collection: MetadataCollection = new MetadataCollection(); const result = MetadataFactory.analyze( props.project.checker, @@ -46,6 +47,7 @@ export namespace HttpFormDataProgrammer { // DO TRANSFORM const object: MetadataObject = result.data.objects[0]!; + const statements: ts.Statement[] = decode_object(props.importer)(object); return { functions: {}, statements: [], @@ -73,7 +75,7 @@ export namespace HttpFormDataProgrammer { false, ), undefined, - ts.factory.createBlock(decode_object(props.importer)(object), true), + ts.factory.createBlock(statements, true), ), }; }; diff --git a/src/programmers/http/HttpHeadersProgrammer.ts b/src/programmers/http/HttpHeadersProgrammer.ts index ac2d4319b1..a167223bfe 100644 --- a/src/programmers/http/HttpHeadersProgrammer.ts +++ b/src/programmers/http/HttpHeadersProgrammer.ts @@ -20,37 +20,42 @@ import { Atomic } from "../../typings/Atomic"; import { Escaper } from "../../utils/Escaper"; import { MapUtil } from "../../utils/MapUtil"; +import { FeatureProgrammer } from "../FeatureProgrammer"; import { FunctionImporter } from "../helpers/FunctionImporter"; import { HttpMetadataUtil } from "../helpers/HttpMetadataUtil"; export namespace HttpHeadersProgrammer { export const INPUT_TYPE = "Record"; - export const write = - (project: IProject) => - (modulo: ts.LeftHandSideExpression) => - (type: ts.Type, name?: string): ts.ArrowFunction => { - // GET OBJECT TYPE - const importer: FunctionImporter = new FunctionImporter(modulo.getText()); - const collection: MetadataCollection = new MetadataCollection(); - const result = MetadataFactory.analyze( - project.checker, - project.context, - )({ - escape: false, - constant: true, - absorb: true, - validate, - })(collection)(type); - if (result.success === false) - throw TransformerError.from(`typia.http.${importer.method}`)( - result.errors, - ); + export const decompose = (props: { + project: IProject; + importer: FunctionImporter; + type: ts.Type; + name: string | undefined; + }): FeatureProgrammer.IDecomposed => { + // ANALYZE TYPE + const collection: MetadataCollection = new MetadataCollection(); + const result = MetadataFactory.analyze( + props.project.checker, + props.project.context, + )({ + escape: false, + constant: true, + absorb: true, + validate, + })(collection)(props.type); + if (result.success === false) + throw TransformerError.from(`typia.http.${props.importer.method}`)( + result.errors, + ); - // DO TRANSFORM - const object: MetadataObject = result.data.objects[0]!; - const statements: ts.Statement[] = decode_object(importer)(object); - return ts.factory.createArrowFunction( + // DO TRANSFORM + const object: MetadataObject = result.data.objects[0]!; + const statements: ts.Statement[] = decode_object(props.importer)(object); + return { + functions: {}, + statements: [], + arrow: ts.factory.createArrowFunction( undefined, undefined, [ @@ -67,17 +72,34 @@ export namespace HttpHeadersProgrammer { ts.factory.createIdentifier("Resolved"), [ ts.factory.createTypeReferenceNode( - name ?? TypeFactory.getFullName(project.checker)(type), + props.name ?? + TypeFactory.getFullName(props.project.checker)(props.type), ), ], false, ), undefined, - ts.factory.createBlock( - [...importer.declare(modulo), ...statements], - true, - ), - ); + ts.factory.createBlock(statements, true), + ), + }; + }; + + export const write = + (project: IProject) => + (modulo: ts.LeftHandSideExpression) => + (type: ts.Type, name?: string): ts.CallExpression => { + const importer: FunctionImporter = new FunctionImporter(modulo.getText()); + const result: FeatureProgrammer.IDecomposed = decompose({ + project, + importer, + type, + name, + }); + return FeatureProgrammer.writeDecomposed({ + modulo, + importer, + result, + }); }; export const validate = ( diff --git a/src/programmers/http/HttpIsFormDataProgrammer.ts b/src/programmers/http/HttpIsFormDataProgrammer.ts index 1b66fd500b..4de53ea2ba 100644 --- a/src/programmers/http/HttpIsFormDataProgrammer.ts +++ b/src/programmers/http/HttpIsFormDataProgrammer.ts @@ -1,90 +1,102 @@ import ts from "typescript"; -import { IdentifierFactory } from "../../factories/IdentifierFactory"; import { StatementFactory } from "../../factories/StatementFactory"; import { TypeFactory } from "../../factories/TypeFactory"; import { IProject } from "../../transformers/IProject"; +import { FeatureProgrammer } from "../FeatureProgrammer"; import { IsProgrammer } from "../IsProgrammer"; +import { FunctionImporter } from "../helpers/FunctionImporter"; import { HttpFormDataProgrammer } from "./HttpFormDataProgrammer"; export namespace HttpIsFormDataProgrammer { - export const write = - (project: IProject) => - (modulo: ts.LeftHandSideExpression) => - (type: ts.Type, name?: string): ts.ArrowFunction => - ts.factory.createArrowFunction( + export const decompose = (props: { + project: IProject; + importer: FunctionImporter; + type: ts.Type; + name: string | undefined; + }): FeatureProgrammer.IDecomposed => { + const is: FeatureProgrammer.IDecomposed = IsProgrammer.decompose({ + ...props, + project: { + ...props.project, + options: { + ...props.project.options, + functional: false, + numeric: false, + }, + }, + equals: false, + }); + const decode: FeatureProgrammer.IDecomposed = + HttpFormDataProgrammer.decompose(props); + return { + functions: { + ...is.functions, + ...decode.functions, + }, + statements: [ + ...is.statements, + ...decode.statements, + StatementFactory.constant("__is", is.arrow), + StatementFactory.constant("__decode", decode.arrow), + ], + arrow: ts.factory.createArrowFunction( undefined, undefined, - [ - IdentifierFactory.parameter( - "input", - ts.factory.createTypeReferenceNode("FormData"), - ), - ], + decode.arrow.parameters, ts.factory.createUnionTypeNode([ - ts.factory.createImportTypeNode( - ts.factory.createLiteralTypeNode( - ts.factory.createStringLiteral("typia"), - ), - undefined, - ts.factory.createIdentifier("Resolved"), - [ - ts.factory.createTypeReferenceNode( - name ?? TypeFactory.getFullName(project.checker)(type), - ), - ], - false, - ), - ts.factory.createLiteralTypeNode(ts.factory.createNull()), + decode.arrow.type ?? TypeFactory.keyword("any"), + ts.factory.createTypeReferenceNode("null"), ]), undefined, - ts.factory.createBlock([ - StatementFactory.constant( - "is", - IsProgrammer.write({ - ...project, - options: { - ...project.options, - functional: false, - numeric: false, - }, - })(modulo)(false)(type, name), - ), - StatementFactory.constant( - "decode", - HttpFormDataProgrammer.write({ - ...project, - options: { - ...project.options, - functional: false, - numeric: false, - }, - })(modulo)(type, name), - ), - StatementFactory.constant( - "output", - ts.factory.createCallExpression( - ts.factory.createIdentifier("decode"), - undefined, - [ts.factory.createIdentifier("input")], - ), - ), - ts.factory.createIfStatement( - ts.factory.createPrefixUnaryExpression( - ts.SyntaxKind.ExclamationToken, + ts.factory.createBlock( + [ + StatementFactory.constant( + "value", ts.factory.createCallExpression( - ts.factory.createIdentifier("is"), + ts.factory.createIdentifier("__decode"), undefined, - [ts.factory.createIdentifier("output")], + [ts.factory.createIdentifier("input")], ), ), - ts.factory.createReturnStatement(ts.factory.createNull()), - ), - ts.factory.createReturnStatement( - ts.factory.createIdentifier("output"), - ), - ]), - ); + ts.factory.createIfStatement( + ts.factory.createPrefixUnaryExpression( + ts.SyntaxKind.ExclamationToken, + ts.factory.createCallExpression( + ts.factory.createIdentifier("__is"), + undefined, + [ts.factory.createIdentifier("value")], + ), + ), + ts.factory.createReturnStatement(ts.factory.createNull()), + ), + ts.factory.createReturnStatement( + ts.factory.createIdentifier("value"), + ), + ], + true, + ), + ), + }; + }; + + export const write = + (project: IProject) => + (modulo: ts.LeftHandSideExpression) => + (type: ts.Type, name?: string): ts.CallExpression => { + const importer: FunctionImporter = new FunctionImporter(modulo.getText()); + const result: FeatureProgrammer.IDecomposed = decompose({ + project, + importer, + type, + name, + }); + return FeatureProgrammer.writeDecomposed({ + modulo, + importer, + result, + }); + }; } diff --git a/src/programmers/http/HttpIsHeadersProgrammer.ts b/src/programmers/http/HttpIsHeadersProgrammer.ts index 7ba02a286c..2318c7a78d 100644 --- a/src/programmers/http/HttpIsHeadersProgrammer.ts +++ b/src/programmers/http/HttpIsHeadersProgrammer.ts @@ -1,92 +1,102 @@ import ts from "typescript"; -import { IdentifierFactory } from "../../factories/IdentifierFactory"; import { StatementFactory } from "../../factories/StatementFactory"; import { TypeFactory } from "../../factories/TypeFactory"; import { IProject } from "../../transformers/IProject"; +import { FeatureProgrammer } from "../FeatureProgrammer"; import { IsProgrammer } from "../IsProgrammer"; +import { FunctionImporter } from "../helpers/FunctionImporter"; import { HttpHeadersProgrammer } from "./HttpHeadersProgrammer"; export namespace HttpIsHeadersProgrammer { - export const write = - (project: IProject) => - (modulo: ts.LeftHandSideExpression) => - (type: ts.Type, name?: string): ts.ArrowFunction => - ts.factory.createArrowFunction( + export const decompose = (props: { + project: IProject; + importer: FunctionImporter; + type: ts.Type; + name: string | undefined; + }): FeatureProgrammer.IDecomposed => { + const is: FeatureProgrammer.IDecomposed = IsProgrammer.decompose({ + ...props, + project: { + ...props.project, + options: { + ...props.project.options, + functional: false, + numeric: true, + }, + }, + equals: false, + }); + const decode: FeatureProgrammer.IDecomposed = + HttpHeadersProgrammer.decompose(props); + return { + functions: { + ...is.functions, + ...decode.functions, + }, + statements: [ + ...is.statements, + ...decode.statements, + StatementFactory.constant("__is", is.arrow), + StatementFactory.constant("__decode", decode.arrow), + ], + arrow: ts.factory.createArrowFunction( undefined, undefined, - [ - IdentifierFactory.parameter( - "input", - ts.factory.createTypeReferenceNode( - HttpHeadersProgrammer.INPUT_TYPE, - ), - ), - ], + decode.arrow.parameters, ts.factory.createUnionTypeNode([ - ts.factory.createImportTypeNode( - ts.factory.createLiteralTypeNode( - ts.factory.createStringLiteral("typia"), - ), - undefined, - ts.factory.createIdentifier("Resolved"), - [ - ts.factory.createTypeReferenceNode( - name ?? TypeFactory.getFullName(project.checker)(type), - ), - ], - false, - ), - ts.factory.createLiteralTypeNode(ts.factory.createNull()), + decode.arrow.type ?? TypeFactory.keyword("any"), + ts.factory.createTypeReferenceNode("null"), ]), undefined, - ts.factory.createBlock([ - StatementFactory.constant( - "is", - IsProgrammer.write({ - ...project, - options: { - ...project.options, - functional: false, - numeric: false, - }, - })(modulo)(false)(type, name), - ), - StatementFactory.constant( - "decode", - HttpHeadersProgrammer.write({ - ...project, - options: { - ...project.options, - functional: false, - numeric: false, - }, - })(modulo)(type, name), - ), - StatementFactory.constant( - "output", - ts.factory.createCallExpression( - ts.factory.createIdentifier("decode"), - undefined, - [ts.factory.createIdentifier("input")], - ), - ), - ts.factory.createIfStatement( - ts.factory.createPrefixUnaryExpression( - ts.SyntaxKind.ExclamationToken, + ts.factory.createBlock( + [ + StatementFactory.constant( + "value", ts.factory.createCallExpression( - ts.factory.createIdentifier("is"), + ts.factory.createIdentifier("__decode"), undefined, - [ts.factory.createIdentifier("output")], + [ts.factory.createIdentifier("input")], ), ), - ts.factory.createReturnStatement(ts.factory.createNull()), - ), - ts.factory.createReturnStatement( - ts.factory.createIdentifier("output"), - ), - ]), - ); + ts.factory.createIfStatement( + ts.factory.createPrefixUnaryExpression( + ts.SyntaxKind.ExclamationToken, + ts.factory.createCallExpression( + ts.factory.createIdentifier("__is"), + undefined, + [ts.factory.createIdentifier("value")], + ), + ), + ts.factory.createReturnStatement(ts.factory.createNull()), + ), + ts.factory.createReturnStatement( + ts.factory.createIdentifier("value"), + ), + ], + true, + ), + ), + }; + }; + + export const write = + (project: IProject) => + (modulo: ts.LeftHandSideExpression) => + (type: ts.Type, name?: string): ts.CallExpression => { + const importer: FunctionImporter = new FunctionImporter(modulo.getText()); + const result: FeatureProgrammer.IDecomposed = decompose({ + project, + importer, + type, + name, + }); + return FeatureProgrammer.writeDecomposed({ + modulo, + importer, + result, + }); + }; } diff --git a/src/programmers/http/HttpIsQueryProgrammer.ts b/src/programmers/http/HttpIsQueryProgrammer.ts index 3e5bb0b5f7..111bb27290 100644 --- a/src/programmers/http/HttpIsQueryProgrammer.ts +++ b/src/programmers/http/HttpIsQueryProgrammer.ts @@ -1,90 +1,104 @@ import ts from "typescript"; -import { IdentifierFactory } from "../../factories/IdentifierFactory"; import { StatementFactory } from "../../factories/StatementFactory"; import { TypeFactory } from "../../factories/TypeFactory"; import { IProject } from "../../transformers/IProject"; +import { FeatureProgrammer } from "../FeatureProgrammer"; import { IsProgrammer } from "../IsProgrammer"; +import { FunctionImporter } from "../helpers/FunctionImporter"; import { HttpQueryProgrammer } from "./HttpQueryProgrammer"; export namespace HttpIsQueryProgrammer { - export const write = - (project: IProject) => - (modulo: ts.LeftHandSideExpression, allowOptional: boolean = false) => - (type: ts.Type, name?: string): ts.ArrowFunction => - ts.factory.createArrowFunction( + export const decompose = (props: { + project: IProject; + importer: FunctionImporter; + type: ts.Type; + name: string | undefined; + allowOptional: boolean; + }): FeatureProgrammer.IDecomposed => { + const is: FeatureProgrammer.IDecomposed = IsProgrammer.decompose({ + ...props, + project: { + ...props.project, + options: { + ...props.project.options, + functional: false, + numeric: true, + }, + }, + equals: false, + }); + const decode: FeatureProgrammer.IDecomposed = + HttpQueryProgrammer.decompose(props); + return { + functions: { + ...is.functions, + ...decode.functions, + }, + statements: [ + ...is.statements, + ...decode.statements, + StatementFactory.constant("__is", is.arrow), + StatementFactory.constant("__decode", decode.arrow), + ], + arrow: ts.factory.createArrowFunction( undefined, undefined, - [ - IdentifierFactory.parameter( - "input", - ts.factory.createTypeReferenceNode(HttpQueryProgrammer.INPUT_TYPE), - ), - ], + decode.arrow.parameters, ts.factory.createUnionTypeNode([ - ts.factory.createImportTypeNode( - ts.factory.createLiteralTypeNode( - ts.factory.createStringLiteral("typia"), - ), - undefined, - ts.factory.createIdentifier("Resolved"), - [ - ts.factory.createTypeReferenceNode( - name ?? TypeFactory.getFullName(project.checker)(type), - ), - ], - false, - ), - ts.factory.createLiteralTypeNode(ts.factory.createNull()), + decode.arrow.type ?? TypeFactory.keyword("any"), + ts.factory.createTypeReferenceNode("null"), ]), undefined, - ts.factory.createBlock([ - StatementFactory.constant( - "is", - IsProgrammer.write({ - ...project, - options: { - ...project.options, - functional: false, - numeric: false, - }, - })(modulo)(false)(type, name), - ), - StatementFactory.constant( - "decode", - HttpQueryProgrammer.write({ - ...project, - options: { - ...project.options, - functional: false, - numeric: false, - }, - })(modulo, allowOptional)(type, name), - ), - StatementFactory.constant( - "output", - ts.factory.createCallExpression( - ts.factory.createIdentifier("decode"), - undefined, - [ts.factory.createIdentifier("input")], - ), - ), - ts.factory.createIfStatement( - ts.factory.createPrefixUnaryExpression( - ts.SyntaxKind.ExclamationToken, + ts.factory.createBlock( + [ + StatementFactory.constant( + "value", ts.factory.createCallExpression( - ts.factory.createIdentifier("is"), + ts.factory.createIdentifier("__decode"), undefined, - [ts.factory.createIdentifier("output")], + [ts.factory.createIdentifier("input")], ), ), - ts.factory.createReturnStatement(ts.factory.createNull()), - ), - ts.factory.createReturnStatement( - ts.factory.createIdentifier("output"), - ), - ]), - ); + ts.factory.createIfStatement( + ts.factory.createPrefixUnaryExpression( + ts.SyntaxKind.ExclamationToken, + ts.factory.createCallExpression( + ts.factory.createIdentifier("__is"), + undefined, + [ts.factory.createIdentifier("value")], + ), + ), + ts.factory.createReturnStatement(ts.factory.createNull()), + ), + ts.factory.createReturnStatement( + ts.factory.createIdentifier("value"), + ), + ], + true, + ), + ), + }; + }; + + export const write = + (project: IProject) => + (modulo: ts.LeftHandSideExpression, allowOptional: boolean = false) => + (type: ts.Type, name?: string): ts.CallExpression => { + const importer: FunctionImporter = new FunctionImporter(modulo.getText()); + const result: FeatureProgrammer.IDecomposed = decompose({ + project, + importer, + type, + name, + allowOptional, + }); + return FeatureProgrammer.writeDecomposed({ + modulo, + importer, + result, + }); + }; } diff --git a/src/programmers/http/HttpQueryProgrammer.ts b/src/programmers/http/HttpQueryProgrammer.ts index a1067826ee..25bacedb00 100644 --- a/src/programmers/http/HttpQueryProgrammer.ts +++ b/src/programmers/http/HttpQueryProgrammer.ts @@ -18,37 +18,43 @@ import { Atomic } from "../../typings/Atomic"; import { Escaper } from "../../utils/Escaper"; +import { FeatureProgrammer } from "../FeatureProgrammer"; import { FunctionImporter } from "../helpers/FunctionImporter"; import { HttpMetadataUtil } from "../helpers/HttpMetadataUtil"; export namespace HttpQueryProgrammer { export const INPUT_TYPE = "string | URLSearchParams"; - export const write = - (project: IProject) => - (modulo: ts.LeftHandSideExpression, allowOptional: boolean = false) => - (type: ts.Type, name?: string): ts.ArrowFunction => { - // GET OBJECT TYPE - const importer: FunctionImporter = new FunctionImporter(modulo.getText()); - const collection: MetadataCollection = new MetadataCollection(); - const result = MetadataFactory.analyze( - project.checker, - project.context, - )({ - escape: false, - constant: true, - absorb: true, - validate: (meta, explore) => validate(meta, explore, allowOptional), - })(collection)(type); - if (result.success === false) - throw TransformerError.from(`typia.http.${importer.method}`)( - result.errors, - ); + export const decompose = (props: { + project: IProject; + importer: FunctionImporter; + allowOptional: boolean; + type: ts.Type; + name: string | undefined; + }): FeatureProgrammer.IDecomposed => { + // ANALYZE TYPE + const collection: MetadataCollection = new MetadataCollection(); + const result = MetadataFactory.analyze( + props.project.checker, + props.project.context, + )({ + escape: false, + constant: true, + absorb: true, + validate: (meta, explore) => validate(meta, explore, props.allowOptional), + })(collection)(props.type); + if (result.success === false) + throw TransformerError.from(`typia.http.${props.importer.method}`)( + result.errors, + ); - // DO TRANSFORM - const object: MetadataObject = result.data.objects[0]!; - const statements: ts.Statement[] = decode_object(importer)(object); - return ts.factory.createArrowFunction( + // DO TRANSFORM + const object: MetadataObject = result.data.objects[0]!; + const statements: ts.Statement[] = decode_object(props.importer)(object); + return { + functions: {}, + statements: [], + arrow: ts.factory.createArrowFunction( undefined, undefined, [ @@ -65,17 +71,35 @@ export namespace HttpQueryProgrammer { ts.factory.createIdentifier("Resolved"), [ ts.factory.createTypeReferenceNode( - name ?? TypeFactory.getFullName(project.checker)(type), + props.name ?? + TypeFactory.getFullName(props.project.checker)(props.type), ), ], false, ), undefined, - ts.factory.createBlock( - [...importer.declare(modulo), ...statements], - true, - ), - ); + ts.factory.createBlock(statements, true), + ), + }; + }; + + export const write = + (project: IProject) => + (modulo: ts.LeftHandSideExpression, allowOptional: boolean = false) => + (type: ts.Type, name?: string): ts.CallExpression => { + const importer: FunctionImporter = new FunctionImporter(modulo.getText()); + const result: FeatureProgrammer.IDecomposed = decompose({ + project, + importer, + type, + name, + allowOptional, + }); + return FeatureProgrammer.writeDecomposed({ + modulo, + importer, + result, + }); }; export const validate = ( diff --git a/src/programmers/http/HttpValidateFormDataProgrammer.ts b/src/programmers/http/HttpValidateFormDataProgrammer.ts index 1de9d423b0..58b5aa3b12 100644 --- a/src/programmers/http/HttpValidateFormDataProgrammer.ts +++ b/src/programmers/http/HttpValidateFormDataProgrammer.ts @@ -1,75 +1,85 @@ import ts from "typescript"; -import { IdentifierFactory } from "../../factories/IdentifierFactory"; import { StatementFactory } from "../../factories/StatementFactory"; import { TypeFactory } from "../../factories/TypeFactory"; import { IProject } from "../../transformers/IProject"; +import { FeatureProgrammer } from "../FeatureProgrammer"; import { ValidateProgrammer } from "../ValidateProgrammer"; +import { FunctionImporter } from "../helpers/FunctionImporter"; import { HttpFormDataProgrammer } from "./HttpFormDataProgrammer"; export namespace HttpValidateFormDataProgrammer { - export const write = - (project: IProject) => - (modulo: ts.LeftHandSideExpression) => - (type: ts.Type, name?: string): ts.ArrowFunction => - ts.factory.createArrowFunction( + export const decompose = (props: { + project: IProject; + modulo: ts.LeftHandSideExpression; + importer: FunctionImporter; + type: ts.Type; + name: string | undefined; + }): FeatureProgrammer.IDecomposed => { + const validate = ValidateProgrammer.decompose({ + ...props, + project: { + ...props.project, + options: { + ...props.project.options, + functional: false, + numeric: false, + }, + }, + equals: false, + }); + const decode = HttpFormDataProgrammer.decompose(props); + return { + functions: { + ...validate.functions, + ...decode.functions, + }, + statements: [ + ...validate.statements, + StatementFactory.constant("__validate", validate.arrow), + StatementFactory.constant("__decode", decode.arrow), + ], + arrow: ts.factory.createArrowFunction( undefined, undefined, - [ - IdentifierFactory.parameter( - "input", - ts.factory.createTypeReferenceNode("FormData"), - ), - ], - ts.factory.createTypeReferenceNode( - `typia.IValidation>`, - ), + decode.arrow.parameters, + ts.factory.createTypeReferenceNode("typia.IValidation", [ + decode.arrow.type ?? TypeFactory.keyword("any"), + ]), undefined, - ts.factory.createBlock([ - StatementFactory.constant( - "validate", - ValidateProgrammer.write({ - ...project, - options: { - ...project.options, - functional: false, - numeric: true, - }, - })(modulo)(false)(type, name), - ), - StatementFactory.constant( - "decode", - HttpFormDataProgrammer.write({ - ...project, - options: { - ...project.options, - functional: false, - numeric: false, - }, - })(modulo)(type, name), - ), - StatementFactory.constant( - "output", + ts.factory.createCallExpression( + ts.factory.createIdentifier("__validate"), + undefined, + [ ts.factory.createCallExpression( - ts.factory.createIdentifier("decode"), + ts.factory.createIdentifier("__decode"), undefined, [ts.factory.createIdentifier("input")], ), - ), - ts.factory.createReturnStatement( - ts.factory.createAsExpression( - ts.factory.createCallExpression( - ts.factory.createIdentifier("validate"), - undefined, - [ts.factory.createIdentifier("output")], - ), - ts.factory.createTypeReferenceNode("any"), - ), - ), - ]), - ); + ], + ), + ), + }; + }; + + export const write = + (project: IProject) => + (modulo: ts.LeftHandSideExpression) => + (type: ts.Type, name?: string): ts.CallExpression => { + const importer: FunctionImporter = new FunctionImporter(modulo.getText()); + const result: FeatureProgrammer.IDecomposed = decompose({ + project, + modulo, + importer, + type, + name, + }); + return FeatureProgrammer.writeDecomposed({ + modulo, + importer, + result, + }); + }; } diff --git a/src/programmers/http/HttpValidateHeadersProgrammer.ts b/src/programmers/http/HttpValidateHeadersProgrammer.ts index 9be51811a4..59fa69fc3f 100644 --- a/src/programmers/http/HttpValidateHeadersProgrammer.ts +++ b/src/programmers/http/HttpValidateHeadersProgrammer.ts @@ -1,77 +1,85 @@ import ts from "typescript"; -import { IdentifierFactory } from "../../factories/IdentifierFactory"; import { StatementFactory } from "../../factories/StatementFactory"; import { TypeFactory } from "../../factories/TypeFactory"; import { IProject } from "../../transformers/IProject"; +import { FeatureProgrammer } from "../FeatureProgrammer"; import { ValidateProgrammer } from "../ValidateProgrammer"; +import { FunctionImporter } from "../helpers/FunctionImporter"; import { HttpHeadersProgrammer } from "./HttpHeadersProgrammer"; export namespace HttpValidateHeadersProgrammer { - export const write = - (project: IProject) => - (modulo: ts.LeftHandSideExpression) => - (type: ts.Type, name?: string): ts.ArrowFunction => - ts.factory.createArrowFunction( + export const decompose = (props: { + project: IProject; + modulo: ts.LeftHandSideExpression; + importer: FunctionImporter; + type: ts.Type; + name: string | undefined; + }): FeatureProgrammer.IDecomposed => { + const validate = ValidateProgrammer.decompose({ + ...props, + project: { + ...props.project, + options: { + ...props.project.options, + functional: false, + numeric: false, + }, + }, + equals: false, + }); + const decode = HttpHeadersProgrammer.decompose(props); + return { + functions: { + ...validate.functions, + ...decode.functions, + }, + statements: [ + ...validate.statements, + StatementFactory.constant("__validate", validate.arrow), + StatementFactory.constant("__decode", decode.arrow), + ], + arrow: ts.factory.createArrowFunction( undefined, undefined, - [ - IdentifierFactory.parameter( - "input", - ts.factory.createTypeReferenceNode( - HttpHeadersProgrammer.INPUT_TYPE, - ), - ), - ], - ts.factory.createTypeReferenceNode( - `typia.IValidation>`, - ), + decode.arrow.parameters, + ts.factory.createTypeReferenceNode("typia.IValidation", [ + decode.arrow.type ?? TypeFactory.keyword("any"), + ]), undefined, - ts.factory.createBlock([ - StatementFactory.constant( - "validate", - ValidateProgrammer.write({ - ...project, - options: { - ...project.options, - functional: false, - numeric: true, - }, - })(modulo)(false)(type, name), - ), - StatementFactory.constant( - "decode", - HttpHeadersProgrammer.write({ - ...project, - options: { - ...project.options, - functional: false, - numeric: false, - }, - })(modulo)(type, name), - ), - StatementFactory.constant( - "output", + ts.factory.createCallExpression( + ts.factory.createIdentifier("__validate"), + undefined, + [ ts.factory.createCallExpression( - ts.factory.createIdentifier("decode"), + ts.factory.createIdentifier("__decode"), undefined, [ts.factory.createIdentifier("input")], ), - ), - ts.factory.createReturnStatement( - ts.factory.createAsExpression( - ts.factory.createCallExpression( - ts.factory.createIdentifier("validate"), - undefined, - [ts.factory.createIdentifier("output")], - ), - ts.factory.createTypeReferenceNode("any"), - ), - ), - ]), - ); + ], + ), + ), + }; + }; + + export const write = + (project: IProject) => + (modulo: ts.LeftHandSideExpression) => + (type: ts.Type, name?: string): ts.CallExpression => { + const importer: FunctionImporter = new FunctionImporter(modulo.getText()); + const result: FeatureProgrammer.IDecomposed = decompose({ + project, + modulo, + importer, + type, + name, + }); + return FeatureProgrammer.writeDecomposed({ + modulo, + importer, + result, + }); + }; } diff --git a/src/programmers/http/HttpValidateQueryProgrammer.ts b/src/programmers/http/HttpValidateQueryProgrammer.ts index 3a9a5c27ed..f82b90cd16 100644 --- a/src/programmers/http/HttpValidateQueryProgrammer.ts +++ b/src/programmers/http/HttpValidateQueryProgrammer.ts @@ -1,75 +1,87 @@ import ts from "typescript"; -import { IdentifierFactory } from "../../factories/IdentifierFactory"; import { StatementFactory } from "../../factories/StatementFactory"; import { TypeFactory } from "../../factories/TypeFactory"; import { IProject } from "../../transformers/IProject"; +import { FeatureProgrammer } from "../FeatureProgrammer"; import { ValidateProgrammer } from "../ValidateProgrammer"; +import { FunctionImporter } from "../helpers/FunctionImporter"; import { HttpQueryProgrammer } from "./HttpQueryProgrammer"; export namespace HttpValidateQueryProgrammer { - export const write = - (project: IProject) => - (modulo: ts.LeftHandSideExpression, allowOptional: boolean = false) => - (type: ts.Type, name?: string): ts.ArrowFunction => - ts.factory.createArrowFunction( + export const decompose = (props: { + project: IProject; + modulo: ts.LeftHandSideExpression; + importer: FunctionImporter; + type: ts.Type; + name: string | undefined; + allowOptional: boolean; + }): FeatureProgrammer.IDecomposed => { + const validate = ValidateProgrammer.decompose({ + ...props, + project: { + ...props.project, + options: { + ...props.project.options, + functional: false, + numeric: false, + }, + }, + equals: false, + }); + const decode = HttpQueryProgrammer.decompose(props); + return { + functions: { + ...validate.functions, + ...decode.functions, + }, + statements: [ + ...validate.statements, + StatementFactory.constant("__validate", validate.arrow), + StatementFactory.constant("__decode", decode.arrow), + ], + arrow: ts.factory.createArrowFunction( undefined, undefined, - [ - IdentifierFactory.parameter( - "input", - ts.factory.createTypeReferenceNode(HttpQueryProgrammer.INPUT_TYPE), - ), - ], - ts.factory.createTypeReferenceNode( - `typia.IValidation>`, - ), + decode.arrow.parameters, + ts.factory.createTypeReferenceNode("typia.IValidation", [ + decode.arrow.type ?? TypeFactory.keyword("any"), + ]), undefined, - ts.factory.createBlock([ - StatementFactory.constant( - "validate", - ValidateProgrammer.write({ - ...project, - options: { - ...project.options, - functional: false, - numeric: true, - }, - })(modulo)(false)(type, name), - ), - StatementFactory.constant( - "decode", - HttpQueryProgrammer.write({ - ...project, - options: { - ...project.options, - functional: false, - numeric: false, - }, - })(modulo, allowOptional)(type, name), - ), - StatementFactory.constant( - "output", + ts.factory.createCallExpression( + ts.factory.createIdentifier("__validate"), + undefined, + [ ts.factory.createCallExpression( - ts.factory.createIdentifier("decode"), + ts.factory.createIdentifier("__decode"), undefined, [ts.factory.createIdentifier("input")], ), - ), - ts.factory.createReturnStatement( - ts.factory.createAsExpression( - ts.factory.createCallExpression( - ts.factory.createIdentifier("validate"), - undefined, - [ts.factory.createIdentifier("output")], - ), - ts.factory.createTypeReferenceNode("any"), - ), - ), - ]), - ); + ], + ), + ), + }; + }; + + export const write = + (project: IProject) => + (modulo: ts.LeftHandSideExpression, allowOptional: boolean = false) => + (type: ts.Type, name?: string): ts.CallExpression => { + const importer: FunctionImporter = new FunctionImporter(modulo.getText()); + const result: FeatureProgrammer.IDecomposed = decompose({ + project, + modulo, + importer, + type, + name, + allowOptional, + }); + return FeatureProgrammer.writeDecomposed({ + modulo, + importer, + result, + }); + }; } diff --git a/test-esm/package.json b/test-esm/package.json index a7d5f6cb9c..83f0397400 100644 --- a/test-esm/package.json +++ b/test-esm/package.json @@ -36,6 +36,6 @@ "typescript": "^5.4.5" }, "dependencies": { - "typia": "../typia-6.7.0-dev.20240803-2.tgz" + "typia": "../typia-6.7.0-dev.20240803-5.tgz" } } \ No newline at end of file diff --git a/test/package.json b/test/package.json index 131fd37a59..83451c980a 100644 --- a/test/package.json +++ b/test/package.json @@ -51,6 +51,6 @@ "suppress-warnings": "^1.0.2", "tstl": "^3.0.0", "uuid": "^9.0.1", - "typia": "../typia-6.7.0-dev.20240803-2.tgz" + "typia": "../typia-6.7.0-dev.20240803-5.tgz" } } \ No newline at end of file diff --git a/website/package.json b/website/package.json index 98f22242f1..e9418c1f0e 100644 --- a/website/package.json +++ b/website/package.json @@ -38,7 +38,7 @@ "tgrid": "^1.0.3", "tstl": "^3.0.0", "typescript": "^5.5.4", - "typia": "^6.6.2" + "typia": "^6.7.0" }, "devDependencies": { "@trivago/prettier-plugin-sort-imports": "^4.3.0", diff --git a/website/pages/docs/json/stringify.mdx b/website/pages/docs/json/stringify.mdx index 5745ed4808..8bf6887d49 100644 --- a/website/pages/docs/json/stringify.mdx +++ b/website/pages/docs/json/stringify.mdx @@ -543,7 +543,7 @@ For reference, `class-transformer` is the most famous library used in `NestJS` w ![Stringify Function Benchmark](https://github.com/samchon/typia/raw/master/benchmark/results/11th%20Gen%20Intel(R)%20Core(TM)%20i5-1135G7%20%40%202.40GHz/images/stringify.svg) -> Measured on [Intel i5-1135g7, Surface Pro 8](https://github.com/samchon/typia/tree/master/benchmark/results/11th%20Gen%20Intel(R)%20Core(TM)%20i5-1135G7%20%40%202.40GHz#stringify) +> Measured on [AMD Ryzen 9 7940HS, Rog Flow x13](https://github.com/samchon/typia/tree/master/benchmark/results/AMD%20Ryzen%209%207940HS%20w%20Radeon%20780M%20Graphics#stringify) @@ -563,4 +563,4 @@ I'll show you the benchmark result that, how JSON serizliation speed affects on ![Server Benchmark](https://raw.githubusercontent.com/samchon/typia/master/benchmark/results/11th%20Gen%20Intel(R)%20Core(TM)%20i5-1135G7%20%40%202.40GHz/images/server.svg) -> Measured on [Intel i5-1135g7, Surface Pro 8](https://github.com/samchon/typia/tree/master/benchmark/results/11th%20Gen%20Intel(R)%20Core(TM)%20i5-1135G7%20%40%202.40GHz#server) \ No newline at end of file +> Measured on [AMD Ryzen 9 7940HS, Rog Flow x13](https://github.com/samchon/typia/tree/master/benchmark/results/AMD%20Ryzen%209%207940HS%20w%20Radeon%20780M%20Graphics#server) \ No newline at end of file diff --git a/website/pages/docs/pure.mdx b/website/pages/docs/pure.mdx index c6e549f1c4..5f85c578ef 100644 --- a/website/pages/docs/pure.mdx +++ b/website/pages/docs/pure.mdx @@ -562,4 +562,4 @@ exports.assertArticle = assertArticle; ![Assert Function Benchmark](https://github.com/samchon/typia/raw/master/benchmark/results/11th%20Gen%20Intel(R)%20Core(TM)%20i5-1135G7%20%40%202.40GHz/images/assert.svg) -> Measured on [Intel i5-1135g7, Surface Pro 8](https://github.com/samchon/typia/tree/master/benchmark/results/11th%20Gen%20Intel(R)%20Core(TM)%20i5-1135G7%20%40%202.40GHz#assert) +> Measured on [AMD Ryzen 9 7940HS, Rog Flow x13](https://github.com/samchon/typia/tree/master/benchmark/results/AMD%20Ryzen%209%207940HS%20w%20Radeon%20780M%20Graphics#assert) diff --git a/website/pages/docs/validators/assert.mdx b/website/pages/docs/validators/assert.mdx index 6be86081cc..f8535c9cd0 100644 --- a/website/pages/docs/validators/assert.mdx +++ b/website/pages/docs/validators/assert.mdx @@ -843,7 +843,7 @@ Furthermore, only `typia` can validate complicate union types. ![Assert Function Benchmark](https://github.com/samchon/typia/raw/master/benchmark/results/11th%20Gen%20Intel(R)%20Core(TM)%20i5-1135G7%20%40%202.40GHz/images/assert.svg) -> Measured on [Intel i5-1135g7, Surface Pro 8](https://github.com/samchon/typia/tree/master/benchmark/results/11th%20Gen%20Intel(R)%20Core(TM)%20i5-1135G7%20%40%202.40GHz#assert) +> Measured on [AMD Ryzen 9 7940HS, Rog Flow x13](https://github.com/samchon/typia/tree/master/benchmark/results/AMD%20Ryzen%209%207940HS%20w%20Radeon%20780M%20Graphics#assert) Components | `typia` | `TypeBox` | `ajv` | `io-ts` | `zod` | `C.V.` -------------------------|--------|-----------|-------|---------|-------|------------------ diff --git a/website/pages/docs/validators/is.mdx b/website/pages/docs/validators/is.mdx index 0adb3fc791..e91fa5fa21 100644 --- a/website/pages/docs/validators/is.mdx +++ b/website/pages/docs/validators/is.mdx @@ -464,7 +464,7 @@ Furthermore, only `typia` can validate complicate union types. ![Is Function Benchmark](https://github.com/samchon/typia/raw/master/benchmark/results/11th%20Gen%20Intel(R)%20Core(TM)%20i5-1135G7%20%40%202.40GHz/images/is.svg) -> Measured on [Intel i5-1135g7, Surface Pro 8](https://github.com/samchon/typia/tree/master/benchmark/results/11th%20Gen%20Intel(R)%20Core(TM)%20i5-1135G7%20%40%202.40GHz#is) +> Measured on [AMD Ryzen 9 7940HS, Rog Flow x13](https://github.com/samchon/typia/tree/master/benchmark/results/AMD%20Ryzen%209%207940HS%20w%20Radeon%20780M%20Graphics#is) Components | `typia` | `TypeBox` | `ajv` | `io-ts` | `zod` | `C.V.` -------------------------|--------|-----------|-------|---------|-------|------------------ diff --git a/website/pages/docs/validators/validate.mdx b/website/pages/docs/validators/validate.mdx index d820434705..fef057bd0c 100644 --- a/website/pages/docs/validators/validate.mdx +++ b/website/pages/docs/validators/validate.mdx @@ -911,7 +911,7 @@ Furthermore, only `typia` can validate complicate union types. ![validate Function Benchmark](https://github.com/samchon/typia/raw/master/benchmark/results/11th%20Gen%20Intel(R)%20Core(TM)%20i5-1135G7%20%40%202.40GHz/images/validate.svg) -> Measured on [Intel i5-1135g7, Surface Pro 8](https://github.com/samchon/typia/tree/master/benchmark/results/11th%20Gen%20Intel(R)%20Core(TM)%20i5-1135G7%20%40%202.40GHz#validate) +> Measured on [AMD Ryzen 9 7940HS, Rog Flow x13](https://github.com/samchon/typia/tree/master/benchmark/results/AMD%20Ryzen%209%207940HS%20w%20Radeon%20780M%20Graphics#validate) Components | `typia` | `TypeBox` | `ajv` | `io-ts` | `zod` | `C.V.` -------------------------|--------|-----------|-------|---------|-------|------------------