diff --git a/README.md b/README.md index 94f52e6e..e7a2dc4d 100644 --- a/README.md +++ b/README.md @@ -215,8 +215,6 @@ type: `NamingConventionMap` default: `{ enumValues: "change-case-all#pascalCase" Uses the full path of the enum type as the default value instead of the stringified value. -Note: This option has not been tested with `namingConvention.transformUnderscore` and `namingConvention.typeNames` options and may not work as expected. - Related: https://the-guild.dev/graphql/codegen/docs/config-reference/naming-convention#namingconvention ### `directives` diff --git a/src/directive.ts b/src/directive.ts index d7807149..346620eb 100644 --- a/src/directive.ts +++ b/src/directive.ts @@ -1,7 +1,7 @@ import type { ConstArgumentNode, ConstDirectiveNode, ConstValueNode } from 'graphql'; +import { Kind, valueFromASTUntyped } from 'graphql'; import type { DirectiveConfig, DirectiveObjectArguments } from './config.js'; -import { Kind, valueFromASTUntyped } from 'graphql'; import { isConvertableRegexp } from './regexp.js'; export interface FormattedDirectiveConfig { diff --git a/src/graphql.ts b/src/graphql.ts index d5f6eeca..0738d16e 100644 --- a/src/graphql.ts +++ b/src/graphql.ts @@ -5,8 +5,8 @@ import type { GraphQLSchema, InterfaceTypeDefinitionNode, ListTypeNode, - NamedTypeNode, NameNode, + NamedTypeNode, NonNullTypeNode, ObjectTypeDefinitionNode, TypeNode, diff --git a/src/index.ts b/src/index.ts index f8ccfd61..c35f789f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,10 +1,10 @@ import type { PluginFunction, Types } from '@graphql-codegen/plugin-helpers'; import type { GraphQLSchema } from 'graphql'; +import { transformSchemaAST } from '@graphql-codegen/schema-ast'; +import { buildSchema, printSchema, visit } from 'graphql'; import type { ValidationSchemaPluginConfig } from './config.js'; import type { SchemaVisitor } from './types.js'; -import { transformSchemaAST } from '@graphql-codegen/schema-ast'; -import { buildSchema, printSchema, visit } from 'graphql'; import { isGeneratedByIntrospection, topologicalSortAST } from './graphql.js'; import { MyZodSchemaVisitor } from './myzod/index.js'; import { ValibotSchemaVisitor } from './valibot/index.js'; diff --git a/src/myzod/index.ts b/src/myzod/index.ts index 8804a153..f06a23c0 100644 --- a/src/myzod/index.ts +++ b/src/myzod/index.ts @@ -10,23 +10,23 @@ import type { TypeNode, UnionTypeDefinitionNode, } from 'graphql'; -import type { ValidationSchemaPluginConfig } from '../config.js'; -import type { Visitor } from '../visitor.js'; import { resolveExternalModuleAndFn } from '@graphql-codegen/plugin-helpers'; -import { convertNameParts, DeclarationBlock, indent } from '@graphql-codegen/visitor-plugin-common'; +import { DeclarationBlock, convertNameParts, indent } from '@graphql-codegen/visitor-plugin-common'; import { Kind, } from 'graphql'; +import type { Visitor } from '../visitor.js'; +import type { ValidationSchemaPluginConfig } from '../config.js'; import { buildApi, formatDirectiveConfig } from '../directive.js'; import { - escapeGraphQLCharacters, InterfaceTypeDefinitionBuilder, + ObjectTypeDefinitionBuilder, + escapeGraphQLCharacters, isInput, isListType, isNamedType, isNonNullType, - ObjectTypeDefinitionBuilder, } from '../graphql.js'; import { BaseSchemaVisitor } from '../schema_visitor.js'; @@ -290,10 +290,10 @@ function generateFieldTypeMyZodSchema(config: ValidationSchemaPluginConfig, visi if (defaultValue?.kind === Kind.STRING || defaultValue?.kind === Kind.ENUM) { if (config.useEnumTypeAsDefaultValue && defaultValue?.kind !== Kind.STRING) { - let value = convertNameParts(defaultValue.value, resolveExternalModuleAndFn('change-case-all#pascalCase')); + let value = convertNameParts(defaultValue.value, resolveExternalModuleAndFn('change-case-all#pascalCase'), config?.namingConvention?.transformUnderscore); if (config.namingConvention?.enumValues) - value = convertNameParts(defaultValue.value, resolveExternalModuleAndFn(config.namingConvention?.enumValues)); + value = convertNameParts(defaultValue.value, resolveExternalModuleAndFn(config.namingConvention?.enumValues), config?.namingConvention?.transformUnderscore); appliedDirectivesGen = `${appliedDirectivesGen}.default(${visitor.convertName(type.name.value)}.${value})`; } diff --git a/src/valibot/index.ts b/src/valibot/index.ts index 71c4d09b..c05c1316 100644 --- a/src/valibot/index.ts +++ b/src/valibot/index.ts @@ -10,18 +10,18 @@ import type { TypeNode, UnionTypeDefinitionNode, } from 'graphql'; +import { DeclarationBlock, indent } from '@graphql-codegen/visitor-plugin-common'; import type { ValidationSchemaPluginConfig } from '../config.js'; import type { Visitor } from '../visitor.js'; -import { DeclarationBlock, indent } from '@graphql-codegen/visitor-plugin-common'; import { buildApiForValibot, formatDirectiveConfig } from '../directive.js'; import { InterfaceTypeDefinitionBuilder, + ObjectTypeDefinitionBuilder, isInput, isListType, isNamedType, isNonNullType, - ObjectTypeDefinitionBuilder, } from '../graphql.js'; import { BaseSchemaVisitor } from '../schema_visitor.js'; diff --git a/src/visitor.ts b/src/visitor.ts index 7866d2a1..5666a3d2 100644 --- a/src/visitor.ts +++ b/src/visitor.ts @@ -5,12 +5,12 @@ import type { NameNode, ObjectTypeDefinitionNode, } from 'graphql'; -import type { ValidationSchemaPluginConfig } from './config.js'; import { TsVisitor } from '@graphql-codegen/typescript'; import { specifiedScalarTypes, } from 'graphql'; +import type { ValidationSchemaPluginConfig } from './config.js'; export class Visitor extends TsVisitor { constructor( diff --git a/src/yup/index.ts b/src/yup/index.ts index b1805d8a..56d71d17 100644 --- a/src/yup/index.ts +++ b/src/yup/index.ts @@ -10,23 +10,23 @@ import type { TypeNode, UnionTypeDefinitionNode, } from 'graphql'; -import type { ValidationSchemaPluginConfig } from '../config.js'; -import type { Visitor } from '../visitor.js'; import { resolveExternalModuleAndFn } from '@graphql-codegen/plugin-helpers'; -import { convertNameParts, DeclarationBlock, indent } from '@graphql-codegen/visitor-plugin-common'; +import { DeclarationBlock, convertNameParts, indent } from '@graphql-codegen/visitor-plugin-common'; import { Kind, } from 'graphql'; +import type { Visitor } from '../visitor.js'; +import type { ValidationSchemaPluginConfig } from '../config.js'; import { buildApi, formatDirectiveConfig } from '../directive.js'; import { - escapeGraphQLCharacters, InterfaceTypeDefinitionBuilder, + ObjectTypeDefinitionBuilder, + escapeGraphQLCharacters, isInput, isListType, isNamedType, isNonNullType, - ObjectTypeDefinitionBuilder, } from '../graphql.js'; import { BaseSchemaVisitor } from '../schema_visitor.js'; @@ -292,10 +292,10 @@ function shapeFields(fields: readonly (FieldDefinitionNode | InputValueDefinitio if (defaultValue?.kind === Kind.STRING || defaultValue?.kind === Kind.ENUM) { if (config.useEnumTypeAsDefaultValue && defaultValue?.kind !== Kind.STRING) { - let value = convertNameParts(defaultValue.value, resolveExternalModuleAndFn('change-case-all#pascalCase')); + let value = convertNameParts(defaultValue.value, resolveExternalModuleAndFn('change-case-all#pascalCase'), config?.namingConvention?.transformUnderscore); if (config.namingConvention?.enumValues) - value = convertNameParts(defaultValue.value, resolveExternalModuleAndFn(config.namingConvention?.enumValues)); + value = convertNameParts(defaultValue.value, resolveExternalModuleAndFn(config.namingConvention?.enumValues), config?.namingConvention?.transformUnderscore); fieldSchema = `${fieldSchema}.default(${visitor.convertName(field.name.value)}.${value})`; } diff --git a/src/zod/index.ts b/src/zod/index.ts index b1164fb0..4c9a77a5 100644 --- a/src/zod/index.ts +++ b/src/zod/index.ts @@ -10,23 +10,23 @@ import type { TypeNode, UnionTypeDefinitionNode, } from 'graphql'; -import type { ValidationSchemaPluginConfig } from '../config.js'; -import type { Visitor } from '../visitor.js'; import { resolveExternalModuleAndFn } from '@graphql-codegen/plugin-helpers'; -import { convertNameParts, DeclarationBlock, indent } from '@graphql-codegen/visitor-plugin-common'; +import { DeclarationBlock, convertNameParts, indent } from '@graphql-codegen/visitor-plugin-common'; import { Kind, } from 'graphql'; +import type { Visitor } from '../visitor.js'; +import type { ValidationSchemaPluginConfig } from '../config.js'; import { buildApi, formatDirectiveConfig } from '../directive.js'; import { - escapeGraphQLCharacters, InterfaceTypeDefinitionBuilder, + ObjectTypeDefinitionBuilder, + escapeGraphQLCharacters, isInput, isListType, isNamedType, isNonNullType, - ObjectTypeDefinitionBuilder, } from '../graphql.js'; import { BaseSchemaVisitor } from '../schema_visitor.js'; @@ -306,10 +306,10 @@ function generateFieldTypeZodSchema(config: ValidationSchemaPluginConfig, visito if (defaultValue?.kind === Kind.STRING || defaultValue?.kind === Kind.ENUM) { if (config.useEnumTypeAsDefaultValue && defaultValue?.kind !== Kind.STRING) { - let value = convertNameParts(defaultValue.value, resolveExternalModuleAndFn('change-case-all#pascalCase')); + let value = convertNameParts(defaultValue.value, resolveExternalModuleAndFn('change-case-all#pascalCase'), config.namingConvention?.transformUnderscore); if (config.namingConvention?.enumValues) - value = convertNameParts(defaultValue.value, resolveExternalModuleAndFn(config.namingConvention?.enumValues)); + value = convertNameParts(defaultValue.value, resolveExternalModuleAndFn(config.namingConvention?.enumValues), config.namingConvention?.transformUnderscore); appliedDirectivesGen = `${appliedDirectivesGen}.default(${type.name.value}.${value})`; } diff --git a/tests/directive.spec.ts b/tests/directive.spec.ts index 1fa8036a..2ac9c6c2 100644 --- a/tests/directive.spec.ts +++ b/tests/directive.spec.ts @@ -1,4 +1,5 @@ import type { ConstArgumentNode, ConstDirectiveNode, ConstValueNode, NameNode } from 'graphql'; +import { Kind, parseConstValue } from 'graphql'; import type { DirectiveConfig, DirectiveObjectArguments } from '../src/config'; import type { @@ -6,7 +7,6 @@ import type { FormattedDirectiveConfig, FormattedDirectiveObjectArguments, } from '../src/directive'; -import { Kind, parseConstValue } from 'graphql'; import { buildApi, buildApiForValibot, diff --git a/tests/graphql.spec.ts b/tests/graphql.spec.ts index 87e90b87..92aae319 100644 --- a/tests/graphql.spec.ts +++ b/tests/graphql.spec.ts @@ -3,16 +3,16 @@ import type { } from 'graphql'; import { Graph } from 'graphlib'; import { + Kind, buildClientSchema, buildSchema, introspectionFromSchema, - Kind, parse, print, } from 'graphql'; import dedent from 'ts-dedent'; -import { escapeGraphQLCharacters, isGeneratedByIntrospection, ObjectTypeDefinitionBuilder, topologicalSortAST, topsort } from '../src/graphql'; +import { ObjectTypeDefinitionBuilder, escapeGraphQLCharacters, isGeneratedByIntrospection, topologicalSortAST, topsort } from '../src/graphql'; describe('graphql', () => { describe('objectTypeDefinitionBuilder', () => { diff --git a/tests/myzod.spec.ts b/tests/myzod.spec.ts index 0497476e..23d073a8 100644 --- a/tests/myzod.spec.ts +++ b/tests/myzod.spec.ts @@ -1444,4 +1444,77 @@ describe('myzod', () => { expect(result.content).toContain('ratio: myzod.number().default(0.5).optional().nullable()'); expect(result.content).toContain('isMember: myzod.boolean().default(true).optional().nullable()'); }); + + it('with default input values as enum types with underscores', async () => { + const schema = buildSchema(/* GraphQL */ ` + enum PageType { + PUBLIC + BASIC_AUTH + } + input PageInput { + pageType: PageType! = BASIC_AUTH + greeting: String = "Hello" + score: Int = 100 + ratio: Float = 0.5 + isMember: Boolean = true + } + `); + const result = await plugin( + schema, + [], + { + schema: 'myzod', + importFrom: './types', + useEnumTypeAsDefaultValue: true, + }, + {}, + ); + + expect(result.content).toContain('export const PageTypeSchema = myzod.enum(PageType)'); + expect(result.content).toContain('export function PageInputSchema(): myzod.Type'); + + expect(result.content).toContain('pageType: PageTypeSchema.default(PageType.Basic_Auth)'); + expect(result.content).toContain('greeting: myzod.string().default("Hello").optional().nullable()'); + expect(result.content).toContain('score: myzod.number().default(100).optional().nullable()'); + expect(result.content).toContain('ratio: myzod.number().default(0.5).optional().nullable()'); + expect(result.content).toContain('isMember: myzod.boolean().default(true).optional().nullable()'); + }); + + it('with default input values as enum types with no underscores', async () => { + const schema = buildSchema(/* GraphQL */ ` + enum PageType { + PUBLIC + BASIC_AUTH + } + input PageInput { + pageType: PageType! = BASIC_AUTH + greeting: String = "Hello" + score: Int = 100 + ratio: Float = 0.5 + isMember: Boolean = true + } + `); + const result = await plugin( + schema, + [], + { + schema: 'myzod', + importFrom: './types', + useEnumTypeAsDefaultValue: true, + namingConvention: { + transformUnderscore: true, + }, + }, + {}, + ); + + expect(result.content).toContain('export const PageTypeSchema = myzod.enum(PageType)'); + expect(result.content).toContain('export function PageInputSchema(): myzod.Type'); + + expect(result.content).toContain('pageType: PageTypeSchema.default(PageType.BasicAuth)'); + expect(result.content).toContain('greeting: myzod.string().default("Hello").optional().nullable()'); + expect(result.content).toContain('score: myzod.number().default(100).optional().nullable()'); + expect(result.content).toContain('ratio: myzod.number().default(0.5).optional().nullable()'); + expect(result.content).toContain('isMember: myzod.boolean().default(true).optional().nullable()'); + }); }); diff --git a/tests/yup.spec.ts b/tests/yup.spec.ts index bad9c1f4..1b7d010c 100644 --- a/tests/yup.spec.ts +++ b/tests/yup.spec.ts @@ -1470,4 +1470,81 @@ describe('yup', () => { expect(result.content).toContain('ratio: yup.number().defined().nullable().default(0.5).optional()'); expect(result.content).toContain('isMember: yup.boolean().defined().nullable().default(true).optional()'); }); + + it('with default input values as enum types with underscores', async () => { + const schema = buildSchema(/* GraphQL */ ` + enum PageType { + PUBLIC + BASIC_AUTH + } + input PageInput { + pageType: PageType! = BASIC_AUTH + greeting: String = "Hello" + score: Int = 100 + ratio: Float = 0.5 + isMember: Boolean = true + } + `); + const result = await plugin( + schema, + [], + { + schema: 'yup', + importFrom: './types', + useEnumTypeAsDefaultValue: true, + }, + {}, + ); + + expect(result.content).toContain( + 'export const PageTypeSchema = yup.string().oneOf(Object.values(PageType)).defined()', + ); + expect(result.content).toContain('export function PageInputSchema(): yup.ObjectSchema'); + + expect(result.content).toContain('pageType: PageTypeSchema.nonNullable().default(PageType.Basic_Auth)'); + expect(result.content).toContain('greeting: yup.string().defined().nullable().default("Hello").optional()'); + expect(result.content).toContain('score: yup.number().defined().nullable().default(100).optional()'); + expect(result.content).toContain('ratio: yup.number().defined().nullable().default(0.5).optional()'); + expect(result.content).toContain('isMember: yup.boolean().defined().nullable().default(true).optional()'); + }); + + it('with default input values as enum types with no underscores', async () => { + const schema = buildSchema(/* GraphQL */ ` + enum PageType { + PUBLIC + BASIC_AUTH + } + input PageInput { + pageType: PageType! = BASIC_AUTH + greeting: String = "Hello" + score: Int = 100 + ratio: Float = 0.5 + isMember: Boolean = true + } + `); + const result = await plugin( + schema, + [], + { + schema: 'yup', + importFrom: './types', + useEnumTypeAsDefaultValue: true, + namingConvention: { + transformUnderscore: true, + }, + }, + {}, + ); + + expect(result.content).toContain( + 'export const PageTypeSchema = yup.string().oneOf(Object.values(PageType)).defined()', + ); + expect(result.content).toContain('export function PageInputSchema(): yup.ObjectSchema'); + + expect(result.content).toContain('pageType: PageTypeSchema.nonNullable().default(PageType.BasicAuth)'); + expect(result.content).toContain('greeting: yup.string().defined().nullable().default("Hello").optional()'); + expect(result.content).toContain('score: yup.number().defined().nullable().default(100).optional()'); + expect(result.content).toContain('ratio: yup.number().defined().nullable().default(0.5).optional()'); + expect(result.content).toContain('isMember: yup.boolean().defined().nullable().default(true).optional()'); + }); }); diff --git a/tests/zod.spec.ts b/tests/zod.spec.ts index 28cbe369..8e58db69 100644 --- a/tests/zod.spec.ts +++ b/tests/zod.spec.ts @@ -566,6 +566,81 @@ describe('zod', () => { expect(result.content).toContain('isMember: z.boolean().default(true).nullish()'); }); + it('with default input values as enum types with underscores', async () => { + const schema = buildSchema(/* GraphQL */ ` + enum PageType { + BASIC_AUTH + PUBLIC + } + input PageInput { + pageType: PageType! = BASIC_AUTH + greeting: String = "Hello" + score: Int = 100 + ratio: Float = 0.5 + isMember: Boolean = true + } + `); + const result = await plugin( + schema, + [], + { + schema: 'zod', + importFrom: './types', + useEnumTypeAsDefaultValue: true, + }, + { + }, + ); + + expect(result.content).toContain('export const PageTypeSchema = z.nativeEnum(PageType)'); + expect(result.content).toContain('export function PageInputSchema(): z.ZodObject>'); + + expect(result.content).toContain('pageType: PageTypeSchema.default(PageType.Basic_Auth)'); + expect(result.content).toContain('greeting: z.string().default("Hello").nullish()'); + expect(result.content).toContain('score: z.number().default(100).nullish()'); + expect(result.content).toContain('ratio: z.number().default(0.5).nullish()'); + expect(result.content).toContain('isMember: z.boolean().default(true).nullish()'); + }); + + it('with default input values as enum types with no underscores', async () => { + const schema = buildSchema(/* GraphQL */ ` + enum PageType { + BASIC_AUTH + PUBLIC + } + input PageInput { + pageType: PageType! = BASIC_AUTH + greeting: String = "Hello" + score: Int = 100 + ratio: Float = 0.5 + isMember: Boolean = true + } + `); + const result = await plugin( + schema, + [], + { + schema: 'zod', + importFrom: './types', + useEnumTypeAsDefaultValue: true, + namingConvention: { + transformUnderscore: true, + }, + }, + { + }, + ); + + expect(result.content).toContain('export const PageTypeSchema = z.nativeEnum(PageType)'); + expect(result.content).toContain('export function PageInputSchema(): z.ZodObject>'); + + expect(result.content).toContain('pageType: PageTypeSchema.default(PageType.BasicAuth)'); + expect(result.content).toContain('greeting: z.string().default("Hello").nullish()'); + expect(result.content).toContain('score: z.number().default(100).nullish()'); + expect(result.content).toContain('ratio: z.number().default(0.5).nullish()'); + expect(result.content).toContain('isMember: z.boolean().default(true).nullish()'); + }); + it('with default input values', async () => { const schema = buildSchema(/* GraphQL */ ` enum PageType {