From 9a54d8d6c50242fc1e41ef8019db2d75dbc6ba2b Mon Sep 17 00:00:00 2001 From: Kei Kamikawa Date: Sun, 6 Feb 2022 01:44:55 +0900 Subject: [PATCH 1/3] added notAllowEmptyString option in config see: https://github.com/Code-Hex/graphql-codegen-typescript-validation-schema/issues/12 --- src/config.ts | 15 +++++++++++++++ src/yup/index.ts | 20 +++++++++++++++++++- src/zod/index.ts | 4 ++++ tests/yup.spec.ts | 31 +++++++++++++++++++++++++++++++ tests/zod.spec.ts | 32 ++++++++++++++++++++++++++++++++ 5 files changed, 101 insertions(+), 1 deletion(-) diff --git a/src/config.ts b/src/config.ts index 40507132..a5e54aed 100644 --- a/src/config.ts +++ b/src/config.ts @@ -73,6 +73,21 @@ export interface ValidationSchemaPluginConfig extends TypeScriptPluginConfig { * ``` */ enumsAsTypes?: boolean; + /** + * @description Generates validation string schema as do not allow empty characters by default. + * @default false + * + * @exampleMarkdown + * ```yml + * generates: + * path/to/file.ts: + * plugins: + * - graphql-codegen-validation-schema + * config: + * notAllowEmptyString: true + * ``` + */ + notAllowEmptyString?: boolean; /** * @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 b6647863..815cf0bf 100644 --- a/src/yup/index.ts +++ b/src/yup/index.ts @@ -123,7 +123,8 @@ const generateInputObjectFieldTypeYupSchema = ( } if (isNonNullType(type)) { const gen = generateInputObjectFieldTypeYupSchema(config, tsVisitor, schema, type.type, type); - return maybeLazy(type.type, `${gen}.defined()`); + const nonNullGen = maybeNonEmptyString(config, tsVisitor, gen, type.type); + return maybeLazy(type.type, nonNullGen); } if (isNamedType(type)) { return generateNameNodeYupSchema(config, tsVisitor, schema, type.name); @@ -162,6 +163,23 @@ const maybeLazy = (type: TypeNode, schema: string): string => { return schema; }; +const maybeNonEmptyString = ( + config: ValidationSchemaPluginConfig, + tsVisitor: TsVisitor, + schema: string, + childType: TypeNode +): string => { + if (config.notAllowEmptyString === true && isNamedType(childType)) { + const maybeScalarName = childType.name.value; + const tsType = tsVisitor.scalars[maybeScalarName]; + if (tsType === 'string') { + return `${schema}.required()`; + } + } + // fallback + return `${schema}.defined()`; +}; + const yup4Scalar = (tsVisitor: TsVisitor, scalarName: string): string => { const tsType = tsVisitor.scalars[scalarName]; switch (tsType) { diff --git a/src/zod/index.ts b/src/zod/index.ts index 04328bcb..29e3ece9 100644 --- a/src/zod/index.ts +++ b/src/zod/index.ts @@ -116,6 +116,10 @@ const generateInputObjectFieldTypeZodSchema = ( if (isNamedType(type)) { const gen = generateNameNodeZodSchema(tsVisitor, schema, type.name); if (isNonNullType(parentType)) { + if (config.notAllowEmptyString === true) { + const tsType = tsVisitor.scalars[type.name.value]; + if (tsType === 'string') return `${gen}.min(1)` + } return gen; } if (isListType(parentType)) { diff --git a/tests/yup.spec.ts b/tests/yup.spec.ts index 0eeaa9ed..d8aad45a 100644 --- a/tests/yup.spec.ts +++ b/tests/yup.spec.ts @@ -212,4 +212,35 @@ describe('yup', () => { ); expect(result.content).toContain("export const PageTypeSchema = yup.mixed().oneOf(['PUBLIC', 'BASIC_AUTH'])"); }); + + it('with notAllowEmptyString', async () => { + const schema = buildSchema(/* GraphQL */ ` + input PrimitiveInput { + a: ID! + b: String! + c: Boolean! + d: Int! + e: Float! + } + `); + const result = await plugin( + schema, + [], + { + notAllowEmptyString: true, + }, + {} + ); + const wantContains = [ + 'export function PrimitiveInputSchema(): yup.SchemaOf', + 'a: yup.string().required(),', + 'b: yup.string().required(),', + 'c: yup.boolean().defined(),', + 'd: yup.number().defined(),', + 'e: yup.number().defined()', + ] + for (const wantContain of wantContains) { + expect(result.content).toContain(wantContain); + } + }); }); diff --git a/tests/zod.spec.ts b/tests/zod.spec.ts index 642a8492..9f478502 100644 --- a/tests/zod.spec.ts +++ b/tests/zod.spec.ts @@ -215,4 +215,36 @@ describe('zod', () => { ); expect(result.content).toContain("export const PageTypeSchema = z.enum(['PUBLIC', 'BASIC_AUTH'])"); }); + + it('with notAllowEmptyString', async () => { + const schema = buildSchema(/* GraphQL */ ` + input PrimitiveInput { + a: ID! + b: String! + c: Boolean! + d: Int! + e: Float! + } + `); + const result = await plugin( + schema, + [], + { + schema: 'zod', + notAllowEmptyString: true, + }, + {} + ); + const wantContains = [ + 'export function PrimitiveInputSchema(): z.ZodSchema', + 'a: z.string().min(1),', + 'b: z.string().min(1),', + 'c: z.boolean(),', + 'd: z.number(),', + 'e: z.number()', + ]; + for (const wantContain of wantContains) { + expect(result.content).toContain(wantContain); + } + }); }); From 43e00d8024d743dd644f1f2895d1e3b2d75b0e82 Mon Sep 17 00:00:00 2001 From: Kei Kamikawa Date: Sun, 6 Feb 2022 01:45:26 +0900 Subject: [PATCH 2/3] fixed README for notAllowEmptyString option --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 1e228732..0907b54b 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,12 @@ type: `boolean` default: `false` Generates enum as TypeScript `type` instead of `enum`. +### `notAllowEmptyString` + +type: `boolean` default: `false` + +Generates validation string schema as do not allow empty characters by default. + ### `directives` type: `DirectiveConfig` From babacae4480eb4068c2d20a980901c320e8d6908 Mon Sep 17 00:00:00 2001 From: Kei Kamikawa Date: Sun, 6 Feb 2022 01:46:02 +0900 Subject: [PATCH 3/3] format --- src/config.ts | 2 +- src/zod/index.ts | 2 +- tests/yup.spec.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/config.ts b/src/config.ts index a5e54aed..976e55e3 100644 --- a/src/config.ts +++ b/src/config.ts @@ -87,7 +87,7 @@ export interface ValidationSchemaPluginConfig extends TypeScriptPluginConfig { * notAllowEmptyString: true * ``` */ - notAllowEmptyString?: boolean; + notAllowEmptyString?: boolean; /** * @description Generates validation schema with more API based on directive schema. * @exampleMarkdown diff --git a/src/zod/index.ts b/src/zod/index.ts index 29e3ece9..70c16a57 100644 --- a/src/zod/index.ts +++ b/src/zod/index.ts @@ -118,7 +118,7 @@ const generateInputObjectFieldTypeZodSchema = ( if (isNonNullType(parentType)) { if (config.notAllowEmptyString === true) { const tsType = tsVisitor.scalars[type.name.value]; - if (tsType === 'string') return `${gen}.min(1)` + if (tsType === 'string') return `${gen}.min(1)`; } return gen; } diff --git a/tests/yup.spec.ts b/tests/yup.spec.ts index d8aad45a..89eb814c 100644 --- a/tests/yup.spec.ts +++ b/tests/yup.spec.ts @@ -238,7 +238,7 @@ describe('yup', () => { 'c: yup.boolean().defined(),', 'd: yup.number().defined(),', 'e: yup.number().defined()', - ] + ]; for (const wantContain of wantContains) { expect(result.content).toContain(wantContain); }