From ba0eee0d171ed6f880c4f40842c82a7c91237839 Mon Sep 17 00:00:00 2001 From: Kei Kamikawa Date: Sat, 27 May 2023 14:09:52 +0900 Subject: [PATCH 1/5] added own visitor and fixed build error for #373 --- src/myzod/index.ts | 80 +++++++++++++++++++++---------------------- src/visitor.ts | 36 ++++++++++++++++++++ src/yup/index.ts | 83 +++++++++++++++++++++------------------------ src/zod/index.ts | 84 ++++++++++++++++++++-------------------------- 4 files changed, 150 insertions(+), 133 deletions(-) create mode 100644 src/visitor.ts diff --git a/src/myzod/index.ts b/src/myzod/index.ts index 09ff6c24..e742ce77 100644 --- a/src/myzod/index.ts +++ b/src/myzod/index.ts @@ -12,7 +12,7 @@ import { UnionTypeDefinitionNode, } from 'graphql'; import { DeclarationBlock, indent } from '@graphql-codegen/visitor-plugin-common'; -import { TsVisitor } from '@graphql-codegen/typescript'; +import { Visitor } from '../visitor'; import { buildApi, formatDirectiveConfig } from '../directive'; import { SchemaVisitor } from '../types'; @@ -20,8 +20,6 @@ const importZod = `import * as myzod from 'myzod'`; const anySchema = `definedNonNullAnySchema`; export const MyZodSchemaVisitor = (schema: GraphQLSchema, config: ValidationSchemaPluginConfig): SchemaVisitor => { - const tsVisitor = new TsVisitor(schema, config); - const importTypes: string[] = []; return { @@ -39,12 +37,11 @@ export const MyZodSchemaVisitor = (schema: GraphQLSchema, config: ValidationSche ].join('\n'), InputObjectTypeDefinition: { leave: (node: InputObjectTypeDefinitionNode) => { - const name = tsVisitor.convertName(node.name.value); + const visitor = new Visitor('input', schema, config); + const name = visitor.convertName(node.name.value); importTypes.push(name); - const shape = node.fields - ?.map(field => generateFieldMyZodSchema(config, tsVisitor, schema, field, 2)) - .join(',\n'); + const shape = node.fields?.map(field => generateFieldMyZodSchema(config, visitor, field, 2)).join(',\n'); return new DeclarationBlock({}) .export() @@ -55,12 +52,11 @@ export const MyZodSchemaVisitor = (schema: GraphQLSchema, config: ValidationSche }, ObjectTypeDefinition: { leave: ObjectTypeDefinitionBuilder(config.withObjectType, (node: ObjectTypeDefinitionNode) => { - const name = tsVisitor.convertName(node.name.value); + const visitor = new Visitor('output', schema, config); + const name = visitor.convertName(node.name.value); importTypes.push(name); - const shape = node.fields - ?.map(field => generateFieldMyZodSchema(config, tsVisitor, schema, field, 2)) - .join(',\n'); + const shape = node.fields?.map(field => generateFieldMyZodSchema(config, visitor, field, 2)).join(',\n'); return new DeclarationBlock({}) .export() @@ -78,7 +74,8 @@ export const MyZodSchemaVisitor = (schema: GraphQLSchema, config: ValidationSche }, EnumTypeDefinition: { leave: (node: EnumTypeDefinitionNode) => { - const enumname = tsVisitor.convertName(node.name.value); + const visitor = new Visitor('both', schema, config); + const enumname = visitor.convertName(node.name.value); importTypes.push(enumname); // z.enum are basically myzod.literals if (config.enumsAsTypes) { @@ -101,11 +98,13 @@ export const MyZodSchemaVisitor = (schema: GraphQLSchema, config: ValidationSche leave: (node: UnionTypeDefinitionNode) => { if (!node.types || !config.withObjectType) return; - const unionName = tsVisitor.convertName(node.name.value); + const visitor = new Visitor('output', schema, config); + + const unionName = visitor.convertName(node.name.value); const unionElements = node.types ?.map(t => { - const element = tsVisitor.convertName(t.name.value); - const typ = schema.getType(t.name.value); + const element = visitor.convertName(t.name.value); + const typ = visitor.getType(t.name.value); if (typ?.astNode?.kind === 'EnumTypeDefinition') { return `${element}Schema`; } @@ -126,25 +125,23 @@ export const MyZodSchemaVisitor = (schema: GraphQLSchema, config: ValidationSche const generateFieldMyZodSchema = ( config: ValidationSchemaPluginConfig, - tsVisitor: TsVisitor, - schema: GraphQLSchema, + visitor: Visitor, field: InputValueDefinitionNode | FieldDefinitionNode, indentCount: number ): string => { - const gen = generateFieldTypeMyZodSchema(config, tsVisitor, schema, field, field.type); + const gen = generateFieldTypeMyZodSchema(config, visitor, field, field.type); return indent(`${field.name.value}: ${maybeLazy(field.type, gen)}`, indentCount); }; const generateFieldTypeMyZodSchema = ( config: ValidationSchemaPluginConfig, - tsVisitor: TsVisitor, - schema: GraphQLSchema, + visitor: Visitor, field: InputValueDefinitionNode | FieldDefinitionNode, type: TypeNode, parentType?: TypeNode ): string => { if (isListType(type)) { - const gen = generateFieldTypeMyZodSchema(config, tsVisitor, schema, field, type.type, type); + const gen = generateFieldTypeMyZodSchema(config, visitor, field, type.type, type); if (!isNonNullType(parentType)) { const arrayGen = `myzod.array(${maybeLazy(type.type, gen)})`; const maybeLazyGen = applyDirectives(config, field, arrayGen); @@ -153,18 +150,18 @@ const generateFieldTypeMyZodSchema = ( return `myzod.array(${maybeLazy(type.type, gen)})`; } if (isNonNullType(type)) { - const gen = generateFieldTypeMyZodSchema(config, tsVisitor, schema, field, type.type, type); + const gen = generateFieldTypeMyZodSchema(config, visitor, field, type.type, type); return maybeLazy(type.type, gen); } if (isNamedType(type)) { - const gen = generateNameNodeMyZodSchema(config, tsVisitor, schema, type.name); + const gen = generateNameNodeMyZodSchema(config, visitor, type.name); if (isListType(parentType)) { return `${gen}.nullable()`; } const appliedDirectivesGen = applyDirectives(config, field, gen); if (isNonNullType(parentType)) { if (config.notAllowEmptyString === true) { - const tsType = tsVisitor.scalars[type.name.value]; + const tsType = visitor.getScalarType(type.name.value); if (tsType === 'string') return `${gen}.min(1)`; } return appliedDirectivesGen; @@ -192,33 +189,32 @@ const applyDirectives = ( const generateNameNodeMyZodSchema = ( config: ValidationSchemaPluginConfig, - tsVisitor: TsVisitor, - schema: GraphQLSchema, + visitor: Visitor, node: NameNode ): string => { - const typ = schema.getType(node.value); + const converter = visitor.getNameNodeConverter(node); - if (typ?.astNode?.kind === 'InputObjectTypeDefinition') { - const enumName = tsVisitor.convertName(typ.astNode.name.value); - return `${enumName}Schema()`; + if (converter?.targetKind === 'InputObjectTypeDefinition') { + const name = converter.convertName(); + return `${name}Schema()`; } - if (typ?.astNode?.kind === 'ObjectTypeDefinition') { - const enumName = tsVisitor.convertName(typ.astNode.name.value); - return `${enumName}Schema()`; + if (converter?.targetKind === 'ObjectTypeDefinition') { + const name = converter.convertName(); + return `${name}Schema()`; } - if (typ?.astNode?.kind === 'EnumTypeDefinition') { - const enumName = tsVisitor.convertName(typ.astNode.name.value); - return `${enumName}Schema`; + if (converter?.targetKind === 'EnumTypeDefinition') { + const name = converter.convertName(); + return `${name}Schema`; } - if (typ?.astNode?.kind === 'UnionTypeDefinition') { - const enumName = tsVisitor.convertName(typ.astNode.name.value); - return `${enumName}Schema()`; + if (converter?.targetKind === 'UnionTypeDefinition') { + const name = converter.convertName(); + return `${name}Schema()`; } - return myzod4Scalar(config, tsVisitor, node.value); + return myzod4Scalar(config, visitor, node.value); }; const maybeLazy = (type: TypeNode, schema: string): string => { @@ -228,11 +224,11 @@ const maybeLazy = (type: TypeNode, schema: string): string => { return schema; }; -const myzod4Scalar = (config: ValidationSchemaPluginConfig, tsVisitor: TsVisitor, scalarName: string): string => { +const myzod4Scalar = (config: ValidationSchemaPluginConfig, visitor: Visitor, scalarName: string): string => { if (config.scalarSchemas?.[scalarName]) { return config.scalarSchemas[scalarName]; } - const tsType = tsVisitor.scalars[scalarName]; + const tsType = visitor.getScalarType(scalarName); switch (tsType) { case 'string': return `myzod.string()`; diff --git a/src/visitor.ts b/src/visitor.ts new file mode 100644 index 00000000..a3181def --- /dev/null +++ b/src/visitor.ts @@ -0,0 +1,36 @@ +import { ValidationSchemaPluginConfig } from './config'; +import { TsVisitor } from '@graphql-codegen/typescript'; +import { NameNode, GraphQLSchema } from 'graphql'; + +export class Visitor extends TsVisitor { + constructor( + private scalarDirection: 'input' | 'output' | 'both', + private schema: GraphQLSchema, + config: ValidationSchemaPluginConfig + ) { + super(schema, config); + } + + public getType(name: string) { + return this.schema.getType(name); + } + + public getNameNodeConverter(node: NameNode) { + const typ = this.schema.getType(node.value); + const astNode = typ?.astNode; + if (astNode === undefined || astNode === null) { + return undefined; + } + return { + targetKind: astNode.kind, + convertName: () => this.convertName(astNode.name.value), + }; + } + + public getScalarType(scalarName: string): string | null { + if (this.scalarDirection === 'both') { + return null; + } + return this.scalars[scalarName][this.scalarDirection]; + } +} diff --git a/src/yup/index.ts b/src/yup/index.ts index 1be7a597..04672a0d 100644 --- a/src/yup/index.ts +++ b/src/yup/index.ts @@ -12,15 +12,13 @@ import { UnionTypeDefinitionNode, } from 'graphql'; import { DeclarationBlock, indent } from '@graphql-codegen/visitor-plugin-common'; -import { TsVisitor } from '@graphql-codegen/typescript'; +import { Visitor } from '../visitor'; import { buildApi, formatDirectiveConfig } from '../directive'; import { SchemaVisitor } from '../types'; const importYup = `import * as yup from 'yup'`; export const YupSchemaVisitor = (schema: GraphQLSchema, config: ValidationSchemaPluginConfig): SchemaVisitor => { - const tsVisitor = new TsVisitor(schema, config); - const importTypes: string[] = []; return { @@ -48,12 +46,13 @@ export const YupSchemaVisitor = (schema: GraphQLSchema, config: ValidationSchema }, InputObjectTypeDefinition: { leave: (node: InputObjectTypeDefinitionNode) => { - const name = tsVisitor.convertName(node.name.value); + const visitor = new Visitor('input', schema, config); + const name = visitor.convertName(node.name.value); importTypes.push(name); const shape = node.fields ?.map(field => { - const fieldSchema = generateFieldYupSchema(config, tsVisitor, schema, field, 2); + const fieldSchema = generateFieldYupSchema(config, visitor, field, 2); return isNonNullType(field.type) ? fieldSchema : `${fieldSchema}.optional()`; }) .join(',\n'); @@ -67,12 +66,13 @@ export const YupSchemaVisitor = (schema: GraphQLSchema, config: ValidationSchema }, ObjectTypeDefinition: { leave: ObjectTypeDefinitionBuilder(config.withObjectType, (node: ObjectTypeDefinitionNode) => { - const name = tsVisitor.convertName(node.name.value); + const visitor = new Visitor('output', schema, config); + const name = visitor.convertName(node.name.value); importTypes.push(name); const shape = node.fields ?.map(field => { - const fieldSchema = generateFieldYupSchema(config, tsVisitor, schema, field, 2); + const fieldSchema = generateFieldYupSchema(config, visitor, field, 2); return isNonNullType(field.type) ? fieldSchema : `${fieldSchema}.optional()`; }) .join(',\n'); @@ -93,7 +93,8 @@ export const YupSchemaVisitor = (schema: GraphQLSchema, config: ValidationSchema }, EnumTypeDefinition: { leave: (node: EnumTypeDefinitionNode) => { - const enumname = tsVisitor.convertName(node.name.value); + const visitor = new Visitor('both', schema, config); + const enumname = visitor.convertName(node.name.value); importTypes.push(enumname); if (config.enumsAsTypes) { @@ -109,7 +110,7 @@ export const YupSchemaVisitor = (schema: GraphQLSchema, config: ValidationSchema const values = node.values ?.map( enumOption => - `${enumname}.${tsVisitor.convertName(enumOption.name, { + `${enumname}.${visitor.convertName(enumOption.name, { useTypesPrefix: false, transformUnderscore: true, })}` @@ -125,14 +126,15 @@ export const YupSchemaVisitor = (schema: GraphQLSchema, config: ValidationSchema UnionTypeDefinition: { leave: (node: UnionTypeDefinitionNode) => { if (!node.types || !config.withObjectType) return; + const visitor = new Visitor('output', schema, config); - const unionName = tsVisitor.convertName(node.name.value); + const unionName = visitor.convertName(node.name.value); importTypes.push(unionName); const unionElements = node.types ?.map(t => { - const element = tsVisitor.convertName(t.name.value); - const typ = schema.getType(t.name.value); + const element = visitor.convertName(t.name.value); + const typ = visitor.getType(t.name.value); if (typ?.astNode?.kind === 'EnumTypeDefinition') { return `${element}Schema`; } @@ -172,12 +174,11 @@ export const YupSchemaVisitor = (schema: GraphQLSchema, config: ValidationSchema const generateFieldYupSchema = ( config: ValidationSchemaPluginConfig, - tsVisitor: TsVisitor, - schema: GraphQLSchema, + visitor: Visitor, field: InputValueDefinitionNode | FieldDefinitionNode, indentCount: number ): string => { - let gen = generateFieldTypeYupSchema(config, tsVisitor, schema, field.type); + let gen = generateFieldTypeYupSchema(config, visitor, field.type); if (config.directives && field.directives) { const formatted = formatDirectiveConfig(config.directives); gen += buildApi(formatted, field.directives); @@ -187,32 +188,31 @@ const generateFieldYupSchema = ( const generateFieldTypeYupSchema = ( config: ValidationSchemaPluginConfig, - tsVisitor: TsVisitor, - schema: GraphQLSchema, + visitor: Visitor, type: TypeNode, parentType?: TypeNode ): string => { if (isListType(type)) { - const gen = generateFieldTypeYupSchema(config, tsVisitor, schema, type.type, type); + const gen = generateFieldTypeYupSchema(config, visitor, type.type, type); if (!isNonNullType(parentType)) { return `yup.array(${maybeLazy(type.type, gen)}).defined().nullable()`; } return `yup.array(${maybeLazy(type.type, gen)}).defined()`; } if (isNonNullType(type)) { - const gen = generateFieldTypeYupSchema(config, tsVisitor, schema, type.type, type); + const gen = generateFieldTypeYupSchema(config, visitor, type.type, type); return maybeLazy(type.type, gen); } if (isNamedType(type)) { - const gen = generateNameNodeYupSchema(config, tsVisitor, schema, type.name); + const gen = generateNameNodeYupSchema(config, visitor, type.name); if (isNonNullType(parentType)) { if (config.notAllowEmptyString === true) { - const tsType = tsVisitor.scalars[type.name.value]; + const tsType = visitor.getScalarType(type.name.value); if (tsType === 'string') return `${gen}.required()`; } return `${gen}.nonNullable()`; } - const typ = schema.getType(type.name.value); + const typ = visitor.getType(type.name.value); if (typ?.astNode?.kind === 'InputObjectTypeDefinition') { return `${gen}`; } @@ -222,35 +222,30 @@ const generateFieldTypeYupSchema = ( return ''; }; -const generateNameNodeYupSchema = ( - config: ValidationSchemaPluginConfig, - tsVisitor: TsVisitor, - schema: GraphQLSchema, - node: NameNode -): string => { - const typ = schema.getType(node.value); +const generateNameNodeYupSchema = (config: ValidationSchemaPluginConfig, visitor: Visitor, node: NameNode): string => { + const converter = visitor.getNameNodeConverter(node); - if (typ?.astNode?.kind === 'InputObjectTypeDefinition') { - const enumName = tsVisitor.convertName(typ.astNode.name.value); - return `${enumName}Schema()`; + if (converter?.targetKind === 'InputObjectTypeDefinition') { + const name = converter.convertName(); + return `${name}Schema()`; } - if (typ?.astNode?.kind === 'ObjectTypeDefinition') { - const enumName = tsVisitor.convertName(typ.astNode.name.value); - return `${enumName}Schema()`; + if (converter?.targetKind === 'ObjectTypeDefinition') { + const name = converter.convertName(); + return `${name}Schema()`; } - if (typ?.astNode?.kind === 'EnumTypeDefinition') { - const enumName = tsVisitor.convertName(typ.astNode.name.value); - return `${enumName}Schema`; + if (converter?.targetKind === 'EnumTypeDefinition') { + const name = converter.convertName(); + return `${name}Schema`; } - if (typ?.astNode?.kind === 'UnionTypeDefinition') { - const enumName = tsVisitor.convertName(typ.astNode.name.value); - return `${enumName}Schema()`; + if (converter?.targetKind === 'UnionTypeDefinition') { + const name = converter.convertName(); + return `${name}Schema()`; } - const primitive = yup4Scalar(config, tsVisitor, node.value); + const primitive = yup4Scalar(config, visitor, node.value); return primitive; }; @@ -262,11 +257,11 @@ const maybeLazy = (type: TypeNode, schema: string): string => { return schema; }; -const yup4Scalar = (config: ValidationSchemaPluginConfig, tsVisitor: TsVisitor, scalarName: string): string => { +const yup4Scalar = (config: ValidationSchemaPluginConfig, visitor: Visitor, scalarName: string): string => { if (config.scalarSchemas?.[scalarName]) { return `${config.scalarSchemas[scalarName]}.defined()`; } - const tsType = tsVisitor.scalars[scalarName]; + const tsType = visitor.getScalarType(scalarName); switch (tsType) { case 'string': return `yup.string().defined()`; diff --git a/src/zod/index.ts b/src/zod/index.ts index dfe07362..cbb08644 100644 --- a/src/zod/index.ts +++ b/src/zod/index.ts @@ -12,7 +12,7 @@ import { FieldDefinitionNode, } from 'graphql'; import { DeclarationBlock, indent } from '@graphql-codegen/visitor-plugin-common'; -import { TsVisitor } from '@graphql-codegen/typescript'; +import { Visitor } from '../visitor'; import { buildApi, formatDirectiveConfig } from '../directive'; import { SchemaVisitor } from '../types'; @@ -20,8 +20,6 @@ const importZod = `import { z } from 'zod'`; const anySchema = `definedNonNullAnySchema`; export const ZodSchemaVisitor = (schema: GraphQLSchema, config: ValidationSchemaPluginConfig): SchemaVisitor => { - const tsVisitor = new TsVisitor(schema, config); - const importTypes: string[] = []; return { @@ -55,12 +53,11 @@ export const ZodSchemaVisitor = (schema: GraphQLSchema, config: ValidationSchema ].join('\n'), InputObjectTypeDefinition: { leave: (node: InputObjectTypeDefinitionNode) => { - const name = tsVisitor.convertName(node.name.value); + const visitor = new Visitor('input', schema, config); + const name = visitor.convertName(node.name.value); importTypes.push(name); - const shape = node.fields - ?.map(field => generateFieldZodSchema(config, tsVisitor, schema, field, 2)) - .join(',\n'); + const shape = node.fields?.map(field => generateFieldZodSchema(config, visitor, field, 2)).join(',\n'); return new DeclarationBlock({}) .export() @@ -71,12 +68,11 @@ export const ZodSchemaVisitor = (schema: GraphQLSchema, config: ValidationSchema }, ObjectTypeDefinition: { leave: ObjectTypeDefinitionBuilder(config.withObjectType, (node: ObjectTypeDefinitionNode) => { - const name = tsVisitor.convertName(node.name.value); + const visitor = new Visitor('output', schema, config); + const name = visitor.convertName(node.name.value); importTypes.push(name); - const shape = node.fields - ?.map(field => generateFieldZodSchema(config, tsVisitor, schema, field, 2)) - .join(',\n'); + const shape = node.fields?.map(field => generateFieldZodSchema(config, visitor, field, 2)).join(',\n'); return new DeclarationBlock({}) .export() @@ -94,7 +90,8 @@ export const ZodSchemaVisitor = (schema: GraphQLSchema, config: ValidationSchema }, EnumTypeDefinition: { leave: (node: EnumTypeDefinitionNode) => { - const enumname = tsVisitor.convertName(node.name.value); + const visitor = new Visitor('both', schema, config); + const enumname = visitor.convertName(node.name.value); importTypes.push(enumname); if (config.enumsAsTypes) { @@ -115,12 +112,12 @@ export const ZodSchemaVisitor = (schema: GraphQLSchema, config: ValidationSchema UnionTypeDefinition: { leave: (node: UnionTypeDefinitionNode) => { if (!node.types || !config.withObjectType) return; - - const unionName = tsVisitor.convertName(node.name.value); + const visitor = new Visitor('output', schema, config); + const unionName = visitor.convertName(node.name.value); const unionElements = node.types .map(t => { - const element = tsVisitor.convertName(t.name.value); - const typ = schema.getType(t.name.value); + const element = visitor.convertName(t.name.value); + const typ = visitor.getType(t.name.value); if (typ?.astNode?.kind === 'EnumTypeDefinition') { return `${element}Schema`; } @@ -141,25 +138,23 @@ export const ZodSchemaVisitor = (schema: GraphQLSchema, config: ValidationSchema const generateFieldZodSchema = ( config: ValidationSchemaPluginConfig, - tsVisitor: TsVisitor, - schema: GraphQLSchema, + visitor: Visitor, field: InputValueDefinitionNode | FieldDefinitionNode, indentCount: number ): string => { - const gen = generateFieldTypeZodSchema(config, tsVisitor, schema, field, field.type); + const gen = generateFieldTypeZodSchema(config, visitor, field, field.type); return indent(`${field.name.value}: ${maybeLazy(field.type, gen)}`, indentCount); }; const generateFieldTypeZodSchema = ( config: ValidationSchemaPluginConfig, - tsVisitor: TsVisitor, - schema: GraphQLSchema, + visitor: Visitor, field: InputValueDefinitionNode | FieldDefinitionNode, type: TypeNode, parentType?: TypeNode ): string => { if (isListType(type)) { - const gen = generateFieldTypeZodSchema(config, tsVisitor, schema, field, type.type, type); + const gen = generateFieldTypeZodSchema(config, visitor, field, type.type, type); if (!isNonNullType(parentType)) { const arrayGen = `z.array(${maybeLazy(type.type, gen)})`; const maybeLazyGen = applyDirectives(config, field, arrayGen); @@ -168,18 +163,18 @@ const generateFieldTypeZodSchema = ( return `z.array(${maybeLazy(type.type, gen)})`; } if (isNonNullType(type)) { - const gen = generateFieldTypeZodSchema(config, tsVisitor, schema, field, type.type, type); + const gen = generateFieldTypeZodSchema(config, visitor, field, type.type, type); return maybeLazy(type.type, gen); } if (isNamedType(type)) { - const gen = generateNameNodeZodSchema(config, tsVisitor, schema, type.name); + const gen = generateNameNodeZodSchema(config, visitor, type.name); if (isListType(parentType)) { return `${gen}.nullable()`; } const appliedDirectivesGen = applyDirectives(config, field, gen); if (isNonNullType(parentType)) { if (config.notAllowEmptyString === true) { - const tsType = tsVisitor.scalars[type.name.value]; + const tsType = visitor.getScalarType(type.name.value); if (tsType === 'string') return `${appliedDirectivesGen}.min(1)`; } return appliedDirectivesGen; @@ -205,35 +200,30 @@ const applyDirectives = ( return gen; }; -const generateNameNodeZodSchema = ( - config: ValidationSchemaPluginConfig, - tsVisitor: TsVisitor, - schema: GraphQLSchema, - node: NameNode -): string => { - const typ = schema.getType(node.value); +const generateNameNodeZodSchema = (config: ValidationSchemaPluginConfig, visitor: Visitor, node: NameNode): string => { + const converter = visitor.getNameNodeConverter(node); - if (typ?.astNode?.kind === 'InputObjectTypeDefinition') { - const enumName = tsVisitor.convertName(typ.astNode.name.value); - return `${enumName}Schema()`; + if (converter?.targetKind === 'InputObjectTypeDefinition') { + const name = converter.convertName(); + return `${name}Schema()`; } - if (typ?.astNode?.kind === 'ObjectTypeDefinition') { - const enumName = tsVisitor.convertName(typ.astNode.name.value); - return `${enumName}Schema()`; + if (converter?.targetKind === 'ObjectTypeDefinition') { + const name = converter.convertName(); + return `${name}Schema()`; } - if (typ?.astNode?.kind === 'EnumTypeDefinition') { - const enumName = tsVisitor.convertName(typ.astNode.name.value); - return `${enumName}Schema`; + if (converter?.targetKind === 'EnumTypeDefinition') { + const name = converter.convertName(); + return `${name}Schema`; } - if (typ?.astNode?.kind === 'UnionTypeDefinition') { - const enumName = tsVisitor.convertName(typ.astNode.name.value); - return `${enumName}Schema()`; + if (converter?.targetKind === 'UnionTypeDefinition') { + const name = converter.convertName(); + return `${name}Schema()`; } - return zod4Scalar(config, tsVisitor, node.value); + return zod4Scalar(config, visitor, node.value); }; const maybeLazy = (type: TypeNode, schema: string): string => { @@ -243,11 +233,11 @@ const maybeLazy = (type: TypeNode, schema: string): string => { return schema; }; -const zod4Scalar = (config: ValidationSchemaPluginConfig, tsVisitor: TsVisitor, scalarName: string): string => { +const zod4Scalar = (config: ValidationSchemaPluginConfig, visitor: Visitor, scalarName: string): string => { if (config.scalarSchemas?.[scalarName]) { return config.scalarSchemas[scalarName]; } - const tsType = tsVisitor.scalars[scalarName]; + const tsType = visitor.getScalarType(scalarName); switch (tsType) { case 'string': return `z.string()`; From 657f8be9693ff0bd350a4c0303c2ecbccc8186e3 Mon Sep 17 00:00:00 2001 From: Kei Kamikawa Date: Sat, 27 May 2023 14:10:18 +0900 Subject: [PATCH 2/5] fixed plugin config in some tests --- tests/myzod.spec.ts | 268 +++++++++++++++++++++++-------------------- tests/yup.spec.ts | 268 +++++++++++++++++++++++-------------------- tests/zod.spec.ts | 269 ++++++++++++++++++++++++-------------------- 3 files changed, 445 insertions(+), 360 deletions(-) diff --git a/tests/myzod.spec.ts b/tests/myzod.spec.ts index ccde0257..b8e6727d 100644 --- a/tests/myzod.spec.ts +++ b/tests/myzod.spec.ts @@ -5,146 +5,171 @@ describe('myzod', () => { test.each([ [ 'non-null and defined', - /* GraphQL */ ` - input PrimitiveInput { - a: ID! - b: String! - c: Boolean! - d: Int! - e: Float! - } - `, - [ - 'export function PrimitiveInputSchema(): myzod.Type {', - 'a: myzod.string()', - 'b: myzod.string()', - 'c: myzod.boolean()', - 'd: myzod.number()', - 'e: myzod.number()', - ], + { + textSchema: /* GraphQL */ ` + input PrimitiveInput { + a: ID! + b: String! + c: Boolean! + d: Int! + e: Float! + } + `, + wantContains: [ + 'export function PrimitiveInputSchema(): myzod.Type {', + 'a: myzod.string()', + 'b: myzod.string()', + 'c: myzod.boolean()', + 'd: myzod.number()', + 'e: myzod.number()', + ], + scalars: { + ID: 'string', + }, + }, ], [ 'nullish', - /* GraphQL */ ` - input PrimitiveInput { - a: ID - b: String - c: Boolean - d: Int - e: Float - z: String! # no defined check - } - `, - [ - 'export function PrimitiveInputSchema(): myzod.Type {', - // alphabet order - 'a: myzod.string().optional().nullable(),', - 'b: myzod.string().optional().nullable(),', - 'c: myzod.boolean().optional().nullable(),', - 'd: myzod.number().optional().nullable(),', - 'e: myzod.number().optional().nullable(),', - ], + { + textSchema: /* GraphQL */ ` + input PrimitiveInput { + a: ID + b: String + c: Boolean + d: Int + e: Float + z: String! # no defined check + } + `, + wantContains: [ + 'export function PrimitiveInputSchema(): myzod.Type {', + // alphabet order + 'a: myzod.string().optional().nullable(),', + 'b: myzod.string().optional().nullable(),', + 'c: myzod.boolean().optional().nullable(),', + 'd: myzod.number().optional().nullable(),', + 'e: myzod.number().optional().nullable(),', + ], + scalars: { + ID: 'string', + }, + }, ], [ 'array', - /* GraphQL */ ` - input ArrayInput { - a: [String] - b: [String!] - c: [String!]! - d: [[String]] - e: [[String]!] - f: [[String]!]! - } - `, - [ - 'export function ArrayInputSchema(): myzod.Type {', - 'a: myzod.array(myzod.string().nullable()).optional().nullable(),', - 'b: myzod.array(myzod.string()).optional().nullable(),', - 'c: myzod.array(myzod.string()),', - 'd: myzod.array(myzod.array(myzod.string().nullable()).optional().nullable()).optional().nullable(),', - 'e: myzod.array(myzod.array(myzod.string().nullable())).optional().nullable(),', - 'f: myzod.array(myzod.array(myzod.string().nullable()))', - ], + { + textSchema: /* GraphQL */ ` + input ArrayInput { + a: [String] + b: [String!] + c: [String!]! + d: [[String]] + e: [[String]!] + f: [[String]!]! + } + `, + wantContains: [ + 'export function ArrayInputSchema(): myzod.Type {', + 'a: myzod.array(myzod.string().nullable()).optional().nullable(),', + 'b: myzod.array(myzod.string()).optional().nullable(),', + 'c: myzod.array(myzod.string()),', + 'd: myzod.array(myzod.array(myzod.string().nullable()).optional().nullable()).optional().nullable(),', + 'e: myzod.array(myzod.array(myzod.string().nullable())).optional().nullable(),', + 'f: myzod.array(myzod.array(myzod.string().nullable()))', + ], + scalars: undefined, + }, ], [ 'ref input object', - /* GraphQL */ ` - input AInput { - b: BInput! - } - input BInput { - c: CInput! - } - input CInput { - a: AInput! - } - `, - [ - 'export function AInputSchema(): myzod.Type {', - 'b: myzod.lazy(() => BInputSchema())', - 'export function BInputSchema(): myzod.Type {', - 'c: myzod.lazy(() => CInputSchema())', - 'export function CInputSchema(): myzod.Type {', - 'a: myzod.lazy(() => AInputSchema())', - ], + { + textSchema: /* GraphQL */ ` + input AInput { + b: BInput! + } + input BInput { + c: CInput! + } + input CInput { + a: AInput! + } + `, + wantContains: [ + 'export function AInputSchema(): myzod.Type {', + 'b: myzod.lazy(() => BInputSchema())', + 'export function BInputSchema(): myzod.Type {', + 'c: myzod.lazy(() => CInputSchema())', + 'export function CInputSchema(): myzod.Type {', + 'a: myzod.lazy(() => AInputSchema())', + ], + scalars: undefined, + }, ], [ 'nested input object', - /* GraphQL */ ` - input NestedInput { - child: NestedInput - childrens: [NestedInput] - } - `, - [ - 'export function NestedInputSchema(): myzod.Type {', - 'child: myzod.lazy(() => NestedInputSchema().optional().nullable()),', - 'childrens: myzod.array(myzod.lazy(() => NestedInputSchema().nullable())).optional().nullable()', - ], + { + textSchema: /* GraphQL */ ` + input NestedInput { + child: NestedInput + childrens: [NestedInput] + } + `, + wantContains: [ + 'export function NestedInputSchema(): myzod.Type {', + 'child: myzod.lazy(() => NestedInputSchema().optional().nullable()),', + 'childrens: myzod.array(myzod.lazy(() => NestedInputSchema().nullable())).optional().nullable()', + ], + scalars: undefined, + }, ], [ 'enum', - /* GraphQL */ ` - enum PageType { - PUBLIC - BASIC_AUTH - } - input PageInput { - pageType: PageType! - } - `, - [ - 'export const PageTypeSchema = myzod.enum(PageType)', - 'export function PageInputSchema(): myzod.Type {', - 'pageType: PageTypeSchema', - ], + { + textSchema: /* GraphQL */ ` + enum PageType { + PUBLIC + BASIC_AUTH + } + input PageInput { + pageType: PageType! + } + `, + wantContains: [ + 'export const PageTypeSchema = myzod.enum(PageType)', + 'export function PageInputSchema(): myzod.Type {', + 'pageType: PageTypeSchema', + ], + scalars: undefined, + }, ], [ 'camelcase', - /* GraphQL */ ` - input HTTPInput { - method: HTTPMethod - url: URL! - } - - enum HTTPMethod { - GET - POST - } - - scalar URL # unknown scalar, should be any (definedNonNullAnySchema) - `, - [ - 'export function HttpInputSchema(): myzod.Type {', - 'export const HttpMethodSchema = myzod.enum(HttpMethod)', - 'method: HttpMethodSchema', - 'url: definedNonNullAnySchema', - ], + { + textSchema: /* GraphQL */ ` + input HTTPInput { + method: HTTPMethod + url: URL! + } + + enum HTTPMethod { + GET + POST + } + + scalar URL # unknown scalar, should be any (definedNonNullAnySchema) + `, + wantContains: [ + 'export function HttpInputSchema(): myzod.Type {', + 'export const HttpMethodSchema = myzod.enum(HttpMethod)', + 'method: HttpMethodSchema', + 'url: definedNonNullAnySchema', + ], + scalars: undefined, + }, ], - ])('%s', async (_, textSchema, wantContains) => { + ])('%s', async (_, { textSchema, wantContains, scalars }) => { const schema = buildSchema(textSchema); - const result = await plugin(schema, [], { schema: 'myzod' }, {}); + const result = await plugin(schema, [], { schema: 'myzod', scalars }, {}); expect(result.prepend).toContain("import * as myzod from 'myzod'"); for (const wantContain of wantContains) { @@ -232,6 +257,9 @@ describe('myzod', () => { { schema: 'myzod', notAllowEmptyString: true, + scalars: { + ID: 'string', + }, }, {} ); diff --git a/tests/yup.spec.ts b/tests/yup.spec.ts index 11f9c47d..4d7fdb8e 100644 --- a/tests/yup.spec.ts +++ b/tests/yup.spec.ts @@ -5,146 +5,171 @@ describe('yup', () => { test.each([ [ 'defined', - /* GraphQL */ ` - input PrimitiveInput { - a: ID! - b: String! - c: Boolean! - d: Int! - e: Float! - } - `, - [ - 'export function PrimitiveInputSchema(): yup.ObjectSchema', - 'a: yup.string().defined()', - 'b: yup.string().defined()', - 'c: yup.boolean().defined()', - 'd: yup.number().defined()', - 'e: yup.number().defined()', - ], + { + textSchema: /* GraphQL */ ` + input PrimitiveInput { + a: ID! + b: String! + c: Boolean! + d: Int! + e: Float! + } + `, + wantContains: [ + 'export function PrimitiveInputSchema(): yup.ObjectSchema', + 'a: yup.string().defined()', + 'b: yup.string().defined()', + 'c: yup.boolean().defined()', + 'd: yup.number().defined()', + 'e: yup.number().defined()', + ], + scalars: { + ID: 'string', + }, + }, ], [ 'optional', - /* GraphQL */ ` - input PrimitiveInput { - a: ID - b: String - c: Boolean - d: Int - e: Float - z: String! # no defined check - } - `, - [ - 'export function PrimitiveInputSchema(): yup.ObjectSchema', - // alphabet order - 'a: yup.string().defined().nullable().optional(),', - 'b: yup.string().defined().nullable().optional(),', - 'c: yup.boolean().defined().nullable().optional(),', - 'd: yup.number().defined().nullable().optional(),', - 'e: yup.number().defined().nullable().optional(),', - ], + { + textSchema: /* GraphQL */ ` + input PrimitiveInput { + a: ID + b: String + c: Boolean + d: Int + e: Float + z: String! # no defined check + } + `, + wantContains: [ + 'export function PrimitiveInputSchema(): yup.ObjectSchema', + // alphabet order + 'a: yup.string().defined().nullable().optional(),', + 'b: yup.string().defined().nullable().optional(),', + 'c: yup.boolean().defined().nullable().optional(),', + 'd: yup.number().defined().nullable().optional(),', + 'e: yup.number().defined().nullable().optional(),', + ], + scalars: { + ID: 'string', + }, + }, ], [ 'array', - /* GraphQL */ ` - input ArrayInput { - a: [String] - b: [String!] - c: [String!]! - d: [[String]] - e: [[String]!] - f: [[String]!]! - } - `, - [ - 'export function ArrayInputSchema(): yup.ObjectSchema', - 'a: yup.array(yup.string().defined().nullable()).defined().nullable().optional(),', - 'b: yup.array(yup.string().defined().nonNullable()).defined().nullable().optional(),', - 'c: yup.array(yup.string().defined().nonNullable()).defined(),', - 'd: yup.array(yup.array(yup.string().defined().nullable()).defined().nullable()).defined().nullable().optional(),', - 'e: yup.array(yup.array(yup.string().defined().nullable()).defined()).defined().nullable().optional(),', - 'f: yup.array(yup.array(yup.string().defined().nullable()).defined()).defined()', - ], + { + textSchema: /* GraphQL */ ` + input ArrayInput { + a: [String] + b: [String!] + c: [String!]! + d: [[String]] + e: [[String]!] + f: [[String]!]! + } + `, + wantContains: [ + 'export function ArrayInputSchema(): yup.ObjectSchema', + 'a: yup.array(yup.string().defined().nullable()).defined().nullable().optional(),', + 'b: yup.array(yup.string().defined().nonNullable()).defined().nullable().optional(),', + 'c: yup.array(yup.string().defined().nonNullable()).defined(),', + 'd: yup.array(yup.array(yup.string().defined().nullable()).defined().nullable()).defined().nullable().optional(),', + 'e: yup.array(yup.array(yup.string().defined().nullable()).defined()).defined().nullable().optional(),', + 'f: yup.array(yup.array(yup.string().defined().nullable()).defined()).defined()', + ], + scalars: undefined, + }, ], [ 'ref input object', - /* GraphQL */ ` - input AInput { - b: BInput! - } - input BInput { - c: CInput! - } - input CInput { - a: AInput! - } - `, - [ - 'export function AInputSchema(): yup.ObjectSchema', - 'b: yup.lazy(() => BInputSchema().nonNullable())', - 'export function BInputSchema(): yup.ObjectSchema', - 'c: yup.lazy(() => CInputSchema().nonNullable())', - 'export function CInputSchema(): yup.ObjectSchema', - 'a: yup.lazy(() => AInputSchema().nonNullable())', - ], + { + textSchema: /* GraphQL */ ` + input AInput { + b: BInput! + } + input BInput { + c: CInput! + } + input CInput { + a: AInput! + } + `, + wantContains: [ + 'export function AInputSchema(): yup.ObjectSchema', + 'b: yup.lazy(() => BInputSchema().nonNullable())', + 'export function BInputSchema(): yup.ObjectSchema', + 'c: yup.lazy(() => CInputSchema().nonNullable())', + 'export function CInputSchema(): yup.ObjectSchema', + 'a: yup.lazy(() => AInputSchema().nonNullable())', + ], + scalars: undefined, + }, ], [ 'nested input object', - /* GraphQL */ ` - input NestedInput { - child: NestedInput - childrens: [NestedInput] - } - `, - [ - 'export function NestedInputSchema(): yup.ObjectSchema', - 'child: yup.lazy(() => NestedInputSchema()).optional(),', - 'childrens: yup.array(yup.lazy(() => NestedInputSchema())).defined().nullable().optional()', - ], + { + textSchema: /* GraphQL */ ` + input NestedInput { + child: NestedInput + childrens: [NestedInput] + } + `, + wantContains: [ + 'export function NestedInputSchema(): yup.ObjectSchema', + 'child: yup.lazy(() => NestedInputSchema()).optional(),', + 'childrens: yup.array(yup.lazy(() => NestedInputSchema())).defined().nullable().optional()', + ], + scalars: undefined, + }, ], [ 'enum', - /* GraphQL */ ` - enum PageType { - PUBLIC - BASIC_AUTH - } - input PageInput { - pageType: PageType! - } - `, - [ - 'export const PageTypeSchema = yup.string().oneOf([PageType.Public, PageType.BasicAuth]).defined();', - 'export function PageInputSchema(): yup.ObjectSchema', - 'pageType: PageTypeSchema.nonNullable()', - ], + { + textSchema: /* GraphQL */ ` + enum PageType { + PUBLIC + BASIC_AUTH + } + input PageInput { + pageType: PageType! + } + `, + wantContains: [ + 'export const PageTypeSchema = yup.string().oneOf([PageType.Public, PageType.BasicAuth]).defined();', + 'export function PageInputSchema(): yup.ObjectSchema', + 'pageType: PageTypeSchema.nonNullable()', + ], + scalars: undefined, + }, ], [ 'camelcase', - /* GraphQL */ ` - input HTTPInput { - method: HTTPMethod - url: URL! - } - - enum HTTPMethod { - GET - POST - } - - scalar URL # unknown scalar, should be any (yup.mixed()) - `, - [ - 'export function HttpInputSchema(): yup.ObjectSchema', - 'export const HttpMethodSchema = yup.string().oneOf([HttpMethod.Get, HttpMethod.Post]).defined();', - 'method: HttpMethodSchema.nullable().optional(),', - 'url: yup.mixed().nonNullable()', - ], + { + textSchema: /* GraphQL */ ` + input HTTPInput { + method: HTTPMethod + url: URL! + } + + enum HTTPMethod { + GET + POST + } + + scalar URL # unknown scalar, should be any (yup.mixed()) + `, + wantContains: [ + 'export function HttpInputSchema(): yup.ObjectSchema', + 'export const HttpMethodSchema = yup.string().oneOf([HttpMethod.Get, HttpMethod.Post]).defined();', + 'method: HttpMethodSchema.nullable().optional(),', + 'url: yup.mixed().nonNullable()', + ], + scalars: undefined, + }, ], - ])('%s', async (_, textSchema, wantContains) => { + ])('%s', async (_, { textSchema, wantContains, scalars }) => { const schema = buildSchema(textSchema); - const result = await plugin(schema, [], {}, {}); + const result = await plugin(schema, [], { scalars }, {}); expect(result.prepend).toContain("import * as yup from 'yup'"); for (const wantContain of wantContains) { @@ -230,6 +255,9 @@ describe('yup', () => { [], { notAllowEmptyString: true, + scalars: { + ID: 'string', + }, }, {} ); diff --git a/tests/zod.spec.ts b/tests/zod.spec.ts index 07c749f4..cb78b6a1 100644 --- a/tests/zod.spec.ts +++ b/tests/zod.spec.ts @@ -1,150 +1,176 @@ import { buildSchema } from 'graphql'; import { plugin } from '../src/index'; +import { ScalarsMap } from '@graphql-codegen/visitor-plugin-common'; describe('zod', () => { test.each([ [ 'non-null and defined', - /* GraphQL */ ` - input PrimitiveInput { - a: ID! - b: String! - c: Boolean! - d: Int! - e: Float! - } - `, - [ - 'export function PrimitiveInputSchema(): z.ZodObject>', - 'a: z.string()', - 'b: z.string()', - 'c: z.boolean()', - 'd: z.number()', - 'e: z.number()', - ], + { + textSchema: /* GraphQL */ ` + input PrimitiveInput { + a: ID! + b: String! + c: Boolean! + d: Int! + e: Float! + } + `, + wantContains: [ + 'export function PrimitiveInputSchema(): z.ZodObject>', + 'a: z.string()', + 'b: z.string()', + 'c: z.boolean()', + 'd: z.number()', + 'e: z.number()', + ], + scalars: { + ID: 'string', + }, + }, ], [ 'nullish', - /* GraphQL */ ` - input PrimitiveInput { - a: ID - b: String - c: Boolean - d: Int - e: Float - z: String! # no defined check - } - `, - [ - 'export function PrimitiveInputSchema(): z.ZodObject>', - // alphabet order - 'a: z.string().nullish(),', - 'b: z.string().nullish(),', - 'c: z.boolean().nullish(),', - 'd: z.number().nullish(),', - 'e: z.number().nullish(),', - ], + { + textSchema: /* GraphQL */ ` + input PrimitiveInput { + a: ID + b: String + c: Boolean + d: Int + e: Float + z: String! # no defined check + } + `, + wantContains: [ + 'export function PrimitiveInputSchema(): z.ZodObject>', + // alphabet order + 'a: z.string().nullish(),', + 'b: z.string().nullish(),', + 'c: z.boolean().nullish(),', + 'd: z.number().nullish(),', + 'e: z.number().nullish(),', + ], + scalars: { + ID: 'string', + }, + }, ], [ 'array', - /* GraphQL */ ` - input ArrayInput { - a: [String] - b: [String!] - c: [String!]! - d: [[String]] - e: [[String]!] - f: [[String]!]! - } - `, - [ - 'export function ArrayInputSchema(): z.ZodObject>', - 'a: z.array(z.string().nullable()).nullish(),', - 'b: z.array(z.string()).nullish(),', - 'c: z.array(z.string()),', - 'd: z.array(z.array(z.string().nullable()).nullish()).nullish(),', - 'e: z.array(z.array(z.string().nullable())).nullish(),', - 'f: z.array(z.array(z.string().nullable()))', - ], + { + textSchema: /* GraphQL */ ` + input ArrayInput { + a: [String] + b: [String!] + c: [String!]! + d: [[String]] + e: [[String]!] + f: [[String]!]! + } + `, + wantContains: [ + 'export function ArrayInputSchema(): z.ZodObject>', + 'a: z.array(z.string().nullable()).nullish(),', + 'b: z.array(z.string()).nullish(),', + 'c: z.array(z.string()),', + 'd: z.array(z.array(z.string().nullable()).nullish()).nullish(),', + 'e: z.array(z.array(z.string().nullable())).nullish(),', + 'f: z.array(z.array(z.string().nullable()))', + ], + scalars: undefined, + }, ], [ 'ref input object', - /* GraphQL */ ` - input AInput { - b: BInput! - } - input BInput { - c: CInput! - } - input CInput { - a: AInput! - } - `, - [ - 'export function AInputSchema(): z.ZodObject>', - 'b: z.lazy(() => BInputSchema())', - 'export function BInputSchema(): z.ZodObject>', - 'c: z.lazy(() => CInputSchema())', - 'export function CInputSchema(): z.ZodObject>', - 'a: z.lazy(() => AInputSchema())', - ], + { + textSchema: /* GraphQL */ ` + input AInput { + b: BInput! + } + input BInput { + c: CInput! + } + input CInput { + a: AInput! + } + `, + wantContains: [ + 'export function AInputSchema(): z.ZodObject>', + 'b: z.lazy(() => BInputSchema())', + 'export function BInputSchema(): z.ZodObject>', + 'c: z.lazy(() => CInputSchema())', + 'export function CInputSchema(): z.ZodObject>', + 'a: z.lazy(() => AInputSchema())', + ], + scalars: undefined, + }, ], [ 'nested input object', - /* GraphQL */ ` - input NestedInput { - child: NestedInput - childrens: [NestedInput] - } - `, - [ - 'export function NestedInputSchema(): z.ZodObject>', - 'child: z.lazy(() => NestedInputSchema().nullish()),', - 'childrens: z.array(z.lazy(() => NestedInputSchema().nullable())).nullish()', - ], + { + textSchema: /* GraphQL */ ` + input NestedInput { + child: NestedInput + childrens: [NestedInput] + } + `, + wantContains: [ + 'export function NestedInputSchema(): z.ZodObject>', + 'child: z.lazy(() => NestedInputSchema().nullish()),', + 'childrens: z.array(z.lazy(() => NestedInputSchema().nullable())).nullish()', + ], + scalars: undefined, + }, ], [ 'enum', - /* GraphQL */ ` - enum PageType { - PUBLIC - BASIC_AUTH - } - input PageInput { - pageType: PageType! - } - `, - [ - 'export const PageTypeSchema = z.nativeEnum(PageType)', - 'export function PageInputSchema(): z.ZodObject>', - 'pageType: PageTypeSchema', - ], + { + textSchema: /* GraphQL */ ` + enum PageType { + PUBLIC + BASIC_AUTH + } + input PageInput { + pageType: PageType! + } + `, + wantContains: [ + 'export const PageTypeSchema = z.nativeEnum(PageType)', + 'export function PageInputSchema(): z.ZodObject>', + 'pageType: PageTypeSchema', + ], + scalars: undefined, + }, ], [ 'camelcase', - /* GraphQL */ ` - input HTTPInput { - method: HTTPMethod - url: URL! - } - - enum HTTPMethod { - GET - POST - } - - scalar URL # unknown scalar, should be any (definedNonNullAnySchema) - `, - [ - 'export function HttpInputSchema(): z.ZodObject>', - 'export const HttpMethodSchema = z.nativeEnum(HttpMethod)', - 'method: HttpMethodSchema', - 'url: definedNonNullAnySchema', - ], + { + textSchema: /* GraphQL */ ` + input HTTPInput { + method: HTTPMethod + url: URL! + } + + enum HTTPMethod { + GET + POST + } + + scalar URL # unknown scalar, should be any (definedNonNullAnySchema) + `, + wantContains: [ + 'export function HttpInputSchema(): z.ZodObject>', + 'export const HttpMethodSchema = z.nativeEnum(HttpMethod)', + 'method: HttpMethodSchema', + 'url: definedNonNullAnySchema', + ], + scalars: undefined, + }, ], - ])('%s', async (_, textSchema, wantContains) => { + ])('%s', async (_, { textSchema, wantContains, scalars }) => { const schema = buildSchema(textSchema); - const result = await plugin(schema, [], { schema: 'zod' }, {}); + const result = await plugin(schema, [], { schema: 'zod', scalars }, {}); expect(result.prepend).toContain("import { z } from 'zod'"); for (const wantContain of wantContains) { @@ -232,6 +258,9 @@ describe('zod', () => { { schema: 'zod', notAllowEmptyString: true, + scalars: { + ID: 'string', + }, }, {} ); From 292bc7703091effbab82c92d880798902f00b022 Mon Sep 17 00:00:00 2001 From: Kei Kamikawa Date: Sat, 27 May 2023 14:28:58 +0900 Subject: [PATCH 3/5] added test case for dotansimha/graphql-code-generator#9375 --- tests/myzod.spec.ts | 14 ++++++++++++++ tests/yup.spec.ts | 14 ++++++++++++++ tests/zod.spec.ts | 47 ++++++++++++++------------------------------- 3 files changed, 42 insertions(+), 33 deletions(-) diff --git a/tests/myzod.spec.ts b/tests/myzod.spec.ts index b8e6727d..d98912a6 100644 --- a/tests/myzod.spec.ts +++ b/tests/myzod.spec.ts @@ -508,6 +508,10 @@ describe('myzod', () => { date: Date! email: Email! } + input UsernameUpdateInput { + updateInputId: ID! + updateName: String! + } type User { id: ID! name: String @@ -539,6 +543,12 @@ describe('myzod', () => { Date: 'myzod.date()', Email: 'myzod.string().email()', }, + scalars: { + ID: { + input: 'number', + output: 'string', + }, + }, }, {} ); @@ -548,6 +558,10 @@ describe('myzod', () => { 'name: myzod.string(),', 'date: myzod.date(),', 'email: myzod.string().email()', + // Username Update Input + 'export function UsernameUpdateInputSchema(): myzod.Type {', + 'updateInputId: myzod.number(),', + 'updateName: myzod.string()', // User 'export function UserSchema(): myzod.Type {', "__typename: myzod.literal('User').optional(),", diff --git a/tests/yup.spec.ts b/tests/yup.spec.ts index 4d7fdb8e..6fa3f85f 100644 --- a/tests/yup.spec.ts +++ b/tests/yup.spec.ts @@ -422,6 +422,10 @@ describe('yup', () => { date: Date! email: Email! } + input UsernameUpdateInput { + updateInputId: ID! + updateName: String! + } type User { id: ID! name: String @@ -454,6 +458,12 @@ describe('yup', () => { Date: 'yup.date()', Email: 'yup.string().email()', }, + scalars: { + ID: { + input: 'number', + output: 'string', + }, + }, }, {} ); @@ -463,6 +473,10 @@ describe('yup', () => { 'name: yup.string().defined().nonNullable(),', 'date: yup.date().defined().nonNullable(),', 'email: yup.string().email().defined().nonNullable()', + // Username Update Input + 'export function UsernameUpdateInputSchema(): yup.ObjectSchema {', + 'updateInputId: yup.number().defined().nonNullable(),', + 'updateName: yup.string().defined().nonNullable()', // User 'export function UserSchema(): yup.ObjectSchema {', "__typename: yup.string<'User'>().optional(),", diff --git a/tests/zod.spec.ts b/tests/zod.spec.ts index cb78b6a1..b84f5fde 100644 --- a/tests/zod.spec.ts +++ b/tests/zod.spec.ts @@ -511,39 +511,6 @@ describe('zod', () => { }); describe('with withObjectType', () => { - 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! - } - - type Mutation { - _empty: String - } - - type Query { - _empty: String - } - - type Subscription { - _empty: String - } - `); - it('not generate if withObjectType false', async () => { const schema = buildSchema(/* GraphQL */ ` type User { @@ -612,6 +579,10 @@ describe('zod', () => { date: Date! email: Email! } + input UsernameUpdateInput { + updateInputId: ID! + updateName: String! + } type User { id: ID! name: String @@ -643,6 +614,12 @@ describe('zod', () => { Date: 'z.date()', Email: 'z.string().email()', }, + scalars: { + ID: { + input: 'number', + output: 'string', + }, + }, }, {} ); @@ -652,6 +629,10 @@ describe('zod', () => { 'name: z.string(),', 'date: z.date(),', 'email: z.string().email()', + // Username Update Input + 'export function UsernameUpdateInputSchema(): z.ZodObject> {', + 'updateInputId: z.number(),', + 'updateName: z.string()', // User 'export function UserSchema(): z.ZodObject> {', "__typename: z.literal('User').optional()", From f895551748ea891c539d71cb9d83fda9e033d8fa Mon Sep 17 00:00:00 2001 From: Kei Kamikawa Date: Sat, 27 May 2023 14:33:02 +0900 Subject: [PATCH 4/5] updated document and example --- README.md | 4 ++++ codegen.yml | 9 ++++++++ example/types.ts | 60 +++++++++++++++++++++++++----------------------- 3 files changed, 44 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 5794a7b0..1f2b88cf 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,10 @@ generates: # You can put the config for typescript plugin here # see: https://www.graphql-code-generator.com/plugins/typescript strictScalars: true + # Overrides built-in ID scalar to both input and output types as string. + # see: https://the-guild.dev/graphql/codegen/plugins/typescript/typescript#scalars + scalars: + ID: string # You can also write the config for this plugin together schema: yup # or zod ``` diff --git a/codegen.yml b/codegen.yml index f56c13f1..b35f9fd3 100644 --- a/codegen.yml +++ b/codegen.yml @@ -4,6 +4,9 @@ generates: example/types.ts: plugins: - typescript + config: + scalars: + ID: string example/yup/schemas.ts: plugins: - ./dist/main/index.js: @@ -38,6 +41,8 @@ generates: max: ['max', '$1 + 1'] exclusiveMin: min exclusiveMax: max + scalars: + ID: string example/zod/schemas.ts: plugins: - ./dist/main/index.js: @@ -59,6 +64,8 @@ generates: startsWith: ['regex', '/^$1/', 'message'] format: email: email + scalars: + ID: string example/myzod/schemas.ts: plugins: - ./dist/main/index.js: @@ -72,3 +79,5 @@ generates: startsWith: ['pattern', '/^$1/'] format: email: email + scalars: + ID: string diff --git a/example/types.ts b/example/types.ts index 20898898..f8f3f610 100644 --- a/example/types.ts +++ b/example/types.ts @@ -3,25 +3,27 @@ export type InputMaybe = Maybe; export type Exact = { [K in keyof T]: T[K] }; export type MakeOptional = Omit & { [SubKey in K]?: Maybe }; export type MakeMaybe = Omit & { [SubKey in K]: Maybe }; +export type MakeEmpty = { [_ in K]?: never }; +export type Incremental = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never }; /** All built-in and custom scalars, mapped to their actual values */ export type Scalars = { - ID: string; - String: string; - Boolean: boolean; - Int: number; - Float: number; - Date: any; - URL: any; + ID: { input: string; output: string; } + String: { input: string; output: string; } + Boolean: { input: boolean; output: boolean; } + Int: { input: number; output: number; } + Float: { input: number; output: number; } + Date: { input: any; output: any; } + URL: { input: any; output: any; } }; export type Admin = { __typename?: 'Admin'; - lastModifiedAt?: Maybe; + lastModifiedAt?: Maybe; }; export type AttributeInput = { - key?: InputMaybe; - val?: InputMaybe; + key?: InputMaybe; + val?: InputMaybe; }; export enum ButtonComponentType { @@ -33,7 +35,7 @@ export type ComponentInput = { child?: InputMaybe; childrens?: InputMaybe>>; event?: InputMaybe; - name: Scalars['String']; + name: Scalars['String']['input']; type: ButtonComponentType; }; @@ -43,8 +45,8 @@ export type DropDownComponentInput = { }; export type EventArgumentInput = { - name: Scalars['String']; - value: Scalars['String']; + name: Scalars['String']['input']; + value: Scalars['String']['input']; }; export type EventInput = { @@ -59,12 +61,12 @@ export enum EventOptionType { export type Guest = { __typename?: 'Guest'; - lastLoggedIn?: Maybe; + lastLoggedIn?: Maybe; }; export type HttpInput = { method?: InputMaybe; - url: Scalars['URL']; + url: Scalars['URL']['input']; }; export enum HttpMethod { @@ -78,16 +80,16 @@ export type LayoutInput = { export type PageInput = { attributes?: InputMaybe>; - date?: InputMaybe; - height: Scalars['Float']; - id: Scalars['ID']; + date?: InputMaybe; + height: Scalars['Float']['input']; + id: Scalars['ID']['input']; layout: LayoutInput; pageType: PageType; - postIDs?: InputMaybe>; - show: Scalars['Boolean']; - tags?: InputMaybe>>; - title: Scalars['String']; - width: Scalars['Int']; + postIDs?: InputMaybe>; + show: Scalars['Boolean']['input']; + tags?: InputMaybe>>; + title: Scalars['String']['input']; + width: Scalars['Int']['input']; }; export enum PageType { @@ -99,13 +101,13 @@ export enum PageType { export type User = { __typename?: 'User'; - createdAt?: Maybe; - email?: Maybe; - id?: Maybe; + createdAt?: Maybe; + email?: Maybe; + id?: Maybe; kind?: Maybe; - name?: Maybe; - password?: Maybe; - updatedAt?: Maybe; + name?: Maybe; + password?: Maybe; + updatedAt?: Maybe; }; export type UserKind = Admin | Guest; From 068482812c2217cf8df06b3e1cd2587476dbbea5 Mon Sep 17 00:00:00 2001 From: Kei Kamikawa Date: Sat, 27 May 2023 14:43:26 +0900 Subject: [PATCH 5/5] fixed README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 1f2b88cf..c37f4889 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,8 @@ generates: schema: yup # or zod ``` +It is recommended to write `scalars` config for built-in type `ID`, as in the yaml example shown above. For more information: [#375](https://github.com/Code-Hex/graphql-codegen-typescript-validation-schema/pull/375) + You can check [example](https://github.com/Code-Hex/graphql-codegen-typescript-validation-schema/tree/main/example) directory if you want to see more complex config example or how is generated some files. The Q&A for each schema is written in the README in the respective example directory.