From 0b6eaa8053c4c77bb994d495ce5d091bba3bb8ba Mon Sep 17 00:00:00 2001 From: Kei Kamikawa Date: Thu, 17 Feb 2022 00:20:58 +0900 Subject: [PATCH 1/2] added a new option scalarSchemas a new option to support overrides the validation schema for any scalars --- src/config.ts | 26 ++++++++++++++++++++++++++ src/yup/index.ts | 7 +++++-- src/zod/index.ts | 16 ++++++++++++---- tests/yup.spec.ts | 32 ++++++++++++++++++++++++++++++++ tests/zod.spec.ts | 33 +++++++++++++++++++++++++++++++++ 5 files changed, 108 insertions(+), 6 deletions(-) diff --git a/src/config.ts b/src/config.ts index 976e55e3..8f894bbb 100644 --- a/src/config.ts +++ b/src/config.ts @@ -12,6 +12,10 @@ export interface DirectiveObjectArguments { [matched: string]: string | string[]; } +interface ScalarSchemas { + [name: string]: string; +} + export interface ValidationSchemaPluginConfig extends TypeScriptPluginConfig { /** * @description specify generate schema @@ -88,6 +92,28 @@ export interface ValidationSchemaPluginConfig extends TypeScriptPluginConfig { * ``` */ notAllowEmptyString?: boolean; + /** + * @description Extends or overrides validation schema for the built-in scalars and custom GraphQL scalars. + * + * @exampleMarkdown + * ```yml + * config: + * schema: yup + * scalarSchemas: + * Date: yup.date() + * Email: yup.string().email() + * ``` + * + * @exampleMarkdown + * ```yml + * config: + * schema: zod + * scalarSchemas: + * Date: z.date() + * Email: z.string().email() + * ``` + */ + scalarSchemas?: ScalarSchemas; /** * @description Generates validation schema with more API based on directive schema. * @exampleMarkdown diff --git a/src/yup/index.ts b/src/yup/index.ts index 815cf0bf..32adde70 100644 --- a/src/yup/index.ts +++ b/src/yup/index.ts @@ -151,7 +151,7 @@ const generateNameNodeYupSchema = ( return `${enumName}Schema`; } - const primitive = yup4Scalar(tsVisitor, node.value); + const primitive = yup4Scalar(config, tsVisitor, node.value); return primitive; }; @@ -180,7 +180,10 @@ const maybeNonEmptyString = ( return `${schema}.defined()`; }; -const yup4Scalar = (tsVisitor: TsVisitor, scalarName: string): string => { +const yup4Scalar = (config: ValidationSchemaPluginConfig, tsVisitor: TsVisitor, scalarName: string): string => { + if (config.scalarSchemas?.[scalarName]) { + return config.scalarSchemas[scalarName]; + } const tsType = tsVisitor.scalars[scalarName]; switch (tsType) { case 'string': diff --git a/src/zod/index.ts b/src/zod/index.ts index 70c16a57..c0ab8f7a 100644 --- a/src/zod/index.ts +++ b/src/zod/index.ts @@ -114,7 +114,7 @@ const generateInputObjectFieldTypeZodSchema = ( return maybeLazy(type.type, gen); } if (isNamedType(type)) { - const gen = generateNameNodeZodSchema(tsVisitor, schema, type.name); + const gen = generateNameNodeZodSchema(config, tsVisitor, schema, type.name); if (isNonNullType(parentType)) { if (config.notAllowEmptyString === true) { const tsType = tsVisitor.scalars[type.name.value]; @@ -131,7 +131,12 @@ const generateInputObjectFieldTypeZodSchema = ( return ''; }; -const generateNameNodeZodSchema = (tsVisitor: TsVisitor, schema: GraphQLSchema, node: NameNode): string => { +const generateNameNodeZodSchema = ( + config: ValidationSchemaPluginConfig, + tsVisitor: TsVisitor, + schema: GraphQLSchema, + node: NameNode +): string => { const typ = schema.getType(node.value); if (typ && typ.astNode?.kind === 'InputObjectTypeDefinition') { @@ -144,7 +149,7 @@ const generateNameNodeZodSchema = (tsVisitor: TsVisitor, schema: GraphQLSchema, return `${enumName}Schema`; } - return zod4Scalar(tsVisitor, node.value); + return zod4Scalar(config, tsVisitor, node.value); }; const maybeLazy = (type: TypeNode, schema: string): string => { @@ -154,7 +159,10 @@ const maybeLazy = (type: TypeNode, schema: string): string => { return schema; }; -const zod4Scalar = (tsVisitor: TsVisitor, scalarName: string): string => { +const zod4Scalar = (config: ValidationSchemaPluginConfig, tsVisitor: TsVisitor, scalarName: string): string => { + if (config.scalarSchemas?.[scalarName]) { + return config.scalarSchemas[scalarName]; + } const tsType = tsVisitor.scalars[scalarName]; switch (tsType) { case 'string': diff --git a/tests/yup.spec.ts b/tests/yup.spec.ts index 89eb814c..70fbebff 100644 --- a/tests/yup.spec.ts +++ b/tests/yup.spec.ts @@ -243,4 +243,36 @@ describe('yup', () => { expect(result.content).toContain(wantContain); } }); + + it('with scalarSchemas', async () => { + const schema = buildSchema(/* GraphQL */ ` + input ScalarsInput { + date: Date! + email: Email + str: String! + } + scalar Date + scalar Email + `); + const result = await plugin( + schema, + [], + { + scalarSchemas: { + Date: 'yup.date()', + Email: 'yup.string().email()', + }, + }, + {} + ); + const wantContains = [ + 'export function ScalarsInputSchema(): yup.SchemaOf', + 'date: yup.date().defined(),', + 'email: yup.string().email(),', + 'str: yup.string().defined()', + ]; + for (const wantContain of wantContains) { + expect(result.content).toContain(wantContain); + } + }); }); diff --git a/tests/zod.spec.ts b/tests/zod.spec.ts index 9f478502..37d9787f 100644 --- a/tests/zod.spec.ts +++ b/tests/zod.spec.ts @@ -247,4 +247,37 @@ describe('zod', () => { expect(result.content).toContain(wantContain); } }); + + it('with scalarSchemas', async () => { + const schema = buildSchema(/* GraphQL */ ` + input ScalarsInput { + date: Date! + email: Email + str: String! + } + scalar Date + scalar Email + `); + const result = await plugin( + schema, + [], + { + schema: 'zod', + scalarSchemas: { + Date: 'z.date()', + Email: 'z.string().email()', + }, + }, + {} + ); + const wantContains = [ + 'export function ScalarsInputSchema(): z.ZodSchema', + 'date: z.date(),', + 'email: z.string().email().nullish(),', + 'str: z.string()', + ]; + for (const wantContain of wantContains) { + expect(result.content).toContain(wantContain); + } + }); }); From b927527c4f91e0ef906597c0bccc8ecf128c2b6c Mon Sep 17 00:00:00 2001 From: Kei Kamikawa Date: Thu, 17 Feb 2022 00:21:47 +0900 Subject: [PATCH 2/2] fixed README for scalarSchemas option --- README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/README.md b/README.md index 0907b54b..26aa6ff6 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,32 @@ type: `boolean` default: `false` Generates validation string schema as do not allow empty characters by default. +### `scalarSchemas` + +type: `ScalarSchemas` + +Extends or overrides validation schema for the built-in scalars and custom GraphQL scalars. + +#### yup schema + +```yml +config: + schema: yup + scalarSchemas: + Date: yup.date() + Email: yup.string().email() +``` + +#### zod schema + +```yml +config: + schema: zod + scalarSchemas: + Date: z.date() + Email: z.string().email() +``` + ### `directives` type: `DirectiveConfig`