Skip to content

Commit f75f3ee

Browse files
authored
Merge pull request #723 from Code-Hex/moist-feat/enum-type-as-default-value
Moist feat/enum type as default value
2 parents 59f3264 + cb2226a commit f75f3ee

File tree

8 files changed

+192
-11
lines changed

8 files changed

+192
-11
lines changed

src/config.ts

+37
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { TypeScriptPluginConfig } from '@graphql-codegen/typescript';
2+
import type { NamingConventionMap } from '@graphql-codegen/visitor-plugin-common';
23

34
export type ValidationSchema = 'yup' | 'zod' | 'myzod' | 'valibot';
45
export type ValidationSchemaExportType = 'function' | 'const';
@@ -210,6 +211,42 @@ export interface ValidationSchemaPluginConfig extends TypeScriptPluginConfig {
210211
* ```
211212
*/
212213
validationSchemaExportType?: ValidationSchemaExportType
214+
/**
215+
* @description Uses the full path of the enum type as the default value instead of the stringified value.
216+
* @default false
217+
*
218+
* @exampleMarkdown
219+
* ```yml
220+
* generates:
221+
* path/to/file.ts:
222+
* plugins:
223+
* - typescript
224+
* - graphql-codegen-validation-schema
225+
* config:
226+
* useEnumTypeAsDefault: true
227+
* ```
228+
*/
229+
useEnumTypeAsDefaultValue?: boolean
230+
/**
231+
* @description Uses the full path of the enum type as the default value instead of the stringified value.
232+
* @default { enumValues: "change-case-all#pascalCase" }
233+
*
234+
* Note: This option has not been tested with `namingConvention.transformUnderscore` and `namingConvention.typeNames` options,
235+
* and may not work as expected.
236+
*
237+
* @exampleMarkdown
238+
* ```yml
239+
* generates:
240+
* path/to/file.ts:
241+
* plugins:
242+
* - typescript
243+
* - graphql-codegen-validation-schema
244+
* config:
245+
* namingConvention:
246+
* enumValues: change-case-all#pascalCase
247+
* ```
248+
*/
249+
namingConvention?: NamingConventionMap
213250
/**
214251
* @description Generates validation schema with more API based on directive schema.
215252
* @exampleMarkdown

src/myzod/index.ts

+15-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { DeclarationBlock, indent } from '@graphql-codegen/visitor-plugin-common';
1+
import { DeclarationBlock, convertNameParts, indent } from '@graphql-codegen/visitor-plugin-common';
22
import type {
33
EnumTypeDefinitionNode,
44
FieldDefinitionNode,
@@ -15,6 +15,7 @@ import {
1515
Kind,
1616
} from 'graphql';
1717

18+
import { resolveExternalModuleAndFn } from '@graphql-codegen/plugin-helpers';
1819
import type { ValidationSchemaPluginConfig } from '../config';
1920
import { buildApi, formatDirectiveConfig } from '../directive';
2021
import { BaseSchemaVisitor } from '../schema_visitor';
@@ -282,8 +283,19 @@ function generateFieldTypeMyZodSchema(config: ValidationSchemaPluginConfig, visi
282283
if (defaultValue?.kind === Kind.INT || defaultValue?.kind === Kind.FLOAT || defaultValue?.kind === Kind.BOOLEAN)
283284
appliedDirectivesGen = `${appliedDirectivesGen}.default(${defaultValue.value})`;
284285

285-
if (defaultValue?.kind === Kind.STRING || defaultValue?.kind === Kind.ENUM)
286-
appliedDirectivesGen = `${appliedDirectivesGen}.default("${escapeGraphQLCharacters(defaultValue.value)}")`;
286+
if (defaultValue?.kind === Kind.STRING || defaultValue?.kind === Kind.ENUM) {
287+
if (config.useEnumTypeAsDefaultValue && defaultValue?.kind !== Kind.STRING) {
288+
let value = convertNameParts(defaultValue.value, resolveExternalModuleAndFn('change-case-all#pascalCase'));
289+
290+
if (config.namingConvention?.enumValues)
291+
value = convertNameParts(defaultValue.value, resolveExternalModuleAndFn(config.namingConvention?.enumValues));
292+
293+
appliedDirectivesGen = `${appliedDirectivesGen}.default(${visitor.convertName(type.name.value)}.${value})`;
294+
}
295+
else {
296+
appliedDirectivesGen = `${appliedDirectivesGen}.default("${escapeGraphQLCharacters(defaultValue.value)}")`;
297+
}
298+
}
287299
}
288300

289301
if (isNonNullType(parentType)) {

src/valibot/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -205,9 +205,9 @@ function generateFieldTypeValibotSchema(config: ValidationSchemaPluginConfig, vi
205205
if (isListType(type)) {
206206
const gen = generateFieldTypeValibotSchema(config, visitor, field, type.type, type);
207207
const arrayGen = `v.array(${maybeLazy(type.type, gen)})`;
208-
if (!isNonNullType(parentType)) {
208+
if (!isNonNullType(parentType))
209209
return `v.nullish(${arrayGen})`;
210-
}
210+
211211
return arrayGen;
212212
}
213213
if (isNonNullType(type)) {

src/yup/index.ts

+15-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { DeclarationBlock, indent } from '@graphql-codegen/visitor-plugin-common';
1+
import { DeclarationBlock, convertNameParts, indent } from '@graphql-codegen/visitor-plugin-common';
22
import type {
33
EnumTypeDefinitionNode,
44
FieldDefinitionNode,
@@ -15,6 +15,7 @@ import {
1515
Kind,
1616
} from 'graphql';
1717

18+
import { resolveExternalModuleAndFn } from '@graphql-codegen/plugin-helpers';
1819
import type { ValidationSchemaPluginConfig } from '../config';
1920
import { buildApi, formatDirectiveConfig } from '../directive';
2021
import { BaseSchemaVisitor } from '../schema_visitor';
@@ -284,8 +285,19 @@ function shapeFields(fields: readonly (FieldDefinitionNode | InputValueDefinitio
284285
fieldSchema = `${fieldSchema}.default(${defaultValue.value})`;
285286
}
286287

287-
if (defaultValue?.kind === Kind.STRING || defaultValue?.kind === Kind.ENUM)
288-
fieldSchema = `${fieldSchema}.default("${escapeGraphQLCharacters(defaultValue.value)}")`;
288+
if (defaultValue?.kind === Kind.STRING || defaultValue?.kind === Kind.ENUM) {
289+
if (config.useEnumTypeAsDefaultValue && defaultValue?.kind !== Kind.STRING) {
290+
let value = convertNameParts(defaultValue.value, resolveExternalModuleAndFn('change-case-all#pascalCase'));
291+
292+
if (config.namingConvention?.enumValues)
293+
value = convertNameParts(defaultValue.value, resolveExternalModuleAndFn(config.namingConvention?.enumValues));
294+
295+
fieldSchema = `${fieldSchema}.default(${visitor.convertName(field.name.value)}.${value})`;
296+
}
297+
else {
298+
fieldSchema = `${fieldSchema}.default("${escapeGraphQLCharacters(defaultValue.value)}")`;
299+
}
300+
}
289301
}
290302

291303
if (isNonNullType(field.type))

src/zod/index.ts

+15-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { DeclarationBlock, indent } from '@graphql-codegen/visitor-plugin-common';
1+
import { DeclarationBlock, convertNameParts, indent } from '@graphql-codegen/visitor-plugin-common';
22
import type {
33
EnumTypeDefinitionNode,
44
FieldDefinitionNode,
@@ -15,6 +15,7 @@ import {
1515
Kind,
1616
} from 'graphql';
1717

18+
import { resolveExternalModuleAndFn } from '@graphql-codegen/plugin-helpers';
1819
import type { ValidationSchemaPluginConfig } from '../config';
1920
import { buildApi, formatDirectiveConfig } from '../directive';
2021
import { BaseSchemaVisitor } from '../schema_visitor';
@@ -295,8 +296,19 @@ function generateFieldTypeZodSchema(config: ValidationSchemaPluginConfig, visito
295296
if (defaultValue?.kind === Kind.INT || defaultValue?.kind === Kind.FLOAT || defaultValue?.kind === Kind.BOOLEAN)
296297
appliedDirectivesGen = `${appliedDirectivesGen}.default(${defaultValue.value})`;
297298

298-
if (defaultValue?.kind === Kind.STRING || defaultValue?.kind === Kind.ENUM)
299-
appliedDirectivesGen = `${appliedDirectivesGen}.default("${escapeGraphQLCharacters(defaultValue.value)}")`;
299+
if (defaultValue?.kind === Kind.STRING || defaultValue?.kind === Kind.ENUM) {
300+
if (config.useEnumTypeAsDefaultValue && defaultValue?.kind !== Kind.STRING) {
301+
let value = convertNameParts(defaultValue.value, resolveExternalModuleAndFn('change-case-all#pascalCase'));
302+
303+
if (config.namingConvention?.enumValues)
304+
value = convertNameParts(defaultValue.value, resolveExternalModuleAndFn(config.namingConvention?.enumValues));
305+
306+
appliedDirectivesGen = `${appliedDirectivesGen}.default(${type.name.value}.${value})`;
307+
}
308+
else {
309+
appliedDirectivesGen = `${appliedDirectivesGen}.default("${escapeGraphQLCharacters(defaultValue.value)}")`;
310+
}
311+
}
300312
}
301313

302314
if (isNonNullType(parentType)) {

tests/myzod.spec.ts

+35
Original file line numberDiff line numberDiff line change
@@ -1409,4 +1409,39 @@ describe('myzod', () => {
14091409
"
14101410
`)
14111411
});
1412+
1413+
it('with default input values as enum types', async () => {
1414+
const schema = buildSchema(/* GraphQL */ `
1415+
enum PageType {
1416+
PUBLIC
1417+
BASIC_AUTH
1418+
}
1419+
input PageInput {
1420+
pageType: PageType! = PUBLIC
1421+
greeting: String = "Hello"
1422+
score: Int = 100
1423+
ratio: Float = 0.5
1424+
isMember: Boolean = true
1425+
}
1426+
`);
1427+
const result = await plugin(
1428+
schema,
1429+
[],
1430+
{
1431+
schema: 'myzod',
1432+
importFrom: './types',
1433+
useEnumTypeAsDefaultValue: true,
1434+
},
1435+
{},
1436+
);
1437+
1438+
expect(result.content).toContain('export const PageTypeSchema = myzod.enum(PageType)');
1439+
expect(result.content).toContain('export function PageInputSchema(): myzod.Type<PageInput>');
1440+
1441+
expect(result.content).toContain('pageType: PageTypeSchema.default(PageType.Public)');
1442+
expect(result.content).toContain('greeting: myzod.string().default("Hello").optional().nullable()');
1443+
expect(result.content).toContain('score: myzod.number().default(100).optional().nullable()');
1444+
expect(result.content).toContain('ratio: myzod.number().default(0.5).optional().nullable()');
1445+
expect(result.content).toContain('isMember: myzod.boolean().default(true).optional().nullable()');
1446+
});
14121447
});

tests/yup.spec.ts

+37
Original file line numberDiff line numberDiff line change
@@ -1433,4 +1433,41 @@ describe('yup', () => {
14331433
"
14341434
`)
14351435
});
1436+
1437+
it('with default input values as enum types', async () => {
1438+
const schema = buildSchema(/* GraphQL */ `
1439+
enum PageType {
1440+
PUBLIC
1441+
BASIC_AUTH
1442+
}
1443+
input PageInput {
1444+
pageType: PageType! = PUBLIC
1445+
greeting: String = "Hello"
1446+
score: Int = 100
1447+
ratio: Float = 0.5
1448+
isMember: Boolean = true
1449+
}
1450+
`);
1451+
const result = await plugin(
1452+
schema,
1453+
[],
1454+
{
1455+
schema: 'yup',
1456+
importFrom: './types',
1457+
useEnumTypeAsDefaultValue: true,
1458+
},
1459+
{},
1460+
);
1461+
1462+
expect(result.content).toContain(
1463+
'export const PageTypeSchema = yup.string<PageType>().oneOf(Object.values(PageType)).defined()',
1464+
);
1465+
expect(result.content).toContain('export function PageInputSchema(): yup.ObjectSchema<PageInput>');
1466+
1467+
expect(result.content).toContain('pageType: PageTypeSchema.nonNullable().default(PageType.Public)');
1468+
expect(result.content).toContain('greeting: yup.string().defined().nullable().default("Hello").optional()');
1469+
expect(result.content).toContain('score: yup.number().defined().nullable().default(100).optional()');
1470+
expect(result.content).toContain('ratio: yup.number().defined().nullable().default(0.5).optional()');
1471+
expect(result.content).toContain('isMember: yup.boolean().defined().nullable().default(true).optional()');
1472+
});
14361473
});

tests/zod.spec.ts

+36
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,42 @@ describe('zod', () => {
530530
`)
531531
});
532532

533+
it('with default input values as enum types', async () => {
534+
const schema = buildSchema(/* GraphQL */ `
535+
enum PageType {
536+
PUBLIC
537+
BASIC_AUTH
538+
}
539+
input PageInput {
540+
pageType: PageType! = PUBLIC
541+
greeting: String = "Hello"
542+
score: Int = 100
543+
ratio: Float = 0.5
544+
isMember: Boolean = true
545+
}
546+
`);
547+
const result = await plugin(
548+
schema,
549+
[],
550+
{
551+
schema: 'zod',
552+
importFrom: './types',
553+
useEnumTypeAsDefaultValue: true,
554+
},
555+
{
556+
},
557+
);
558+
559+
expect(result.content).toContain('export const PageTypeSchema = z.nativeEnum(PageType)');
560+
expect(result.content).toContain('export function PageInputSchema(): z.ZodObject<Properties<PageInput>>');
561+
562+
expect(result.content).toContain('pageType: PageTypeSchema.default(PageType.Public)');
563+
expect(result.content).toContain('greeting: z.string().default("Hello").nullish()');
564+
expect(result.content).toContain('score: z.number().default(100).nullish()');
565+
expect(result.content).toContain('ratio: z.number().default(0.5).nullish()');
566+
expect(result.content).toContain('isMember: z.boolean().default(true).nullish()');
567+
});
568+
533569
it('with default input values', async () => {
534570
const schema = buildSchema(/* GraphQL */ `
535571
enum PageType {

0 commit comments

Comments
 (0)