diff --git a/.vscode/settings.json b/.vscode/settings.json index 679e7f3..4b23af4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -20,4 +20,9 @@ }, "js/ts.tsdk.path": "node_modules/typescript/lib", + "js/ts.format.enabled": false, + "js/ts.tsdk.promptToUseWorkspaceVersion": true, + "cSpell.words": [ + "SDPE" + ], } \ No newline at end of file diff --git a/eslint.config.js b/eslint.config.js index 49f2e94..9a37b93 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -11,7 +11,7 @@ export default [ }, }, files: ["**/*.test.ts", "**/*.bench.ts", "test/**/*.ts"], - ignores: ["**/*.d.ts"] + ignores: ["**/*.d.ts", "**/*.gen.ts"] }, { ...duplojsEslintOpen, @@ -23,7 +23,7 @@ export default [ }, }, files: ["**/*.ts"], - ignores: ["**/*.test.ts", "**/*.bench.ts", "test/**/*.ts", "**/*.d.ts"], + ignores: ["**/*.test.ts", "**/*.bench.ts", "test/**/*.ts", "**/*.d.ts", "**/*.gen.ts"], }, { ignores: ["coverage", "dist"] diff --git a/integration/toDataParser/__snapshots__/basic.gen.ts b/integration/toDataParser/__snapshots__/basic.gen.ts new file mode 100644 index 0000000..3df86ec --- /dev/null +++ b/integration/toDataParser/__snapshots__/basic.gen.ts @@ -0,0 +1,38 @@ +import * as DP from "@duplojs/utils/dataParser"; + +export const userProfileParser = DP.object({ + id: DP.templateLiteral(["user-", DP.number(), "-db1"]), + name: DP.string({ checkers: [DP.checkerStringMin(2)] }), + email: DP.string({ checkers: [DP.checkerEmail()] }), + role: DP.literal(["admin", "editor", "viewer"]), + age: DP.optional(DP.number({ + coerce: true, + checkers: [ + DP.checkerNumberMin(0), + DP.checkerNumberMax(80) + ] + })), + contact: DP.union([ + DP.object({ + phone: DP.string({ checkers: [DP.checkerRegex(/^[+\d][\d\s-]{5,}$/)] }) + }), + DP.object({ + email: DP.string({ checkers: [DP.checkerEmail()] }) + }) + ]), + address: DP.object({ + street: DP.string(), + city: DP.string(), + zip: DP.string({ checkers: [DP.checkerRegex(/^\d{5}$/)] }), + country: DP.literal(["FR"]) + }), + roles: DP.array(DP.literal(["admin", "editor", "viewer"]), { checkers: [DP.checkerArrayMin(1)] }), + preferences: DP.nullable(DP.object({ + theme: DP.literal(["light", "dark"]), + newsletter: DP.boolean() + })), + metadata: DP.record(DP.string(), DP.string()), + location: DP.tuple([DP.number(), DP.number()], { rest: DP.number() }), + createdAt: DP.date({ coerce: true }), + startAt: DP.time() +}); \ No newline at end of file diff --git a/integration/toDataParser/__snapshots__/recursive.gen.ts b/integration/toDataParser/__snapshots__/recursive.gen.ts new file mode 100644 index 0000000..728ed2d --- /dev/null +++ b/integration/toDataParser/__snapshots__/recursive.gen.ts @@ -0,0 +1,43 @@ +import * as DP from "@duplojs/utils/dataParser"; + +export type RecursiveType0 = { + name: string; + children: RecursiveType0[]; + comment: { + id: string; + replies: RecursiveType1[]; + }; + meta: [ + string, + RecursiveType2[] + ]; +}; + +export type RecursiveType1 = { + id: string; + replies: RecursiveType1[]; +}; + +export type RecursiveType2 = [ + string, + RecursiveType2[] +]; + +export type RecursiveNode = RecursiveType0; + +export const recursiveDataParser1: DP.DataParser = DP.object({ + id: DP.string(), + replies: DP.array(DP.lazy(() => recursiveDataParser1)) +}); + +export const recursiveDataParser2: DP.DataParser = DP.tuple([DP.string(), DP.array(DP.lazy(() => recursiveDataParser2))]); + +export const recursiveNodeDataParser: DP.DataParser = DP.object({ + name: DP.string(), + children: DP.array(DP.lazy(() => recursiveNodeDataParser)), + comment: DP.object({ + id: DP.string(), + replies: DP.array(DP.lazy(() => recursiveDataParser1)) + }), + meta: DP.tuple([DP.string(), DP.array(DP.lazy(() => recursiveDataParser2))]) +}); \ No newline at end of file diff --git a/integration/toDataParser/__snapshots__/withConstName.gen.ts b/integration/toDataParser/__snapshots__/withConstName.gen.ts new file mode 100644 index 0000000..f5ec1fb --- /dev/null +++ b/integration/toDataParser/__snapshots__/withConstName.gen.ts @@ -0,0 +1,10 @@ +import * as DP from "@duplojs/utils/dataParser"; + +export const userRoleDataParser = DP.literal(["admin", "editor", "viewer"]); + +export const userDataParser = DP.object({ + id: DP.templateLiteral(["user-", DP.number(), "-db1"]), + name: DP.string({ checkers: [DP.checkerStringMin(2)] }), + email: DP.string({ checkers: [DP.checkerEmail()] }), + role: userRoleDataParser +}); \ No newline at end of file diff --git a/integration/toDataParser/index.test.ts b/integration/toDataParser/index.test.ts new file mode 100644 index 0000000..011dcf2 --- /dev/null +++ b/integration/toDataParser/index.test.ts @@ -0,0 +1,140 @@ +import { asserts, DPE, E, Path, S, unwrap } from "@duplojs/utils"; +import { SF } from "@duplojs/server-utils"; +import { defaultCheckerTransformers, defaultTransformers, render } from "@duplojs/data-parser-tools/toDataParser"; +import { defaultTransformers as tsDefaultTransformers } from "@duplojs/data-parser-tools/toTypescript"; + +async function typedSnapshot(value: string, filePath: string) { + const absoluteFilePath = Path.resolveRelative([import.meta.dirname, filePath]); + + const exist = await SF.exists(absoluteFilePath); + + if (E.isLeft(exist)) { + const writeTextFileResult = await SF.writeTextFile(absoluteFilePath, value); + asserts(writeTextFileResult, E.isRight); + } + + const expected = await E.rightAsyncPipe( + absoluteFilePath, + SF.readTextFile, + S.trim, + ); + + asserts(expected, E.isRight); + + expect(value).toBe(unwrap(expected)); +} + +describe("integration", () => { + it("basic", async() => { + const userSchema = DPE.object({ + id: DPE.templateLiteral(["user-", DPE.number(), "-db1"]), + name: DPE.string().min(2), + email: DPE.email(), + role: DPE.literal(["admin", "editor", "viewer"]), + age: DPE + .coerce + .number() + .min(0) + .max(80) + .optional(), + contact: DPE.union([ + DPE.object({ + phone: DPE.string().regex(/^[+\d][\d\s-]{5,}$/), + }), + DPE.object({ + email: DPE.email(), + }), + ]), + address: DPE.object({ + street: DPE.string(), + city: DPE.string(), + zip: DPE.string().regex(/^\d{5}$/), + country: DPE.literal("FR"), + }), + roles: DPE.literal(["admin", "editor", "viewer"]).array().min(1), + preferences: DPE.object({ + theme: DPE.literal(["light", "dark"]), + newsletter: DPE.boolean(), + }).nullable(), + metadata: DPE.record(DPE.string(), DPE.string()), + location: DPE.tuple([DPE.number(), DPE.number()], { rest: DPE.number() }), + createdAt: DPE.date({ coerce: true }), + startAt: DPE.time(), + }).addConstName("userProfileParser"); + + const result = render( + userSchema, + { + constName: "userProfileParser", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript: { + identifier: "UserProfile", + transformers: tsDefaultTransformers, + }, + }, + ); + + await typedSnapshot(result, "__snapshots__/basic.gen.ts"); + }); + + it("recursive", async() => { + const commentSchema: DPE.DataParser = DPE.object({ + id: DPE.string(), + replies: DPE.lazy(() => commentSchema).array(), + }); + + const metaSchema: DPE.DataParser = DPE.tuple([ + DPE.string(), + DPE.lazy(() => metaSchema).array(), + ]); + + const nodeSchema: DPE.DataParser = DPE.object({ + name: DPE.string(), + children: DPE.lazy(() => nodeSchema).array(), + comment: commentSchema, + meta: metaSchema, + }).addConstName("recursiveNodeDataParser"); + + const result = render( + nodeSchema, + { + constName: "recursiveNodeDataParser", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript: { + identifier: "RecursiveNode", + transformers: tsDefaultTransformers, + }, + }, + ); + + await typedSnapshot(result, "__snapshots__/recursive.gen.ts"); + }); + + it("divide with constName", async() => { + const roleDataParser = DPE.literal(["admin", "editor", "viewer"]).addConstName("userRoleDataParser"); + + const userDataParser = DPE.object({ + id: DPE.templateLiteral(["user-", DPE.number(), "-db1"]), + name: DPE.string().min(2), + email: DPE.email(), + role: roleDataParser, + }).addConstName("userDataParser"); + + const result = render( + userDataParser, + { + constName: "userDataParser", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript: { + identifier: "UserProfile", + transformers: tsDefaultTransformers, + }, + }, + ); + + await typedSnapshot(result, "__snapshots__/withConstName.gen.ts"); + }); +}); diff --git a/package.json b/package.json index e89e5ba..635b278 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,11 @@ "import": "./dist/toJsonSchema/index.mjs", "require": "./dist/toJsonSchema/index.cjs", "types": "./dist/toJsonSchema/index.d.ts" + }, + "./toDataParser": { + "import": "./dist/toDataParser/index.mjs", + "require": "./dist/toDataParser/index.cjs", + "types": "./dist/toDataParser/index.d.ts" } }, "files": [ diff --git a/scripts/index.ts b/scripts/index.ts index 57f1224..87b45e0 100644 --- a/scripts/index.ts +++ b/scripts/index.ts @@ -1,4 +1,5 @@ export * as DataParserToTypescript from "./toTypescript"; export * as DataParserToJsonSchema from "./toJsonSchema"; +export * as DataParserToDataParser from "./toDataParser"; export * from "./utils"; diff --git a/scripts/toDataParser/checkerTransformer/create.ts b/scripts/toDataParser/checkerTransformer/create.ts new file mode 100644 index 0000000..1def70c --- /dev/null +++ b/scripts/toDataParser/checkerTransformer/create.ts @@ -0,0 +1,70 @@ +import type { CallExpression, ObjectLiteralExpression, PropertyAssignment } from "typescript"; +import { type DP, E } from "@duplojs/utils"; + +export type CheckerTransformerSuccessEither = E.Right<"buildSuccess", CallExpression>; + +export type CheckerTransformerCheckerNotSupportedEither = E.Left<"checkerNotSupport", DP.DataParserChecker>; + +export type CheckerTransformerBuildErrorEither = E.Left<"buildCheckerError", DP.DataParserChecker>; + +export type CheckerTransformerEither = + | CheckerTransformerSuccessEither + | CheckerTransformerCheckerNotSupportedEither + | CheckerTransformerBuildErrorEither; + +export interface CheckerTransformerParams { + success( + result: CallExpression, + ): CheckerTransformerSuccessEither; + buildError(): CheckerTransformerBuildErrorEither; + getDefinition( + customProperties?: readonly PropertyAssignment[] + ): readonly [ObjectLiteralExpression] | readonly []; +} + +/** + * @deprecated + */ +export type DataParserCheckers = ( + | DP.DataParserChecker + | DP.DataParserCheckerArrayMax + | DP.DataParserCheckerArrayMin + | DP.DataParserCheckerBigIntMax + | DP.DataParserCheckerBigIntMin + | DP.DataParserCheckerNumberMax + | DP.DataParserCheckerNumberMin + | DP.DataParserCheckerInt + | DP.DataParserCheckerStringMax + | DP.DataParserCheckerStringMin + | DP.DataParserCheckerEmail + | DP.DataParserCheckerRegex + | DP.DataParserCheckerUrl + | DP.DataParserCheckerUuid + | DP.DataParserCheckerRefine + | DP.DataParserCheckerTimeMin + | DP.DataParserCheckerTimeMax +); + +export type CheckerTransformerBuildFunction< + GenericChecker extends DataParserCheckers = DataParserCheckers, +> = ( + checker: GenericChecker, + params: CheckerTransformerParams, +) => CheckerTransformerEither; + +export function createCheckerTransformer< + GenericChecker extends DataParserCheckers, +>( + support: (checker: DataParserCheckers) => checker is GenericChecker, + builder: CheckerTransformerBuildFunction, +) { + return ( + checker: DataParserCheckers, + params: CheckerTransformerParams, + ): CheckerTransformerEither => support(checker) + ? builder( + checker as GenericChecker, + params, + ) + : E.left("checkerNotSupport", checker); +} diff --git a/scripts/toDataParser/checkerTransformer/defaults/array/index.ts b/scripts/toDataParser/checkerTransformer/defaults/array/index.ts new file mode 100644 index 0000000..cc0a179 --- /dev/null +++ b/scripts/toDataParser/checkerTransformer/defaults/array/index.ts @@ -0,0 +1,2 @@ +export * from "./max"; +export * from "./min"; diff --git a/scripts/toDataParser/checkerTransformer/defaults/array/max.ts b/scripts/toDataParser/checkerTransformer/defaults/array/max.ts new file mode 100644 index 0000000..2a76487 --- /dev/null +++ b/scripts/toDataParser/checkerTransformer/defaults/array/max.ts @@ -0,0 +1,28 @@ +import { DP } from "@duplojs/utils"; +import { createCheckerTransformer } from "../../create"; +import { factory } from "typescript"; + +export const checkerArrayMaxTransformer = createCheckerTransformer( + DP.checkerArrayMaxKind.has, + ( + checker, + { + success, + getDefinition, + }, + ) => { + const expression = factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("DP"), + factory.createIdentifier("checkerArrayMax"), + ), + undefined, + [ + factory.createNumericLiteral(checker.definition.max), + ...getDefinition(), + ], + ); + + return success(expression); + }, +); diff --git a/scripts/toDataParser/checkerTransformer/defaults/array/min.ts b/scripts/toDataParser/checkerTransformer/defaults/array/min.ts new file mode 100644 index 0000000..43c5384 --- /dev/null +++ b/scripts/toDataParser/checkerTransformer/defaults/array/min.ts @@ -0,0 +1,28 @@ +import { DP } from "@duplojs/utils"; +import { createCheckerTransformer } from "../../create"; +import { factory } from "typescript"; + +export const checkerArrayMinTransformer = createCheckerTransformer( + DP.checkerArrayMinKind.has, + ( + checker, + { + success, + getDefinition, + }, + ) => { + const expression = factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("DP"), + factory.createIdentifier("checkerArrayMin"), + ), + undefined, + [ + factory.createNumericLiteral(checker.definition.min), + ...getDefinition(), + ], + ); + + return success(expression); + }, +); diff --git a/scripts/toDataParser/checkerTransformer/defaults/bigint/index.ts b/scripts/toDataParser/checkerTransformer/defaults/bigint/index.ts new file mode 100644 index 0000000..cc0a179 --- /dev/null +++ b/scripts/toDataParser/checkerTransformer/defaults/bigint/index.ts @@ -0,0 +1,2 @@ +export * from "./max"; +export * from "./min"; diff --git a/scripts/toDataParser/checkerTransformer/defaults/bigint/max.ts b/scripts/toDataParser/checkerTransformer/defaults/bigint/max.ts new file mode 100644 index 0000000..0be6bf9 --- /dev/null +++ b/scripts/toDataParser/checkerTransformer/defaults/bigint/max.ts @@ -0,0 +1,28 @@ +import { DP } from "@duplojs/utils"; +import { createCheckerTransformer } from "../../create"; +import { factory } from "typescript"; + +export const checkerBigIntMaxTransformer = createCheckerTransformer( + DP.checkerBigIntMaxKind.has, + ( + checker, + { + success, + getDefinition, + }, + ) => { + const expression = factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("DP"), + factory.createIdentifier("checkerBigIntMax"), + ), + undefined, + [ + factory.createBigIntLiteral(`${checker.definition.max.toString()}n`), + ...getDefinition(), + ], + ); + + return success(expression); + }, +); diff --git a/scripts/toDataParser/checkerTransformer/defaults/bigint/min.ts b/scripts/toDataParser/checkerTransformer/defaults/bigint/min.ts new file mode 100644 index 0000000..98c8693 --- /dev/null +++ b/scripts/toDataParser/checkerTransformer/defaults/bigint/min.ts @@ -0,0 +1,28 @@ +import { DP } from "@duplojs/utils"; +import { createCheckerTransformer } from "../../create"; +import { factory } from "typescript"; + +export const checkerBigIntMinTransformer = createCheckerTransformer( + DP.checkerBigIntMinKind.has, + ( + checker, + { + success, + getDefinition, + }, + ) => { + const expression = factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("DP"), + factory.createIdentifier("checkerBigIntMin"), + ), + undefined, + [ + factory.createBigIntLiteral(`${checker.definition.min.toString()}n`), + ...getDefinition(), + ], + ); + + return success(expression); + }, +); diff --git a/scripts/toDataParser/checkerTransformer/defaults/index.ts b/scripts/toDataParser/checkerTransformer/defaults/index.ts new file mode 100644 index 0000000..457426e --- /dev/null +++ b/scripts/toDataParser/checkerTransformer/defaults/index.ts @@ -0,0 +1,36 @@ +export * from "./number"; +export * from "./string"; +export * from "./array"; + +import type { createCheckerTransformer } from "../create"; + +import { checkerIntTransformer, checkerNumberMaxTransformer, checkerNumberMinTransformer } from "./number"; +import { checkerEmailTransformer, checkerRegexTransformer, checkerStringMaxTransformer, checkerStringMinTransformer, checkerUrlTransformer, checkerUuidTransformer } from "./string"; +import { checkerArrayMaxTransformer, checkerArrayMinTransformer } from "./array"; +import { checkerBigIntMaxTransformer, checkerBigIntMinTransformer } from "./bigint"; +import { checkerTimeMaxTransformer, checkerTimeMinTransformer } from "./time"; +import { checkerRefineTransformer } from "./refine"; + +export const defaultCheckerTransformers = [ + checkerRefineTransformer, + // number + checkerNumberMaxTransformer, + checkerNumberMinTransformer, + checkerIntTransformer, + // string + checkerStringMaxTransformer, + checkerStringMinTransformer, + checkerEmailTransformer, + checkerRegexTransformer, + checkerUuidTransformer, + checkerUrlTransformer, + // array + checkerArrayMaxTransformer, + checkerArrayMinTransformer, + // bigint + checkerBigIntMaxTransformer, + checkerBigIntMinTransformer, + // time + checkerTimeMaxTransformer, + checkerTimeMinTransformer, +] as const satisfies readonly ReturnType[]; diff --git a/scripts/toDataParser/checkerTransformer/defaults/number/index.ts b/scripts/toDataParser/checkerTransformer/defaults/number/index.ts new file mode 100644 index 0000000..a077ac2 --- /dev/null +++ b/scripts/toDataParser/checkerTransformer/defaults/number/index.ts @@ -0,0 +1,3 @@ +export * from "./int"; +export * from "./min"; +export * from "./max"; diff --git a/scripts/toDataParser/checkerTransformer/defaults/number/int.ts b/scripts/toDataParser/checkerTransformer/defaults/number/int.ts new file mode 100644 index 0000000..2e875d5 --- /dev/null +++ b/scripts/toDataParser/checkerTransformer/defaults/number/int.ts @@ -0,0 +1,25 @@ +import { DP } from "@duplojs/utils"; +import { createCheckerTransformer } from "../../create"; +import { factory } from "typescript"; + +export const checkerIntTransformer = createCheckerTransformer( + DP.checkerIntKind.has, + ( + checker, + { + success, + getDefinition, + }, + ) => { + const expression = factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("DP"), + factory.createIdentifier("checkerInt"), + ), + undefined, + getDefinition(), + ); + + return success(expression); + }, +); diff --git a/scripts/toDataParser/checkerTransformer/defaults/number/max.ts b/scripts/toDataParser/checkerTransformer/defaults/number/max.ts new file mode 100644 index 0000000..5c2939b --- /dev/null +++ b/scripts/toDataParser/checkerTransformer/defaults/number/max.ts @@ -0,0 +1,39 @@ +import { DP } from "@duplojs/utils"; +import { createCheckerTransformer } from "../../create"; +import { factory, type PropertyAssignment } from "typescript"; + +export const checkerNumberMaxTransformer = createCheckerTransformer( + DP.checkerNumberMaxKind.has, + ( + checker, + { + success, + getDefinition, + }, + ) => { + const checkerDefinition: PropertyAssignment[] = []; + + if (checker.definition.exclusive) { + checkerDefinition.push( + factory.createPropertyAssignment( + factory.createIdentifier("exclusive"), + factory.createTrue(), + ), + ); + } + + const expression = factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("DP"), + factory.createIdentifier("checkerNumberMax"), + ), + undefined, + [ + factory.createNumericLiteral(checker.definition.max), + ...getDefinition(checkerDefinition), + ], + ); + + return success(expression); + }, +); diff --git a/scripts/toDataParser/checkerTransformer/defaults/number/min.ts b/scripts/toDataParser/checkerTransformer/defaults/number/min.ts new file mode 100644 index 0000000..3fef8c7 --- /dev/null +++ b/scripts/toDataParser/checkerTransformer/defaults/number/min.ts @@ -0,0 +1,39 @@ +import { DP } from "@duplojs/utils"; +import { createCheckerTransformer } from "../../create"; +import { factory, type PropertyAssignment } from "typescript"; + +export const checkerNumberMinTransformer = createCheckerTransformer( + DP.checkerNumberMinKind.has, + ( + checker, + { + success, + getDefinition, + }, + ) => { + const checkerDefinition: PropertyAssignment[] = []; + + if (checker.definition.exclusive) { + checkerDefinition.push( + factory.createPropertyAssignment( + factory.createIdentifier("exclusive"), + factory.createTrue(), + ), + ); + } + + const expression = factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("DP"), + factory.createIdentifier("checkerNumberMin"), + ), + undefined, + [ + factory.createNumericLiteral(checker.definition.min), + ...getDefinition(checkerDefinition), + ], + ); + + return success(expression); + }, +); diff --git a/scripts/toDataParser/checkerTransformer/defaults/refine.ts b/scripts/toDataParser/checkerTransformer/defaults/refine.ts new file mode 100644 index 0000000..e4ceab1 --- /dev/null +++ b/scripts/toDataParser/checkerTransformer/defaults/refine.ts @@ -0,0 +1,61 @@ +import { DP } from "@duplojs/utils"; +import { createCheckerTransformer } from "../create"; +import { createSourceFile, factory, isArrowFunction, isExpressionStatement, isFunctionExpression, isParenthesizedExpression, ScriptTarget } from "typescript"; + +export const checkerRefineTransformer = createCheckerTransformer( + DP.dataParserCheckerRefineKind.has, + ( + checker, + { + success, + buildError, + getDefinition, + }, + ) => { + const functionSource = checker.definition.theFunction.toString(); + + if (functionSource.includes("[native code]")) { + return buildError(); + } + + const sourceFile = createSourceFile( + "refine-function.ts", + `(${functionSource})`, + ScriptTarget.Latest, + false, + ); + + const statement = sourceFile.statements[0]; + if (!statement || !isExpressionStatement(statement)) { + return buildError(); + } + + const functionExpression = isParenthesizedExpression(statement.expression) + ? statement.expression.expression + : undefined; + + if ( + !functionExpression + || ( + !isFunctionExpression(functionExpression) + && !isArrowFunction(functionExpression) + ) + ) { + return buildError(); + } + + const expression = factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("DP"), + factory.createIdentifier("checkerRefine"), + ), + undefined, + [ + functionExpression, + ...getDefinition(), + ], + ); + + return success(expression); + }, +); diff --git a/scripts/toDataParser/checkerTransformer/defaults/string/email.ts b/scripts/toDataParser/checkerTransformer/defaults/string/email.ts new file mode 100644 index 0000000..9f7bd25 --- /dev/null +++ b/scripts/toDataParser/checkerTransformer/defaults/string/email.ts @@ -0,0 +1,25 @@ +import { DP } from "@duplojs/utils"; +import { createCheckerTransformer } from "../../create"; +import { factory } from "typescript"; + +export const checkerEmailTransformer = createCheckerTransformer( + DP.checkerEmailKind.has, + ( + checker, + { + success, + getDefinition, + }, + ) => { + const expression = factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("DP"), + factory.createIdentifier("checkerEmail"), + ), + undefined, + getDefinition(), + ); + + return success(expression); + }, +); diff --git a/scripts/toDataParser/checkerTransformer/defaults/string/index.ts b/scripts/toDataParser/checkerTransformer/defaults/string/index.ts new file mode 100644 index 0000000..7713ea4 --- /dev/null +++ b/scripts/toDataParser/checkerTransformer/defaults/string/index.ts @@ -0,0 +1,6 @@ +export * from "./email"; +export * from "./max"; +export * from "./min"; +export * from "./regex"; +export * from "./url"; +export * from "./uuid"; diff --git a/scripts/toDataParser/checkerTransformer/defaults/string/max.ts b/scripts/toDataParser/checkerTransformer/defaults/string/max.ts new file mode 100644 index 0000000..660d4ae --- /dev/null +++ b/scripts/toDataParser/checkerTransformer/defaults/string/max.ts @@ -0,0 +1,28 @@ +import { DP } from "@duplojs/utils"; +import { createCheckerTransformer } from "../../create"; +import { factory } from "typescript"; + +export const checkerStringMaxTransformer = createCheckerTransformer( + DP.checkerStringMaxKind.has, + ( + checker, + { + success, + getDefinition, + }, + ) => { + const expression = factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("DP"), + factory.createIdentifier("checkerStringMax"), + ), + undefined, + [ + factory.createNumericLiteral(checker.definition.max), + ...getDefinition(), + ], + ); + + return success(expression); + }, +); diff --git a/scripts/toDataParser/checkerTransformer/defaults/string/min.ts b/scripts/toDataParser/checkerTransformer/defaults/string/min.ts new file mode 100644 index 0000000..f840880 --- /dev/null +++ b/scripts/toDataParser/checkerTransformer/defaults/string/min.ts @@ -0,0 +1,28 @@ +import { DP } from "@duplojs/utils"; +import { createCheckerTransformer } from "../../create"; +import { factory } from "typescript"; + +export const checkerStringMinTransformer = createCheckerTransformer( + DP.checkerStringMinKind.has, + ( + checker, + { + success, + getDefinition, + }, + ) => { + const expression = factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("DP"), + factory.createIdentifier("checkerStringMin"), + ), + undefined, + [ + factory.createNumericLiteral(checker.definition.min), + ...getDefinition(), + ], + ); + + return success(expression); + }, +); diff --git a/scripts/toDataParser/checkerTransformer/defaults/string/regex.ts b/scripts/toDataParser/checkerTransformer/defaults/string/regex.ts new file mode 100644 index 0000000..ca0346e --- /dev/null +++ b/scripts/toDataParser/checkerTransformer/defaults/string/regex.ts @@ -0,0 +1,32 @@ +import { DP } from "@duplojs/utils"; +import { createCheckerTransformer } from "../../create"; +import { factory } from "typescript"; + +export const checkerRegexTransformer = createCheckerTransformer( + DP.checkerRegexKind.has, + ( + checker, + { + success, + getDefinition, + }, + ) => { + const expression = factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("DP"), + factory.createIdentifier("checkerRegex"), + ), + undefined, + [ + factory.createRegularExpressionLiteral( + `/${checker.definition.regex.source}/${checker.definition.regex.flags}`, + ), + ...getDefinition(), + ], + ); + + return success(expression); + }, +); + +DP.checkerRegex(/^/); diff --git a/scripts/toDataParser/checkerTransformer/defaults/string/url.ts b/scripts/toDataParser/checkerTransformer/defaults/string/url.ts new file mode 100644 index 0000000..f6cfc16 --- /dev/null +++ b/scripts/toDataParser/checkerTransformer/defaults/string/url.ts @@ -0,0 +1,58 @@ +import { DP } from "@duplojs/utils"; +import { createCheckerTransformer } from "../../create"; +import { factory, type PropertyAssignment } from "typescript"; + +export const checkerUrlTransformer = createCheckerTransformer( + DP.checkerUrlKind.has, + ( + checker, + { + success, + getDefinition, + }, + ) => { + const checkerDefinition: PropertyAssignment[] = []; + + if (checker.definition.hostname) { + checkerDefinition.push( + factory.createPropertyAssignment( + factory.createIdentifier("hostname"), + factory.createRegularExpressionLiteral( + `/${checker.definition.hostname.source}/${checker.definition.hostname.flags}`, + ), + ), + ); + } + + if (checker.definition.normalize) { + checkerDefinition.push( + factory.createPropertyAssignment( + factory.createIdentifier("normalize"), + factory.createTrue(), + ), + ); + } + + if (checker.definition.protocol) { + checkerDefinition.push( + factory.createPropertyAssignment( + factory.createIdentifier("protocol"), + factory.createRegularExpressionLiteral( + `/${checker.definition.protocol.source}/${checker.definition.protocol.flags}`, + ), + ), + ); + } + + const expression = factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("DP"), + factory.createIdentifier("checkerUrl"), + ), + undefined, + getDefinition(checkerDefinition), + ); + + return success(expression); + }, +); diff --git a/scripts/toDataParser/checkerTransformer/defaults/string/uuid.ts b/scripts/toDataParser/checkerTransformer/defaults/string/uuid.ts new file mode 100644 index 0000000..a488c41 --- /dev/null +++ b/scripts/toDataParser/checkerTransformer/defaults/string/uuid.ts @@ -0,0 +1,25 @@ +import { DP } from "@duplojs/utils"; +import { createCheckerTransformer } from "../../create"; +import { factory } from "typescript"; + +export const checkerUuidTransformer = createCheckerTransformer( + DP.checkerUuidKind.has, + ( + checker, + { + success, + getDefinition, + }, + ) => { + const expression = factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("DP"), + factory.createIdentifier("checkerUuid"), + ), + undefined, + getDefinition(), + ); + + return success(expression); + }, +); diff --git a/scripts/toDataParser/checkerTransformer/defaults/time/index.ts b/scripts/toDataParser/checkerTransformer/defaults/time/index.ts new file mode 100644 index 0000000..cc0a179 --- /dev/null +++ b/scripts/toDataParser/checkerTransformer/defaults/time/index.ts @@ -0,0 +1,2 @@ +export * from "./max"; +export * from "./min"; diff --git a/scripts/toDataParser/checkerTransformer/defaults/time/max.ts b/scripts/toDataParser/checkerTransformer/defaults/time/max.ts new file mode 100644 index 0000000..e5100cf --- /dev/null +++ b/scripts/toDataParser/checkerTransformer/defaults/time/max.ts @@ -0,0 +1,38 @@ +import { DP } from "@duplojs/utils"; +import { createCheckerTransformer } from "../../create"; +import { factory } from "typescript"; + +export const checkerTimeMaxTransformer = createCheckerTransformer( + DP.checkerTimeMaxKind.has, + ( + checker, + { + success, + getDefinition, + }, + ) => { + const expression = factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("DP"), + factory.createIdentifier("checkerTimeMax"), + ), + undefined, + [ + factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("DDate"), + factory.createIdentifier("createTime"), + ), + undefined, + [ + factory.createNumericLiteral(checker.definition.max.toNative()), + factory.createStringLiteral("millisecond"), + ], + ), + ...getDefinition(), + ], + ); + + return success(expression); + }, +); diff --git a/scripts/toDataParser/checkerTransformer/defaults/time/min.ts b/scripts/toDataParser/checkerTransformer/defaults/time/min.ts new file mode 100644 index 0000000..6edc1c6 --- /dev/null +++ b/scripts/toDataParser/checkerTransformer/defaults/time/min.ts @@ -0,0 +1,38 @@ +import { DP } from "@duplojs/utils"; +import { createCheckerTransformer } from "../../create"; +import { factory } from "typescript"; + +export const checkerTimeMinTransformer = createCheckerTransformer( + DP.checkerTimeMinKind.has, + ( + checker, + { + success, + getDefinition, + }, + ) => { + const expression = factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("DP"), + factory.createIdentifier("checkerTimeMin"), + ), + undefined, + [ + factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("DDate"), + factory.createIdentifier("createTime"), + ), + undefined, + [ + factory.createNumericLiteral(checker.definition.min.toNative()), + factory.createStringLiteral("millisecond"), + ], + ), + ...getDefinition(), + ], + ); + + return success(expression); + }, +); diff --git a/scripts/toDataParser/checkerTransformer/index.ts b/scripts/toDataParser/checkerTransformer/index.ts new file mode 100644 index 0000000..a51ba64 --- /dev/null +++ b/scripts/toDataParser/checkerTransformer/index.ts @@ -0,0 +1,3 @@ +export * from "./create"; +export * from "./transformer"; +export * from "./defaults"; diff --git a/scripts/toDataParser/checkerTransformer/transformer.ts b/scripts/toDataParser/checkerTransformer/transformer.ts new file mode 100644 index 0000000..8c69945 --- /dev/null +++ b/scripts/toDataParser/checkerTransformer/transformer.ts @@ -0,0 +1,71 @@ +import { A, E, type DP } from "@duplojs/utils"; +import type { CheckerTransformerParams, createCheckerTransformer, CheckerTransformerEither } from "./create"; +import { factory, type PropertyAssignment } from "typescript"; + +export interface CheckerTransformerFunctionParams { + readonly transformers: readonly ReturnType[]; +} + +export function getCheckerDefinition(checker: DP.DataParserChecker, customProperties?: readonly PropertyAssignment[]) { + const propertyAssignments: PropertyAssignment[] = []; + + if (checker.definition.errorMessage) { + propertyAssignments.push( + factory.createPropertyAssignment( + factory.createIdentifier("errorMessage"), + factory.createStringLiteral(checker.definition.errorMessage), + ), + ); + } + + if (customProperties) { + propertyAssignments.push(...customProperties); + } + + return A.minElements(propertyAssignments, 1) + ? [factory.createObjectLiteralExpression(propertyAssignments)] + : []; +} + +export function checkerTransformer( + checker: DP.DataParserChecker, + params: CheckerTransformerFunctionParams, +) { + const functionParams: CheckerTransformerParams = { + success(result) { + return E.right("buildSuccess", result); + }, + buildError() { + return E.left("buildCheckerError", checker); + }, + getDefinition(customProperties) { + return getCheckerDefinition(checker, customProperties); + }, + }; + + return A.reduce( + params.transformers, + A.reduceFrom( + E.left("checkerNotSupport", checker), + ), + ({ + element: functionBuilder, + lastValue, + next, + exit, + }) => { + const result = functionBuilder(checker, functionParams); + + if (E.isLeft(result)) { + if (!E.hasInformation(result, "checkerNotSupport")) { + return exit(result); + } + + return next(lastValue); + } + + return exit(result); + }, + ); +} + diff --git a/scripts/toDataParser/dataParserTransformer/create.ts b/scripts/toDataParser/dataParserTransformer/create.ts new file mode 100644 index 0000000..43bdeef --- /dev/null +++ b/scripts/toDataParser/dataParserTransformer/create.ts @@ -0,0 +1,78 @@ +import type { CallExpression, Identifier, ObjectLiteralExpression, PropertyAssignment } from "typescript"; +import { type DP, E } from "@duplojs/utils"; +import type { CheckerTransformerBuildErrorEither, CheckerTransformerCheckerNotSupportedEither } from "../checkerTransformer"; + +export type TransformerSuccessEither = E.Right<"buildSuccess", CallExpression | Identifier>; + +export type DataParserNotSupportedEither = E.Left<"dataParserNotSupport", DP.DataParser>; + +export type DataParserErrorEither = E.Left<"buildDataParserError", DP.DataParser>; + +export type DataParserGetDefinitionErrorEither = E.Left< + "buildDataParserGetDefinitionError", + { + dataParser: DP.DataParser; + error: CheckerTransformerCheckerNotSupportedEither | CheckerTransformerBuildErrorEither; + } +>; + +export interface MapContextValue { + readonly constName: Identifier; + readonly expression: CallExpression | Identifier; +} + +export type MapContext = Map; + +export type MapImportClause = Map; + +export type MaybeTransformerEither = + | TransformerSuccessEither + | DataParserNotSupportedEither + | DataParserErrorEither + | DataParserGetDefinitionErrorEither; + +export interface TransformerParams { + readonly dependencyIdentifier: Identifier; + readonly context: MapContext; + readonly importClause: MapImportClause; + readonly indent: boolean; + + transformer( + dataParser: DP.DataParser, + ): MaybeTransformerEither; + + success( + result: CallExpression | Identifier, + ): TransformerSuccessEither; + + buildError(): DataParserErrorEither; + addImportClause(path: string, clause: string): void; + getDefinition( + customProperties?: readonly PropertyAssignment[], + indent?: boolean, + ): readonly [ObjectLiteralExpression] | readonly [] | DataParserGetDefinitionErrorEither; +} + +export type TransformerBuildFunction< + GenericDataParser extends DP.DataParsers = DP.DataParsers, +> = ( + dataParser: GenericDataParser, + params: TransformerParams, +) => MaybeTransformerEither; + +export function createTransformer< + GenericDataParser extends DP.DataParsers, +>( + support: (dataParser: DP.DataParsers) => dataParser is GenericDataParser, + builder: TransformerBuildFunction, +) { + return ( + dataParser: DP.DataParsers, + params: TransformerParams, + ): MaybeTransformerEither => support(dataParser) + ? builder( + dataParser, + params, + ) + : E.left("dataParserNotSupport", dataParser); +} diff --git a/scripts/toDataParser/dataParserTransformer/defaults/array.ts b/scripts/toDataParser/dataParserTransformer/defaults/array.ts new file mode 100644 index 0000000..0fb3aa2 --- /dev/null +++ b/scripts/toDataParser/dataParserTransformer/defaults/array.ts @@ -0,0 +1,43 @@ +import { DP, E, pipe, unwrap } from "@duplojs/utils"; +import { createTransformer } from "../create"; +import { factory } from "typescript"; + +export const arrayTransformer = createTransformer( + DP.arrayKind.has, + ( + dataParser, + { + success, + dependencyIdentifier, + getDefinition, + transformer, + }, + ) => { + const element = transformer(dataParser.definition.element); + + if (E.isLeft(element)) { + return element; + } + + const definition = getDefinition(); + + if (E.isLeft(definition)) { + return definition; + } + + return pipe( + factory.createCallExpression( + factory.createPropertyAccessExpression( + dependencyIdentifier, + factory.createIdentifier("array"), + ), + undefined, + [ + unwrap(element), + ...definition, + ], + ), + success, + ); + }, +); diff --git a/scripts/toDataParser/dataParserTransformer/defaults/bigint.ts b/scripts/toDataParser/dataParserTransformer/defaults/bigint.ts new file mode 100644 index 0000000..ddccd28 --- /dev/null +++ b/scripts/toDataParser/dataParserTransformer/defaults/bigint.ts @@ -0,0 +1,42 @@ +import { DP, E, pipe } from "@duplojs/utils"; +import { createTransformer } from "../create"; +import { factory } from "typescript"; + +export const bigIntTransformer = createTransformer( + DP.bigIntKind.has, + ( + dataParser, + { + success, + dependencyIdentifier, + getDefinition, + }, + ) => { + const definition = getDefinition( + dataParser.definition.coerce + ? [ + factory.createPropertyAssignment( + factory.createIdentifier("coerce"), + factory.createTrue(), + ), + ] + : undefined, + ); + + if (E.isLeft(definition)) { + return definition; + } + + return pipe( + factory.createCallExpression( + factory.createPropertyAccessExpression( + dependencyIdentifier, + factory.createIdentifier("bigint"), + ), + undefined, + definition, + ), + success, + ); + }, +); diff --git a/scripts/toDataParser/dataParserTransformer/defaults/boolean.ts b/scripts/toDataParser/dataParserTransformer/defaults/boolean.ts new file mode 100644 index 0000000..cf38b21 --- /dev/null +++ b/scripts/toDataParser/dataParserTransformer/defaults/boolean.ts @@ -0,0 +1,42 @@ +import { DP, E, pipe } from "@duplojs/utils"; +import { createTransformer } from "../create"; +import { factory } from "typescript"; + +export const booleanTransformer = createTransformer( + DP.booleanKind.has, + ( + dataParser, + { + success, + dependencyIdentifier, + getDefinition, + }, + ) => { + const definition = getDefinition( + dataParser.definition.coerce + ? [ + factory.createPropertyAssignment( + factory.createIdentifier("coerce"), + factory.createTrue(), + ), + ] + : undefined, + ); + + if (E.isLeft(definition)) { + return definition; + } + + return pipe( + factory.createCallExpression( + factory.createPropertyAccessExpression( + dependencyIdentifier, + factory.createIdentifier("boolean"), + ), + undefined, + definition, + ), + success, + ); + }, +); diff --git a/scripts/toDataParser/dataParserTransformer/defaults/date.ts b/scripts/toDataParser/dataParserTransformer/defaults/date.ts new file mode 100644 index 0000000..cc3d154 --- /dev/null +++ b/scripts/toDataParser/dataParserTransformer/defaults/date.ts @@ -0,0 +1,42 @@ +import { DP, E, pipe } from "@duplojs/utils"; +import { createTransformer } from "../create"; +import { factory } from "typescript"; + +export const dateTransformer = createTransformer( + DP.dateKind.has, + ( + dataParser, + { + success, + dependencyIdentifier, + getDefinition, + }, + ) => { + const definition = getDefinition( + dataParser.definition.coerce + ? [ + factory.createPropertyAssignment( + factory.createIdentifier("coerce"), + factory.createTrue(), + ), + ] + : undefined, + ); + + if (E.isLeft(definition)) { + return definition; + } + + return pipe( + factory.createCallExpression( + factory.createPropertyAccessExpression( + dependencyIdentifier, + factory.createIdentifier("date"), + ), + undefined, + definition, + ), + success, + ); + }, +); diff --git a/scripts/toDataParser/dataParserTransformer/defaults/empty.ts b/scripts/toDataParser/dataParserTransformer/defaults/empty.ts new file mode 100644 index 0000000..357d8ef --- /dev/null +++ b/scripts/toDataParser/dataParserTransformer/defaults/empty.ts @@ -0,0 +1,42 @@ +import { DP, E, pipe } from "@duplojs/utils"; +import { createTransformer } from "../create"; +import { factory } from "typescript"; + +export const emptyTransformer = createTransformer( + DP.emptyKind.has, + ( + dataParser, + { + success, + dependencyIdentifier, + getDefinition, + }, + ) => { + const definition = getDefinition( + dataParser.definition.coerce + ? [ + factory.createPropertyAssignment( + factory.createIdentifier("coerce"), + factory.createTrue(), + ), + ] + : undefined, + ); + + if (E.isLeft(definition)) { + return definition; + } + + return pipe( + factory.createCallExpression( + factory.createPropertyAccessExpression( + dependencyIdentifier, + factory.createIdentifier("empty"), + ), + undefined, + definition, + ), + success, + ); + }, +); diff --git a/scripts/toDataParser/dataParserTransformer/defaults/file.ts b/scripts/toDataParser/dataParserTransformer/defaults/file.ts new file mode 100644 index 0000000..335d32a --- /dev/null +++ b/scripts/toDataParser/dataParserTransformer/defaults/file.ts @@ -0,0 +1,99 @@ +import { A, E, justExec, pipe } from "@duplojs/utils"; +import { createTransformer } from "../create"; +import { factory, type PropertyAssignment } from "typescript"; +import { SDP } from "@duplojs/server-utils"; + +export const fileTransformer = createTransformer( + SDP.fileKind.has, + ( + dataParser, + { + success, + dependencyIdentifier, + getDefinition, + addImportClause, + indent, + }, + ) => { + const definition = getDefinition(); + + if (E.isLeft(definition)) { + return definition; + } + + const dataParserFileParams: PropertyAssignment[] = []; + + if (dataParser.definition.coerce) { + dataParserFileParams.push( + factory.createPropertyAssignment( + factory.createIdentifier("coerce"), + factory.createTrue(), + ), + ); + } + if (dataParser.definition.checkExist) { + dataParserFileParams.push( + factory.createPropertyAssignment( + factory.createIdentifier("checkExist"), + factory.createTrue(), + ), + ); + } + if (dataParser.definition.maxSize) { + dataParserFileParams.push( + factory.createPropertyAssignment( + factory.createIdentifier("maxSize"), + factory.createNumericLiteral(dataParser.definition.maxSize), + ), + ); + } + if (dataParser.definition.minSize) { + dataParserFileParams.push( + factory.createPropertyAssignment( + factory.createIdentifier("minSize"), + factory.createNumericLiteral(dataParser.definition.minSize), + ), + ); + } + if (dataParser.definition.mimeType) { + dataParserFileParams.push( + factory.createPropertyAssignment( + factory.createIdentifier("mimeType"), + factory.createRegularExpressionLiteral(dataParser.definition.mimeType.toString()), + ), + ); + } + + const namespace = dependencyIdentifier.text === "DP" + ? justExec( + () => { + addImportClause("@duplojs/server-utils/dataParser", "SDP"); + return "SDP"; + }, + ) + : justExec( + () => { + addImportClause("@duplojs/server-utils/dataParserExtended", "SDPE"); + return "SDPE"; + }, + ); + + return pipe( + factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier(namespace), + factory.createIdentifier("file"), + ), + undefined, + [ + factory.createObjectLiteralExpression( + dataParserFileParams, + indent && A.minElements(dataParserFileParams, 2), + ), + ...definition, + ], + ), + success, + ); + }, +); diff --git a/scripts/toDataParser/dataParserTransformer/defaults/index.ts b/scripts/toDataParser/dataParserTransformer/defaults/index.ts new file mode 100644 index 0000000..2b30fdc --- /dev/null +++ b/scripts/toDataParser/dataParserTransformer/defaults/index.ts @@ -0,0 +1,74 @@ +export * from "./array"; +export * from "./bigint"; +export * from "./boolean"; +export * from "./date"; +export * from "./empty"; +export * from "./file"; +export * from "./lazy"; +export * from "./literal"; +export * from "./nil"; +export * from "./nullable"; +export * from "./number"; +export * from "./object"; +export * from "./optional"; +export * from "./pipe"; +export * from "./record"; +export * from "./recover"; +export * from "./string"; +export * from "./templateLiteral"; +export * from "./time"; +export * from "./transform"; +export * from "./tuple"; +export * from "./union"; +export * from "./unknown"; + +import type { createTransformer } from "../create"; +import { arrayTransformer } from "./array"; +import { bigIntTransformer } from "./bigint"; +import { booleanTransformer } from "./boolean"; +import { dateTransformer } from "./date"; +import { emptyTransformer } from "./empty"; +import { fileTransformer } from "./file"; +import { lazyTransformer } from "./lazy"; +import { literalTransformer } from "./literal"; +import { nilTransformer } from "./nil"; +import { nullableTransformer } from "./nullable"; +import { numberTransformer } from "./number"; +import { objectTransformer } from "./object"; +import { optionalTransformer } from "./optional"; +import { pipeTransformer } from "./pipe"; +import { recordTransformer } from "./record"; +import { recoverTransformer } from "./recover"; +import { stringTransformer } from "./string"; +import { templateLiteralTransformer } from "./templateLiteral"; +import { timeTransformer } from "./time"; +import { transformTransformer } from "./transform"; +import { tupleTransformer } from "./tuple"; +import { unionTransformer } from "./union"; +import { unknownTransformer } from "./unknown"; + +export const defaultTransformers = [ + arrayTransformer, + bigIntTransformer, + booleanTransformer, + dateTransformer, + emptyTransformer, + fileTransformer, + lazyTransformer, + literalTransformer, + nilTransformer, + nullableTransformer, + numberTransformer, + objectTransformer, + optionalTransformer, + pipeTransformer, + recordTransformer, + recoverTransformer, + stringTransformer, + templateLiteralTransformer, + timeTransformer, + transformTransformer, + tupleTransformer, + unionTransformer, + unknownTransformer, +] as const satisfies readonly ReturnType[]; diff --git a/scripts/toDataParser/dataParserTransformer/defaults/lazy.ts b/scripts/toDataParser/dataParserTransformer/defaults/lazy.ts new file mode 100644 index 0000000..2f85b27 --- /dev/null +++ b/scripts/toDataParser/dataParserTransformer/defaults/lazy.ts @@ -0,0 +1,50 @@ +import { DP, E, pipe, unwrap } from "@duplojs/utils"; +import { createTransformer } from "../create"; +import { factory, SyntaxKind } from "typescript"; + +export const lazyTransformer = createTransformer( + DP.lazyKind.has, + ( + dataParser, + { + success, + dependencyIdentifier, + getDefinition, + transformer, + }, + ) => { + const getter = transformer(dataParser.definition.getter.value); + + if (E.isLeft(getter)) { + return getter; + } + + const definition = getDefinition(); + + if (E.isLeft(definition)) { + return definition; + } + + return pipe( + factory.createCallExpression( + factory.createPropertyAccessExpression( + dependencyIdentifier, + factory.createIdentifier("lazy"), + ), + undefined, + [ + factory.createArrowFunction( + undefined, + undefined, + [], + undefined, + factory.createToken(SyntaxKind.EqualsGreaterThanToken), + unwrap(getter), + ), + ...definition, + ], + ), + success, + ); + }, +); diff --git a/scripts/toDataParser/dataParserTransformer/defaults/literal.ts b/scripts/toDataParser/dataParserTransformer/defaults/literal.ts new file mode 100644 index 0000000..07ec58f --- /dev/null +++ b/scripts/toDataParser/dataParserTransformer/defaults/literal.ts @@ -0,0 +1,72 @@ +import { A, DP, E, isType, P, pipe, pipeCall } from "@duplojs/utils"; +import { createTransformer } from "../create"; +import { factory } from "typescript"; + +export const literalTransformer = createTransformer( + DP.literalKind.has, + ( + dataParser, + { + success, + dependencyIdentifier, + getDefinition, + indent, + }, + ) => { + const definition = getDefinition(); + + if (E.isLeft(definition)) { + return definition; + } + + const values = A.map( + dataParser.definition.value, + (element) => P.match(element) + .when( + isType("string"), + factory.createStringLiteral, + ) + .when( + isType("bigint"), + (value) => factory.createBigIntLiteral(`${value.toString()}n`), + ) + .when( + isType("number"), + pipeCall(factory.createNumericLiteral), + ) + .when( + isType("boolean"), + (value) => value + ? factory.createTrue() + : factory.createFalse(), + ) + .when( + isType("null"), + () => factory.createNull(), + ) + .when( + isType("undefined"), + () => factory.createIdentifier("undefined"), + ) + .exhaustive(), + ); + + return pipe( + factory.createCallExpression( + factory.createPropertyAccessExpression( + dependencyIdentifier, + factory.createIdentifier("literal"), + ), + undefined, + [ + factory.createArrayLiteralExpression( + values, + indent && A.minElements(values, 4), + ), + ...definition, + ], + ), + success, + ); + }, +); diff --git a/scripts/toDataParser/dataParserTransformer/defaults/nil.ts b/scripts/toDataParser/dataParserTransformer/defaults/nil.ts new file mode 100644 index 0000000..6a256b8 --- /dev/null +++ b/scripts/toDataParser/dataParserTransformer/defaults/nil.ts @@ -0,0 +1,42 @@ +import { DP, E, pipe } from "@duplojs/utils"; +import { createTransformer } from "../create"; +import { factory } from "typescript"; + +export const nilTransformer = createTransformer( + DP.nilKind.has, + ( + dataParser, + { + success, + dependencyIdentifier, + getDefinition, + }, + ) => { + const definition = getDefinition( + dataParser.definition.coerce + ? [ + factory.createPropertyAssignment( + factory.createIdentifier("coerce"), + factory.createTrue(), + ), + ] + : undefined, + ); + + if (E.isLeft(definition)) { + return definition; + } + + return pipe( + factory.createCallExpression( + factory.createPropertyAccessExpression( + dependencyIdentifier, + factory.createIdentifier("nil"), + ), + undefined, + definition, + ), + success, + ); + }, +); diff --git a/scripts/toDataParser/dataParserTransformer/defaults/nullable.ts b/scripts/toDataParser/dataParserTransformer/defaults/nullable.ts new file mode 100644 index 0000000..f803578 --- /dev/null +++ b/scripts/toDataParser/dataParserTransformer/defaults/nullable.ts @@ -0,0 +1,43 @@ +import { DP, E, pipe, unwrap } from "@duplojs/utils"; +import { createTransformer } from "../create"; +import { factory } from "typescript"; + +export const nullableTransformer = createTransformer( + DP.nullableKind.has, + ( + dataParser, + { + success, + dependencyIdentifier, + getDefinition, + transformer, + }, + ) => { + const inner = transformer(dataParser.definition.inner); + + if (E.isLeft(inner)) { + return inner; + } + + const definition = getDefinition(); + + if (E.isLeft(definition)) { + return definition; + } + + return pipe( + factory.createCallExpression( + factory.createPropertyAccessExpression( + dependencyIdentifier, + factory.createIdentifier("nullable"), + ), + undefined, + [ + unwrap(inner), + ...definition, + ], + ), + success, + ); + }, +); diff --git a/scripts/toDataParser/dataParserTransformer/defaults/number.ts b/scripts/toDataParser/dataParserTransformer/defaults/number.ts new file mode 100644 index 0000000..6a9aa1d --- /dev/null +++ b/scripts/toDataParser/dataParserTransformer/defaults/number.ts @@ -0,0 +1,42 @@ +import { DP, E, pipe } from "@duplojs/utils"; +import { createTransformer } from "../create"; +import { factory } from "typescript"; + +export const numberTransformer = createTransformer( + DP.numberKind.has, + ( + dataParser, + { + success, + dependencyIdentifier, + getDefinition, + }, + ) => { + const definition = getDefinition( + dataParser.definition.coerce + ? [ + factory.createPropertyAssignment( + factory.createIdentifier("coerce"), + factory.createTrue(), + ), + ] + : undefined, + ); + + if (E.isLeft(definition)) { + return definition; + } + + return pipe( + factory.createCallExpression( + factory.createPropertyAccessExpression( + dependencyIdentifier, + factory.createIdentifier("number"), + ), + undefined, + definition, + ), + success, + ); + }, +); diff --git a/scripts/toDataParser/dataParserTransformer/defaults/object.ts b/scripts/toDataParser/dataParserTransformer/defaults/object.ts new file mode 100644 index 0000000..3bf0ce3 --- /dev/null +++ b/scripts/toDataParser/dataParserTransformer/defaults/object.ts @@ -0,0 +1,70 @@ +import { A, DP, E, O, pipe, when } from "@duplojs/utils"; +import { createTransformer } from "../create"; +import { factory, type PropertyAssignment } from "typescript"; + +export const objectTransformer = createTransformer( + DP.objectKind.has, + ( + dataParser, + { + success, + dependencyIdentifier, + getDefinition, + transformer, + indent, + }, + ) => { + const shape = pipe( + dataParser.definition.shape, + O.entries, + A.reduce( + A.reduceFrom([]), + ({ element: [identifier, parser], lastValue, nextPush, exit }) => pipe( + parser, + transformer, + when( + E.isLeft, + exit, + ), + E.whenIsRight( + (result) => nextPush( + lastValue, + factory.createPropertyAssignment( + factory.createIdentifier(identifier), + result, + ), + ), + ), + ), + ), + ); + + if (E.isLeft(shape)) { + return shape; + } + + const definition = getDefinition(); + + if (E.isLeft(definition)) { + return definition; + } + + return pipe( + factory.createCallExpression( + factory.createPropertyAccessExpression( + dependencyIdentifier, + factory.createIdentifier("object"), + ), + undefined, + [ + factory.createObjectLiteralExpression( + shape, + indent, + ), + ...definition, + ], + ), + success, + ); + }, +); diff --git a/scripts/toDataParser/dataParserTransformer/defaults/optional.ts b/scripts/toDataParser/dataParserTransformer/defaults/optional.ts new file mode 100644 index 0000000..1798e47 --- /dev/null +++ b/scripts/toDataParser/dataParserTransformer/defaults/optional.ts @@ -0,0 +1,43 @@ +import { DP, E, pipe, unwrap } from "@duplojs/utils"; +import { createTransformer } from "../create"; +import { factory } from "typescript"; + +export const optionalTransformer = createTransformer( + DP.optionalKind.has, + ( + dataParser, + { + success, + dependencyIdentifier, + getDefinition, + transformer, + }, + ) => { + const inner = transformer(dataParser.definition.inner); + + if (E.isLeft(inner)) { + return inner; + } + + const definition = getDefinition(); + + if (E.isLeft(definition)) { + return definition; + } + + return pipe( + factory.createCallExpression( + factory.createPropertyAccessExpression( + dependencyIdentifier, + factory.createIdentifier("optional"), + ), + undefined, + [ + unwrap(inner), + ...definition, + ], + ), + success, + ); + }, +); diff --git a/scripts/toDataParser/dataParserTransformer/defaults/pipe.ts b/scripts/toDataParser/dataParserTransformer/defaults/pipe.ts new file mode 100644 index 0000000..0ca443f --- /dev/null +++ b/scripts/toDataParser/dataParserTransformer/defaults/pipe.ts @@ -0,0 +1,50 @@ +import { DP, E, pipe, unwrap } from "@duplojs/utils"; +import { createTransformer } from "../create"; +import { factory } from "typescript"; + +export const pipeTransformer = createTransformer( + DP.pipeKind.has, + ( + dataParser, + { + success, + dependencyIdentifier, + getDefinition, + transformer, + }, + ) => { + const input = transformer(dataParser.definition.input); + + if (E.isLeft(input)) { + return input; + } + + const output = transformer(dataParser.definition.output); + + if (E.isLeft(output)) { + return output; + } + + const definition = getDefinition(); + + if (E.isLeft(definition)) { + return definition; + } + + return pipe( + factory.createCallExpression( + factory.createPropertyAccessExpression( + dependencyIdentifier, + factory.createIdentifier("pipe"), + ), + undefined, + [ + unwrap(input), + unwrap(output), + ...definition, + ], + ), + success, + ); + }, +); diff --git a/scripts/toDataParser/dataParserTransformer/defaults/record.ts b/scripts/toDataParser/dataParserTransformer/defaults/record.ts new file mode 100644 index 0000000..31929c1 --- /dev/null +++ b/scripts/toDataParser/dataParserTransformer/defaults/record.ts @@ -0,0 +1,50 @@ +import { DP, E, pipe, unwrap } from "@duplojs/utils"; +import { createTransformer } from "../create"; +import { factory } from "typescript"; + +export const recordTransformer = createTransformer( + DP.recordKind.has, + ( + dataParser, + { + success, + dependencyIdentifier, + getDefinition, + transformer, + }, + ) => { + const key = transformer(dataParser.definition.key); + + if (E.isLeft(key)) { + return key; + } + + const value = transformer(dataParser.definition.value); + + if (E.isLeft(value)) { + return value; + } + + const definition = getDefinition(); + + if (E.isLeft(definition)) { + return definition; + } + + return pipe( + factory.createCallExpression( + factory.createPropertyAccessExpression( + dependencyIdentifier, + factory.createIdentifier("record"), + ), + undefined, + [ + unwrap(key), + unwrap(value), + ...definition, + ], + ), + success, + ); + }, +); diff --git a/scripts/toDataParser/dataParserTransformer/defaults/recover.ts b/scripts/toDataParser/dataParserTransformer/defaults/recover.ts new file mode 100644 index 0000000..b787a27 --- /dev/null +++ b/scripts/toDataParser/dataParserTransformer/defaults/recover.ts @@ -0,0 +1,12 @@ +import { DP } from "@duplojs/utils"; +import { createTransformer } from "../create"; + +export const recoverTransformer = createTransformer( + DP.recoverKind.has, + ( + dataParser, + { + transformer, + }, + ) => transformer(dataParser.definition.inner), +); diff --git a/scripts/toDataParser/dataParserTransformer/defaults/string.ts b/scripts/toDataParser/dataParserTransformer/defaults/string.ts new file mode 100644 index 0000000..9b17658 --- /dev/null +++ b/scripts/toDataParser/dataParserTransformer/defaults/string.ts @@ -0,0 +1,33 @@ +import { DP, E, pipe } from "@duplojs/utils"; +import { createTransformer } from "../create"; +import { factory } from "typescript"; + +export const stringTransformer = createTransformer( + DP.stringKind.has, + ( + dataParser, + { + success, + dependencyIdentifier, + getDefinition, + }, + ) => { + const definition = getDefinition(); + + if (E.isLeft(definition)) { + return definition; + } + + return pipe( + factory.createCallExpression( + factory.createPropertyAccessExpression( + dependencyIdentifier, + factory.createIdentifier("string"), + ), + undefined, + definition, + ), + success, + ); + }, +); diff --git a/scripts/toDataParser/dataParserTransformer/defaults/templateLiteral.ts b/scripts/toDataParser/dataParserTransformer/defaults/templateLiteral.ts new file mode 100644 index 0000000..bbc589c --- /dev/null +++ b/scripts/toDataParser/dataParserTransformer/defaults/templateLiteral.ts @@ -0,0 +1,92 @@ +import { A, DP, E, isType, P, pipe, pipeCall, unwrap } from "@duplojs/utils"; +import { createTransformer } from "../create"; +import { type Expression, factory } from "typescript"; + +export const templateLiteralTransformer = createTransformer( + DP.templateLiteralKind.has, + ( + dataParser, + { + success, + dependencyIdentifier, + getDefinition, + transformer, + indent, + }, + ) => { + const parts = A.reduce( + dataParser.definition.template, + A.reduceFrom([]), + ({ element, lastValue, nextPush, exit }) => { + if (DP.dataParserKind.has(element)) { + const transformResult = transformer(element); + + if (E.isLeft(transformResult)) { + return exit(transformResult); + } + + return nextPush(lastValue, unwrap(transformResult)); + } + + const result = P.match(element) + .when( + isType("bigint"), + (value) => factory.createBigIntLiteral(`${value.toString()}n`), + ) + .when( + isType("boolean"), + (value) => value + ? factory.createTrue() + : factory.createFalse(), + ) + .when( + isType("string"), + pipeCall(factory.createStringLiteral), + ) + .when( + isType("number"), + pipeCall(factory.createNumericLiteral), + ) + .when( + isType("null"), + () => factory.createNull(), + ) + .when( + isType("undefined"), + () => factory.createIdentifier("undefined"), + ) + .exhaustive(); + + return nextPush(lastValue, result); + }, + ); + + if (E.isLeft(parts)) { + return parts; + } + + const definition = getDefinition(); + + if (E.isLeft(definition)) { + return definition; + } + + return pipe( + factory.createCallExpression( + factory.createPropertyAccessExpression( + dependencyIdentifier, + factory.createIdentifier("templateLiteral"), + ), + undefined, + [ + factory.createArrayLiteralExpression( + parts, + indent && A.minElements(parts, 4), + ), + ...definition, + ], + ), + success, + ); + }, +); diff --git a/scripts/toDataParser/dataParserTransformer/defaults/time.ts b/scripts/toDataParser/dataParserTransformer/defaults/time.ts new file mode 100644 index 0000000..957dc1e --- /dev/null +++ b/scripts/toDataParser/dataParserTransformer/defaults/time.ts @@ -0,0 +1,47 @@ +import { A, DP, E, pipe } from "@duplojs/utils"; +import { createTransformer } from "../create"; +import { factory } from "typescript"; + +export const timeTransformer = createTransformer( + DP.timeKind.has, + ( + dataParser, + { + success, + dependencyIdentifier, + getDefinition, + addImportClause, + }, + ) => { + const definition = getDefinition( + dataParser.definition.coerce + ? [ + factory.createPropertyAssignment( + factory.createIdentifier("coerce"), + factory.createTrue(), + ), + ] + : undefined, + ); + + if (E.isLeft(definition)) { + return definition; + } + + if (A.minElements(dataParser.definition.checkers, 1)) { + addImportClause("@duplojs/utils/date", "DDate"); + } + + return pipe( + factory.createCallExpression( + factory.createPropertyAccessExpression( + dependencyIdentifier, + factory.createIdentifier("time"), + ), + undefined, + definition, + ), + success, + ); + }, +); diff --git a/scripts/toDataParser/dataParserTransformer/defaults/transform.ts b/scripts/toDataParser/dataParserTransformer/defaults/transform.ts new file mode 100644 index 0000000..0397686 --- /dev/null +++ b/scripts/toDataParser/dataParserTransformer/defaults/transform.ts @@ -0,0 +1,77 @@ +import { DP, E, pipe, unwrap } from "@duplojs/utils"; +import { createTransformer } from "../create"; +import { createSourceFile, factory, isArrowFunction, isExpressionStatement, isFunctionExpression, isParenthesizedExpression, ScriptTarget } from "typescript"; + +export const transformTransformer = createTransformer( + DP.transformKind.has, + ( + dataParser, + { + success, + dependencyIdentifier, + getDefinition, + transformer, + buildError, + }, + ) => { + const inner = transformer(dataParser.definition.inner); + + if (E.isLeft(inner)) { + return inner; + } + + const theFunction = dataParser.definition.theFunction.toString(); + + if (theFunction.includes("[native code]")) { + return buildError(); + } + + const sourceFile = createSourceFile( + "refine-function.ts", + `(${theFunction})`, + ScriptTarget.Latest, + false, + ); + + const statement = sourceFile.statements[0]; + if (!statement || !isExpressionStatement(statement)) { + return buildError(); + } + + const functionExpression = isParenthesizedExpression(statement.expression) + ? statement.expression.expression + : undefined; + + if ( + !functionExpression + || ( + !isFunctionExpression(functionExpression) + && !isArrowFunction(functionExpression) + ) + ) { + return buildError(); + } + + const definition = getDefinition(); + + if (E.isLeft(definition)) { + return definition; + } + + return pipe( + factory.createCallExpression( + factory.createPropertyAccessExpression( + dependencyIdentifier, + factory.createIdentifier("transform"), + ), + undefined, + [ + unwrap(inner), + functionExpression, + ...definition, + ], + ), + success, + ); + }, +); diff --git a/scripts/toDataParser/dataParserTransformer/defaults/tuple.ts b/scripts/toDataParser/dataParserTransformer/defaults/tuple.ts new file mode 100644 index 0000000..fb8cdc7 --- /dev/null +++ b/scripts/toDataParser/dataParserTransformer/defaults/tuple.ts @@ -0,0 +1,78 @@ +import { A, DP, E, pipe, unwrap } from "@duplojs/utils"; +import { createTransformer } from "../create"; +import { type Expression, factory } from "typescript"; + +export const tupleTransformer = createTransformer( + DP.tupleKind.has, + ( + dataParser, + { + success, + dependencyIdentifier, + getDefinition, + transformer, + indent, + }, + ) => { + const shape = A.reduce( + dataParser.definition.shape, + A.reduceFrom([]), + ({ element, lastValue, nextPush, exit }) => { + const result = transformer(element); + + if (E.isLeft(result)) { + return exit(result); + } + + return nextPush(lastValue, unwrap(result)); + }, + ); + + if (E.isLeft(shape)) { + return shape; + } + + const rest = dataParser.definition.rest + ? pipe( + dataParser.definition.rest, + transformer, + E.whenIsRight( + (expression) => [ + factory.createPropertyAssignment( + factory.createIdentifier("rest"), + expression, + ), + ], + ), + ) + : undefined; + + if (E.isLeft(rest)) { + return rest; + } + + const definition = getDefinition(rest); + + if (E.isLeft(definition)) { + return definition; + } + + return pipe( + factory.createCallExpression( + factory.createPropertyAccessExpression( + dependencyIdentifier, + factory.createIdentifier("tuple"), + ), + undefined, + [ + factory.createArrayLiteralExpression( + shape, + indent && A.minElements(shape, 3), + ), + ...definition, + ], + ), + success, + ); + }, +); diff --git a/scripts/toDataParser/dataParserTransformer/defaults/union.ts b/scripts/toDataParser/dataParserTransformer/defaults/union.ts new file mode 100644 index 0000000..5b8284e --- /dev/null +++ b/scripts/toDataParser/dataParserTransformer/defaults/union.ts @@ -0,0 +1,59 @@ +import { A, DP, E, pipe, unwrap } from "@duplojs/utils"; +import { createTransformer } from "../create"; +import { type Expression, factory } from "typescript"; + +export const unionTransformer = createTransformer( + DP.unionKind.has, + ( + dataParser, + { + dependencyIdentifier, + getDefinition, + success, + transformer, + indent, + }, + ) => { + const options = A.reduce( + dataParser.definition.options, + A.reduceFrom([]), + ({ element, lastValue, nextPush, exit }) => { + const result = transformer(element); + + if (E.isLeft(result)) { + return exit(result); + } + + return nextPush(lastValue, unwrap(result)); + }, + ); + + if (E.isLeft(options)) { + return options; + } + + const definition = getDefinition(); + + if (E.isLeft(definition)) { + return definition; + } + + return pipe( + factory.createCallExpression( + factory.createPropertyAccessExpression( + dependencyIdentifier, + factory.createIdentifier("union"), + ), + undefined, + [ + factory.createArrayLiteralExpression( + options, + indent, + ), + ...definition, + ], + ), + success, + ); + }, +); diff --git a/scripts/toDataParser/dataParserTransformer/defaults/unknown.ts b/scripts/toDataParser/dataParserTransformer/defaults/unknown.ts new file mode 100644 index 0000000..ec89969 --- /dev/null +++ b/scripts/toDataParser/dataParserTransformer/defaults/unknown.ts @@ -0,0 +1,33 @@ +import { DP, E, pipe } from "@duplojs/utils"; +import { createTransformer } from "../create"; +import { factory } from "typescript"; + +export const unknownTransformer = createTransformer( + DP.unknownKind.has, + ( + dataParser, + { + success, + dependencyIdentifier, + getDefinition, + }, + ) => { + const definition = getDefinition(); + + if (E.isLeft(definition)) { + return definition; + } + + return pipe( + factory.createCallExpression( + factory.createPropertyAccessExpression( + dependencyIdentifier, + factory.createIdentifier("unknown"), + ), + undefined, + definition, + ), + success, + ); + }, +); diff --git a/scripts/toDataParser/dataParserTransformer/hook.ts b/scripts/toDataParser/dataParserTransformer/hook.ts new file mode 100644 index 0000000..e6abd59 --- /dev/null +++ b/scripts/toDataParser/dataParserTransformer/hook.ts @@ -0,0 +1,22 @@ +import type { DP } from "@duplojs/utils"; +import type { MapContext, MapImportClause } from "./create"; + +export type TransformerHookAction = "stop" | "next"; + +export interface TransformerHookOutput { + dataParser: DP.DataParsers; + action: TransformerHookAction; +} + +export interface TransformerHookParams { + dataParser: DP.DataParsers; + context: MapContext; + importDataParser: MapImportClause; + + output( + action: TransformerHookAction, + dataParser: DP.DataParsers + ): TransformerHookOutput; +} + +export type TransformerHook = (params: TransformerHookParams) => TransformerHookOutput; diff --git a/scripts/toDataParser/dataParserTransformer/importClause.ts b/scripts/toDataParser/dataParserTransformer/importClause.ts new file mode 100644 index 0000000..4f4e850 --- /dev/null +++ b/scripts/toDataParser/dataParserTransformer/importClause.ts @@ -0,0 +1,19 @@ +import { G } from "@duplojs/utils"; +import type { MapImportClause } from "./create"; +import { factory } from "typescript"; + +export function importClauseTransformer(imports: MapImportClause) { + return G.map( + imports, + ([path, clause]) => factory.createImportDeclaration( + undefined, + factory.createImportClause( + undefined, + undefined, + factory.createNamespaceImport(factory.createIdentifier(clause)), + ), + factory.createStringLiteral(path), + undefined, + ), + ); +} diff --git a/scripts/toDataParser/dataParserTransformer/index.ts b/scripts/toDataParser/dataParserTransformer/index.ts new file mode 100644 index 0000000..23d444f --- /dev/null +++ b/scripts/toDataParser/dataParserTransformer/index.ts @@ -0,0 +1,4 @@ +export * from "./create"; +export * from "./transformer"; +export * from "./hook"; +export * from "./defaults"; diff --git a/scripts/toDataParser/dataParserTransformer/transformer.ts b/scripts/toDataParser/dataParserTransformer/transformer.ts new file mode 100644 index 0000000..1b00f65 --- /dev/null +++ b/scripts/toDataParser/dataParserTransformer/transformer.ts @@ -0,0 +1,231 @@ +import { A, E, type DP, pipe, unwrap, when } from "@duplojs/utils"; +import type { MapContext, TransformerParams, createTransformer, MapImportClause, MaybeTransformerEither } from "./create"; +import { factory, type PropertyAssignment, type CallExpression, type Identifier } from "typescript"; +import type { TransformerHook } from "./hook"; +import { type createCheckerTransformer, checkerTransformer } from "../checkerTransformer"; + +export interface getDefinitionDataParserParams { + readonly dataParser: DP.DataParser; + readonly checkerTransformers: readonly ReturnType[]; + readonly customProperties: readonly PropertyAssignment[]; + readonly indent: boolean; +} + +export function getDefinitionDataParser(params: getDefinitionDataParserParams) { + const propertyAssignments: PropertyAssignment[] = []; + + if (params.dataParser.definition.errorMessage) { + propertyAssignments.push( + factory.createPropertyAssignment( + factory.createIdentifier("errorMessage"), + factory.createStringLiteral(params.dataParser.definition.errorMessage), + ), + ); + } + if (A.minElements(params.customProperties, 1)) { + propertyAssignments.push(...params.customProperties); + } + if (A.minElements(params.dataParser.definition.checkers, 1)) { + const checkers = A.reduce( + params.dataParser.definition.checkers, + A.reduceFrom([]), + ({ element, lastValue, nextPush, exit }) => pipe( + checkerTransformer(element, { transformers: params.checkerTransformers }), + E.whenIsRight( + (value) => nextPush(lastValue, value), + ), + when( + E.isLeft, + exit, + ), + ), + ); + + if (E.isLeft(checkers)) { + return E.left("buildDataParserGetDefinitionError", { + dataParser: params.dataParser, + error: checkers, + }); + } + + propertyAssignments.push( + factory.createPropertyAssignment( + factory.createIdentifier("checkers"), + factory.createArrayLiteralExpression( + checkers, + params.indent && A.minElements(checkers, 2), + ), + ), + ); + } + + return A.minElements(propertyAssignments, 1) + ? [ + factory.createObjectLiteralExpression( + propertyAssignments, + params.indent && A.minElements(propertyAssignments, 2), + ), + ] + : []; +} + +export interface TransformerFunctionParams { + readonly dataParserTransformers: readonly ReturnType[]; + readonly checkerTransformers: readonly ReturnType[]; + readonly context: MapContext; + readonly buildingConstNames: Map; + readonly dependencyIdentifier: Identifier; + readonly hooks: readonly TransformerHook[]; + readonly recursiveDataParsers: DP.DataParser[]; + readonly importClause: MapImportClause; + readonly indent: boolean; +} + +export function transformer( + dataParser: DP.DataParser, + params: TransformerFunctionParams, +) { + const currentDataParser = A.reduce( + params.hooks, + A.reduceFrom(dataParser), + ({ element: hook, lastValue, next, exit }) => { + const result = hook({ + dataParser: lastValue, + context: params.context, + importDataParser: params.importClause, + output: (action, dataParser) => ({ + dataParser, + action, + }), + }); + if (result.action === "stop") { + return exit(result.dataParser); + } else { + return next(result.dataParser); + } + }, + ); + + const contextValue = params.context.get(currentDataParser); + + if (contextValue) { + return E.right( + "buildSuccess", + contextValue.constName, + ); + } + + const currentBuildingConstName = params.buildingConstNames.get(currentDataParser); + + if (currentBuildingConstName) { + return E.right( + "buildSuccess", + currentBuildingConstName, + ); + } + + const shouldCreateConstDeclaration = A.includes(params.recursiveDataParsers, currentDataParser) + || !!currentDataParser.definition.constName; + const currentConstName = shouldCreateConstDeclaration + ? factory.createIdentifier( + currentDataParser.definition.constName + ?? `recursiveDataParser${params.context.size + params.buildingConstNames.size}`, + ) + : undefined; + + if (currentConstName) { + params.buildingConstNames.set( + currentDataParser, + currentConstName, + ); + } + + const functionParams: TransformerParams = { + success(result) { + return E.right("buildSuccess", result); + }, + transformer(dataParser) { + return transformer( + dataParser, + params, + ); + }, + context: params.context, + dependencyIdentifier: params.dependencyIdentifier, + indent: params.indent, + buildError() { + return E.left("buildDataParserError", currentDataParser); + }, + importClause: params.importClause, + getDefinition(customProperties = [], indent = true) { + return getDefinitionDataParser({ + dataParser: currentDataParser, + checkerTransformers: params.checkerTransformers, + customProperties, + indent: indent && params.indent, + }); + }, + addImportClause(path, clause) { + params.importClause.set(path, clause); + }, + }; + + const result = currentDataParser.definition.overrideDataParserTransformer + ? currentDataParser.definition.overrideDataParserTransformer( + currentDataParser.addOverrideDataParserTransformer(null), + functionParams, + ) + : A.reduce( + params.dataParserTransformers, + A.reduceFrom( + E.left("dataParserNotSupport", currentDataParser), + ), + ({ + element: functionBuilder, + lastValue, + next, + exit, + }) => { + const result = functionBuilder(currentDataParser, functionParams); + + if (E.isLeft(result)) { + if (!E.hasInformation(result, "dataParserNotSupport")) { + return exit(result); + } + + return next(lastValue); + } + + return exit(result); + }, + ); + + if (E.isLeft(result)) { + if (currentConstName) { + params.buildingConstNames.delete(currentDataParser); + } + + return result; + } + + if (currentConstName) { + params.buildingConstNames.delete(currentDataParser); + params.context.set( + currentDataParser, + { + constName: currentConstName, + expression: unwrap(result), + }, + ); + + return E.right( + "buildSuccess", + currentConstName, + ); + } + + return E.right( + "buildSuccess", + unwrap(result), + ); +} diff --git a/scripts/toDataParser/index.ts b/scripts/toDataParser/index.ts new file mode 100644 index 0000000..7da6024 --- /dev/null +++ b/scripts/toDataParser/index.ts @@ -0,0 +1,5 @@ +export * from "./kind"; +export * from "./override"; +export * from "./checkerTransformer"; +export * from "./dataParserTransformer"; +export * from "./render"; diff --git a/scripts/toDataParser/kind.ts b/scripts/toDataParser/kind.ts new file mode 100644 index 0000000..0487403 --- /dev/null +++ b/scripts/toDataParser/kind.ts @@ -0,0 +1,12 @@ +import { createKindNamespace } from "@duplojs/utils"; + +declare module "@duplojs/utils" { + interface ReservedKindNamespace { + DuplojsDataParserToolsToDataParser: true; + } +} + +export const createToDataParserKind = createKindNamespace( + // @ts-expect-error reserved kind namespace + "DuplojsDataParserToolsToDataParser", +); diff --git a/scripts/toDataParser/override.ts b/scripts/toDataParser/override.ts new file mode 100644 index 0000000..e37acf7 --- /dev/null +++ b/scripts/toDataParser/override.ts @@ -0,0 +1,75 @@ +import { dataParserBaseInit } from "@duplojs/utils/dataParser"; +import type { CallExpression, Identifier } from "typescript"; +import type { TransformerBuildFunction } from "./dataParserTransformer"; + +declare module "@duplojs/utils/dataParser" { + interface DataParserBase { + + /** + * @deprecated this method mutated the dataParser by adding an identifier + */ + setConstName(input: string): this; + addConstName(input: string): this; + + /** + * @deprecated this method mutated the dataParser by adding an override transformer + */ + setOverrideDataParserTransformer( + transformer: CallExpression | Identifier | TransformerBuildFunction | null, + ): this; + addOverrideDataParserTransformer( + transformer: CallExpression | Identifier | TransformerBuildFunction | null, + ): this; + } + + interface DataParserDefinition { + constName?: string; + overrideDataParserTransformer?: TransformerBuildFunction; + } +} + +dataParserBaseInit.overrideHandler.setMethod( + "setConstName", + (schema, constName) => { + schema.definition.constName = constName; + + return schema; + }, +); + +dataParserBaseInit.overrideHandler.setMethod( + "addConstName", + (schema, identifier) => { + const newSchema = schema.clone(); + + newSchema.setConstName(identifier); + + return newSchema; + }, +); + +dataParserBaseInit.overrideHandler.setMethod( + "setOverrideDataParserTransformer", + (schema, overrideTransformer) => { + if (overrideTransformer) { + schema.definition.overrideDataParserTransformer = typeof overrideTransformer === "function" + ? overrideTransformer + : (__, { success }) => success(overrideTransformer); + } else { + schema.definition.overrideDataParserTransformer = undefined; + } + + return schema; + }, +); + +dataParserBaseInit.overrideHandler.setMethod( + "addOverrideDataParserTransformer", + (schema, overrideTransformer) => { + const newSchema = schema.clone(); + + newSchema.setOverrideDataParserTransformer(overrideTransformer); + + return newSchema; + }, +); diff --git a/scripts/toDataParser/render.ts b/scripts/toDataParser/render.ts new file mode 100644 index 0000000..a198980 --- /dev/null +++ b/scripts/toDataParser/render.ts @@ -0,0 +1,228 @@ +import { A, DP, E, kindHeritage, pipe, S, unwrap } from "@duplojs/utils"; +import { createPrinter, createSourceFile, EmitHint, factory, NodeFlags, ScriptKind, ScriptTarget, SyntaxKind, type TypeAliasDeclaration, type VariableStatement } from "typescript"; +import * as TST from "@scripts/toTypescript"; +import { type createCheckerTransformer } from "./checkerTransformer"; +import { type TransformerHook, type createTransformer, type MapContext, type MapImportClause, transformer, type DataParserNotSupportedEither, type DataParserGetDefinitionErrorEither, type DataParserErrorEither } from "./dataParserTransformer"; +import { getRecursiveDataParser } from "@scripts/utils"; +import { createToDataParserKind } from "./kind"; +import { importClauseTransformer } from "./dataParserTransformer/importClause"; + +export class DataParserToDataParserRenderError extends kindHeritage( + "data-parser-to-data-parser-render-error", + createToDataParserKind("data-parser-to-data-parser-render-error"), + Error, +) { + public constructor( + public dataParser: DP.DataParser, + public error: DataParserNotSupportedEither | DataParserErrorEither | DataParserGetDefinitionErrorEither, + ) { + super({}, ["Error during the render of dataParser in dataParser."]); + } +} + +export class DataParserToDataParserTypeRenderError extends kindHeritage( + "data-parser-to-data-parser-type-render-error", + createToDataParserKind("data-parser-to-data-parser-type-render-error"), + Error, +) { + public constructor( + public dataParser: DP.DataParser, + public error: TST.DataParserNotSupportedEither | TST.DataParserErrorEither, + ) { + super({}, ["Error during the render of recursive dataParser type."]); + } +} + +export interface RenderParams { + readonly constName: string; + readonly dataParserTransformers: readonly ReturnType[]; + readonly checkerTransformers: readonly ReturnType[]; + readonly exportMode?: "normal" | "extended"; + readonly context?: MapContext; + readonly hooks?: readonly TransformerHook[]; + readonly importClause?: MapImportClause; + readonly indent?: boolean; + + readonly toTypescript: { + readonly identifier: string; + readonly transformers: readonly ReturnType[]; + readonly context?: TST.MapContext; + readonly mode?: TST.TransformerMode; + readonly hooks?: readonly TST.TransformerHook[]; + readonly importType?: TST.MapImportType; + }; +} + +export function render(dataParser: DP.DataParser, params: RenderParams) { + const context: MapContext = new Map(params.context); + const importClause: MapImportClause = new Map(params.importClause); + const dependencyIdentifier = factory.createIdentifier(params.exportMode === "extended" ? "DPE" : "DP"); + const recursiveDataParsers = getRecursiveDataParser(dataParser); + const tsContext: TST.MapContext = new Map(params.toTypescript.context); + const importType: TST.MapImportType = new Map(params.toTypescript.importType); + const recursiveTypeAliasDeclarations: TypeAliasDeclaration[] = []; + const recursiveTypeNameByDataParser = new Map(); + + importClause.set("@duplojs/utils/dataParser", "DP"); + + if (params.exportMode === "extended") { + importClause.set("@duplojs/utils/dataParserExtended", "DPE"); + } + + if (A.minElements(recursiveDataParsers, 1)) { + const tsResult = TST.transformer( + dataParser, + { + context: tsContext, + importType, + hooks: params.toTypescript.hooks ?? [], + mode: params.toTypescript.mode ?? "out", + recursiveDataParsers, + transformers: params.toTypescript.transformers, + }, + ); + + if (E.isLeft(tsResult)) { + throw new DataParserToDataParserTypeRenderError( + dataParser, + tsResult, + ); + } + + const typeDeclarations = A.reduce( + recursiveDataParsers, + A.reduceFrom([]), + ({ element: recursiveDataParser, lastValue, nextPush, next }) => { + const declaration = tsContext.get(recursiveDataParser); + + if (!declaration) { + return next(lastValue); + } + + recursiveTypeNameByDataParser.set(recursiveDataParser, declaration.name.text); + + return nextPush(lastValue, declaration); + }, + ); + + recursiveTypeAliasDeclarations.push(...typeDeclarations); + + if (A.includes(recursiveDataParsers, dataParser)) { + if ( + dataParser.definition.identifier + && dataParser.definition.identifier !== params.toTypescript.identifier + ) { + recursiveTypeAliasDeclarations.push( + factory.createTypeAliasDeclaration( + [factory.createToken(SyntaxKind.ExportKeyword)], + factory.createIdentifier(params.toTypescript.identifier), + undefined, + factory.createTypeReferenceNode( + dataParser.definition.identifier, + ), + ), + ); + + recursiveTypeNameByDataParser.set(dataParser, params.toTypescript.identifier); + } else if (dataParser.definition.identifier !== params.toTypescript.identifier) { + recursiveTypeAliasDeclarations.push( + factory.createTypeAliasDeclaration( + [factory.createToken(SyntaxKind.ExportKeyword)], + factory.createIdentifier(params.toTypescript.identifier), + undefined, + unwrap(tsResult), + ), + ); + + recursiveTypeNameByDataParser.set(dataParser, params.toTypescript.identifier); + } + } + } + + const result = transformer( + dataParser, + { + dataParserTransformers: params.dataParserTransformers, + checkerTransformers: params.checkerTransformers, + context, + buildingConstNames: new Map(), + recursiveDataParsers, + importClause, + dependencyIdentifier, + hooks: params.hooks ?? [], + indent: params.indent ?? true, + }, + ); + + if (E.isLeft(result)) { + throw new DataParserToDataParserRenderError( + dataParser, + result, + ); + } + + if (dataParser.definition.constName !== params.constName) { + context.set( + DP.empty(), + { + constName: factory.createIdentifier(params.constName), + expression: unwrap(result), + }, + ); + } + + const dataParserConstStatements = A.reduce( + A.from(context), + A.reduceFrom([]), + ({ element: [currentDataParser, contextValue], lastValue, nextPush }) => { + const recursiveTypeName = recursiveTypeNameByDataParser.get(currentDataParser); + + return nextPush( + lastValue, + factory.createVariableStatement( + [factory.createToken(SyntaxKind.ExportKeyword)], + factory.createVariableDeclarationList( + [ + factory.createVariableDeclaration( + contextValue.constName, + undefined, + recursiveTypeName + ? factory.createTypeReferenceNode( + factory.createQualifiedName( + dependencyIdentifier, + factory.createIdentifier("DataParser"), + ), + [factory.createTypeReferenceNode(recursiveTypeName)], + ) + : undefined, + contextValue.expression, + ), + ], + NodeFlags.Const, + ), + ), + ); + }, + ); + + const sourceFile = createSourceFile("print.ts", "", ScriptTarget.Latest, false, ScriptKind.TS); + const printer = createPrinter(); + + return pipe( + [ + ...importClauseTransformer(importClause), + ...TST.importTypesTransformer(importType), + ...recursiveTypeAliasDeclarations, + ...dataParserConstStatements, + ], + A.map( + (value) => printer.printNode( + EmitHint.Unspecified, + value, + sourceFile, + ), + ), + A.join("\n\n"), + S.trim, + ); +} diff --git a/scripts/toJsonSchema/transformer/defaults/literal.ts b/scripts/toJsonSchema/transformer/defaults/literal.ts index 911ae8b..b56179b 100644 --- a/scripts/toJsonSchema/transformer/defaults/literal.ts +++ b/scripts/toJsonSchema/transformer/defaults/literal.ts @@ -1,5 +1,5 @@ import { A, DP, isType, justReturn, P, pipe } from "@duplojs/utils"; -import { createTransformer, type SupportedVersions, type SupportedVersionsUrl } from "../create"; +import { createTransformer, type SupportedVersions } from "../create"; type JsonPrimitive = string | number | boolean | null; type JsonType = "string" | "number" | "integer" | "boolean" | "null"; diff --git a/scripts/toJsonSchema/transformer/transformer.ts b/scripts/toJsonSchema/transformer/transformer.ts index edf651d..c6ff83b 100644 --- a/scripts/toJsonSchema/transformer/transformer.ts +++ b/scripts/toJsonSchema/transformer/transformer.ts @@ -1,4 +1,4 @@ -import { A, E, justExec, justReturn, unwrap, whenElse, type DP } from "@duplojs/utils"; +import { A, E, justExec, unwrap, type DP } from "@duplojs/utils"; import { type MapContext, type DataParserNotSupportedEither, diff --git a/scripts/toTypescript/render.ts b/scripts/toTypescript/render.ts index 679bd32..b40d1a2 100644 --- a/scripts/toTypescript/render.ts +++ b/scripts/toTypescript/render.ts @@ -1,4 +1,4 @@ -import { DP, unwrap, E, pipe, G, A, S, kindHeritage } from "@duplojs/utils"; +import { DP, unwrap, E, pipe, A, S, kindHeritage } from "@duplojs/utils"; import { type DataParserErrorEither, type DataParserNotSupportedEither, transformer, type MapContext, type TransformerMode, type TransformerHook, type createTransformer, type MapImportType } from "./transformer"; import { createPrinter, createSourceFile, EmitHint, factory, ScriptKind, ScriptTarget, SyntaxKind } from "typescript"; import { createToTypescriptKind } from "./kind"; diff --git a/scripts/toTypescript/transformer/index.ts b/scripts/toTypescript/transformer/index.ts index c894811..9d575e8 100644 --- a/scripts/toTypescript/transformer/index.ts +++ b/scripts/toTypescript/transformer/index.ts @@ -3,4 +3,5 @@ export * from "./transformer"; export * from "./hook"; export * from "./defaults"; export * from "./includesUndefinedTypeNode"; +export * from "./importTypesTransformer"; export { defaultTransformers } from "./defaults"; diff --git a/tests/toDataParser/__snapshots__/render.test.ts.snap b/tests/toDataParser/__snapshots__/render.test.ts.snap new file mode 100644 index 0000000..0f0e1ba --- /dev/null +++ b/tests/toDataParser/__snapshots__/render.test.ts.snap @@ -0,0 +1,131 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`render > renders anonymous recursive dataParser with a temporary const 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export type RecursiveType0 = { + next: RecursiveType0; +}; + +export type RecursiveNode = RecursiveType0; + +export const recursiveDataParser0: DP.DataParser = DP.object({ + next: DP.lazy(() => recursiveDataParser0) +}); + +export const recursiveNodeParser = recursiveDataParser0;" +`; + +exports[`render > renders checked dataParsers in normal and extended modes 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +import * as DDate from "@duplojs/utils/date"; + +export const checkedParser = DP.object({ + startAt: DP.time({ checkers: [ + DP.checkerTimeMin(DDate.createTime(1000, "millisecond")), + DP.checkerTimeMax(DDate.createTime(5000, "millisecond")) + ] }), + name: DP.string({ checkers: [DP.checkerStringMin(2)] }), + count: DP.number({ checkers: [ + DP.checkerInt(), + DP.checkerNumberMin(1) + ] }) +});" +`; + +exports[`render > renders checked dataParsers in normal and extended modes 2`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +import * as DPE from "@duplojs/utils/dataParserExtended"; + +import * as DDate from "@duplojs/utils/date"; + +export const checkedExtendedParser = DPE.object({ + startAt: DPE.time({ checkers: [DP.checkerTimeMin(DDate.createTime(250, "millisecond"))] }), + tags: DPE.array(DPE.string({ checkers: [DP.checkerStringMin(1)] }), { checkers: [DP.checkerArrayMin(1)] }) +});" +`; + +exports[`render > renders complex nested recursive dataParsers 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export type RecursiveType0 = { + recursiveProp: { + self: RecursiveType0; + array: RecursiveType1; + tuple: RecursiveType2; + union: RecursiveType3; + }; + children: RecursiveType0[]; + tuple: RecursiveType2; + array: RecursiveType1; + union: RecursiveType3; +}; + +export type RecursiveType1 = (string | RecursiveType1 | RecursiveType0)[]; + +export type RecursiveType2 = [ + string, + RecursiveType0, + (RecursiveType2 | RecursiveType3 | string)[] +]; + +export type RecursiveType3 = string | RecursiveType0 | RecursiveType2 | RecursiveType3[]; + +export type ComplexRecursive = RecursiveType0; + +export const recursiveDataParser1: DP.DataParser = DP.array(DP.union([ + DP.string(), + DP.lazy(() => recursiveDataParser1), + DP.lazy(() => recursiveDataParser0) +])); + +export const recursiveDataParser3: DP.DataParser = DP.union([ + DP.string(), + DP.lazy(() => recursiveDataParser0), + DP.lazy(() => recursiveDataParser2), + DP.array(DP.lazy(() => recursiveDataParser3)) +]); + +export const recursiveDataParser2: DP.DataParser = DP.tuple([ + DP.string(), + DP.lazy(() => recursiveDataParser0), + DP.array(DP.union([ + DP.lazy(() => recursiveDataParser2), + DP.lazy(() => recursiveDataParser3), + DP.string() + ])) +]); + +export const recursiveDataParser0: DP.DataParser = DP.object({ + recursiveProp: DP.object({ + self: DP.lazy(() => recursiveDataParser0), + array: recursiveDataParser1, + tuple: recursiveDataParser2, + union: recursiveDataParser3 + }), + children: DP.array(DP.lazy(() => recursiveDataParser0)), + tuple: recursiveDataParser2, + array: recursiveDataParser1, + union: recursiveDataParser3 +}); + +export const complexRecursiveParser = recursiveDataParser0;" +`; + +exports[`render > renders dataParser in compact mode when indent is false 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export const compactParser = DP.object({ name: DP.string({ checkers: [DP.checkerStringMin(2)] }), roles: DP.array(DP.literal(["admin", "editor"]), { checkers: [DP.checkerArrayMin(1)] }), contact: DP.union([DP.object({ email: DP.string({ checkers: [DP.checkerEmail()] }) }), DP.object({ phone: DP.string({ checkers: [DP.checkerRegex(/^[+\\d][\\d\\s-]{5,}$/)] }) })]) });" +`; + +exports[`render > renders named dependencies before their consumers 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export const childParser = DP.string(); + +export const parentParser = DP.object({ + child: childParser +});" +`; diff --git a/tests/toDataParser/checkerTransfomers/__snapshots__/arrayMax.test.ts.snap b/tests/toDataParser/checkerTransfomers/__snapshots__/arrayMax.test.ts.snap new file mode 100644 index 0000000..01e3724 --- /dev/null +++ b/tests/toDataParser/checkerTransfomers/__snapshots__/arrayMax.test.ts.snap @@ -0,0 +1,3 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`checkerArrayMax > basic 1`] = `"DP.checkerArrayMax(5)"`; diff --git a/tests/toDataParser/checkerTransfomers/__snapshots__/arrayMin.test.ts.snap b/tests/toDataParser/checkerTransfomers/__snapshots__/arrayMin.test.ts.snap new file mode 100644 index 0000000..7057cfa --- /dev/null +++ b/tests/toDataParser/checkerTransfomers/__snapshots__/arrayMin.test.ts.snap @@ -0,0 +1,3 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`checkerArrayMin > basic 1`] = `"DP.checkerArrayMin(1)"`; diff --git a/tests/toDataParser/checkerTransfomers/__snapshots__/bigintMax.test.ts.snap b/tests/toDataParser/checkerTransfomers/__snapshots__/bigintMax.test.ts.snap new file mode 100644 index 0000000..ad9b3c4 --- /dev/null +++ b/tests/toDataParser/checkerTransfomers/__snapshots__/bigintMax.test.ts.snap @@ -0,0 +1,3 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`checkerBigIntMax > basic 1`] = `"DP.checkerBigIntMax(10n)"`; diff --git a/tests/toDataParser/checkerTransfomers/__snapshots__/bigintMin.test.ts.snap b/tests/toDataParser/checkerTransfomers/__snapshots__/bigintMin.test.ts.snap new file mode 100644 index 0000000..ac627fd --- /dev/null +++ b/tests/toDataParser/checkerTransfomers/__snapshots__/bigintMin.test.ts.snap @@ -0,0 +1,3 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`checkerBigIntMin > basic 1`] = `"DP.checkerBigIntMin(1n)"`; diff --git a/tests/toDataParser/checkerTransfomers/__snapshots__/email.test.ts.snap b/tests/toDataParser/checkerTransfomers/__snapshots__/email.test.ts.snap new file mode 100644 index 0000000..e37e578 --- /dev/null +++ b/tests/toDataParser/checkerTransfomers/__snapshots__/email.test.ts.snap @@ -0,0 +1,3 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`checkerEmail > basic 1`] = `"DP.checkerEmail()"`; diff --git a/tests/toDataParser/checkerTransfomers/__snapshots__/int.test.ts.snap b/tests/toDataParser/checkerTransfomers/__snapshots__/int.test.ts.snap new file mode 100644 index 0000000..8b88003 --- /dev/null +++ b/tests/toDataParser/checkerTransfomers/__snapshots__/int.test.ts.snap @@ -0,0 +1,3 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`checkerInt > basic 1`] = `"DP.checkerInt()"`; diff --git a/tests/toDataParser/checkerTransfomers/__snapshots__/numberMax.test.ts.snap b/tests/toDataParser/checkerTransfomers/__snapshots__/numberMax.test.ts.snap new file mode 100644 index 0000000..60c27e1 --- /dev/null +++ b/tests/toDataParser/checkerTransfomers/__snapshots__/numberMax.test.ts.snap @@ -0,0 +1,5 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`checkerNumberMax > exclusive 1`] = `"DP.checkerNumberMax(10, { exclusive: true })"`; + +exports[`checkerNumberMax > non exclusive 1`] = `"DP.checkerNumberMax(10)"`; diff --git a/tests/toDataParser/checkerTransfomers/__snapshots__/numberMin.test.ts.snap b/tests/toDataParser/checkerTransfomers/__snapshots__/numberMin.test.ts.snap new file mode 100644 index 0000000..85eb900 --- /dev/null +++ b/tests/toDataParser/checkerTransfomers/__snapshots__/numberMin.test.ts.snap @@ -0,0 +1,5 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`checkerNumberMin > exclusive 1`] = `"DP.checkerNumberMin(1, { exclusive: true })"`; + +exports[`checkerNumberMin > non exclusive 1`] = `"DP.checkerNumberMin(1)"`; diff --git a/tests/toDataParser/checkerTransfomers/__snapshots__/refine.test.ts.snap b/tests/toDataParser/checkerTransfomers/__snapshots__/refine.test.ts.snap new file mode 100644 index 0000000..bba7671 --- /dev/null +++ b/tests/toDataParser/checkerTransfomers/__snapshots__/refine.test.ts.snap @@ -0,0 +1,3 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`checkerRefine > basic 1`] = `"DP.checkerRefine((value) => value.length > 0)"`; diff --git a/tests/toDataParser/checkerTransfomers/__snapshots__/regex.test.ts.snap b/tests/toDataParser/checkerTransfomers/__snapshots__/regex.test.ts.snap new file mode 100644 index 0000000..17f4b89 --- /dev/null +++ b/tests/toDataParser/checkerTransfomers/__snapshots__/regex.test.ts.snap @@ -0,0 +1,3 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`checkerRegex > basic 1`] = `"DP.checkerRegex(/^[a-z]+$/)"`; diff --git a/tests/toDataParser/checkerTransfomers/__snapshots__/stringMax.test.ts.snap b/tests/toDataParser/checkerTransfomers/__snapshots__/stringMax.test.ts.snap new file mode 100644 index 0000000..c85d6d7 --- /dev/null +++ b/tests/toDataParser/checkerTransfomers/__snapshots__/stringMax.test.ts.snap @@ -0,0 +1,3 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`checkerStringMax > basic 1`] = `"DP.checkerStringMax(10)"`; diff --git a/tests/toDataParser/checkerTransfomers/__snapshots__/stringMin.test.ts.snap b/tests/toDataParser/checkerTransfomers/__snapshots__/stringMin.test.ts.snap new file mode 100644 index 0000000..8c255a2 --- /dev/null +++ b/tests/toDataParser/checkerTransfomers/__snapshots__/stringMin.test.ts.snap @@ -0,0 +1,3 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`checkerStringMin > basic 1`] = `"DP.checkerStringMin(2)"`; diff --git a/tests/toDataParser/checkerTransfomers/__snapshots__/timeMax.test.ts.snap b/tests/toDataParser/checkerTransfomers/__snapshots__/timeMax.test.ts.snap new file mode 100644 index 0000000..56342ad --- /dev/null +++ b/tests/toDataParser/checkerTransfomers/__snapshots__/timeMax.test.ts.snap @@ -0,0 +1,3 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`checkerTimeMax > basic 1`] = `"DP.checkerTimeMax(DDate.createTime(2000, "millisecond"))"`; diff --git a/tests/toDataParser/checkerTransfomers/__snapshots__/timeMin.test.ts.snap b/tests/toDataParser/checkerTransfomers/__snapshots__/timeMin.test.ts.snap new file mode 100644 index 0000000..467aa91 --- /dev/null +++ b/tests/toDataParser/checkerTransfomers/__snapshots__/timeMin.test.ts.snap @@ -0,0 +1,3 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`checkerTimeMin > basic 1`] = `"DP.checkerTimeMin(DDate.createTime(1000, "millisecond"))"`; diff --git a/tests/toDataParser/checkerTransfomers/__snapshots__/url.test.ts.snap b/tests/toDataParser/checkerTransfomers/__snapshots__/url.test.ts.snap new file mode 100644 index 0000000..6044bf2 --- /dev/null +++ b/tests/toDataParser/checkerTransfomers/__snapshots__/url.test.ts.snap @@ -0,0 +1,5 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`checkerUrl > with options 1`] = `"DP.checkerUrl({ hostname: /^[a-z.-]+$/, normalize: true, protocol: /^https?$/ })"`; + +exports[`checkerUrl > without options 1`] = `"DP.checkerUrl()"`; diff --git a/tests/toDataParser/checkerTransfomers/__snapshots__/uuid.test.ts.snap b/tests/toDataParser/checkerTransfomers/__snapshots__/uuid.test.ts.snap new file mode 100644 index 0000000..7df9ebf --- /dev/null +++ b/tests/toDataParser/checkerTransfomers/__snapshots__/uuid.test.ts.snap @@ -0,0 +1,3 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`checkerUuid > basic 1`] = `"DP.checkerUuid()"`; diff --git a/tests/toDataParser/checkerTransfomers/arrayMax.test.ts b/tests/toDataParser/checkerTransfomers/arrayMax.test.ts new file mode 100644 index 0000000..c5eb3aa --- /dev/null +++ b/tests/toDataParser/checkerTransfomers/arrayMax.test.ts @@ -0,0 +1,15 @@ +import { DataParserToDataParser } from "@scripts/index"; +import { asserts, DP, E, unwrap } from "@duplojs/utils"; +import { printExpression } from "./utils"; + +describe("checkerArrayMax", () => { + it("basic", () => { + const result = DataParserToDataParser.checkerTransformer( + DP.checkerArrayMax(5), + { transformers: DataParserToDataParser.defaultCheckerTransformers }, + ); + + asserts(result, E.isRight); + expect(printExpression(unwrap(result))).toMatchSnapshot(); + }); +}); diff --git a/tests/toDataParser/checkerTransfomers/arrayMin.test.ts b/tests/toDataParser/checkerTransfomers/arrayMin.test.ts new file mode 100644 index 0000000..7a660ce --- /dev/null +++ b/tests/toDataParser/checkerTransfomers/arrayMin.test.ts @@ -0,0 +1,15 @@ +import { DataParserToDataParser } from "@scripts/index"; +import { asserts, DP, E, unwrap } from "@duplojs/utils"; +import { printExpression } from "./utils"; + +describe("checkerArrayMin", () => { + it("basic", () => { + const result = DataParserToDataParser.checkerTransformer( + DP.checkerArrayMin(1), + { transformers: DataParserToDataParser.defaultCheckerTransformers }, + ); + + asserts(result, E.isRight); + expect(printExpression(unwrap(result))).toMatchSnapshot(); + }); +}); diff --git a/tests/toDataParser/checkerTransfomers/bigintMax.test.ts b/tests/toDataParser/checkerTransfomers/bigintMax.test.ts new file mode 100644 index 0000000..74588a3 --- /dev/null +++ b/tests/toDataParser/checkerTransfomers/bigintMax.test.ts @@ -0,0 +1,15 @@ +import { DataParserToDataParser } from "@scripts/index"; +import { asserts, DP, E, unwrap } from "@duplojs/utils"; +import { printExpression } from "./utils"; + +describe("checkerBigIntMax", () => { + it("basic", () => { + const result = DataParserToDataParser.checkerTransformer( + DP.checkerBigIntMax(10n), + { transformers: DataParserToDataParser.defaultCheckerTransformers }, + ); + + asserts(result, E.isRight); + expect(printExpression(unwrap(result))).toMatchSnapshot(); + }); +}); diff --git a/tests/toDataParser/checkerTransfomers/bigintMin.test.ts b/tests/toDataParser/checkerTransfomers/bigintMin.test.ts new file mode 100644 index 0000000..5b7b4e7 --- /dev/null +++ b/tests/toDataParser/checkerTransfomers/bigintMin.test.ts @@ -0,0 +1,15 @@ +import { DataParserToDataParser } from "@scripts/index"; +import { asserts, DP, E, unwrap } from "@duplojs/utils"; +import { printExpression } from "./utils"; + +describe("checkerBigIntMin", () => { + it("basic", () => { + const result = DataParserToDataParser.checkerTransformer( + DP.checkerBigIntMin(1n), + { transformers: DataParserToDataParser.defaultCheckerTransformers }, + ); + + asserts(result, E.isRight); + expect(printExpression(unwrap(result))).toMatchSnapshot(); + }); +}); diff --git a/tests/toDataParser/checkerTransfomers/email.test.ts b/tests/toDataParser/checkerTransfomers/email.test.ts new file mode 100644 index 0000000..90b48ca --- /dev/null +++ b/tests/toDataParser/checkerTransfomers/email.test.ts @@ -0,0 +1,15 @@ +import { DataParserToDataParser } from "@scripts/index"; +import { asserts, DP, E, unwrap } from "@duplojs/utils"; +import { printExpression } from "./utils"; + +describe("checkerEmail", () => { + it("basic", () => { + const result = DataParserToDataParser.checkerTransformer( + DP.checkerEmail(), + { transformers: DataParserToDataParser.defaultCheckerTransformers }, + ); + + asserts(result, E.isRight); + expect(printExpression(unwrap(result))).toMatchSnapshot(); + }); +}); diff --git a/tests/toDataParser/checkerTransfomers/int.test.ts b/tests/toDataParser/checkerTransfomers/int.test.ts new file mode 100644 index 0000000..35b6db5 --- /dev/null +++ b/tests/toDataParser/checkerTransfomers/int.test.ts @@ -0,0 +1,15 @@ +import { DataParserToDataParser } from "@scripts/index"; +import { asserts, DP, E, unwrap } from "@duplojs/utils"; +import { printExpression } from "./utils"; + +describe("checkerInt", () => { + it("basic", () => { + const result = DataParserToDataParser.checkerTransformer( + DP.checkerInt(), + { transformers: DataParserToDataParser.defaultCheckerTransformers }, + ); + + asserts(result, E.isRight); + expect(printExpression(unwrap(result))).toMatchSnapshot(); + }); +}); diff --git a/tests/toDataParser/checkerTransfomers/numberMax.test.ts b/tests/toDataParser/checkerTransfomers/numberMax.test.ts new file mode 100644 index 0000000..3c32d65 --- /dev/null +++ b/tests/toDataParser/checkerTransfomers/numberMax.test.ts @@ -0,0 +1,25 @@ +import { DataParserToDataParser } from "@scripts/index"; +import { asserts, DP, E, unwrap } from "@duplojs/utils"; +import { printExpression } from "./utils"; + +describe("checkerNumberMax", () => { + it("exclusive", () => { + const result = DataParserToDataParser.checkerTransformer( + DP.checkerNumberMax(10, { exclusive: true }), + { transformers: DataParserToDataParser.defaultCheckerTransformers }, + ); + + asserts(result, E.isRight); + expect(printExpression(unwrap(result))).toMatchSnapshot(); + }); + + it("non exclusive", () => { + const result = DataParserToDataParser.checkerTransformer( + DP.checkerNumberMax(10), + { transformers: DataParserToDataParser.defaultCheckerTransformers }, + ); + + asserts(result, E.isRight); + expect(printExpression(unwrap(result))).toMatchSnapshot(); + }); +}); diff --git a/tests/toDataParser/checkerTransfomers/numberMin.test.ts b/tests/toDataParser/checkerTransfomers/numberMin.test.ts new file mode 100644 index 0000000..8e626c4 --- /dev/null +++ b/tests/toDataParser/checkerTransfomers/numberMin.test.ts @@ -0,0 +1,25 @@ +import { DataParserToDataParser } from "@scripts/index"; +import { asserts, DP, E, unwrap } from "@duplojs/utils"; +import { printExpression } from "./utils"; + +describe("checkerNumberMin", () => { + it("exclusive", () => { + const result = DataParserToDataParser.checkerTransformer( + DP.checkerNumberMin(1, { exclusive: true }), + { transformers: DataParserToDataParser.defaultCheckerTransformers }, + ); + + asserts(result, E.isRight); + expect(printExpression(unwrap(result))).toMatchSnapshot(); + }); + + it("non exclusive", () => { + const result = DataParserToDataParser.checkerTransformer( + DP.checkerNumberMin(1), + { transformers: DataParserToDataParser.defaultCheckerTransformers }, + ); + + asserts(result, E.isRight); + expect(printExpression(unwrap(result))).toMatchSnapshot(); + }); +}); diff --git a/tests/toDataParser/checkerTransfomers/refine.test.ts b/tests/toDataParser/checkerTransfomers/refine.test.ts new file mode 100644 index 0000000..3b4aedc --- /dev/null +++ b/tests/toDataParser/checkerTransfomers/refine.test.ts @@ -0,0 +1,113 @@ +/* eslint-disable @typescript-eslint/consistent-type-imports */ +import { DataParserToDataParser } from "@scripts/index"; +import { asserts, DP, E, unwrap } from "@duplojs/utils"; +import { afterEach, vi } from "vitest"; +import { printExpression } from "./utils"; + +afterEach(() => { + vi.resetModules(); + vi.doUnmock("typescript"); +}); + +describe("checkerRefine", () => { + it("basic", () => { + const checker = DP.checkerRefine((value: number[]) => value.length > 0); + const result = DataParserToDataParser.checkerTransformer( + checker, + { transformers: DataParserToDataParser.defaultCheckerTransformers }, + ); + + asserts(result, E.isRight); + expect(printExpression(unwrap(result))).toMatchSnapshot(); + }); + + it("native function error", () => { + const checker = DP.checkerRefine(Array.isArray as never); + const result = DataParserToDataParser.checkerTransformer( + checker, + { transformers: DataParserToDataParser.defaultCheckerTransformers }, + ); + + expect(result).toEqual(E.left("buildCheckerError", checker)); + }); + + it("invalid source statement error", () => { + const checker = DP.checkerRefine(() => true); + (checker.definition.theFunction as { toString(): string }).toString = () => ";"; + + const result = DataParserToDataParser.checkerTransformer( + checker, + { transformers: DataParserToDataParser.defaultCheckerTransformers }, + ); + + expect(result).toEqual(E.left("buildCheckerError", checker)); + }); + + it("non function expression error", () => { + const checker = DP.checkerRefine(() => true); + (checker.definition.theFunction as { toString(): string }).toString = () => "1 + 1"; + + const result = DataParserToDataParser.checkerTransformer( + checker, + { transformers: DataParserToDataParser.defaultCheckerTransformers }, + ); + + expect(result).toEqual(E.left("buildCheckerError", checker)); + }); + + it("error when sourceFile has no statement", async() => { + vi.doMock("typescript", async() => { + const actual = await vi.importActual("typescript"); + return { + ...actual, + createSourceFile() { + return { statements: [] }; + }, + }; + }); + + const { checkerRefineTransformer } = await import("@scripts/toDataParser/checkerTransformer/defaults/refine"); + const checker = DP.checkerRefine(() => true); + const result = checkerRefineTransformer(checker, { + success(value) { + return E.right("buildSuccess", value); + }, + buildError() { + return E.left("buildCheckerError", checker); + }, + getDefinition() { + return []; + }, + }); + + expect(result).toEqual(E.left("buildCheckerError", checker)); + }); + + it("error when expression is not parenthesized", async() => { + vi.doMock("typescript", async() => { + const actual = await vi.importActual("typescript"); + return { + ...actual, + isParenthesizedExpression() { + return false; + }, + }; + }); + + const { checkerRefineTransformer } = await import("@scripts/toDataParser/checkerTransformer/defaults/refine"); + const checker = DP.checkerRefine(() => true); + const result = checkerRefineTransformer(checker, { + success(value) { + return E.right("buildSuccess", value); + }, + buildError() { + return E.left("buildCheckerError", checker); + }, + getDefinition() { + return []; + }, + }); + + expect(result).toEqual(E.left("buildCheckerError", checker)); + }); +}); diff --git a/tests/toDataParser/checkerTransfomers/regex.test.ts b/tests/toDataParser/checkerTransfomers/regex.test.ts new file mode 100644 index 0000000..f038a6a --- /dev/null +++ b/tests/toDataParser/checkerTransfomers/regex.test.ts @@ -0,0 +1,15 @@ +import { DataParserToDataParser } from "@scripts/index"; +import { asserts, DP, E, unwrap } from "@duplojs/utils"; +import { printExpression } from "./utils"; + +describe("checkerRegex", () => { + it("basic", () => { + const result = DataParserToDataParser.checkerTransformer( + DP.checkerRegex(/^[a-z]+$/), + { transformers: DataParserToDataParser.defaultCheckerTransformers }, + ); + + asserts(result, E.isRight); + expect(printExpression(unwrap(result))).toMatchSnapshot(); + }); +}); diff --git a/tests/toDataParser/checkerTransfomers/stringMax.test.ts b/tests/toDataParser/checkerTransfomers/stringMax.test.ts new file mode 100644 index 0000000..4577a2b --- /dev/null +++ b/tests/toDataParser/checkerTransfomers/stringMax.test.ts @@ -0,0 +1,15 @@ +import { DataParserToDataParser } from "@scripts/index"; +import { asserts, DP, E, unwrap } from "@duplojs/utils"; +import { printExpression } from "./utils"; + +describe("checkerStringMax", () => { + it("basic", () => { + const result = DataParserToDataParser.checkerTransformer( + DP.checkerStringMax(10), + { transformers: DataParserToDataParser.defaultCheckerTransformers }, + ); + + asserts(result, E.isRight); + expect(printExpression(unwrap(result))).toMatchSnapshot(); + }); +}); diff --git a/tests/toDataParser/checkerTransfomers/stringMin.test.ts b/tests/toDataParser/checkerTransfomers/stringMin.test.ts new file mode 100644 index 0000000..984f844 --- /dev/null +++ b/tests/toDataParser/checkerTransfomers/stringMin.test.ts @@ -0,0 +1,15 @@ +import { DataParserToDataParser } from "@scripts/index"; +import { asserts, DP, E, unwrap } from "@duplojs/utils"; +import { printExpression } from "./utils"; + +describe("checkerStringMin", () => { + it("basic", () => { + const result = DataParserToDataParser.checkerTransformer( + DP.checkerStringMin(2), + { transformers: DataParserToDataParser.defaultCheckerTransformers }, + ); + + asserts(result, E.isRight); + expect(printExpression(unwrap(result))).toMatchSnapshot(); + }); +}); diff --git a/tests/toDataParser/checkerTransfomers/timeMax.test.ts b/tests/toDataParser/checkerTransfomers/timeMax.test.ts new file mode 100644 index 0000000..4fa5ac9 --- /dev/null +++ b/tests/toDataParser/checkerTransfomers/timeMax.test.ts @@ -0,0 +1,15 @@ +import { DataParserToDataParser } from "@scripts/index"; +import { asserts, DDate, DP, E, unwrap } from "@duplojs/utils"; +import { printExpression } from "./utils"; + +describe("checkerTimeMax", () => { + it("basic", () => { + const result = DataParserToDataParser.checkerTransformer( + DP.checkerTimeMax(DDate.createTime(2000, "millisecond")), + { transformers: DataParserToDataParser.defaultCheckerTransformers }, + ); + + asserts(result, E.isRight); + expect(printExpression(unwrap(result))).toMatchSnapshot(); + }); +}); diff --git a/tests/toDataParser/checkerTransfomers/timeMin.test.ts b/tests/toDataParser/checkerTransfomers/timeMin.test.ts new file mode 100644 index 0000000..8d62221 --- /dev/null +++ b/tests/toDataParser/checkerTransfomers/timeMin.test.ts @@ -0,0 +1,15 @@ +import { DataParserToDataParser } from "@scripts/index"; +import { asserts, DDate, DP, E, unwrap } from "@duplojs/utils"; +import { printExpression } from "./utils"; + +describe("checkerTimeMin", () => { + it("basic", () => { + const result = DataParserToDataParser.checkerTransformer( + DP.checkerTimeMin(DDate.createTime(1000, "millisecond")), + { transformers: DataParserToDataParser.defaultCheckerTransformers }, + ); + + asserts(result, E.isRight); + expect(printExpression(unwrap(result))).toMatchSnapshot(); + }); +}); diff --git a/tests/toDataParser/checkerTransfomers/url.test.ts b/tests/toDataParser/checkerTransfomers/url.test.ts new file mode 100644 index 0000000..89a394a --- /dev/null +++ b/tests/toDataParser/checkerTransfomers/url.test.ts @@ -0,0 +1,29 @@ +import { DataParserToDataParser } from "@scripts/index"; +import { asserts, DP, E, unwrap } from "@duplojs/utils"; +import { printExpression } from "./utils"; + +describe("checkerUrl", () => { + it("with options", () => { + const result = DataParserToDataParser.checkerTransformer( + DP.checkerUrl({ + hostname: /^[a-z.-]+$/, + normalize: true, + protocol: /^https?$/, + }), + { transformers: DataParserToDataParser.defaultCheckerTransformers }, + ); + + asserts(result, E.isRight); + expect(printExpression(unwrap(result))).toMatchSnapshot(); + }); + + it("without options", () => { + const result = DataParserToDataParser.checkerTransformer( + DP.checkerUrl(), + { transformers: DataParserToDataParser.defaultCheckerTransformers }, + ); + + asserts(result, E.isRight); + expect(printExpression(unwrap(result))).toMatchSnapshot(); + }); +}); diff --git a/tests/toDataParser/checkerTransfomers/utils.ts b/tests/toDataParser/checkerTransfomers/utils.ts new file mode 100644 index 0000000..5209fbb --- /dev/null +++ b/tests/toDataParser/checkerTransfomers/utils.ts @@ -0,0 +1,7 @@ +import { createPrinter, createSourceFile, EmitHint, ScriptKind, ScriptTarget, type CallExpression } from "typescript"; + +export function printExpression(expression: CallExpression) { + const printer = createPrinter(); + const sourceFile = createSourceFile("test.ts", "", ScriptTarget.Latest, false, ScriptKind.TS); + return printer.printNode(EmitHint.Unspecified, expression, sourceFile); +} diff --git a/tests/toDataParser/checkerTransfomers/uuid.test.ts b/tests/toDataParser/checkerTransfomers/uuid.test.ts new file mode 100644 index 0000000..a93ab5d --- /dev/null +++ b/tests/toDataParser/checkerTransfomers/uuid.test.ts @@ -0,0 +1,15 @@ +import { DataParserToDataParser } from "@scripts/index"; +import { asserts, DP, E, unwrap } from "@duplojs/utils"; +import { printExpression } from "./utils"; + +describe("checkerUuid", () => { + it("basic", () => { + const result = DataParserToDataParser.checkerTransformer( + DP.checkerUuid(), + { transformers: DataParserToDataParser.defaultCheckerTransformers }, + ); + + asserts(result, E.isRight); + expect(printExpression(unwrap(result))).toMatchSnapshot(); + }); +}); diff --git a/tests/toDataParser/dataParserTransformer/__snapshots__/array.test.ts.snap b/tests/toDataParser/dataParserTransformer/__snapshots__/array.test.ts.snap new file mode 100644 index 0000000..87a981e --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/__snapshots__/array.test.ts.snap @@ -0,0 +1,7 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`array > renders array parser 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export const arrayParser = DP.array(DP.string());" +`; diff --git a/tests/toDataParser/dataParserTransformer/__snapshots__/bigint.test.ts.snap b/tests/toDataParser/dataParserTransformer/__snapshots__/bigint.test.ts.snap new file mode 100644 index 0000000..ddf8d1e --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/__snapshots__/bigint.test.ts.snap @@ -0,0 +1,13 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`bigint > renders bigint parser 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export const bigintParser = DP.bigint({ coerce: true });" +`; + +exports[`bigint > renders bigint parser without coerce 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export const bigintParserNoCoerce = DP.bigint();" +`; diff --git a/tests/toDataParser/dataParserTransformer/__snapshots__/boolean.test.ts.snap b/tests/toDataParser/dataParserTransformer/__snapshots__/boolean.test.ts.snap new file mode 100644 index 0000000..3f7acdb --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/__snapshots__/boolean.test.ts.snap @@ -0,0 +1,13 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`boolean > renders boolean parser 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export const booleanParser = DP.boolean({ coerce: true });" +`; + +exports[`boolean > renders boolean parser without coerce 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export const booleanParserNoCoerce = DP.boolean();" +`; diff --git a/tests/toDataParser/dataParserTransformer/__snapshots__/date.test.ts.snap b/tests/toDataParser/dataParserTransformer/__snapshots__/date.test.ts.snap new file mode 100644 index 0000000..175a736 --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/__snapshots__/date.test.ts.snap @@ -0,0 +1,13 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`date > renders date parser 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export const dateParser = DP.date({ coerce: true });" +`; + +exports[`date > renders date parser without coerce 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export const dateParserNoCoerce = DP.date();" +`; diff --git a/tests/toDataParser/dataParserTransformer/__snapshots__/empty.test.ts.snap b/tests/toDataParser/dataParserTransformer/__snapshots__/empty.test.ts.snap new file mode 100644 index 0000000..7168874 --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/__snapshots__/empty.test.ts.snap @@ -0,0 +1,13 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`empty > renders empty parser 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export const emptyParser = DP.empty({ coerce: true });" +`; + +exports[`empty > renders empty parser without coerce 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export const emptyParserNoCoerce = DP.empty();" +`; diff --git a/tests/toDataParser/dataParserTransformer/__snapshots__/file.test.ts.snap b/tests/toDataParser/dataParserTransformer/__snapshots__/file.test.ts.snap new file mode 100644 index 0000000..30f6b58 --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/__snapshots__/file.test.ts.snap @@ -0,0 +1,33 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`file > renders extended file parser namespace 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +import * as DPE from "@duplojs/utils/dataParserExtended"; + +import * as SDPE from "@duplojs/server-utils/dataParserExtended"; + +export const fileParserExtended = SDPE.file({});" +`; + +exports[`file > renders file parser with async constraints 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +import * as SDP from "@duplojs/server-utils/dataParser"; + +export const fileParser = SDP.file({ + coerce: true, + checkExist: true, + maxSize: 10, + minSize: 5, + mimeType: /image\\/png/ +});" +`; + +exports[`file > renders file parser without options 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +import * as SDP from "@duplojs/server-utils/dataParser"; + +export const fileParserNoOptions = SDP.file({});" +`; diff --git a/tests/toDataParser/dataParserTransformer/__snapshots__/lazy.test.ts.snap b/tests/toDataParser/dataParserTransformer/__snapshots__/lazy.test.ts.snap new file mode 100644 index 0000000..e43e016 --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/__snapshots__/lazy.test.ts.snap @@ -0,0 +1,7 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`lazy > renders lazy parser 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export const lazyParser = DP.lazy(() => DP.string());" +`; diff --git a/tests/toDataParser/dataParserTransformer/__snapshots__/literal.test.ts.snap b/tests/toDataParser/dataParserTransformer/__snapshots__/literal.test.ts.snap new file mode 100644 index 0000000..5fdaf13 --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/__snapshots__/literal.test.ts.snap @@ -0,0 +1,15 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`literal > renders literal parser 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export const literalParser = DP.literal([ + "foo", + 1, + 1n, + true, + false, + null, + undefined +]);" +`; diff --git a/tests/toDataParser/dataParserTransformer/__snapshots__/nil.test.ts.snap b/tests/toDataParser/dataParserTransformer/__snapshots__/nil.test.ts.snap new file mode 100644 index 0000000..e198311 --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/__snapshots__/nil.test.ts.snap @@ -0,0 +1,13 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`nil > renders nil parser 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export const nilParser = DP.nil({ coerce: true });" +`; + +exports[`nil > renders nil parser without coerce 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export const nilParserNoCoerce = DP.nil();" +`; diff --git a/tests/toDataParser/dataParserTransformer/__snapshots__/nullable.test.ts.snap b/tests/toDataParser/dataParserTransformer/__snapshots__/nullable.test.ts.snap new file mode 100644 index 0000000..6630899 --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/__snapshots__/nullable.test.ts.snap @@ -0,0 +1,7 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`nullable > renders nullable parser 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export const nullableParser = DP.nullable(DP.string());" +`; diff --git a/tests/toDataParser/dataParserTransformer/__snapshots__/number.test.ts.snap b/tests/toDataParser/dataParserTransformer/__snapshots__/number.test.ts.snap new file mode 100644 index 0000000..180c46d --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/__snapshots__/number.test.ts.snap @@ -0,0 +1,13 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`number > renders number parser 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export const numberParser = DP.number({ coerce: true });" +`; + +exports[`number > renders number parser without coerce 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export const numberParserNoCoerce = DP.number();" +`; diff --git a/tests/toDataParser/dataParserTransformer/__snapshots__/object.test.ts.snap b/tests/toDataParser/dataParserTransformer/__snapshots__/object.test.ts.snap new file mode 100644 index 0000000..9ea60f4 --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/__snapshots__/object.test.ts.snap @@ -0,0 +1,10 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`object > renders object parser 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export const objectParser = DP.object({ + foo: DP.string(), + bar: DP.number() +});" +`; diff --git a/tests/toDataParser/dataParserTransformer/__snapshots__/optional.test.ts.snap b/tests/toDataParser/dataParserTransformer/__snapshots__/optional.test.ts.snap new file mode 100644 index 0000000..85323f9 --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/__snapshots__/optional.test.ts.snap @@ -0,0 +1,7 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`optional > renders optional parser 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export const optionalParser = DP.optional(DP.string());" +`; diff --git a/tests/toDataParser/dataParserTransformer/__snapshots__/pipe.test.ts.snap b/tests/toDataParser/dataParserTransformer/__snapshots__/pipe.test.ts.snap new file mode 100644 index 0000000..0be75c7 --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/__snapshots__/pipe.test.ts.snap @@ -0,0 +1,7 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`pipe > renders pipe parser 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export const pipeParser = DP.pipe(DP.string(), DP.number());" +`; diff --git a/tests/toDataParser/dataParserTransformer/__snapshots__/record.test.ts.snap b/tests/toDataParser/dataParserTransformer/__snapshots__/record.test.ts.snap new file mode 100644 index 0000000..2c478dd --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/__snapshots__/record.test.ts.snap @@ -0,0 +1,7 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`record > renders record parser 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export const recordParser = DP.record(DP.string(), DP.number());" +`; diff --git a/tests/toDataParser/dataParserTransformer/__snapshots__/recover.test.ts.snap b/tests/toDataParser/dataParserTransformer/__snapshots__/recover.test.ts.snap new file mode 100644 index 0000000..91c49e6 --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/__snapshots__/recover.test.ts.snap @@ -0,0 +1,7 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`recover > renders inner parser 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export const recoverParser = DP.string();" +`; diff --git a/tests/toDataParser/dataParserTransformer/__snapshots__/string.test.ts.snap b/tests/toDataParser/dataParserTransformer/__snapshots__/string.test.ts.snap new file mode 100644 index 0000000..97f8f78 --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/__snapshots__/string.test.ts.snap @@ -0,0 +1,7 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`string > renders string parser 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export const stringParser = DP.string();" +`; diff --git a/tests/toDataParser/dataParserTransformer/__snapshots__/templateLiteral.test.ts.snap b/tests/toDataParser/dataParserTransformer/__snapshots__/templateLiteral.test.ts.snap new file mode 100644 index 0000000..ec97465 --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/__snapshots__/templateLiteral.test.ts.snap @@ -0,0 +1,24 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`templateLiteral > renders template literal parser 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export const templateLiteralParser = DP.templateLiteral([ + "pre-", + 1n, + "mid-", + true, + "-false-", + false, + "-str-", + "abc", + "-num-", + 42, + "-null-", + null, + "-undef-", + undefined, + "-dp-", + DP.string() +]);" +`; diff --git a/tests/toDataParser/dataParserTransformer/__snapshots__/time.test.ts.snap b/tests/toDataParser/dataParserTransformer/__snapshots__/time.test.ts.snap new file mode 100644 index 0000000..5cf4178 --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/__snapshots__/time.test.ts.snap @@ -0,0 +1,18 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`time > renders time parser with checker imports 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +import * as DDate from "@duplojs/utils/date"; + +export const timeParser = DP.time({ + coerce: true, + checkers: [DP.checkerTimeMin(DDate.createTime(1, "millisecond"))] +});" +`; + +exports[`time > renders time parser without coerce and without checkers 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export const timeParserNoCoerce = DP.time();" +`; diff --git a/tests/toDataParser/dataParserTransformer/__snapshots__/transform.test.ts.snap b/tests/toDataParser/dataParserTransformer/__snapshots__/transform.test.ts.snap new file mode 100644 index 0000000..baecd5e --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/__snapshots__/transform.test.ts.snap @@ -0,0 +1,7 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`transform > renders transform parser 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export const transformParser = DP.transform(DP.string(), (value) => value.trim());" +`; diff --git a/tests/toDataParser/dataParserTransformer/__snapshots__/tuple.test.ts.snap b/tests/toDataParser/dataParserTransformer/__snapshots__/tuple.test.ts.snap new file mode 100644 index 0000000..d0bd0b0 --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/__snapshots__/tuple.test.ts.snap @@ -0,0 +1,13 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`tuple > renders tuple parser with rest 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export const tupleParser = DP.tuple([DP.string()], { rest: DP.number() });" +`; + +exports[`tuple > renders tuple parser without rest 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export const tupleParserNoRest = DP.tuple([DP.string()]);" +`; diff --git a/tests/toDataParser/dataParserTransformer/__snapshots__/union.test.ts.snap b/tests/toDataParser/dataParserTransformer/__snapshots__/union.test.ts.snap new file mode 100644 index 0000000..801da41 --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/__snapshots__/union.test.ts.snap @@ -0,0 +1,10 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`union > renders union parser 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export const unionParser = DP.union([ + DP.string(), + DP.number() +]);" +`; diff --git a/tests/toDataParser/dataParserTransformer/__snapshots__/unknown.test.ts.snap b/tests/toDataParser/dataParserTransformer/__snapshots__/unknown.test.ts.snap new file mode 100644 index 0000000..871f0ee --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/__snapshots__/unknown.test.ts.snap @@ -0,0 +1,7 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`unknown > renders unknown parser 1`] = ` +"import * as DP from "@duplojs/utils/dataParser"; + +export const unknownParser = DP.unknown();" +`; diff --git a/tests/toDataParser/dataParserTransformer/array.test.ts b/tests/toDataParser/dataParserTransformer/array.test.ts new file mode 100644 index 0000000..42f4682 --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/array.test.ts @@ -0,0 +1,65 @@ +import { DP, DPE, E } from "@duplojs/utils"; +import { defaultTransformers, defaultCheckerTransformers, render } from "@scripts/toDataParser"; +import { defaultTransformers as tsDefaultTransformers } from "@scripts/toTypescript"; + +const toTypescript = { + identifier: "ArrayParser", + transformers: tsDefaultTransformers, +}; + +describe("array", () => { + it("renders array parser", () => { + expect( + render( + DPE.array(DPE.string()), + { + constName: "arrayParser", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toMatchSnapshot(); + }); + + it("fails when inner element cannot be rendered", () => { + expect( + () => render( + DPE.array(DPE.string()), + { + constName: "arrayParserError", + dataParserTransformers: [ + ((dataParser, { buildError }) => DP.stringKind.has(dataParser) + ? buildError() + : E.left("dataParserNotSupport", dataParser)), + ...defaultTransformers, + ], + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toThrow(); + }); + + it("fails when definition checker cannot be rendered", () => { + const schema = DPE.array(DPE.string(), { + checkers: [{ kind: "forced-error" } as any], + }); + + expect( + () => render( + schema, + { + constName: "arrayParserCheckerError", + dataParserTransformers: defaultTransformers, + checkerTransformers: [ + ((checker, { buildError }) => (checker as any).kind === "forced-error" + ? buildError() + : E.left("checkerNotSupport", checker)), + ], + toTypescript, + }, + ), + ).toThrow(); + }); +}); diff --git a/tests/toDataParser/dataParserTransformer/bigint.test.ts b/tests/toDataParser/dataParserTransformer/bigint.test.ts new file mode 100644 index 0000000..ca7e8fc --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/bigint.test.ts @@ -0,0 +1,60 @@ +import { DPE, E } from "@duplojs/utils"; +import { defaultTransformers, defaultCheckerTransformers, render } from "@scripts/toDataParser"; +import { defaultTransformers as tsDefaultTransformers } from "@scripts/toTypescript"; + +const toTypescript = { + identifier: "BigintParser", + transformers: tsDefaultTransformers, +}; + +describe("bigint", () => { + it("renders bigint parser", () => { + expect( + render( + DPE.bigint({ coerce: true }), + { + constName: "bigintParser", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toMatchSnapshot(); + }); + + it("renders bigint parser without coerce", () => { + expect( + render( + DPE.bigint(), + { + constName: "bigintParserNoCoerce", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toMatchSnapshot(); + }); + + it("fails when definition checker cannot be rendered", () => { + const schema = DPE.bigint({ + checkers: [{ kind: "forced-error" } as any], + }); + + expect( + () => render( + schema, + { + constName: "bigintParserError", + dataParserTransformers: defaultTransformers, + checkerTransformers: [ + ((checker, { buildError }) => (checker as any).kind === "forced-error" + ? buildError() + : E.left("checkerNotSupport", checker)), + ], + toTypescript, + }, + ), + ).toThrow(); + }); +}); diff --git a/tests/toDataParser/dataParserTransformer/boolean.test.ts b/tests/toDataParser/dataParserTransformer/boolean.test.ts new file mode 100644 index 0000000..6897282 --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/boolean.test.ts @@ -0,0 +1,60 @@ +import { DPE, E } from "@duplojs/utils"; +import { defaultTransformers, defaultCheckerTransformers, render } from "@scripts/toDataParser"; +import { defaultTransformers as tsDefaultTransformers } from "@scripts/toTypescript"; + +const toTypescript = { + identifier: "BooleanParser", + transformers: tsDefaultTransformers, +}; + +describe("boolean", () => { + it("renders boolean parser", () => { + expect( + render( + DPE.boolean({ coerce: true }), + { + constName: "booleanParser", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toMatchSnapshot(); + }); + + it("renders boolean parser without coerce", () => { + expect( + render( + DPE.boolean(), + { + constName: "booleanParserNoCoerce", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toMatchSnapshot(); + }); + + it("fails when definition checker cannot be rendered", () => { + const schema = DPE.boolean({ + checkers: [{ kind: "forced-error" } as any], + }); + + expect( + () => render( + schema, + { + constName: "booleanParserError", + dataParserTransformers: defaultTransformers, + checkerTransformers: [ + ((checker, { buildError }) => (checker as any).kind === "forced-error" + ? buildError() + : E.left("checkerNotSupport", checker)), + ], + toTypescript, + }, + ), + ).toThrow(); + }); +}); diff --git a/tests/toDataParser/dataParserTransformer/date.test.ts b/tests/toDataParser/dataParserTransformer/date.test.ts new file mode 100644 index 0000000..e22bf7a --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/date.test.ts @@ -0,0 +1,60 @@ +import { DPE, E } from "@duplojs/utils"; +import { defaultTransformers, defaultCheckerTransformers, render } from "@scripts/toDataParser"; +import { defaultTransformers as tsDefaultTransformers } from "@scripts/toTypescript"; + +const toTypescript = { + identifier: "DateParser", + transformers: tsDefaultTransformers, +}; + +describe("date", () => { + it("renders date parser", () => { + expect( + render( + DPE.date({ coerce: true }), + { + constName: "dateParser", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toMatchSnapshot(); + }); + + it("renders date parser without coerce", () => { + expect( + render( + DPE.date(), + { + constName: "dateParserNoCoerce", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toMatchSnapshot(); + }); + + it("fails when definition checker cannot be rendered", () => { + const schema = DPE.date({ + checkers: [{ kind: "forced-error" } as any], + }); + + expect( + () => render( + schema, + { + constName: "dateParserError", + dataParserTransformers: defaultTransformers, + checkerTransformers: [ + ((checker, { buildError }) => (checker as any).kind === "forced-error" + ? buildError() + : E.left("checkerNotSupport", checker)), + ], + toTypescript, + }, + ), + ).toThrow(); + }); +}); diff --git a/tests/toDataParser/dataParserTransformer/empty.test.ts b/tests/toDataParser/dataParserTransformer/empty.test.ts new file mode 100644 index 0000000..eb7e12a --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/empty.test.ts @@ -0,0 +1,60 @@ +import { DPE, E } from "@duplojs/utils"; +import { defaultTransformers, defaultCheckerTransformers, render } from "@scripts/toDataParser"; +import { defaultTransformers as tsDefaultTransformers } from "@scripts/toTypescript"; + +const toTypescript = { + identifier: "EmptyParser", + transformers: tsDefaultTransformers, +}; + +describe("empty", () => { + it("renders empty parser", () => { + expect( + render( + DPE.empty({ coerce: true }), + { + constName: "emptyParser", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toMatchSnapshot(); + }); + + it("renders empty parser without coerce", () => { + expect( + render( + DPE.empty(), + { + constName: "emptyParserNoCoerce", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toMatchSnapshot(); + }); + + it("fails when definition checker cannot be rendered", () => { + const schema = DPE.empty({ + checkers: [{ kind: "forced-error" } as any], + }); + + expect( + () => render( + schema, + { + constName: "emptyParserError", + dataParserTransformers: defaultTransformers, + checkerTransformers: [ + ((checker, { buildError }) => (checker as any).kind === "forced-error" + ? buildError() + : E.left("checkerNotSupport", checker)), + ], + toTypescript, + }, + ), + ).toThrow(); + }); +}); diff --git a/tests/toDataParser/dataParserTransformer/file.test.ts b/tests/toDataParser/dataParserTransformer/file.test.ts new file mode 100644 index 0000000..06f346b --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/file.test.ts @@ -0,0 +1,81 @@ +import { SDP } from "@duplojs/server-utils"; +import { E } from "@duplojs/utils"; +import { defaultTransformers, defaultCheckerTransformers, render } from "@scripts/toDataParser"; +import { defaultTransformers as tsDefaultTransformers } from "@scripts/toTypescript"; + +const toTypescript = { + identifier: "FileParser", + transformers: tsDefaultTransformers, +}; + +describe("file", () => { + it("renders file parser with async constraints", () => { + expect( + render( + SDP.coerce.file({ + checkExist: true, + maxSize: 10, + minSize: 5, + mimeType: /image\/png/, + }), + { + constName: "fileParser", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toMatchSnapshot(); + }); + + it("renders file parser without options", () => { + expect( + render( + SDP.file(), + { + constName: "fileParserNoOptions", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toMatchSnapshot(); + }); + + it("renders extended file parser namespace", () => { + expect( + render( + SDP.file(), + { + constName: "fileParserExtended", + exportMode: "extended", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toMatchSnapshot(); + }); + + it("fails when definition checker cannot be rendered", () => { + const schema = SDP.file({}, { + checkers: [{ kind: "forced-error" } as any], + }); + + expect( + () => render( + schema, + { + constName: "fileParserError", + dataParserTransformers: defaultTransformers, + checkerTransformers: [ + ((checker, { buildError }) => (checker as any).kind === "forced-error" + ? buildError() + : E.left("checkerNotSupport", checker)), + ], + toTypescript, + }, + ), + ).toThrow(); + }); +}); diff --git a/tests/toDataParser/dataParserTransformer/lazy.test.ts b/tests/toDataParser/dataParserTransformer/lazy.test.ts new file mode 100644 index 0000000..b34643f --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/lazy.test.ts @@ -0,0 +1,65 @@ +import { DP, DPE, E } from "@duplojs/utils"; +import { defaultTransformers, defaultCheckerTransformers, render } from "@scripts/toDataParser"; +import { defaultTransformers as tsDefaultTransformers } from "@scripts/toTypescript"; + +const toTypescript = { + identifier: "LazyParser", + transformers: tsDefaultTransformers, +}; + +describe("lazy", () => { + it("renders lazy parser", () => { + expect( + render( + DPE.lazy(() => DPE.string()), + { + constName: "lazyParser", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toMatchSnapshot(); + }); + + it("fails when lazy inner parser cannot be rendered", () => { + expect( + () => render( + DPE.lazy(() => DPE.string()), + { + constName: "lazyParserError", + dataParserTransformers: [ + ((dataParser, { buildError }) => DP.stringKind.has(dataParser) + ? buildError() + : E.left("dataParserNotSupport", dataParser)), + ...defaultTransformers, + ], + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toThrow(); + }); + + it("fails when definition checker cannot be rendered", () => { + const schema = DPE.lazy(() => DPE.string(), { + checkers: [{ kind: "forced-error" } as any], + }); + + expect( + () => render( + schema, + { + constName: "lazyParserCheckerError", + dataParserTransformers: defaultTransformers, + checkerTransformers: [ + ((checker, { buildError }) => (checker as any).kind === "forced-error" + ? buildError() + : E.left("checkerNotSupport", checker)), + ], + toTypescript, + }, + ), + ).toThrow(); + }); +}); diff --git a/tests/toDataParser/dataParserTransformer/literal.test.ts b/tests/toDataParser/dataParserTransformer/literal.test.ts new file mode 100644 index 0000000..6eef50b --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/literal.test.ts @@ -0,0 +1,46 @@ +import { DPE, E } from "@duplojs/utils"; +import { defaultTransformers, defaultCheckerTransformers, render } from "@scripts/toDataParser"; +import { defaultTransformers as tsDefaultTransformers } from "@scripts/toTypescript"; + +const toTypescript = { + identifier: "LiteralParser", + transformers: tsDefaultTransformers, +}; + +describe("literal", () => { + it("renders literal parser", () => { + expect( + render( + DPE.literal(["foo", 1, 1n, true, false, null, undefined]), + { + constName: "literalParser", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toMatchSnapshot(); + }); + + it("fails when definition checker cannot be rendered", () => { + const schema = DPE.literal(["foo"], { + checkers: [{ kind: "forced-error" } as any], + }); + + expect( + () => render( + schema, + { + constName: "literalParserCheckerError", + dataParserTransformers: defaultTransformers, + checkerTransformers: [ + ((checker, { buildError }) => (checker as any).kind === "forced-error" + ? buildError() + : E.left("checkerNotSupport", checker)), + ], + toTypescript, + }, + ), + ).toThrow(); + }); +}); diff --git a/tests/toDataParser/dataParserTransformer/nil.test.ts b/tests/toDataParser/dataParserTransformer/nil.test.ts new file mode 100644 index 0000000..a6457da --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/nil.test.ts @@ -0,0 +1,58 @@ +import { DPE, E } from "@duplojs/utils"; +import { defaultTransformers, defaultCheckerTransformers, render } from "@scripts/toDataParser"; +import { defaultTransformers as tsDefaultTransformers } from "@scripts/toTypescript"; + +const toTypescript = { + identifier: "NilParser", + transformers: tsDefaultTransformers, +}; + +describe("nil", () => { + it("renders nil parser", () => { + expect( + render( + DPE.nil({ coerce: true }), + { + constName: "nilParser", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toMatchSnapshot(); + }); + + it("renders nil parser without coerce", () => { + expect( + render( + DPE.nil(), + { + constName: "nilParserNoCoerce", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toMatchSnapshot(); + }); + + it("fails when definition checker cannot be rendered", () => { + const schema = DPE.nil({ checkers: [{ kind: "forced-error" } as any] }); + + expect( + () => render( + schema, + { + constName: "nilParserError", + dataParserTransformers: defaultTransformers, + checkerTransformers: [ + ((checker, { buildError }) => (checker as any).kind === "forced-error" + ? buildError() + : E.left("checkerNotSupport", checker)), + ], + toTypescript, + }, + ), + ).toThrow(); + }); +}); diff --git a/tests/toDataParser/dataParserTransformer/nullable.test.ts b/tests/toDataParser/dataParserTransformer/nullable.test.ts new file mode 100644 index 0000000..1f0af35 --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/nullable.test.ts @@ -0,0 +1,65 @@ +import { DP, DPE, E } from "@duplojs/utils"; +import { defaultTransformers, defaultCheckerTransformers, render } from "@scripts/toDataParser"; +import { defaultTransformers as tsDefaultTransformers } from "@scripts/toTypescript"; + +const toTypescript = { + identifier: "NullableParser", + transformers: tsDefaultTransformers, +}; + +describe("nullable", () => { + it("renders nullable parser", () => { + expect( + render( + DPE.nullable(DPE.string()), + { + constName: "nullableParser", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toMatchSnapshot(); + }); + + it("fails when inner parser cannot be rendered", () => { + expect( + () => render( + DPE.nullable(DPE.string()), + { + constName: "nullableParserError", + dataParserTransformers: [ + ((dataParser, { buildError }) => DP.stringKind.has(dataParser) + ? buildError() + : E.left("dataParserNotSupport", dataParser)), + ...defaultTransformers, + ], + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toThrow(); + }); + + it("fails when definition checker cannot be rendered", () => { + const schema = DPE.nullable(DPE.string(), { + checkers: [{ kind: "forced-error" } as any], + }); + + expect( + () => render( + schema, + { + constName: "nullableParserCheckerError", + dataParserTransformers: defaultTransformers, + checkerTransformers: [ + ((checker, { buildError }) => (checker as any).kind === "forced-error" + ? buildError() + : E.left("checkerNotSupport", checker)), + ], + toTypescript, + }, + ), + ).toThrow(); + }); +}); diff --git a/tests/toDataParser/dataParserTransformer/number.test.ts b/tests/toDataParser/dataParserTransformer/number.test.ts new file mode 100644 index 0000000..ed71e20 --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/number.test.ts @@ -0,0 +1,60 @@ +import { DPE, E } from "@duplojs/utils"; +import { defaultTransformers, defaultCheckerTransformers, render } from "@scripts/toDataParser"; +import { defaultTransformers as tsDefaultTransformers } from "@scripts/toTypescript"; + +const toTypescript = { + identifier: "NumberParser", + transformers: tsDefaultTransformers, +}; + +describe("number", () => { + it("renders number parser", () => { + expect( + render( + DPE.number({ coerce: true }), + { + constName: "numberParser", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toMatchSnapshot(); + }); + + it("renders number parser without coerce", () => { + expect( + render( + DPE.number(), + { + constName: "numberParserNoCoerce", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toMatchSnapshot(); + }); + + it("fails when definition checker cannot be rendered", () => { + const schema = DPE.number({ + checkers: [{ kind: "forced-error" } as any], + }); + + expect( + () => render( + schema, + { + constName: "numberParserError", + dataParserTransformers: defaultTransformers, + checkerTransformers: [ + ((checker, { buildError }) => (checker as any).kind === "forced-error" + ? buildError() + : E.left("checkerNotSupport", checker)), + ], + toTypescript, + }, + ), + ).toThrow(); + }); +}); diff --git a/tests/toDataParser/dataParserTransformer/object.test.ts b/tests/toDataParser/dataParserTransformer/object.test.ts new file mode 100644 index 0000000..3c9fe41 --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/object.test.ts @@ -0,0 +1,74 @@ +import { DP, DPE, E } from "@duplojs/utils"; +import { defaultTransformers, defaultCheckerTransformers, render } from "@scripts/toDataParser"; +import { defaultTransformers as tsDefaultTransformers } from "@scripts/toTypescript"; + +const toTypescript = { + identifier: "ObjectParser", + transformers: tsDefaultTransformers, +}; + +describe("object", () => { + it("renders object parser", () => { + expect( + render( + DPE.object({ + foo: DPE.string(), + bar: DPE.number(), + }), + { + constName: "objectParser", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toMatchSnapshot(); + }); + + it("fails when a shape property cannot be rendered", () => { + expect( + () => render( + DPE.object({ + foo: DPE.string(), + bar: DPE.number(), + }), + { + constName: "objectParserError", + dataParserTransformers: [ + ((dataParser, { buildError }) => DP.stringKind.has(dataParser) + ? buildError() + : E.left("dataParserNotSupport", dataParser)), + ...defaultTransformers, + ], + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toThrow(); + }); + + it("fails when definition checker cannot be rendered", () => { + const schema = DPE.object({ + foo: DPE.string(), + bar: DPE.number(), + }, { + checkers: [{ kind: "forced-error" } as any], + }); + + expect( + () => render( + schema, + { + constName: "objectParserCheckerError", + dataParserTransformers: defaultTransformers, + checkerTransformers: [ + ((checker, { buildError }) => (checker as any).kind === "forced-error" + ? buildError() + : E.left("checkerNotSupport", checker)), + ], + toTypescript, + }, + ), + ).toThrow(); + }); +}); diff --git a/tests/toDataParser/dataParserTransformer/optional.test.ts b/tests/toDataParser/dataParserTransformer/optional.test.ts new file mode 100644 index 0000000..169eb55 --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/optional.test.ts @@ -0,0 +1,65 @@ +import { DP, DPE, E } from "@duplojs/utils"; +import { defaultTransformers, defaultCheckerTransformers, render } from "@scripts/toDataParser"; +import { defaultTransformers as tsDefaultTransformers } from "@scripts/toTypescript"; + +const toTypescript = { + identifier: "OptionalParser", + transformers: tsDefaultTransformers, +}; + +describe("optional", () => { + it("renders optional parser", () => { + expect( + render( + DPE.optional(DPE.string()), + { + constName: "optionalParser", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toMatchSnapshot(); + }); + + it("fails when inner parser cannot be rendered", () => { + expect( + () => render( + DPE.optional(DPE.string()), + { + constName: "optionalParserError", + dataParserTransformers: [ + ((dataParser, { buildError }) => DP.stringKind.has(dataParser) + ? buildError() + : E.left("dataParserNotSupport", dataParser)), + ...defaultTransformers, + ], + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toThrow(); + }); + + it("fails when definition checker cannot be rendered", () => { + const schema = DPE.optional(DPE.string(), { + checkers: [{ kind: "forced-error" } as any], + }); + + expect( + () => render( + schema, + { + constName: "optionalParserCheckerError", + dataParserTransformers: defaultTransformers, + checkerTransformers: [ + ((checker, { buildError }) => (checker as any).kind === "forced-error" + ? buildError() + : E.left("checkerNotSupport", checker)), + ], + toTypescript, + }, + ), + ).toThrow(); + }); +}); diff --git a/tests/toDataParser/dataParserTransformer/pipe.test.ts b/tests/toDataParser/dataParserTransformer/pipe.test.ts new file mode 100644 index 0000000..0247350 --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/pipe.test.ts @@ -0,0 +1,84 @@ +import { DP, DPE, E } from "@duplojs/utils"; +import { defaultTransformers, defaultCheckerTransformers, render } from "@scripts/toDataParser"; +import { defaultTransformers as tsDefaultTransformers } from "@scripts/toTypescript"; + +const toTypescript = { + identifier: "PipeParser", + transformers: tsDefaultTransformers, +}; + +describe("pipe", () => { + it("renders pipe parser", () => { + expect( + render( + DPE.pipe(DPE.string(), DPE.number()), + { + constName: "pipeParser", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toMatchSnapshot(); + }); + + it("fails when input parser cannot be rendered", () => { + expect( + () => render( + DPE.pipe(DPE.string(), DPE.number()), + { + constName: "pipeParserInputError", + dataParserTransformers: [ + ((dataParser, { buildError }) => DP.stringKind.has(dataParser) + ? buildError() + : E.left("dataParserNotSupport", dataParser)), + ...defaultTransformers, + ], + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toThrow(); + }); + + it("fails when output parser cannot be rendered", () => { + expect( + () => render( + DPE.pipe(DPE.string(), DPE.number()), + { + constName: "pipeParserOutputError", + dataParserTransformers: [ + ((dataParser, { buildError }) => DP.numberKind.has(dataParser) + ? buildError() + : E.left("dataParserNotSupport", dataParser)), + ...defaultTransformers, + ], + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toThrow(); + }); + + it("fails when definition checker cannot be rendered", () => { + const schema = DPE.pipe(DPE.string(), DPE.number(), { + checkers: [{ kind: "forced-error" } as any], + }); + + expect( + () => render( + schema, + { + constName: "pipeParserCheckerError", + dataParserTransformers: defaultTransformers, + checkerTransformers: [ + ((checker, { buildError }) => (checker as any).kind === "forced-error" + ? buildError() + : E.left("checkerNotSupport", checker)), + ], + toTypescript, + }, + ), + ).toThrow(); + }); +}); diff --git a/tests/toDataParser/dataParserTransformer/record.test.ts b/tests/toDataParser/dataParserTransformer/record.test.ts new file mode 100644 index 0000000..af10a40 --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/record.test.ts @@ -0,0 +1,88 @@ +import { DP, DPE, E } from "@duplojs/utils"; +import { defaultTransformers, defaultCheckerTransformers, render } from "@scripts/toDataParser"; +import { defaultTransformers as tsDefaultTransformers } from "@scripts/toTypescript"; + +const toTypescript = { + identifier: "RecordParser", + transformers: tsDefaultTransformers, +}; + +describe("record", () => { + it("renders record parser", () => { + expect( + render( + DPE.record(DPE.string(), DPE.number()), + { + constName: "recordParser", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toMatchSnapshot(); + }); + + it("fails when key parser cannot be rendered", () => { + expect( + () => render( + DPE.record(DPE.string(), DPE.number()), + { + constName: "recordParserKeyError", + dataParserTransformers: [ + ((dataParser, { buildError }) => DP.stringKind.has(dataParser) + ? buildError() + : E.left("dataParserNotSupport", dataParser)), + ...defaultTransformers, + ], + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toThrow(); + }); + + it("fails when value parser cannot be rendered", () => { + expect( + () => render( + DPE.record(DPE.string(), DPE.number()), + { + constName: "recordParserValueError", + dataParserTransformers: [ + ((dataParser, { buildError }) => DP.numberKind.has(dataParser) + ? buildError() + : E.left("dataParserNotSupport", dataParser)), + ...defaultTransformers, + ], + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toThrow(); + }); + + it("fails when definition checker cannot be rendered", () => { + const schema = DPE.record( + DPE.string(), + DPE.number(), + { + checkers: [{ kind: "forced-error" } as any], + }, + ); + + expect( + () => render( + schema, + { + constName: "recordParserCheckerError", + dataParserTransformers: defaultTransformers, + checkerTransformers: [ + ((checker, { buildError }) => (checker as any).kind === "forced-error" + ? buildError() + : E.left("checkerNotSupport", checker)), + ], + toTypescript, + }, + ), + ).toThrow(); + }); +}); diff --git a/tests/toDataParser/dataParserTransformer/recover.test.ts b/tests/toDataParser/dataParserTransformer/recover.test.ts new file mode 100644 index 0000000..ee32210 --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/recover.test.ts @@ -0,0 +1,22 @@ +import { render, defaultTransformers, defaultCheckerTransformers } from "@scripts/toDataParser"; +import { defaultTransformers as tsDefaultTransformers } from "@scripts/toTypescript"; +import { DPE } from "@duplojs/utils"; + +describe("recover", () => { + it("renders inner parser", () => { + expect( + render( + DPE.recover(DPE.string(), "fallback"), + { + constName: "recoverParser", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript: { + identifier: "RecoverParser", + transformers: tsDefaultTransformers, + }, + }, + ), + ).toMatchSnapshot(); + }); +}); diff --git a/tests/toDataParser/dataParserTransformer/string.test.ts b/tests/toDataParser/dataParserTransformer/string.test.ts new file mode 100644 index 0000000..34e8340 --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/string.test.ts @@ -0,0 +1,46 @@ +import { DPE, E } from "@duplojs/utils"; +import { defaultTransformers, defaultCheckerTransformers, render } from "@scripts/toDataParser"; +import { defaultTransformers as tsDefaultTransformers } from "@scripts/toTypescript"; + +const toTypescript = { + identifier: "StringParser", + transformers: tsDefaultTransformers, +}; + +describe("string", () => { + it("renders string parser", () => { + expect( + render( + DPE.string(), + { + constName: "stringParser", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toMatchSnapshot(); + }); + + it("renders string parser when definition has a checker error", () => { + const schema = DPE.string({ + checkers: [{ kind: "forced-error" } as any], + }); + + expect( + () => render( + schema, + { + constName: "stringParserError", + dataParserTransformers: defaultTransformers, + checkerTransformers: [ + ((checker, { buildError }) => (checker as any).kind === "forced-error" + ? buildError() + : E.left("checkerNotSupport", checker)), + ], + toTypescript, + }, + ), + ).toThrow(); + }); +}); diff --git a/tests/toDataParser/dataParserTransformer/templateLiteral.test.ts b/tests/toDataParser/dataParserTransformer/templateLiteral.test.ts new file mode 100644 index 0000000..b09df1b --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/templateLiteral.test.ts @@ -0,0 +1,82 @@ +import { DP, DPE, E } from "@duplojs/utils"; +import { defaultTransformers, defaultCheckerTransformers, render } from "@scripts/toDataParser"; +import { defaultTransformers as tsDefaultTransformers } from "@scripts/toTypescript"; + +const toTypescript = { + identifier: "TemplateLiteralParser", + transformers: tsDefaultTransformers, +}; + +describe("templateLiteral", () => { + it("renders template literal parser", () => { + expect( + render( + DPE.templateLiteral([ + "pre-", + 1n, + "mid-", + true, + "-false-", + false, + "-str-", + "abc", + "-num-", + 42, + "-null-", + null, + "-undef-", + undefined, + "-dp-", + DPE.string(), + ]), + { + constName: "templateLiteralParser", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toMatchSnapshot(); + }); + + it("fails when nested parser cannot be rendered", () => { + expect( + () => render( + DPE.templateLiteral(["user-", DPE.number(), "-id"]), + { + constName: "templateLiteralParserError", + dataParserTransformers: [ + ((dataParser, { buildError }) => DP.numberKind.has(dataParser) + ? buildError() + : E.left("dataParserNotSupport", dataParser)), + ...defaultTransformers, + ], + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toThrow(); + }); + + it("fails when definition checker cannot be rendered", () => { + const schema = DPE.templateLiteral(["user-", DPE.number(), "-id"], { + checkers: [{ kind: "forced-error" } as any], + }); + + expect( + () => render( + schema, + { + constName: "templateLiteralParserCheckerError", + dataParserTransformers: defaultTransformers, + checkerTransformers: [ + ((checker, { buildError }) => (checker as any).kind === "forced-error" + ? buildError() + : E.left("checkerNotSupport", checker)), + ], + toTypescript, + }, + ), + ).toThrow(); + }); +}); diff --git a/tests/toDataParser/dataParserTransformer/time.test.ts b/tests/toDataParser/dataParserTransformer/time.test.ts new file mode 100644 index 0000000..854ddf9 --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/time.test.ts @@ -0,0 +1,62 @@ +import { DDate, DP, DPE, E } from "@duplojs/utils"; +import { defaultTransformers, defaultCheckerTransformers, render } from "@scripts/toDataParser"; +import { defaultTransformers as tsDefaultTransformers } from "@scripts/toTypescript"; + +const toTypescript = { + identifier: "TimeParser", + transformers: tsDefaultTransformers, +}; + +describe("time", () => { + it("renders time parser with checker imports", () => { + expect( + render( + DPE.time({ coerce: true }).addChecker( + DP.checkerTimeMin(DDate.createTime(1, "millisecond")), + ), + { + constName: "timeParser", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toMatchSnapshot(); + }); + + it("renders time parser without coerce and without checkers", () => { + expect( + render( + DPE.time(), + { + constName: "timeParserNoCoerce", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toMatchSnapshot(); + }); + + it("fails when definition checker cannot be rendered", () => { + const schema = DPE.time({ + checkers: [{ kind: "forced-error" } as any], + }); + + expect( + () => render( + schema, + { + constName: "timeParserError", + dataParserTransformers: defaultTransformers, + checkerTransformers: [ + ((checker, { buildError }) => (checker as any).kind === "forced-error" + ? buildError() + : E.left("checkerNotSupport", checker)), + ], + toTypescript, + }, + ), + ).toThrow(); + }); +}); diff --git a/tests/toDataParser/dataParserTransformer/transform.test.ts b/tests/toDataParser/dataParserTransformer/transform.test.ts new file mode 100644 index 0000000..3edce87 --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/transform.test.ts @@ -0,0 +1,171 @@ +/* eslint-disable @typescript-eslint/consistent-type-imports */ +import { DP, DPE, E } from "@duplojs/utils"; +import { defaultTransformers, defaultCheckerTransformers, render } from "@scripts/toDataParser"; +import { defaultTransformers as tsDefaultTransformers } from "@scripts/toTypescript"; +import { afterEach, vi } from "vitest"; +import * as ts from "typescript"; + +vi.mock("typescript", async() => { + const actual = await vi.importActual("typescript"); + + return { + ...actual, + createSourceFile: vi.fn(actual.createSourceFile), + isExpressionStatement: vi.fn(actual.isExpressionStatement), + isParenthesizedExpression: vi.fn(actual.isParenthesizedExpression), + }; +}); + +const toTypescript = { + identifier: "TransformParser", + transformers: tsDefaultTransformers, +}; + +afterEach(() => { + vi.restoreAllMocks(); +}); + +describe("transform", () => { + it("renders transform parser", () => { + expect( + render( + DPE.transform(DPE.string(), (value) => value.trim()), + { + constName: "transformParser", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toMatchSnapshot(); + }); + + it("fails when inner parser cannot be rendered", () => { + expect( + () => render( + DPE.transform(DPE.string(), (value) => value.trim()), + { + constName: "transformParserInnerError", + dataParserTransformers: [ + ((dataParser, { buildError }) => DP.stringKind.has(dataParser) + ? buildError() + : E.left("dataParserNotSupport", dataParser)), + ...defaultTransformers, + ], + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toThrow(); + }); + + it("fails on native functions", () => { + expect( + () => render( + DPE.transform(DPE.string(), Math.max as any), + { + constName: "transformParserNativeError", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toThrow(); + }); + + it("fails on malformed functions", () => { + const schema = DPE.transform(DPE.string(), { toString: () => "foo" } as any); + expect( + () => render( + schema, + { + constName: "transformParserMalformedError", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toThrow(); + }); + + it("fails when the function source cannot be parsed as an expression", () => { + const schema = DPE.transform(DPE.string(), { toString: () => "" } as any); + + expect( + () => render( + schema, + { + constName: "transformParserEmptySourceError", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toThrow(); + }); + + it("fails when the parsed statement is not an expression statement", () => { + vi.mocked(ts.createSourceFile).mockReturnValue({ + statements: [{ kind: ts.SyntaxKind.VariableStatement }], + } as any); + vi.mocked(ts.isExpressionStatement).mockReturnValue(false); + + expect( + () => render( + DPE.transform(DPE.string(), (value) => value.trim()), + { + constName: "transformParserStatementError", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toThrow(); + }); + + it("fails when the parsed function is not parenthesized", () => { + vi.mocked(ts.createSourceFile).mockReturnValue({ + statements: [ + { + expression: {}, + }, + ], + } as any); + vi.mocked(ts.isExpressionStatement).mockReturnValue(true); + vi.mocked(ts.isParenthesizedExpression).mockReturnValue(false); + + expect( + () => render( + DPE.transform(DPE.string(), (value) => value.trim()), + { + constName: "transformParserParenthesizedError", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toThrow(); + }); + + it("fails when definition checker cannot be rendered", () => { + const schema = DPE.transform(DPE.string(), (value) => value.trim(), { + checkers: [{ kind: "forced-error" } as any], + }); + + expect( + () => render( + schema, + { + constName: "transformParserCheckerError", + dataParserTransformers: defaultTransformers, + checkerTransformers: [ + ((checker, { buildError }) => (checker as any).kind === "forced-error" + ? buildError() + : E.left("checkerNotSupport", checker)), + ], + toTypescript, + }, + ), + ).toThrow(); + }); +}); diff --git a/tests/toDataParser/dataParserTransformer/tuple.test.ts b/tests/toDataParser/dataParserTransformer/tuple.test.ts new file mode 100644 index 0000000..600fbfe --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/tuple.test.ts @@ -0,0 +1,99 @@ +import { DP, DPE, E } from "@duplojs/utils"; +import { defaultTransformers, defaultCheckerTransformers, render } from "@scripts/toDataParser"; +import { defaultTransformers as tsDefaultTransformers } from "@scripts/toTypescript"; + +const toTypescript = { + identifier: "TupleParser", + transformers: tsDefaultTransformers, +}; + +describe("tuple", () => { + it("renders tuple parser with rest", () => { + expect( + render( + DPE.tuple([DPE.string()], { rest: DPE.number() }), + { + constName: "tupleParser", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toMatchSnapshot(); + }); + + it("renders tuple parser without rest", () => { + expect( + render( + DPE.tuple([DPE.string()]), + { + constName: "tupleParserNoRest", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toMatchSnapshot(); + }); + + it("fails when tuple shape cannot be rendered", () => { + expect( + () => render( + DPE.tuple([DPE.string()], { rest: DPE.number() }), + { + constName: "tupleParserShapeError", + dataParserTransformers: [ + ((dataParser, { buildError }) => DP.stringKind.has(dataParser) + ? buildError() + : E.left("dataParserNotSupport", dataParser)), + ...defaultTransformers, + ], + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toThrow(); + }); + + it("fails when tuple rest cannot be rendered", () => { + expect( + () => render( + DPE.tuple([DPE.string()], { rest: DPE.number() }), + { + constName: "tupleParserRestError", + dataParserTransformers: [ + ((dataParser, { buildError }) => DP.numberKind.has(dataParser) + ? buildError() + : E.left("dataParserNotSupport", dataParser)), + ...defaultTransformers, + ], + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toThrow(); + }); + + it("fails when definition checker cannot be rendered", () => { + const schema = DPE.tuple([DPE.string()], { + rest: DPE.number(), + checkers: [{ kind: "forced-error" } as any], + }); + + expect( + () => render( + schema, + { + constName: "tupleParserCheckerError", + dataParserTransformers: defaultTransformers, + checkerTransformers: [ + ((checker, { buildError }) => (checker as any).kind === "forced-error" + ? buildError() + : E.left("checkerNotSupport", checker)), + ], + toTypescript, + }, + ), + ).toThrow(); + }); +}); diff --git a/tests/toDataParser/dataParserTransformer/union.test.ts b/tests/toDataParser/dataParserTransformer/union.test.ts new file mode 100644 index 0000000..d5e4fbb --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/union.test.ts @@ -0,0 +1,65 @@ +import { DP, DPE, E } from "@duplojs/utils"; +import { defaultTransformers, defaultCheckerTransformers, render } from "@scripts/toDataParser"; +import { defaultTransformers as tsDefaultTransformers } from "@scripts/toTypescript"; + +const toTypescript = { + identifier: "UnionParser", + transformers: tsDefaultTransformers, +}; + +describe("union", () => { + it("renders union parser", () => { + expect( + render( + DPE.union([DPE.string(), DPE.number()]), + { + constName: "unionParser", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toMatchSnapshot(); + }); + + it("fails when option parser cannot be rendered", () => { + expect( + () => render( + DPE.union([DPE.string(), DPE.number()]), + { + constName: "unionParserOptionError", + dataParserTransformers: [ + ((dataParser, { buildError }) => DP.stringKind.has(dataParser) + ? buildError() + : E.left("dataParserNotSupport", dataParser)), + ...defaultTransformers, + ], + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toThrow(); + }); + + it("fails when definition checker cannot be rendered", () => { + const schema = DPE.union([DPE.string(), DPE.number()], { + checkers: [{ kind: "forced-error" } as any], + }); + + expect( + () => render( + schema, + { + constName: "unionParserCheckerError", + dataParserTransformers: defaultTransformers, + checkerTransformers: [ + ((checker, { buildError }) => (checker as any).kind === "forced-error" + ? buildError() + : E.left("checkerNotSupport", checker)), + ], + toTypescript, + }, + ), + ).toThrow(); + }); +}); diff --git a/tests/toDataParser/dataParserTransformer/unknown.test.ts b/tests/toDataParser/dataParserTransformer/unknown.test.ts new file mode 100644 index 0000000..9b56b3a --- /dev/null +++ b/tests/toDataParser/dataParserTransformer/unknown.test.ts @@ -0,0 +1,46 @@ +import { DPE, E } from "@duplojs/utils"; +import { defaultTransformers, defaultCheckerTransformers, render } from "@scripts/toDataParser"; +import { defaultTransformers as tsDefaultTransformers } from "@scripts/toTypescript"; + +const toTypescript = { + identifier: "UnknownParser", + transformers: tsDefaultTransformers, +}; + +describe("unknown", () => { + it("renders unknown parser", () => { + expect( + render( + DPE.unknown(), + { + constName: "unknownParser", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript, + }, + ), + ).toMatchSnapshot(); + }); + + it("fails when definition checker cannot be rendered", () => { + const schema = DPE.unknown({ + checkers: [{ kind: "forced-error" } as any], + }); + + expect( + () => render( + schema, + { + constName: "unknownParserError", + dataParserTransformers: defaultTransformers, + checkerTransformers: [ + ((checker, { buildError }) => (checker as any).kind === "forced-error" + ? buildError() + : E.left("checkerNotSupport", checker)), + ], + toTypescript, + }, + ), + ).toThrow(); + }); +}); diff --git a/tests/toDataParser/override.test.ts b/tests/toDataParser/override.test.ts new file mode 100644 index 0000000..8a81b2c --- /dev/null +++ b/tests/toDataParser/override.test.ts @@ -0,0 +1,111 @@ +import "@scripts/toDataParser/override"; +import { DP, DPE, justReturn } from "@duplojs/utils"; +import { factory } from "typescript"; +import { defaultCheckerTransformers, defaultTransformers, render } from "@scripts/toDataParser"; +import { defaultTransformers as tsDefaultTransformers } from "@scripts/toTypescript"; +import { type TransformerBuildFunction } from "@scripts/toDataParser/dataParserTransformer"; + +describe("override", () => { + it("setConstName", () => { + const schema = DP.string(); + + expect(schema.definition.constName).toBe(undefined); + + schema.setConstName("test"); + + expect(schema.definition.constName).toBe("test"); + }); + + it("addConstName", () => { + const schema = DP.string(); + + expect(schema.definition.constName).toBe(undefined); + + const newSchema = schema.addConstName("test"); + + expect(schema.definition.constName).toBe(undefined); + expect(newSchema.definition.constName).toBe("test"); + + const newSchemaWithChecker = newSchema.addChecker(DP.checkerRefine(justReturn(true))); + + schema.setConstName("test1"); + + expect(newSchemaWithChecker.definition.constName).toBe("test"); + }); + + it("setOverrideDataParserTransformer", () => { + const schema = DP.string(); + + expect(schema.definition.overrideDataParserTransformer).toBe(undefined); + + schema.setOverrideDataParserTransformer( + factory.createCallExpression( + factory.createIdentifier("test"), + undefined, + [], + ), + ); + + expect(typeof schema.definition.overrideDataParserTransformer).toBe("function"); + }); + + it("addOverrideDataParserTransformer", () => { + const schema = DP.string(); + const overrideTransformer: TransformerBuildFunction = (__, { success }) => success( + factory.createCallExpression( + factory.createIdentifier("test"), + undefined, + [], + ), + ); + + expect(schema.definition.overrideDataParserTransformer).toBe(undefined); + + const newSchema = schema.addOverrideDataParserTransformer(overrideTransformer); + + expect(schema.definition.overrideDataParserTransformer).toBe(undefined); + expect(newSchema.definition.overrideDataParserTransformer).toBe(overrideTransformer); + + const newSchemaWithChecker = newSchema.addChecker(DP.checkerRefine(justReturn(true))); + + schema.setOverrideDataParserTransformer( + factory.createCallExpression( + factory.createIdentifier("test1"), + undefined, + [], + ), + ); + + expect(newSchemaWithChecker.definition.overrideDataParserTransformer).toBe(overrideTransformer); + }); + + it("uses overrideDataParserTransformer in render", () => { + const schema = DPE.string().addOverrideDataParserTransformer( + (__, { success, dependencyIdentifier }) => success( + factory.createCallExpression( + factory.createPropertyAccessExpression( + dependencyIdentifier, + factory.createIdentifier("number"), + ), + undefined, + [], + ), + ), + ); + + const result = render( + schema, + { + constName: "overrideParser", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript: { + identifier: "OverrideParser", + transformers: tsDefaultTransformers, + }, + }, + ); + + expect(result).toContain("export const overrideParser = DP.number();"); + }); +}); diff --git a/tests/toDataParser/render.test.ts b/tests/toDataParser/render.test.ts new file mode 100644 index 0000000..728dc08 --- /dev/null +++ b/tests/toDataParser/render.test.ts @@ -0,0 +1,218 @@ +import { DDate, DP, DPE } from "@duplojs/utils"; +import { defaultTransformers as tsDefaultTransformers } from "@scripts/toTypescript"; +import { defaultCheckerTransformers, defaultTransformers, render } from "@scripts/toDataParser"; + +describe("render", () => { + it("renders anonymous recursive dataParser with a temporary const", () => { + interface RecursiveNode { + next: RecursiveNode; + } + + const schema: DPE.DataParser = DPE.object({ + next: DPE.lazy(() => schema), + }).contract(); + + expect( + render( + schema, + { + constName: "recursiveNodeParser", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript: { + identifier: "RecursiveNode", + transformers: tsDefaultTransformers, + }, + }, + ), + ).toMatchSnapshot(); + }); + + it("renders named dependencies before their consumers", () => { + const child = DPE.string().addConstName("childParser"); + const schema = DPE.object({ child }).addConstName("parentParser"); + + expect( + render( + schema, + { + constName: "parentParser", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript: { + identifier: "Parent", + transformers: tsDefaultTransformers, + }, + }, + ), + ).toMatchSnapshot(); + }); + + it("renders complex nested recursive dataParsers", () => { + type RecursiveArray = (RecursiveArray | RecursiveObject | string)[]; + + type RecursiveTuple = [ + string, + RecursiveObject, + (RecursiveTuple | RecursiveUnion | string)[], + ]; + + type RecursiveUnion = + | string + | RecursiveObject + | RecursiveTuple + | RecursiveUnion[]; + + interface RecursiveObject { + recursiveProp: { + self: RecursiveObject; + array: RecursiveArray; + tuple: RecursiveTuple; + union: RecursiveUnion; + }; + children: RecursiveObject[]; + tuple: RecursiveTuple; + array: RecursiveArray; + union: RecursiveUnion; + } + + const schemaArray: DPE.DataParser = DPE.array( + DPE.union([ + DPE.string(), + DPE.lazy(() => schemaArray), + DPE.lazy(() => schema), + ]), + ).contract(); + + const schemaTuple: DPE.DataParser = DPE.tuple([ + DPE.string(), + DPE.lazy(() => schema), + DPE.union([ + DPE.lazy(() => schemaTuple), + DPE.lazy(() => schemaUnion), + DPE.string(), + ]).array(), + ]).contract(); + + const schemaUnion: DPE.DataParser = DPE.union([ + DPE.string(), + DPE.lazy(() => schema), + DPE.lazy(() => schemaTuple), + DPE.array(DPE.lazy(() => schemaUnion)), + ]).contract(); + + const schema: DPE.DataParser = DPE.object({ + recursiveProp: DPE.object({ + self: DPE.lazy(() => schema), + array: schemaArray, + tuple: schemaTuple, + union: schemaUnion, + }), + children: DPE.array(DPE.lazy(() => schema)), + tuple: schemaTuple, + array: schemaArray, + union: schemaUnion, + }).contract(); + + expect( + render( + schema, + { + constName: "complexRecursiveParser", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript: { + identifier: "ComplexRecursive", + transformers: tsDefaultTransformers, + }, + }, + ), + ).toMatchSnapshot(); + }); + + it("renders checked dataParsers in normal and extended modes", () => { + const checkedSchema = DP.object({ + startAt: DP.time().addChecker( + DP.checkerTimeMin(DDate.createTime(1000, "millisecond")), + DP.checkerTimeMax(DDate.createTime(5000, "millisecond")), + ), + name: DP.string().addChecker(DP.checkerStringMin(2)), + count: DP.number().addChecker( + DP.checkerInt(), + DP.checkerNumberMin(1), + ), + }); + const checkedExtendedSchema = DPE.object({ + startAt: DPE + .time() + .min(DDate.createTime(250, "millisecond")), + tags: DPE + .string() + .min(1) + .array() + .min(1), + }); + + expect( + render( + checkedSchema, + { + constName: "checkedParser", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript: { + identifier: "Checked", + transformers: tsDefaultTransformers, + }, + }, + ), + ).toMatchSnapshot(); + + expect( + render( + checkedExtendedSchema, + { + constName: "checkedExtendedParser", + exportMode: "extended", + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript: { + identifier: "CheckedExtended", + transformers: tsDefaultTransformers, + }, + }, + ), + ).toMatchSnapshot(); + }); + + it("renders dataParser in compact mode when indent is false", () => { + const compactSchema = DPE.object({ + name: DPE.string().min(2), + roles: DPE.literal(["admin", "editor"]).array().min(1), + contact: DPE.union([ + DPE.object({ + email: DPE.email(), + }), + DPE.object({ + phone: DPE.string().regex(/^[+\d][\d\s-]{5,}$/), + }), + ]), + }); + + expect( + render( + compactSchema, + { + constName: "compactParser", + indent: false, + dataParserTransformers: defaultTransformers, + checkerTransformers: defaultCheckerTransformers, + toTypescript: { + identifier: "CompactSchema", + transformers: tsDefaultTransformers, + }, + }, + ), + ).toMatchSnapshot(); + }); +}); diff --git a/vitest.config.js b/vitest.config.js index 70d35f4..ba66b0d 100644 --- a/vitest.config.js +++ b/vitest.config.js @@ -15,7 +15,9 @@ export default defineConfig({ reportsDirectory: "coverage", include: [ "scripts/toTypescript/transformer/defaults", - "scripts/toJsonSchema/transformer/defaults" + "scripts/toJsonSchema/transformer/defaults", + "scripts/toDataParser/checkerTransformer/defaults", + "scripts/toDataParser/dataParserTransformer/defaults" ], exclude: [ "**/*.test.ts",