From c9083904eb32d731845f6c42367992da0c3ff554 Mon Sep 17 00:00:00 2001 From: kuro Date: Thu, 28 Jul 2022 11:24:48 +0900 Subject: [PATCH 1/2] Adding GraphQL type support to codegen --- codegen.yml | 3 ++ example/myzod/schemas.ts | 14 +++++++- example/test.graphql | 9 +++++ example/types.ts | 10 ++++++ example/yup/schemas.ts | 14 +++++++- example/zod/schemas.ts | 14 +++++++- src/config.ts | 20 ++++++++++- src/myzod/index.ts | 41 +++++++++++++++++----- src/yup/index.ts | 37 +++++++++++++++----- src/zod/index.ts | 43 ++++++++++++++++------- tests/myzod.spec.ts | 74 ++++++++++++++++++++++++++++++++++++++++ tests/yup.spec.ts | 73 +++++++++++++++++++++++++++++++++++++++ tests/zod.spec.ts | 73 +++++++++++++++++++++++++++++++++++++++ 13 files changed, 391 insertions(+), 34 deletions(-) diff --git a/codegen.yml b/codegen.yml index f56baf9b..00f78617 100644 --- a/codegen.yml +++ b/codegen.yml @@ -9,6 +9,7 @@ generates: - ./dist/main/index.js: schema: yup importFrom: ../types + useObjectTypes: true directives: required: msg: required @@ -42,6 +43,7 @@ generates: - ./dist/main/index.js: schema: zod importFrom: ../types + useObjectTypes: true directives: # Write directives like # @@ -62,6 +64,7 @@ generates: - ./dist/main/index.js: schema: myzod importFrom: ../types + useObjectTypes: true directives: constraint: minLength: min diff --git a/example/myzod/schemas.ts b/example/myzod/schemas.ts index b953fa98..8cac6fdb 100644 --- a/example/myzod/schemas.ts +++ b/example/myzod/schemas.ts @@ -1,5 +1,5 @@ import * as myzod from 'myzod' -import { AttributeInput, ButtonComponentType, ComponentInput, DropDownComponentInput, EventArgumentInput, EventInput, EventOptionType, HttpInput, HttpMethod, LayoutInput, PageInput, PageType } from '../types' +import { AttributeInput, ButtonComponentType, ComponentInput, DropDownComponentInput, EventArgumentInput, EventInput, EventOptionType, HttpInput, HttpMethod, LayoutInput, PageInput, PageType, User } from '../types' export const definedNonNullAnySchema = myzod.object({}); @@ -77,3 +77,15 @@ export function PageInputSchema(): myzod.Type { } export const PageTypeSchema = myzod.enum(PageType); + +export function UserSchema(): myzod.Type { + return myzod.object({ + __typename: myzod.literal('User').optional(), + createdAt: definedNonNullAnySchema.optional().nullable(), + email: myzod.string().optional().nullable(), + id: myzod.string().optional().nullable(), + name: myzod.string().optional().nullable(), + password: myzod.string().optional().nullable(), + updatedAt: definedNonNullAnySchema.optional().nullable() + }) +} diff --git a/example/test.graphql b/example/test.graphql index b5c4cfe7..93cf53b8 100644 --- a/example/test.graphql +++ b/example/test.graphql @@ -5,6 +5,15 @@ enum PageType { BASIC_AUTH } +type User { + id: ID + name: String + email: String + password: String + createdAt: Date + updatedAt: Date +} + input PageInput { id: ID! title: String! diff --git a/example/types.ts b/example/types.ts index 62283b3c..b0b5bd10 100644 --- a/example/types.ts +++ b/example/types.ts @@ -86,3 +86,13 @@ export enum PageType { Restricted = 'RESTRICTED', Service = 'SERVICE' } + +export type User = { + __typename?: 'User'; + createdAt?: Maybe; + email?: Maybe; + id?: Maybe; + name?: Maybe; + password?: Maybe; + updatedAt?: Maybe; +}; diff --git a/example/yup/schemas.ts b/example/yup/schemas.ts index f77b3545..59d9dbd6 100644 --- a/example/yup/schemas.ts +++ b/example/yup/schemas.ts @@ -1,5 +1,5 @@ import * as yup from 'yup' -import { AttributeInput, ButtonComponentType, ComponentInput, DropDownComponentInput, EventArgumentInput, EventInput, EventOptionType, HttpInput, HttpMethod, LayoutInput, PageInput, PageType } from '../types' +import { AttributeInput, ButtonComponentType, ComponentInput, DropDownComponentInput, EventArgumentInput, EventInput, EventOptionType, HttpInput, HttpMethod, LayoutInput, PageInput, PageType, User } from '../types' export function AttributeInputSchema(): yup.SchemaOf { return yup.object({ @@ -75,3 +75,15 @@ export function PageInputSchema(): yup.SchemaOf { } export const PageTypeSchema = yup.mixed().oneOf([PageType.BasicAuth, PageType.Lp, PageType.Restricted, PageType.Service]); + +export function UserSchema(): yup.SchemaOf { + return yup.object({ + __typename: yup.mixed().oneOf(['User', undefined]), + createdAt: yup.mixed(), + email: yup.string(), + id: yup.string(), + name: yup.string(), + password: yup.string(), + updatedAt: yup.mixed() + }) +} diff --git a/example/zod/schemas.ts b/example/zod/schemas.ts index 7592f8e0..4f842608 100644 --- a/example/zod/schemas.ts +++ b/example/zod/schemas.ts @@ -1,5 +1,5 @@ import { z } from 'zod' -import { AttributeInput, ButtonComponentType, ComponentInput, DropDownComponentInput, EventArgumentInput, EventInput, EventOptionType, HttpInput, HttpMethod, LayoutInput, PageInput, PageType } from '../types' +import { AttributeInput, ButtonComponentType, ComponentInput, DropDownComponentInput, EventArgumentInput, EventInput, EventOptionType, HttpInput, HttpMethod, LayoutInput, PageInput, PageType, User } from '../types' type Properties = Required<{ [K in keyof T]: z.ZodType; @@ -85,3 +85,15 @@ export function PageInputSchema(): z.ZodObject> { } export const PageTypeSchema = z.nativeEnum(PageType); + +export function UserSchema(): z.ZodObject> { + return z.object({ + __typename: z.literal('User').optional(), + createdAt: definedNonNullAnySchema.nullish(), + email: z.string().nullish(), + id: z.string().nullish(), + name: z.string().nullish(), + password: z.string().nullish(), + updatedAt: definedNonNullAnySchema.nullish() + }) +} diff --git a/src/config.ts b/src/config.ts index 36ff9fd7..bc5790d4 100644 --- a/src/config.ts +++ b/src/config.ts @@ -37,7 +37,7 @@ export interface ValidationSchemaPluginConfig extends TypeScriptPluginConfig { * @description import types from generated typescript type path * if not given, omit import statement. * - * @exampeMarkdown + * @exampleMarkdown * ```yml * generates: * path/to/types.ts: @@ -193,4 +193,22 @@ export interface ValidationSchemaPluginConfig extends TypeScriptPluginConfig { * ``` */ directives?: DirectiveConfig; + /** + * @description Converts the regular graphql type into a zod validation function. + * + * @exampleMarkdown + * ```yml + * generates: + * path/to/types.ts: + * plugins: + * - typescript + * path/to/schemas.ts: + * plugins: + * - graphql-codegen-validation-schema + * config: + * schema: yup + * useObjectTypes: true + * ``` + */ + useObjectTypes?: boolean; } diff --git a/src/myzod/index.ts b/src/myzod/index.ts index e3bd7f97..9730fcf0 100644 --- a/src/myzod/index.ts +++ b/src/myzod/index.ts @@ -6,7 +6,9 @@ import { TypeNode, GraphQLSchema, InputObjectTypeDefinitionNode, + ObjectTypeDefinitionNode, EnumTypeDefinitionNode, + FieldDefinitionNode, } from 'graphql'; import { DeclarationBlock, indent } from '@graphql-codegen/visitor-plugin-common'; import { TsVisitor } from '@graphql-codegen/typescript'; @@ -38,7 +40,7 @@ export const MyZodSchemaVisitor = (schema: GraphQLSchema, config: ValidationSche importTypes.push(name); const shape = node.fields - ?.map(field => generateInputObjectFieldMyZodSchema(config, tsVisitor, schema, field, 2)) + ?.map(field => generateFieldMyZodSchema(config, tsVisitor, schema, field, 2)) .join(',\n'); return new DeclarationBlock({}) @@ -47,6 +49,27 @@ export const MyZodSchemaVisitor = (schema: GraphQLSchema, config: ValidationSche .withName(`${name}Schema(): myzod.Type<${name}>`) .withBlock([indent(`return myzod.object({`), shape, indent('})')].join('\n')).string; }, + ObjectTypeDefinition: (node: ObjectTypeDefinitionNode) => { + if (!config.useObjectTypes) return; + const name = tsVisitor.convertName(node.name.value); + importTypes.push(name); + + const shape = node.fields + ?.map(field => generateFieldMyZodSchema(config, tsVisitor, schema, field, 2)) + .join(',\n'); + + return new DeclarationBlock({}) + .export() + .asKind('function') + .withName(`${name}Schema(): myzod.Type<${name}>`) + .withBlock( + [ + indent(`return myzod.object({`), + ` __typename: myzod.literal('${node.name.value}').optional(),\n${shape}`, + indent('})'), + ].join('\n') + ).string; + }, EnumTypeDefinition: (node: EnumTypeDefinitionNode) => { const enumname = tsVisitor.convertName(node.name.value); importTypes.push(enumname); @@ -69,27 +92,27 @@ export const MyZodSchemaVisitor = (schema: GraphQLSchema, config: ValidationSche }; }; -const generateInputObjectFieldMyZodSchema = ( +const generateFieldMyZodSchema = ( config: ValidationSchemaPluginConfig, tsVisitor: TsVisitor, schema: GraphQLSchema, - field: InputValueDefinitionNode, + field: InputValueDefinitionNode | FieldDefinitionNode, indentCount: number ): string => { - const gen = generateInputObjectFieldTypeMyZodSchema(config, tsVisitor, schema, field, field.type); + const gen = generateFieldTypeMyZodSchema(config, tsVisitor, schema, field, field.type); return indent(`${field.name.value}: ${maybeLazy(field.type, gen)}`, indentCount); }; -const generateInputObjectFieldTypeMyZodSchema = ( +const generateFieldTypeMyZodSchema = ( config: ValidationSchemaPluginConfig, tsVisitor: TsVisitor, schema: GraphQLSchema, - field: InputValueDefinitionNode, + field: InputValueDefinitionNode | FieldDefinitionNode, type: TypeNode, parentType?: TypeNode ): string => { if (isListType(type)) { - const gen = generateInputObjectFieldTypeMyZodSchema(config, tsVisitor, schema, field, type.type, type); + const gen = generateFieldTypeMyZodSchema(config, tsVisitor, schema, field, type.type, type); if (!isNonNullType(parentType)) { const arrayGen = `myzod.array(${maybeLazy(type.type, gen)})`; const maybeLazyGen = applyDirectives(config, field, arrayGen); @@ -98,7 +121,7 @@ const generateInputObjectFieldTypeMyZodSchema = ( return `myzod.array(${maybeLazy(type.type, gen)})`; } if (isNonNullType(type)) { - const gen = generateInputObjectFieldTypeMyZodSchema(config, tsVisitor, schema, field, type.type, type); + const gen = generateFieldTypeMyZodSchema(config, tsVisitor, schema, field, type.type, type); return maybeLazy(type.type, gen); } if (isNamedType(type)) { @@ -125,7 +148,7 @@ const generateInputObjectFieldTypeMyZodSchema = ( const applyDirectives = ( config: ValidationSchemaPluginConfig, - field: InputValueDefinitionNode, + field: InputValueDefinitionNode | FieldDefinitionNode, gen: string ): string => { if (config.directives && field.directives) { diff --git a/src/yup/index.ts b/src/yup/index.ts index 32adde70..08bd20d5 100644 --- a/src/yup/index.ts +++ b/src/yup/index.ts @@ -7,6 +7,8 @@ import { GraphQLSchema, InputObjectTypeDefinitionNode, EnumTypeDefinitionNode, + ObjectTypeDefinitionNode, + FieldDefinitionNode, } from 'graphql'; import { DeclarationBlock, indent } from '@graphql-codegen/visitor-plugin-common'; import { TsVisitor } from '@graphql-codegen/typescript'; @@ -31,9 +33,7 @@ export const YupSchemaVisitor = (schema: GraphQLSchema, config: ValidationSchema const name = tsVisitor.convertName(node.name.value); importTypes.push(name); - const shape = node.fields - ?.map(field => generateInputObjectFieldYupSchema(config, tsVisitor, schema, field, 2)) - .join(',\n'); + const shape = node.fields?.map(field => generateFieldYupSchema(config, tsVisitor, schema, field, 2)).join(',\n'); return new DeclarationBlock({}) .export() @@ -41,6 +41,25 @@ export const YupSchemaVisitor = (schema: GraphQLSchema, config: ValidationSchema .withName(`${name}Schema(): yup.SchemaOf<${name}>`) .withBlock([indent(`return yup.object({`), shape, indent('})')].join('\n')).string; }, + ObjectTypeDefinition: (node: ObjectTypeDefinitionNode) => { + if (!config.useObjectTypes) return; + const name = tsVisitor.convertName(node.name.value); + importTypes.push(name); + + const shape = node.fields?.map(field => generateFieldYupSchema(config, tsVisitor, schema, field, 2)).join(',\n'); + + return new DeclarationBlock({}) + .export() + .asKind('function') + .withName(`${name}Schema(): yup.SchemaOf<${name}>`) + .withBlock( + [ + indent(`return yup.object({`), + ` __typename: yup.mixed().oneOf(['${node.name.value}', undefined]),\n${shape}`, + indent('})'), + ].join('\n') + ).string; + }, EnumTypeDefinition: (node: EnumTypeDefinitionNode) => { const enumname = tsVisitor.convertName(node.name.value); importTypes.push(enumname); @@ -92,14 +111,14 @@ export const YupSchemaVisitor = (schema: GraphQLSchema, config: ValidationSchema }; }; -const generateInputObjectFieldYupSchema = ( +const generateFieldYupSchema = ( config: ValidationSchemaPluginConfig, tsVisitor: TsVisitor, schema: GraphQLSchema, - field: InputValueDefinitionNode, + field: InputValueDefinitionNode | FieldDefinitionNode, indentCount: number ): string => { - let gen = generateInputObjectFieldTypeYupSchema(config, tsVisitor, schema, field.type); + let gen = generateFieldTypeYupSchema(config, tsVisitor, schema, field.type); if (config.directives && field.directives) { const formatted = formatDirectiveConfig(config.directives); gen += buildApi(formatted, field.directives); @@ -107,7 +126,7 @@ const generateInputObjectFieldYupSchema = ( return indent(`${field.name.value}: ${maybeLazy(field.type, gen)}`, indentCount); }; -const generateInputObjectFieldTypeYupSchema = ( +const generateFieldTypeYupSchema = ( config: ValidationSchemaPluginConfig, tsVisitor: TsVisitor, schema: GraphQLSchema, @@ -115,14 +134,14 @@ const generateInputObjectFieldTypeYupSchema = ( parentType?: TypeNode ): string => { if (isListType(type)) { - const gen = generateInputObjectFieldTypeYupSchema(config, tsVisitor, schema, type.type, type); + const gen = generateFieldTypeYupSchema(config, tsVisitor, schema, type.type, type); if (!isNonNullType(parentType)) { return `yup.array().of(${maybeLazy(type.type, gen)}).optional()`; } return `yup.array().of(${maybeLazy(type.type, gen)})`; } if (isNonNullType(type)) { - const gen = generateInputObjectFieldTypeYupSchema(config, tsVisitor, schema, type.type, type); + const gen = generateFieldTypeYupSchema(config, tsVisitor, schema, type.type, type); const nonNullGen = maybeNonEmptyString(config, tsVisitor, gen, type.type); return maybeLazy(type.type, nonNullGen); } diff --git a/src/zod/index.ts b/src/zod/index.ts index e9a7a5fc..15ebd960 100644 --- a/src/zod/index.ts +++ b/src/zod/index.ts @@ -6,7 +6,9 @@ import { TypeNode, GraphQLSchema, InputObjectTypeDefinitionNode, + ObjectTypeDefinitionNode, EnumTypeDefinitionNode, + FieldDefinitionNode, } from 'graphql'; import { DeclarationBlock, indent } from '@graphql-codegen/visitor-plugin-common'; import { TsVisitor } from '@graphql-codegen/typescript'; @@ -53,9 +55,7 @@ export const ZodSchemaVisitor = (schema: GraphQLSchema, config: ValidationSchema const name = tsVisitor.convertName(node.name.value); importTypes.push(name); - const shape = node.fields - ?.map(field => generateInputObjectFieldZodSchema(config, tsVisitor, schema, field, 2)) - .join(',\n'); + const shape = node.fields?.map(field => generateFieldZodSchema(config, tsVisitor, schema, field, 2)).join(',\n'); return new DeclarationBlock({}) .export() @@ -63,6 +63,25 @@ export const ZodSchemaVisitor = (schema: GraphQLSchema, config: ValidationSchema .withName(`${name}Schema(): z.ZodObject>`) .withBlock([indent(`return z.object({`), shape, indent('})')].join('\n')).string; }, + ObjectTypeDefinition: (node: ObjectTypeDefinitionNode) => { + if (!config.useObjectTypes) return; + const name = tsVisitor.convertName(node.name.value); + importTypes.push(name); + + const shape = node.fields?.map(field => generateFieldZodSchema(config, tsVisitor, schema, field, 2)).join(',\n'); + + return new DeclarationBlock({}) + .export() + .asKind('function') + .withName(`${name}Schema(): z.ZodObject>`) + .withBlock( + [ + indent(`return z.object({`), + ` __typename: z.literal('${node.name.value}').optional(),\n${shape}`, + indent('})'), + ].join('\n') + ).string; + }, EnumTypeDefinition: (node: EnumTypeDefinitionNode) => { const enumname = tsVisitor.convertName(node.name.value); importTypes.push(enumname); @@ -84,27 +103,27 @@ export const ZodSchemaVisitor = (schema: GraphQLSchema, config: ValidationSchema }; }; -const generateInputObjectFieldZodSchema = ( +const generateFieldZodSchema = ( config: ValidationSchemaPluginConfig, tsVisitor: TsVisitor, schema: GraphQLSchema, - field: InputValueDefinitionNode, + field: InputValueDefinitionNode | FieldDefinitionNode, indentCount: number ): string => { - const gen = generateInputObjectFieldTypeZodSchema(config, tsVisitor, schema, field, field.type); + const gen = generateFieldTypeZodSchema(config, tsVisitor, schema, field, field.type); return indent(`${field.name.value}: ${maybeLazy(field.type, gen)}`, indentCount); }; -const generateInputObjectFieldTypeZodSchema = ( +const generateFieldTypeZodSchema = ( config: ValidationSchemaPluginConfig, tsVisitor: TsVisitor, schema: GraphQLSchema, - field: InputValueDefinitionNode, + field: InputValueDefinitionNode | FieldDefinitionNode, type: TypeNode, parentType?: TypeNode ): string => { if (isListType(type)) { - const gen = generateInputObjectFieldTypeZodSchema(config, tsVisitor, schema, field, type.type, type); + const gen = generateFieldTypeZodSchema(config, tsVisitor, schema, field, type.type, type); if (!isNonNullType(parentType)) { const arrayGen = `z.array(${maybeLazy(type.type, gen)})`; const maybeLazyGen = applyDirectives(config, field, arrayGen); @@ -113,7 +132,7 @@ const generateInputObjectFieldTypeZodSchema = ( return `z.array(${maybeLazy(type.type, gen)})`; } if (isNonNullType(type)) { - const gen = generateInputObjectFieldTypeZodSchema(config, tsVisitor, schema, field, type.type, type); + const gen = generateFieldTypeZodSchema(config, tsVisitor, schema, field, type.type, type); return maybeLazy(type.type, gen); } if (isNamedType(type)) { @@ -125,7 +144,7 @@ const generateInputObjectFieldTypeZodSchema = ( if (isNonNullType(parentType)) { if (config.notAllowEmptyString === true) { const tsType = tsVisitor.scalars[type.name.value]; - if (tsType === 'string') return `${appliedDirectivesGen}.min(1)`; + if (tsType === 'string') return `${gen}.min(1)`; } return appliedDirectivesGen; } @@ -140,7 +159,7 @@ const generateInputObjectFieldTypeZodSchema = ( const applyDirectives = ( config: ValidationSchemaPluginConfig, - field: InputValueDefinitionNode, + field: InputValueDefinitionNode | FieldDefinitionNode, gen: string ): string => { if (config.directives && field.directives) { diff --git a/tests/myzod.spec.ts b/tests/myzod.spec.ts index 7f2ac073..12357ad9 100644 --- a/tests/myzod.spec.ts +++ b/tests/myzod.spec.ts @@ -410,4 +410,78 @@ describe('myzod', () => { } }); }); + + describe('GraphQl Type Support', () => { + const schema = buildSchema(/* GraphQL */ ` + input ScalarsInput { + date: Date! + email: Email + } + scalar Date + scalar Email + input UserCreateInput { + name: String! + email: Email! + } + type User { + id: ID! + name: String + age: Int + email: Email + isMember: Boolean + createdAt: Date! + } + `); + + it('not generate if useObjectTypes false', async () => { + const result = await plugin( + schema, + [], + { + schema: 'myzod', + }, + {} + ); + expect(result.content).not.toContain("export function UserSchema(): myzod.Type {"); + }); + + it('generate both input & type', async () => { + const result = await plugin( + schema, + [], + { + schema: 'myzod', + useObjectTypes: true, + scalarSchemas: { + Date: 'myzod.date()', + Email: 'myzod.string().email()', + }, + }, + {} + ); + const wantContains = [ + // ScalarsInput + "export const definedNonNullAnySchema = myzod.object({});", + "export function ScalarsInputSchema(): myzod.Type {", + "date: myzod.date(),", + "email: myzod.string().email().optional().nullable()", + // User Create Input + "export function UserCreateInputSchema(): myzod.Type {", + "name: myzod.string(),", + "email: myzod.string().email()", + // User + "export function UserSchema(): myzod.Type {", + "__typename: myzod.literal('User').optional(),", + "id: myzod.string(),", + "name: myzod.string().optional().nullable(),", + "age: myzod.number().optional().nullable(),", + "email: myzod.string().email().optional().nullable(),", + "isMember: myzod.boolean().optional().nullable(),", + "createdAt: myzod.date()", + ] + for (const wantContain of wantContains) { + expect(result.content).toContain(wantContain); + } + }); + }); }); diff --git a/tests/yup.spec.ts b/tests/yup.spec.ts index ec9d1058..d9f9802d 100644 --- a/tests/yup.spec.ts +++ b/tests/yup.spec.ts @@ -313,4 +313,77 @@ describe('yup', () => { expect(result.prepend).toContain("import { SayI } from './types'"); expect(result.content).toContain('export function SayISchema(): yup.SchemaOf {'); }); + describe('GraphQl Type Support', () => { + const schema = buildSchema(/* GraphQL */ ` + input ScalarsInput { + date: Date! + email: Email + } + scalar Date + scalar Email + input UserCreateInput { + name: String! + email: Email! + } + type User { + id: ID! + name: String + age: Int + email: Email + isMember: Boolean + createdAt: Date! + } + `); + + it('not generate if useObjectTypes false', async () => { + const result = await plugin( + schema, + [], + { + schema: 'yup', + }, + {} + ); + expect(result.content).not.toContain("export function UserSchema(): yup.SchemaOf {"); + }); + + it('generate both input & type if useObjectTypes true', async () => { + const result = await plugin( + schema, + [], + { + schema: 'yup', + useObjectTypes: true, + scalarSchemas: { + Date: 'yup.date()', + Email: 'yup.string().email()', + }, + }, + {} + ); + const wantContains = [ + // ScalarsInput + "export function ScalarsInputSchema(): yup.SchemaOf {", + "return yup.object({", + "date: yup.date().defined()", + "email: yup.string().email()", + // User Create Input + "export function UserCreateInputSchema(): yup.SchemaOf {", + "name: yup.string().defined()", + "email: yup.string().email().defined()", + // User + "export function UserSchema(): yup.SchemaOf {", + "__typename: yup.mixed().oneOf(['User', undefined])", + "id: yup.string().defined()", + "name: yup.string()", + "age: yup.number()", + "isMember: yup.boolean()", + "email: yup.string().email()", + "createdAt: yup.date().defined()", + ] + for (const wantContain of wantContains) { + expect(result.content).toContain(wantContain); + } + }); + }); }); diff --git a/tests/zod.spec.ts b/tests/zod.spec.ts index 16070981..0d9bc6db 100644 --- a/tests/zod.spec.ts +++ b/tests/zod.spec.ts @@ -412,4 +412,77 @@ describe('zod', () => { } }); }); + describe('GraphQl Type Support', () => { + const schema = buildSchema(/* GraphQL */ ` + input ScalarsInput { + date: Date! + email: Email + } + scalar Date + scalar Email + input UserCreateInput { + name: String! + email: Email! + } + type User { + id: ID! + name: String + age: Int + email: Email + isMember: Boolean + createdAt: Date! + } + `); + + it('not generate if useObjectTypes false', async () => { + const result = await plugin( + schema, + [], + { + schema: 'zod', + }, + {} + ); + expect(result.content).not.toContain("export function UserSchema(): z.ZodObject>"); + }); + + it('generate both input & type', async () => { + const result = await plugin( + schema, + [], + { + schema: 'zod', + useObjectTypes: true, + scalarSchemas: { + Date: 'z.date()', + Email: 'z.string().email()', + }, + }, + {} + ); + const wantContains = [ + // ScalarsInput + "export function ScalarsInputSchema(): z.ZodObject> {", + "return z.object({", + "date: z.date()", + "email: z.string().email().nullish()", + // User Create Input + "export function UserCreateInputSchema(): z.ZodObject> {", + "name: z.string()", + "email: z.string().email()", + // User + "export function UserSchema(): z.ZodObject> {", + "__typename: z.literal('User').optional()", + "id: z.string()", + "name: z.string().nullish(),", + "age: z.number().nullish()", + "isMember: z.boolean().nullish()", + "email: z.string().email().nullish()", + "createdAt: z.date()", + ] + for (const wantContain of wantContains) { + expect(result.content).toContain(wantContain); + } + }); + }); }); From 538fa5b8919c51a8ccf417ab03c79e1a72248b4e Mon Sep 17 00:00:00 2001 From: Kei Kamikawa Date: Fri, 29 Jul 2022 23:46:16 +0900 Subject: [PATCH 2/2] format --- tests/myzod.spec.ts | 32 ++++++++++++++++---------------- tests/yup.spec.ts | 32 ++++++++++++++++---------------- tests/zod.spec.ts | 32 ++++++++++++++++---------------- 3 files changed, 48 insertions(+), 48 deletions(-) diff --git a/tests/myzod.spec.ts b/tests/myzod.spec.ts index 12357ad9..7a1a5c4a 100644 --- a/tests/myzod.spec.ts +++ b/tests/myzod.spec.ts @@ -442,7 +442,7 @@ describe('myzod', () => { }, {} ); - expect(result.content).not.toContain("export function UserSchema(): myzod.Type {"); + expect(result.content).not.toContain('export function UserSchema(): myzod.Type {'); }); it('generate both input & type', async () => { @@ -461,24 +461,24 @@ describe('myzod', () => { ); const wantContains = [ // ScalarsInput - "export const definedNonNullAnySchema = myzod.object({});", - "export function ScalarsInputSchema(): myzod.Type {", - "date: myzod.date(),", - "email: myzod.string().email().optional().nullable()", + 'export const definedNonNullAnySchema = myzod.object({});', + 'export function ScalarsInputSchema(): myzod.Type {', + 'date: myzod.date(),', + 'email: myzod.string().email().optional().nullable()', // User Create Input - "export function UserCreateInputSchema(): myzod.Type {", - "name: myzod.string(),", - "email: myzod.string().email()", + 'export function UserCreateInputSchema(): myzod.Type {', + 'name: myzod.string(),', + 'email: myzod.string().email()', // User - "export function UserSchema(): myzod.Type {", + 'export function UserSchema(): myzod.Type {', "__typename: myzod.literal('User').optional(),", - "id: myzod.string(),", - "name: myzod.string().optional().nullable(),", - "age: myzod.number().optional().nullable(),", - "email: myzod.string().email().optional().nullable(),", - "isMember: myzod.boolean().optional().nullable(),", - "createdAt: myzod.date()", - ] + 'id: myzod.string(),', + 'name: myzod.string().optional().nullable(),', + 'age: myzod.number().optional().nullable(),', + 'email: myzod.string().email().optional().nullable(),', + 'isMember: myzod.boolean().optional().nullable(),', + 'createdAt: myzod.date()', + ]; for (const wantContain of wantContains) { expect(result.content).toContain(wantContain); } diff --git a/tests/yup.spec.ts b/tests/yup.spec.ts index d9f9802d..3b87bc06 100644 --- a/tests/yup.spec.ts +++ b/tests/yup.spec.ts @@ -344,7 +344,7 @@ describe('yup', () => { }, {} ); - expect(result.content).not.toContain("export function UserSchema(): yup.SchemaOf {"); + expect(result.content).not.toContain('export function UserSchema(): yup.SchemaOf {'); }); it('generate both input & type if useObjectTypes true', async () => { @@ -363,24 +363,24 @@ describe('yup', () => { ); const wantContains = [ // ScalarsInput - "export function ScalarsInputSchema(): yup.SchemaOf {", - "return yup.object({", - "date: yup.date().defined()", - "email: yup.string().email()", + 'export function ScalarsInputSchema(): yup.SchemaOf {', + 'return yup.object({', + 'date: yup.date().defined()', + 'email: yup.string().email()', // User Create Input - "export function UserCreateInputSchema(): yup.SchemaOf {", - "name: yup.string().defined()", - "email: yup.string().email().defined()", + 'export function UserCreateInputSchema(): yup.SchemaOf {', + 'name: yup.string().defined()', + 'email: yup.string().email().defined()', // User - "export function UserSchema(): yup.SchemaOf {", + 'export function UserSchema(): yup.SchemaOf {', "__typename: yup.mixed().oneOf(['User', undefined])", - "id: yup.string().defined()", - "name: yup.string()", - "age: yup.number()", - "isMember: yup.boolean()", - "email: yup.string().email()", - "createdAt: yup.date().defined()", - ] + 'id: yup.string().defined()', + 'name: yup.string()', + 'age: yup.number()', + 'isMember: yup.boolean()', + 'email: yup.string().email()', + 'createdAt: yup.date().defined()', + ]; for (const wantContain of wantContains) { expect(result.content).toContain(wantContain); } diff --git a/tests/zod.spec.ts b/tests/zod.spec.ts index 0d9bc6db..a7c98578 100644 --- a/tests/zod.spec.ts +++ b/tests/zod.spec.ts @@ -443,7 +443,7 @@ describe('zod', () => { }, {} ); - expect(result.content).not.toContain("export function UserSchema(): z.ZodObject>"); + expect(result.content).not.toContain('export function UserSchema(): z.ZodObject>'); }); it('generate both input & type', async () => { @@ -462,24 +462,24 @@ describe('zod', () => { ); const wantContains = [ // ScalarsInput - "export function ScalarsInputSchema(): z.ZodObject> {", - "return z.object({", - "date: z.date()", - "email: z.string().email().nullish()", + 'export function ScalarsInputSchema(): z.ZodObject> {', + 'return z.object({', + 'date: z.date()', + 'email: z.string().email().nullish()', // User Create Input - "export function UserCreateInputSchema(): z.ZodObject> {", - "name: z.string()", - "email: z.string().email()", + 'export function UserCreateInputSchema(): z.ZodObject> {', + 'name: z.string()', + 'email: z.string().email()', // User - "export function UserSchema(): z.ZodObject> {", + 'export function UserSchema(): z.ZodObject> {', "__typename: z.literal('User').optional()", - "id: z.string()", - "name: z.string().nullish(),", - "age: z.number().nullish()", - "isMember: z.boolean().nullish()", - "email: z.string().email().nullish()", - "createdAt: z.date()", - ] + 'id: z.string()', + 'name: z.string().nullish(),', + 'age: z.number().nullish()', + 'isMember: z.boolean().nullish()', + 'email: z.string().email().nullish()', + 'createdAt: z.date()', + ]; for (const wantContain of wantContains) { expect(result.content).toContain(wantContain); }