diff --git a/README.md b/README.md index fd6cdae6..a28f820c 100644 --- a/README.md +++ b/README.md @@ -187,6 +187,26 @@ config: Email: z.string().email() ``` +### `defaultScalarTypeSchema` + +type: `string` + +Fallback scalar type for undefined scalar types in the schema not found in `scalarSchemas`. + +#### yup schema +```yml +config: + schema: yup + defaultScalarSchema: yup.unknown() +``` + +#### zod schema +```yml +config: + schema: zod + defaultScalarSchema: z.unknown() +``` + ### `withObjectType` type: `boolean` default: `false` diff --git a/src/config.ts b/src/config.ts index 2485435b..b0471bdf 100644 --- a/src/config.ts +++ b/src/config.ts @@ -176,6 +176,24 @@ export interface ValidationSchemaPluginConfig extends TypeScriptPluginConfig { * ``` */ scalarSchemas?: ScalarSchemas + /** + * @description Fallback scalar type for undefined scalar types in the schema not found in `scalarSchemas`. + * + * @exampleMarkdown + * ```yml + * config: + * schema: yup + * defaultScalarSchema: yup.unknown() + * ``` + * + * @exampleMarkdown + * ```yml + * config: + * schema: zod + * defaultScalarSchema: z.unknown() + * ``` + */ + defaultScalarTypeSchema?: string /** * @description Generates validation schema with GraphQL type objects. * but excludes "Query", "Mutation", "Subscription" objects. diff --git a/src/myzod/index.ts b/src/myzod/index.ts index bf8a81f4..77010759 100644 --- a/src/myzod/index.ts +++ b/src/myzod/index.ts @@ -374,6 +374,11 @@ function myzod4Scalar(config: ValidationSchemaPluginConfig, visitor: Visitor, sc case 'boolean': return `myzod.boolean()`; } + + if (config.defaultScalarTypeSchema) { + return config.defaultScalarTypeSchema; + } + console.warn('unhandled name:', scalarName); return anySchema; } diff --git a/src/valibot/index.ts b/src/valibot/index.ts index aa8e2a69..f0a0baff 100644 --- a/src/valibot/index.ts +++ b/src/valibot/index.ts @@ -299,6 +299,11 @@ function valibot4Scalar(config: ValidationSchemaPluginConfig, visitor: Visitor, case 'boolean': return `v.boolean()`; } + + if (config.defaultScalarTypeSchema) { + return config.defaultScalarTypeSchema; + } + console.warn('unhandled scalar name:', scalarName); return 'v.any()'; } diff --git a/src/yup/index.ts b/src/yup/index.ts index 955f7cc6..007d3dec 100644 --- a/src/yup/index.ts +++ b/src/yup/index.ts @@ -396,6 +396,11 @@ function yup4Scalar(config: ValidationSchemaPluginConfig, visitor: Visitor, scal case 'boolean': return `yup.boolean().defined()`; } + + if (config.defaultScalarTypeSchema) { + return config.defaultScalarTypeSchema + } + console.warn('unhandled name:', scalarName); return `yup.mixed()`; } diff --git a/src/zod/index.ts b/src/zod/index.ts index 2256ec80..224c8c52 100644 --- a/src/zod/index.ts +++ b/src/zod/index.ts @@ -390,6 +390,11 @@ function zod4Scalar(config: ValidationSchemaPluginConfig, visitor: Visitor, scal case 'boolean': return `z.boolean()`; } + + if (config.defaultScalarTypeSchema) { + return config.defaultScalarTypeSchema; + } + console.warn('unhandled scalar name:', scalarName); return anySchema; } diff --git a/tests/myzod.spec.ts b/tests/myzod.spec.ts index 23d073a8..51d10a6f 100644 --- a/tests/myzod.spec.ts +++ b/tests/myzod.spec.ts @@ -467,6 +467,41 @@ describe('myzod', () => { " `) }); + + it('with defaultScalarTypeSchema', async () => { + const schema = buildSchema(/* GraphQL */ ` + input ScalarsInput { + date: Date! + email: Email + str: String! + } + scalar Date + scalar Email + `); + const result = await plugin( + schema, + [], + { + schema: 'myzod', + scalarSchemas: { + Email: 'myzod.string()', // generate the basic type. User can later extend it using `withPredicate(fn: (val: string) => boolean), errMsg?: string }` + }, + defaultScalarTypeSchema: 'myzod.string()', + }, + {}, + ); + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export function ScalarsInputSchema(): myzod.Type { + return myzod.object({ + date: myzod.string(), + email: myzod.string().optional().nullable(), + str: myzod.string() + }) + } + " + `) + }); it('with typesPrefix', async () => { const schema = buildSchema(/* GraphQL */ ` input Say { diff --git a/tests/valibot.spec.ts b/tests/valibot.spec.ts index ad77d5f6..8afcc11d 100644 --- a/tests/valibot.spec.ts +++ b/tests/valibot.spec.ts @@ -433,6 +433,43 @@ describe('valibot', () => { " `) }); + + it('with defaultScalarTypeSchema', async () => { + const schema = buildSchema(/* GraphQL */ ` + input ScalarsInput { + date: Date! + email: Email + str: String! + } + scalar Date + scalar Email + `); + const result = await plugin( + schema, + [], + { + schema: 'valibot', + scalarSchemas: { + Email: 'v.string([v.email()])', + }, + defaultScalarTypeSchema: 'v.string()', + }, + {}, + ); + expect(result.content).toMatchInlineSnapshot(` + " + + export function ScalarsInputSchema(): v.GenericSchema { + return v.object({ + date: v.string(), + email: v.nullish(v.string([v.email()])), + str: v.string() + }) + } + " + `) + }); + it('with typesPrefix', async () => { const schema = buildSchema(/* GraphQL */ ` input Say { diff --git a/tests/yup.spec.ts b/tests/yup.spec.ts index 1b7d010c..7e603ada 100644 --- a/tests/yup.spec.ts +++ b/tests/yup.spec.ts @@ -472,6 +472,41 @@ describe('yup', () => { `) }); + it('with defaultScalarTypeSchema', async () => { + const schema = buildSchema(/* GraphQL */ ` + input ScalarsInput { + date: Date! + email: Email + str: String! + } + scalar Date + scalar Email + `); + const result = await plugin( + schema, + [], + { + scalarSchemas: { + Email: 'yup.string().email()', + }, + defaultScalarTypeSchema: 'yup.string()', + }, + {}, + ); + expect(result.content).toMatchInlineSnapshot(` + " + + export function ScalarsInputSchema(): yup.ObjectSchema { + return yup.object({ + date: yup.string().nonNullable(), + email: yup.string().email().defined().nullable().optional(), + str: yup.string().defined().nonNullable() + }) + } + " + `) + }); + it('with typesPrefix', async () => { const schema = buildSchema(/* GraphQL */ ` input Say { diff --git a/tests/zod.spec.ts b/tests/zod.spec.ts index 8e58db69..dd04fe89 100644 --- a/tests/zod.spec.ts +++ b/tests/zod.spec.ts @@ -464,6 +464,41 @@ describe('zod', () => { `) }); + it('with defaultScalarTypeSchema', async () => { + const schema = buildSchema(/* GraphQL */ ` + input ScalarsInput { + date: Date! + email: Email + str: String! + } + scalar Date + scalar Email + `); + const result = await plugin( + schema, + [], + { + schema: 'zod', + scalarSchemas: { + Email: 'z.string().email()', + }, + defaultScalarTypeSchema: 'z.string()', + }, + {}, + ); + expect(removedInitialEmitValue(result.content)).toMatchInlineSnapshot(` + " + export function ScalarsInputSchema(): z.ZodObject> { + return z.object({ + date: z.string(), + email: z.string().email().nullish(), + str: z.string() + }) + } + " + `) + }); + it('with typesPrefix', async () => { const schema = buildSchema(/* GraphQL */ ` input Say {