diff --git a/README.md b/README.md index 13d3d4a4..6d690a4a 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Start by installing this plugin and write simple plugin config; ```sh -$ npm i graphql-codegen-typescript-validation-schema +npm i graphql-codegen-typescript-validation-schema ``` ```yml @@ -154,6 +154,42 @@ type: `boolean` default: `false` Generates enum as TypeScript `type` instead of `enum`. +### `maybeSchemaValue` + +type: `MyZodNullishSchemaType` | `YupNullishSchemaType` | `ZodNullishSchemaType` + +| schema | type | values | default | +| ------- | ------------------------ | ----------------------------------------------- | ------------ | +| `myzod` | `MyZodNullishSchemaType` | `never` | `never` | +| `yup` | `YupNullishSchemaType` | `'nullable'` \| `'optional'` \| `'notRequired'` | `'nullable'` | +| `zod` | `ZodNullishSchemaType` | `'nullable'` \| `'optional'` \| `'nullish'` | `'nullish'` | + +Chooses which nullish schema to use + +### `maybeSchemaValue`: myzod schema + +```yml +config: + schema: myzod + # maybeSchemaValue: no valid options +``` + +#### `maybeSchemaValue`: yup schema + +```yml +config: + schema: yup + maybeSchemaValue: nullable +``` + +#### `maybeSchemaValue`: zod schema + +```yml +config: + schema: zod + maybeSchemaValue: optional +``` + ### `notAllowEmptyString` type: `boolean` default: `false` @@ -166,7 +202,7 @@ type: `ScalarSchemas` Extends or overrides validation schema for the built-in scalars and custom GraphQL scalars. -#### yup schema +#### `scalarSchemas`: yup schema ```yml config: @@ -176,7 +212,7 @@ config: Email: yup.string().email() ``` -#### zod schema +#### `scalarSchemas`: zod schema ```yml config: @@ -310,4 +346,4 @@ generates: preset: 'client', plugins: ... -``` \ No newline at end of file +``` diff --git a/package.json b/package.json index f11f2b18..64fb56c9 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,6 @@ "README.md" ], "scripts": { - "preinstall": "npx only-allow pnpm", "type-check": "tsc --noEmit", "type-check:yup": "tsc --strict --noEmit example/yup/schemas.ts", "type-check:zod": "tsc --strict --noEmit example/zod/schemas.ts", diff --git a/src/config.ts b/src/config.ts index 04c1af45..ea731f80 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,8 +1,15 @@ -import { TypeScriptPluginConfig } from '@graphql-codegen/typescript'; +import { type TypeScriptPluginConfig } from '@graphql-codegen/typescript' export type ValidationSchema = 'yup' | 'zod' | 'myzod'; export type ValidationSchemaExportType = 'function' | 'const'; +export const MyZodNullishSchemaTypes = [] as const; +export type MyZodNullishSchemaType = typeof MyZodNullishSchemaTypes[number]; +export const ZodNullishSchemaTypes = ['nullable', 'optional', 'nullish'] as const; +export type ZodNullishSchemaType = typeof ZodNullishSchemaTypes[number]; +export const YupNullishSchemaTypes = ['nullable', 'optional', 'notRequired'] as const; +export type YupNullishSchemaType = typeof YupNullishSchemaTypes[number]; + export interface DirectiveConfig { [directive: string]: { [argument: string]: string | string[] | DirectiveObjectArguments; @@ -17,23 +24,96 @@ interface ScalarSchemas { [name: string]: string; } -export interface ValidationSchemaPluginConfig extends TypeScriptPluginConfig { +interface MyZodValidationSchemaPluginConfig extends BaseValidationSchemaPluginConfig { +/** + * @description specify generate schema + * @default yup + * @exampleMarkdown + * ```yml + * generates: + * path/to/file.ts: + * plugins: + * - typescript + * - graphql-codegen-validation-schema + * config: + * schema: myzod + * ``` + */ + schema?: 'myzod'; + maybeSchemaValue?: MyZodNullishSchemaType +} + +interface YupNullableSchemaTypesPluginConfig extends BaseValidationSchemaPluginConfig { /** - * @description specify generate schema - * @default yup - * - * @exampleMarkdown - * ```yml - * generates: - * path/to/file.ts: - * plugins: - * - typescript - * - graphql-codegen-validation-schema - * config: - * schema: yup - * ``` - */ - schema?: ValidationSchema; + * @description specify generate schema + * @default yup + * @exampleMarkdown + * ```yml + * generates: + * path/to/file.ts: + * plugins: + * - typescript + * - graphql-codegen-validation-schema + * config: + * schema: yup + * ``` + */ + schema?: 'yup' + /** + * @description Set the schema value for nullish types. + * @default nullable + * @exampleMarkdown + * ```yml + * generates: + * path/to/file.ts: + * plugins: + * - typescript + * - graphql-codegen-validation-schema + * config: + * maybeSchemaValue: nullable + * ``` + * + * + */ + maybeSchemaValue?: YupNullishSchemaType +} + +interface ZodNullableSchemaTypesPluginConfig extends BaseValidationSchemaPluginConfig { + /** + * @description specify generate schema + * @default yup + * @exampleMarkdown + * ```yml + * generates: + * path/to/file.ts: + * plugins: + * - typescript + * - graphql-codegen-validation-schema + * config: + * schema: zod + * ``` + */ + schema?: 'zod' + /** + * @description Set the schema value for nullish types. + * @default nullish + * @exampleMarkdown + * ```yml + * generates: + * path/to/file.ts: + * plugins: + * - typescript + * - graphql-codegen-validation-schema + * config: + * maybeSchemaValue: nullish + * ``` + * + * + */ + maybeSchemaValue?: ZodNullishSchemaType +} + +export interface BaseValidationSchemaPluginConfig extends TypeScriptPluginConfig { /** * @description import types from generated typescript type path * if not given, omit import statement. @@ -252,3 +332,8 @@ export interface ValidationSchemaPluginConfig extends TypeScriptPluginConfig { */ directives?: DirectiveConfig; } + +export type ValidationSchemaPluginConfig = + | MyZodValidationSchemaPluginConfig + | YupNullableSchemaTypesPluginConfig + | ZodNullableSchemaTypesPluginConfig diff --git a/src/yup/index.ts b/src/yup/index.ts index 171d47f4..84c1a7a8 100644 --- a/src/yup/index.ts +++ b/src/yup/index.ts @@ -252,7 +252,7 @@ const generateFieldTypeYupSchema = ( if (isListType(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().${config.maybeSchemaValue ? config.maybeSchemaValue : 'nullable'}()`; } return `yup.array(${maybeLazy(type.type, gen)}).defined()`; } @@ -272,7 +272,7 @@ const generateFieldTypeYupSchema = ( if (typ?.astNode?.kind === 'InputObjectTypeDefinition') { return `${gen}`; } - return `${gen}.nullable()`; + return `${gen}.${config.maybeSchemaValue ? config.maybeSchemaValue : 'nullable'}()`; } console.warn('unhandled type:', type); return ''; diff --git a/src/zod/index.ts b/src/zod/index.ts index ef83ad1d..143e0998 100644 --- a/src/zod/index.ts +++ b/src/zod/index.ts @@ -234,7 +234,7 @@ const generateFieldTypeZodSchema = ( if (!isNonNullType(parentType)) { const arrayGen = `z.array(${maybeLazy(type.type, gen)})`; const maybeLazyGen = applyDirectives(config, field, arrayGen); - return `${maybeLazyGen}.nullish()`; + return `${maybeLazyGen}.${config.maybeSchemaValue ? config.maybeSchemaValue : 'nullish'}()`; } return `z.array(${maybeLazy(type.type, gen)})`; } @@ -257,7 +257,7 @@ const generateFieldTypeZodSchema = ( if (isListType(parentType)) { return `${appliedDirectivesGen}.nullable()`; } - return `${appliedDirectivesGen}.nullish()`; + return `${appliedDirectivesGen}.${config.maybeSchemaValue ? config.maybeSchemaValue : 'nullish'}()`; } console.warn('unhandled type:', type); return ''; diff --git a/tests/myzod.spec.ts b/tests/myzod.spec.ts index c0b91ecb..64679977 100644 --- a/tests/myzod.spec.ts +++ b/tests/myzod.spec.ts @@ -1,6 +1,7 @@ import { buildClientSchema, buildSchema, introspectionFromSchema } from 'graphql'; import dedent from 'ts-dedent'; +import { MyZodNullishSchemaTypes } from '../src/config'; import { plugin } from '../src/index'; describe('myzod', () => { @@ -244,6 +245,39 @@ describe('myzod', () => { expect(result.content).toContain('phrase: myzod.string()'); }); + it('without maybeSchemaValue', async () => { + const schema = buildSchema(/* GraphQL */ ` + input Say { + phrase: String + } + `) + const result = await plugin( + schema, + [], + { + schema: 'zod', + } + ) + expect(result.content).toContain('phrase: z.string().nullish()') + }) + + it.each(MyZodNullishSchemaTypes)('with maybeSchemaValue: %s', async (maybeSchemaValue) => { + const schema = buildSchema(/* GraphQL */ ` + input Say { + phrase: String + } + `) + const result = await plugin( + schema, + [], + { + schema: 'myzod', + maybeSchemaValue, + } + ) + expect(result.content).toContain(`phrase: z.string().${ maybeSchemaValue }()`) + }); + it('with enumsAsTypes', async () => { const schema = buildSchema(/* GraphQL */ ` enum PageType { diff --git a/tests/yup.spec.ts b/tests/yup.spec.ts index 50316ad7..8ba730b7 100644 --- a/tests/yup.spec.ts +++ b/tests/yup.spec.ts @@ -1,6 +1,7 @@ import { buildClientSchema, buildSchema, introspectionFromSchema } from 'graphql'; import dedent from 'ts-dedent'; +import { YupNullishSchemaTypes } from '../src/config'; import { plugin } from '../src/index'; describe('yup', () => { @@ -241,6 +242,39 @@ describe('yup', () => { expect(result.content).toContain('phrase: yup.string().defined()'); }); + it('without maybeSchemaValue', async () => { + const schema = buildSchema(/* GraphQL */ ` + input Say { + phrase: String + } + `) + const result = await plugin( + schema, + [], + { + schema: 'zod', + } + ) + expect(result.content).toContain('phrase: z.string().nullish()') + }) + + it.each(YupNullishSchemaTypes)('with maybeSchemaValue: %s', async (maybeSchemaValue) => { + const schema = buildSchema(/* GraphQL */ ` + input Say { + phrase: String + } + `) + const result = await plugin( + schema, + [], + { + schema: 'yup', + maybeSchemaValue, + } + ) + expect(result.content).toContain(`phrase: z.string().${ maybeSchemaValue }()`) + }); + it('with enumsAsTypes', async () => { const schema = buildSchema(/* GraphQL */ ` enum PageType { diff --git a/tests/zod.spec.ts b/tests/zod.spec.ts index 76f3ecde..3e9498fc 100644 --- a/tests/zod.spec.ts +++ b/tests/zod.spec.ts @@ -1,7 +1,7 @@ -import { getCachedDocumentNodeFromSchema } from '@graphql-codegen/plugin-helpers'; -import { buildClientSchema, buildSchema, introspectionFromSchema, isSpecifiedScalarType } from 'graphql'; +import { buildClientSchema, buildSchema, introspectionFromSchema } from 'graphql'; import { dedent } from 'ts-dedent'; +import { ZodNullishSchemaTypes } from '../src/config'; import { plugin } from '../src/index'; describe('zod', () => { @@ -245,6 +245,39 @@ describe('zod', () => { expect(result.content).toContain('phrase: z.string()'); }); + it('without maybeSchemaValue', async () => { + const schema = buildSchema(/* GraphQL */ ` + input Say { + phrase: String + } + `); + const result = await plugin( + schema, + [], + { + schema: 'zod', + } + ) + expect(result.content).toContain('phrase: z.string().nullish()'); + }); + + it.each(ZodNullishSchemaTypes)('with maybeSchemaValue: %s', async (maybeSchemaValue) => { + const schema = buildSchema(/* GraphQL */ ` + input Say { + phrase: String + } + `); + const result = await plugin( + schema, + [], + { + schema: 'zod', + maybeSchemaValue, + } + ) + expect(result.content).toContain(`phrase: z.string().${maybeSchemaValue}()`); + }); + it('with enumsAsTypes', async () => { const schema = buildSchema(/* GraphQL */ ` enum PageType {