@davars/graphql-codegen-zod 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/zod.d.ts +13 -0
- package/dist/zod.js +63 -26
- package/package.json +5 -3
package/dist/zod.d.ts
CHANGED
|
@@ -6,11 +6,24 @@ import { BaseSchemaVisitor } from './schema_visitor.js';
|
|
|
6
6
|
export declare class ZodSchemaVisitor extends BaseSchemaVisitor {
|
|
7
7
|
private resolvedTransforms;
|
|
8
8
|
private usedTransformImports;
|
|
9
|
+
/** GraphQL type names whose schemas had annotations dropped (divergent output types). */
|
|
10
|
+
private droppedAnnotationTypes;
|
|
11
|
+
/** Whether the definedNonNullAnySchema fallback was referenced during generation. */
|
|
12
|
+
private usedAnySchema;
|
|
13
|
+
/** Called by zod4Scalar when the anySchema fallback is used. */
|
|
14
|
+
markAnySchemaUsed(): void;
|
|
9
15
|
constructor(schema: GraphQLSchema, config: ValidationSchemaPluginConfig, resolvedTransforms?: Record<string, ResolvedTransform>);
|
|
10
16
|
private addTransformImport;
|
|
11
17
|
buildImports(): string[];
|
|
12
18
|
private getTransformForGraphQLName;
|
|
13
19
|
private hasTransformedFieldRef;
|
|
20
|
+
/**
|
|
21
|
+
* Check if any field references a type whose schema output diverges from the
|
|
22
|
+
* TypeScript type. This includes direct custom scalar fields (e.g. z.coerce.date()
|
|
23
|
+
* outputs Date but TS says string) and transitive references to types that already
|
|
24
|
+
* had their annotations dropped.
|
|
25
|
+
*/
|
|
26
|
+
private hasDivergentFieldRef;
|
|
14
27
|
importValidationSchema(): string;
|
|
15
28
|
initialEmit(): string;
|
|
16
29
|
get InputObjectTypeDefinition(): {
|
package/dist/zod.js
CHANGED
|
@@ -8,6 +8,14 @@ const anySchema = `definedNonNullAnySchema`;
|
|
|
8
8
|
export class ZodSchemaVisitor extends BaseSchemaVisitor {
|
|
9
9
|
resolvedTransforms;
|
|
10
10
|
usedTransformImports = new Map();
|
|
11
|
+
/** GraphQL type names whose schemas had annotations dropped (divergent output types). */
|
|
12
|
+
droppedAnnotationTypes = new Set();
|
|
13
|
+
/** Whether the definedNonNullAnySchema fallback was referenced during generation. */
|
|
14
|
+
usedAnySchema = false;
|
|
15
|
+
/** Called by zod4Scalar when the anySchema fallback is used. */
|
|
16
|
+
markAnySchemaUsed() {
|
|
17
|
+
this.usedAnySchema = true;
|
|
18
|
+
}
|
|
11
19
|
constructor(schema, config, resolvedTransforms = {}) {
|
|
12
20
|
super(schema, config);
|
|
13
21
|
this.resolvedTransforms = resolvedTransforms;
|
|
@@ -18,6 +26,8 @@ export class ZodSchemaVisitor extends BaseSchemaVisitor {
|
|
|
18
26
|
this.usedTransformImports.set(transform.importPath, existing);
|
|
19
27
|
}
|
|
20
28
|
buildImports() {
|
|
29
|
+
// Filter out types whose annotations were dropped (they'd be imported but unused)
|
|
30
|
+
this.importTypes = this.importTypes.filter(t => !this.droppedAnnotationTypes.has(t));
|
|
21
31
|
const baseImports = super.buildImports();
|
|
22
32
|
const transformImports = Array.from(this.usedTransformImports.entries())
|
|
23
33
|
.map(([modulePath, symbols]) => `import { ${Array.from(symbols).sort().join(', ')} } from '${modulePath}'`);
|
|
@@ -36,34 +46,54 @@ export class ZodSchemaVisitor extends BaseSchemaVisitor {
|
|
|
36
46
|
return type.kind === Kind.NAMED_TYPE && this.resolvedTransforms[type.name.value] !== undefined;
|
|
37
47
|
});
|
|
38
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Check if any field references a type whose schema output diverges from the
|
|
51
|
+
* TypeScript type. This includes direct custom scalar fields (e.g. z.coerce.date()
|
|
52
|
+
* outputs Date but TS says string) and transitive references to types that already
|
|
53
|
+
* had their annotations dropped.
|
|
54
|
+
*/
|
|
55
|
+
hasDivergentFieldRef(fields) {
|
|
56
|
+
if (!fields)
|
|
57
|
+
return false;
|
|
58
|
+
return fields.some((field) => {
|
|
59
|
+
let type = field.type;
|
|
60
|
+
while (type.kind === Kind.NON_NULL_TYPE || type.kind === Kind.LIST_TYPE)
|
|
61
|
+
type = type.type;
|
|
62
|
+
if (type.kind !== Kind.NAMED_TYPE)
|
|
63
|
+
return false;
|
|
64
|
+
const name = type.name.value;
|
|
65
|
+
return (this.config.scalarSchemas?.[name] !== undefined) || this.droppedAnnotationTypes.has(name);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
39
68
|
importValidationSchema() {
|
|
40
69
|
return `import * as z from 'zod'`;
|
|
41
70
|
}
|
|
42
71
|
initialEmit() {
|
|
43
|
-
|
|
72
|
+
const blocks = [
|
|
44
73
|
new DeclarationBlock({})
|
|
45
74
|
.asKind('type')
|
|
46
75
|
.withName('Properties<T>')
|
|
47
76
|
.withContent(['Required<{', ' [K in keyof T]: z.ZodType<T[K], T[K]>;', '}>'].join('\n'))
|
|
48
77
|
.string,
|
|
78
|
+
];
|
|
79
|
+
if (this.usedAnySchema) {
|
|
49
80
|
// Unfortunately, zod doesn't provide non-null defined any schema.
|
|
50
81
|
// This is a temporary hack until it is fixed.
|
|
51
82
|
// see: https://github.com/colinhacks/zod/issues/884
|
|
52
|
-
new DeclarationBlock({}).asKind('type').withName('definedNonNullAny').withContent('{}').string,
|
|
53
|
-
new DeclarationBlock({})
|
|
83
|
+
blocks.push(new DeclarationBlock({}).asKind('type').withName('definedNonNullAny').withContent('{}').string, new DeclarationBlock({})
|
|
54
84
|
.export()
|
|
55
85
|
.asKind('const')
|
|
56
86
|
.withName(`isDefinedNonNullAny`)
|
|
57
87
|
.withContent(`(v: any): v is definedNonNullAny => v !== undefined && v !== null`)
|
|
58
|
-
.string,
|
|
59
|
-
new DeclarationBlock({})
|
|
88
|
+
.string, new DeclarationBlock({})
|
|
60
89
|
.export()
|
|
61
90
|
.asKind('const')
|
|
62
91
|
.withName(`${anySchema}`)
|
|
63
92
|
.withContent(`z.any().refine((v) => isDefinedNonNullAny(v))`)
|
|
64
|
-
.string
|
|
65
|
-
|
|
66
|
-
|
|
93
|
+
.string);
|
|
94
|
+
}
|
|
95
|
+
blocks.push(...this.enumDeclarations);
|
|
96
|
+
return `\n${blocks.join('\n')}`;
|
|
67
97
|
}
|
|
68
98
|
get InputObjectTypeDefinition() {
|
|
69
99
|
return {
|
|
@@ -90,9 +120,11 @@ export class ZodSchemaVisitor extends BaseSchemaVisitor {
|
|
|
90
120
|
const argumentBlocks = this.buildTypeDefinitionArguments(node, visitor);
|
|
91
121
|
const appendArguments = argumentBlocks ? `\n${argumentBlocks}` : '';
|
|
92
122
|
// Building schema for fields.
|
|
93
|
-
const shape = node.fields?.map(field => generateFieldZodSchema(this.config, visitor, field, 2)).join(',\n');
|
|
123
|
+
const shape = node.fields?.map(field => generateFieldZodSchema(this.config, visitor, field, 2, this)).join(',\n');
|
|
94
124
|
const transformSuffix = transform ? `.transform(${transform.symbolName})` : '';
|
|
95
|
-
const dropAnnotation = !!transform || this.hasTransformedFieldRef(node.fields);
|
|
125
|
+
const dropAnnotation = !!transform || this.hasTransformedFieldRef(node.fields) || this.hasDivergentFieldRef(node.fields);
|
|
126
|
+
if (dropAnnotation)
|
|
127
|
+
this.droppedAnnotationTypes.add(node.name.value);
|
|
96
128
|
const schemaName = dropAnnotation
|
|
97
129
|
? `${name}Schema`
|
|
98
130
|
: `${name}Schema: z.ZodObject<Properties<${typeName}>>`;
|
|
@@ -131,12 +163,14 @@ export class ZodSchemaVisitor extends BaseSchemaVisitor {
|
|
|
131
163
|
const argumentBlocks = this.buildTypeDefinitionArguments(node, visitor);
|
|
132
164
|
const appendArguments = argumentBlocks ? `\n${argumentBlocks}` : '';
|
|
133
165
|
// Building schema for fields.
|
|
134
|
-
const shape = node.fields?.map(field => generateFieldZodSchema(this.config, visitor, field, 2)).join(',\n');
|
|
166
|
+
const shape = node.fields?.map(field => generateFieldZodSchema(this.config, visitor, field, 2, this)).join(',\n');
|
|
135
167
|
const transformSuffix = transform ? `.transform(${transform.symbolName})` : '';
|
|
136
168
|
// Drop the explicit type annotation when a transform is applied (changes
|
|
137
169
|
// return type from ZodObject to ZodPipe) or when any field references a
|
|
138
170
|
// transformed type (input/output types diverge, breaking Properties<T>).
|
|
139
|
-
const dropAnnotation = !!transform || this.hasTransformedFieldRef(node.fields);
|
|
171
|
+
const dropAnnotation = !!transform || this.hasTransformedFieldRef(node.fields) || this.hasDivergentFieldRef(node.fields);
|
|
172
|
+
if (dropAnnotation)
|
|
173
|
+
this.droppedAnnotationTypes.add(node.name.value);
|
|
140
174
|
const schemaName = dropAnnotation
|
|
141
175
|
? `${name}Schema`
|
|
142
176
|
: `${name}Schema: z.ZodObject<Properties<${typeName}>>`;
|
|
@@ -148,7 +182,7 @@ export class ZodSchemaVisitor extends BaseSchemaVisitor {
|
|
|
148
182
|
.withName(schemaName)
|
|
149
183
|
.withContent([
|
|
150
184
|
`z.object({`,
|
|
151
|
-
indent(`__typename: z.literal('${node.name.value}')
|
|
185
|
+
indent(`__typename: z.literal('${node.name.value}'),`, 2),
|
|
152
186
|
shape,
|
|
153
187
|
`})${transformSuffix}`,
|
|
154
188
|
].join('\n'))
|
|
@@ -161,7 +195,7 @@ export class ZodSchemaVisitor extends BaseSchemaVisitor {
|
|
|
161
195
|
.withName(dropAnnotation ? `${name}Schema()` : `${name}Schema(): z.ZodObject<Properties<${typeName}>>`)
|
|
162
196
|
.withBlock([
|
|
163
197
|
indent(`return z.object({`),
|
|
164
|
-
indent(`__typename: z.literal('${node.name.value}')
|
|
198
|
+
indent(`__typename: z.literal('${node.name.value}'),`, 2),
|
|
165
199
|
shape,
|
|
166
200
|
indent(`})${transformSuffix}`),
|
|
167
201
|
].join('\n'))
|
|
@@ -228,13 +262,15 @@ export class ZodSchemaVisitor extends BaseSchemaVisitor {
|
|
|
228
262
|
}
|
|
229
263
|
buildInputFields(fields, visitor, name, graphqlName) {
|
|
230
264
|
const typeName = visitor.prefixTypeNamespace(name);
|
|
231
|
-
const shape = fields.map(field => generateFieldZodSchema(this.config, visitor, field, 2)).join(',\n');
|
|
265
|
+
const shape = fields.map(field => generateFieldZodSchema(this.config, visitor, field, 2, this)).join(',\n');
|
|
232
266
|
const transform = graphqlName ? this.getTransformForGraphQLName(graphqlName) : undefined;
|
|
233
267
|
if (transform) {
|
|
234
268
|
this.addTransformImport(transform);
|
|
235
269
|
}
|
|
236
270
|
const transformSuffix = transform ? `.transform(${transform.symbolName})` : '';
|
|
237
|
-
const dropAnnotation = !!transform || this.hasTransformedFieldRef(fields);
|
|
271
|
+
const dropAnnotation = !!transform || this.hasTransformedFieldRef(fields) || this.hasDivergentFieldRef(fields);
|
|
272
|
+
if (dropAnnotation && graphqlName)
|
|
273
|
+
this.droppedAnnotationTypes.add(graphqlName);
|
|
238
274
|
const schemaName = dropAnnotation
|
|
239
275
|
? `${name}Schema`
|
|
240
276
|
: `${name}Schema: z.ZodObject<Properties<${typeName}>>`;
|
|
@@ -257,13 +293,13 @@ export class ZodSchemaVisitor extends BaseSchemaVisitor {
|
|
|
257
293
|
}
|
|
258
294
|
}
|
|
259
295
|
}
|
|
260
|
-
function generateFieldZodSchema(config, visitor, field, indentCount) {
|
|
261
|
-
const gen = generateFieldTypeZodSchema(config, visitor, field, field.type);
|
|
296
|
+
function generateFieldZodSchema(config, visitor, field, indentCount, schemaVisitor) {
|
|
297
|
+
const gen = generateFieldTypeZodSchema(config, visitor, field, field.type, undefined, schemaVisitor);
|
|
262
298
|
return indent(`${field.name.value}: ${maybeLazy(visitor, field.type, gen)}`, indentCount);
|
|
263
299
|
}
|
|
264
|
-
function generateFieldTypeZodSchema(config, visitor, field, type, parentType) {
|
|
300
|
+
function generateFieldTypeZodSchema(config, visitor, field, type, parentType, schemaVisitor) {
|
|
265
301
|
if (isListType(type)) {
|
|
266
|
-
const gen = generateFieldTypeZodSchema(config, visitor, field, type.type, type);
|
|
302
|
+
const gen = generateFieldTypeZodSchema(config, visitor, field, type.type, type, schemaVisitor);
|
|
267
303
|
if (!isNonNullType(parentType)) {
|
|
268
304
|
const arrayGen = `z.array(${maybeLazy(visitor, type.type, gen)})`;
|
|
269
305
|
const maybeLazyGen = applyDirectives(config, field, arrayGen);
|
|
@@ -272,11 +308,11 @@ function generateFieldTypeZodSchema(config, visitor, field, type, parentType) {
|
|
|
272
308
|
return `z.array(${maybeLazy(visitor, type.type, gen)})`;
|
|
273
309
|
}
|
|
274
310
|
if (isNonNullType(type)) {
|
|
275
|
-
const gen = generateFieldTypeZodSchema(config, visitor, field, type.type, type);
|
|
311
|
+
const gen = generateFieldTypeZodSchema(config, visitor, field, type.type, type, schemaVisitor);
|
|
276
312
|
return maybeLazy(visitor, type.type, gen);
|
|
277
313
|
}
|
|
278
314
|
if (isNamedType(type)) {
|
|
279
|
-
const gen = generateNameNodeZodSchema(config, visitor, type.name);
|
|
315
|
+
const gen = generateNameNodeZodSchema(config, visitor, type.name, schemaVisitor);
|
|
280
316
|
if (isListType(parentType))
|
|
281
317
|
return `${gen}.nullable()`;
|
|
282
318
|
let appliedDirectivesGen = applyDirectives(config, field, gen);
|
|
@@ -315,7 +351,7 @@ function applyDirectives(config, field, gen) {
|
|
|
315
351
|
}
|
|
316
352
|
return gen;
|
|
317
353
|
}
|
|
318
|
-
function generateNameNodeZodSchema(config, visitor, node) {
|
|
354
|
+
function generateNameNodeZodSchema(config, visitor, node, schemaVisitor) {
|
|
319
355
|
const converter = visitor.getNameNodeConverter(node);
|
|
320
356
|
switch (converter?.targetKind) {
|
|
321
357
|
case 'InterfaceTypeDefinition':
|
|
@@ -333,11 +369,11 @@ function generateNameNodeZodSchema(config, visitor, node) {
|
|
|
333
369
|
case 'EnumTypeDefinition':
|
|
334
370
|
return `${converter.convertName()}Schema`;
|
|
335
371
|
case 'ScalarTypeDefinition':
|
|
336
|
-
return zod4Scalar(config, visitor, node.value);
|
|
372
|
+
return zod4Scalar(config, visitor, node.value, schemaVisitor);
|
|
337
373
|
default:
|
|
338
374
|
if (converter?.targetKind)
|
|
339
375
|
console.warn('Unknown targetKind', converter?.targetKind);
|
|
340
|
-
return zod4Scalar(config, visitor, node.value);
|
|
376
|
+
return zod4Scalar(config, visitor, node.value, schemaVisitor);
|
|
341
377
|
}
|
|
342
378
|
}
|
|
343
379
|
function maybeLazy(visitor, type, schema) {
|
|
@@ -348,7 +384,7 @@ function maybeLazy(visitor, type, schema) {
|
|
|
348
384
|
const isComplexType = !isScalarType(schemaType) && !isEnumType(schemaType);
|
|
349
385
|
return isComplexType ? `z.lazy(() => ${schema})` : schema;
|
|
350
386
|
}
|
|
351
|
-
function zod4Scalar(config, visitor, scalarName) {
|
|
387
|
+
function zod4Scalar(config, visitor, scalarName, schemaVisitor) {
|
|
352
388
|
if (config.scalarSchemas?.[scalarName])
|
|
353
389
|
return config.scalarSchemas[scalarName];
|
|
354
390
|
const tsType = visitor.getScalarType(scalarName);
|
|
@@ -364,5 +400,6 @@ function zod4Scalar(config, visitor, scalarName) {
|
|
|
364
400
|
return config.defaultScalarTypeSchema;
|
|
365
401
|
}
|
|
366
402
|
console.warn('unhandled scalar name:', scalarName);
|
|
403
|
+
schemaVisitor?.markAnySchemaUsed();
|
|
367
404
|
return anySchema;
|
|
368
405
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@davars/graphql-codegen-zod",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "GraphQL Code Generator plugin that generates Zod v4 validation schemas with transform support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -13,13 +13,15 @@
|
|
|
13
13
|
"import": {
|
|
14
14
|
"types": "./dist/index.d.ts",
|
|
15
15
|
"default": "./dist/index.js"
|
|
16
|
-
}
|
|
16
|
+
},
|
|
17
|
+
"default": "./dist/index.js"
|
|
17
18
|
},
|
|
18
19
|
"./config": {
|
|
19
20
|
"import": {
|
|
20
21
|
"types": "./dist/transform_config.d.ts",
|
|
21
22
|
"default": "./dist/transform_config.js"
|
|
22
|
-
}
|
|
23
|
+
},
|
|
24
|
+
"default": "./dist/transform_config.js"
|
|
23
25
|
}
|
|
24
26
|
},
|
|
25
27
|
"main": "dist/index.js",
|