@kubb/plugin-zod 5.0.0-beta.3 → 5.0.0-beta.31
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/README.md +25 -5
- package/dist/index.cjs +644 -197
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +103 -34
- package/dist/index.js +640 -199
- package/dist/index.js.map +1 -1
- package/extension.yaml +968 -0
- package/package.json +10 -13
- package/src/components/Operations.tsx +6 -5
- package/src/components/Zod.tsx +1 -1
- package/src/generators/zodGenerator.tsx +210 -60
- package/src/plugin.ts +23 -20
- package/src/printers/printerZod.ts +91 -68
- package/src/printers/printerZodMini.ts +46 -49
- package/src/resolvers/resolverZod.ts +31 -19
- package/src/types.ts +57 -21
- package/src/utils.ts +113 -36
|
@@ -2,7 +2,7 @@ import { stringify } from '@internals/utils'
|
|
|
2
2
|
|
|
3
3
|
import { ast } from '@kubb/core'
|
|
4
4
|
import type { PluginZod, ResolverZod } from '../types.ts'
|
|
5
|
-
import { applyModifiers, formatLiteral, lengthConstraints, numberConstraints, shouldCoerce } from '../utils.ts'
|
|
5
|
+
import { applyModifiers, containsCodec, formatLiteral, getCodec, lengthConstraints, numberConstraints, shouldCoerce } from '../utils.ts'
|
|
6
6
|
import type { AdapterOas } from '@kubb/adapter-oas'
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -53,12 +53,21 @@ export type PrinterZodOptions = {
|
|
|
53
53
|
/**
|
|
54
54
|
* Properties to exclude using `.omit({ key: true })`.
|
|
55
55
|
*/
|
|
56
|
-
keysToOmit?: Array<string>
|
|
56
|
+
keysToOmit?: Array<string> | null
|
|
57
57
|
/**
|
|
58
58
|
* Schema names that form circular dependency chains.
|
|
59
59
|
* Properties referencing these emit lazy getters wrapping refs in `z.lazy(() => …)`.
|
|
60
60
|
*/
|
|
61
61
|
cyclicSchemas?: ReadonlySet<string>
|
|
62
|
+
/**
|
|
63
|
+
* Print direction for `dateType: 'date'` fields (`Date` in TypeScript):
|
|
64
|
+
* - `'output'` (default) — decode the wire `string` into a `Date` (response bodies).
|
|
65
|
+
* - `'input'` — encode a `Date` back into the wire `string` (request bodies/params).
|
|
66
|
+
*
|
|
67
|
+
* Diverging the directions requires the generator to emit an `${name}InputSchema`
|
|
68
|
+
* variant for each date-bearing component.
|
|
69
|
+
*/
|
|
70
|
+
direction?: 'input' | 'output'
|
|
62
71
|
/**
|
|
63
72
|
* Custom handler map for node type overrides.
|
|
64
73
|
*/
|
|
@@ -70,6 +79,33 @@ export type PrinterZodOptions = {
|
|
|
70
79
|
*/
|
|
71
80
|
export type PrinterZodFactory = ast.PrinterFactoryOptions<'zod', PrinterZodOptions, string, string>
|
|
72
81
|
|
|
82
|
+
function strictOneOfMember(member: string, node: ast.SchemaNode): string {
|
|
83
|
+
if (node.type === 'object' && node.additionalProperties === undefined) {
|
|
84
|
+
return `${member}.strict()`
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (node.type === 'ref') {
|
|
88
|
+
if (member.startsWith('z.lazy(')) {
|
|
89
|
+
return member
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const schema = ast.syncSchemaRef(node)
|
|
93
|
+
|
|
94
|
+
if (schema.type === 'object' && (schema.additionalProperties === undefined || schema.additionalProperties === false)) {
|
|
95
|
+
return `${member}.strict()`
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return member
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function getMemberConstraint(member: ast.SchemaNode): string | undefined {
|
|
103
|
+
if (member.primitive === 'string') return lengthConstraints(ast.narrowSchema(member, 'string') ?? {}) || undefined
|
|
104
|
+
if (member.primitive === 'number' || member.primitive === 'integer')
|
|
105
|
+
return numberConstraints(ast.narrowSchema(member, 'number') ?? ast.narrowSchema(member, 'integer') ?? {}) || undefined
|
|
106
|
+
if (member.primitive === 'array') return lengthConstraints(ast.narrowSchema(member, 'array') ?? {}) || undefined
|
|
107
|
+
}
|
|
108
|
+
|
|
73
109
|
/**
|
|
74
110
|
* Zod v4 printer built with `definePrinter`.
|
|
75
111
|
*
|
|
@@ -112,11 +148,13 @@ export const printerZod = ast.definePrinter<PrinterZodFactory>((options) => {
|
|
|
112
148
|
return shouldCoerce(this.options.coercion, 'numbers') ? 'z.coerce.bigint()' : 'z.bigint()'
|
|
113
149
|
},
|
|
114
150
|
date(node) {
|
|
115
|
-
|
|
116
|
-
|
|
151
|
+
// representation: 'date' → typed as `Date`; decode/encode at the boundary.
|
|
152
|
+
const codec = getCodec(node)
|
|
153
|
+
if (codec) {
|
|
154
|
+
return this.options.direction === 'input' ? codec.encode(node) : codec.decode(node)
|
|
117
155
|
}
|
|
118
156
|
|
|
119
|
-
return
|
|
157
|
+
return 'z.iso.date()'
|
|
120
158
|
},
|
|
121
159
|
datetime(node) {
|
|
122
160
|
const offset = node.offset || this.options.dateType === 'stringOffset'
|
|
@@ -164,9 +202,17 @@ export const printerZod = ast.definePrinter<PrinterZodFactory>((options) => {
|
|
|
164
202
|
return `z.enum([${nonNullValues.map(formatLiteral).join(', ')}])`
|
|
165
203
|
},
|
|
166
204
|
ref(node) {
|
|
167
|
-
if (!node.name) return
|
|
205
|
+
if (!node.name) return null
|
|
168
206
|
const refName = node.ref ? (ast.extractRefName(node.ref) ?? node.name) : node.name
|
|
169
|
-
|
|
207
|
+
|
|
208
|
+
// In the input direction, a date-bearing component resolves to its `${name}InputSchema`
|
|
209
|
+
// variant so request bodies encode `Date → string` instead of decoding.
|
|
210
|
+
const useInputVariant = node.ref != null && this.options.direction === 'input' && containsCodec(node)
|
|
211
|
+
const resolvedName = node.ref
|
|
212
|
+
? useInputVariant
|
|
213
|
+
? (this.options.resolver?.resolveInputSchemaName(refName) ?? refName)
|
|
214
|
+
: (this.options.resolver?.default(refName, 'function') ?? refName)
|
|
215
|
+
: node.name
|
|
170
216
|
|
|
171
217
|
if (node.ref && this.options.cyclicSchemas?.has(refName)) {
|
|
172
218
|
return `z.lazy(() => ${resolvedName})`
|
|
@@ -188,19 +234,19 @@ export const printerZod = ast.definePrinter<PrinterZodFactory>((options) => {
|
|
|
188
234
|
const hasSelfRef = this.options.cyclicSchemas != null && ast.containsCircularRef(schema, { circularSchemas: this.options.cyclicSchemas })
|
|
189
235
|
// Inside a getter the getter itself defers evaluation, so suppress
|
|
190
236
|
// z.lazy() wrapping on nested refs by temporarily clearing cyclicSchemas.
|
|
237
|
+
// Save before clearing: this.options === options (same reference via definePrinter),
|
|
238
|
+
// so reading options.cyclicSchemas after mutation would return undefined.
|
|
239
|
+
const savedCyclicSchemas = this.options.cyclicSchemas
|
|
191
240
|
if (hasSelfRef) this.options.cyclicSchemas = undefined
|
|
192
241
|
const baseOutput = this.transform(schema) ?? this.transform(ast.createSchema({ type: 'unknown' }))!
|
|
193
|
-
if (hasSelfRef) this.options.cyclicSchemas =
|
|
242
|
+
if (hasSelfRef) this.options.cyclicSchemas = savedCyclicSchemas
|
|
194
243
|
|
|
195
244
|
const wrappedOutput = this.options.wrapOutput ? this.options.wrapOutput({ output: baseOutput, schema }) || baseOutput : baseOutput
|
|
196
245
|
|
|
197
246
|
// When a property schema is not a ref but the metadata is from a ref (e.g., discriminator
|
|
198
247
|
// property override), skip applying the description from the ref target to avoid applying
|
|
199
248
|
// metadata from a replaced schema.
|
|
200
|
-
|
|
201
|
-
if (schema.type !== 'ref' && meta.type === 'ref') {
|
|
202
|
-
descriptionToApply = undefined
|
|
203
|
-
}
|
|
249
|
+
const descriptionToApply = schema.type !== 'ref' && meta.type === 'ref' ? undefined : meta.description
|
|
204
250
|
|
|
205
251
|
const value = applyModifiers({
|
|
206
252
|
value: wrappedOutput,
|
|
@@ -218,32 +264,27 @@ export const printerZod = ast.definePrinter<PrinterZodFactory>((options) => {
|
|
|
218
264
|
})
|
|
219
265
|
.join(',\n ')
|
|
220
266
|
|
|
221
|
-
|
|
267
|
+
const objectBase = `z.object({\n ${properties}\n })`
|
|
222
268
|
|
|
223
269
|
// Handle additionalProperties as .catchall() or .strict()
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
270
|
+
const result = (() => {
|
|
271
|
+
if (node.additionalProperties && node.additionalProperties !== true) {
|
|
272
|
+
const catchallType = this.transform(node.additionalProperties)
|
|
273
|
+
return catchallType ? `${objectBase}.catchall(${catchallType})` : objectBase
|
|
228
274
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
}
|
|
275
|
+
if (node.additionalProperties === true) return `${objectBase}.catchall(${this.transform(ast.createSchema({ type: 'unknown' }))})`
|
|
276
|
+
if (node.additionalProperties === false) return `${objectBase}.strict()`
|
|
277
|
+
return objectBase
|
|
278
|
+
})()
|
|
234
279
|
|
|
235
280
|
return result
|
|
236
281
|
},
|
|
237
282
|
array(node) {
|
|
238
283
|
const items = (node.items ?? []).map((item) => this.transform(item)).filter(Boolean)
|
|
239
284
|
const inner = items.join(', ') || this.transform(ast.createSchema({ type: 'unknown' }))!
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
if (node.unique) {
|
|
243
|
-
result += `.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })`
|
|
244
|
-
}
|
|
285
|
+
const base = `z.array(${inner})${lengthConstraints(node)}`
|
|
245
286
|
|
|
246
|
-
return
|
|
287
|
+
return node.unique ? `${base}.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })` : base
|
|
247
288
|
},
|
|
248
289
|
tuple(node) {
|
|
249
290
|
const items = (node.items ?? []).map((item) => this.transform(item)).filter(Boolean)
|
|
@@ -252,7 +293,13 @@ export const printerZod = ast.definePrinter<PrinterZodFactory>((options) => {
|
|
|
252
293
|
},
|
|
253
294
|
union(node) {
|
|
254
295
|
const nodeMembers = node.members ?? []
|
|
255
|
-
const members = nodeMembers
|
|
296
|
+
const members = nodeMembers
|
|
297
|
+
.map((memberNode) => {
|
|
298
|
+
const member = this.transform(memberNode)
|
|
299
|
+
|
|
300
|
+
return member && node.strategy === 'one' ? strictOneOfMember(member, memberNode) : member
|
|
301
|
+
})
|
|
302
|
+
.filter(Boolean)
|
|
256
303
|
if (members.length === 0) return ''
|
|
257
304
|
if (members.length === 1) return members[0]!
|
|
258
305
|
if (node.discriminatorPropertyName && !nodeMembers.some((m) => m.type === 'intersection')) {
|
|
@@ -270,61 +317,37 @@ export const printerZod = ast.definePrinter<PrinterZodFactory>((options) => {
|
|
|
270
317
|
const [first, ...rest] = members
|
|
271
318
|
if (!first) return ''
|
|
272
319
|
|
|
273
|
-
|
|
274
|
-
if (!
|
|
320
|
+
const firstBase = this.transform(first)
|
|
321
|
+
if (!firstBase) return ''
|
|
275
322
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
const c = lengthConstraints(s ?? {})
|
|
280
|
-
if (c) {
|
|
281
|
-
base += c
|
|
282
|
-
continue
|
|
283
|
-
}
|
|
284
|
-
} else if (member.primitive === 'number' || member.primitive === 'integer') {
|
|
285
|
-
const n = ast.narrowSchema(member, 'number') ?? ast.narrowSchema(member, 'integer')
|
|
286
|
-
const c = numberConstraints(n ?? {})
|
|
287
|
-
if (c) {
|
|
288
|
-
base += c
|
|
289
|
-
continue
|
|
290
|
-
}
|
|
291
|
-
} else if (member.primitive === 'array') {
|
|
292
|
-
const a = ast.narrowSchema(member, 'array')
|
|
293
|
-
const c = lengthConstraints(a ?? {})
|
|
294
|
-
if (c) {
|
|
295
|
-
base += c
|
|
296
|
-
continue
|
|
297
|
-
}
|
|
298
|
-
}
|
|
323
|
+
return rest.reduce((acc, member) => {
|
|
324
|
+
const constraint = getMemberConstraint(member)
|
|
325
|
+
if (constraint) return acc + constraint
|
|
299
326
|
const transformed = this.transform(member)
|
|
300
|
-
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
return base
|
|
327
|
+
return transformed ? `${acc}.and(${transformed})` : acc
|
|
328
|
+
}, firstBase)
|
|
304
329
|
},
|
|
305
330
|
...options.nodes,
|
|
306
331
|
},
|
|
307
332
|
print(node) {
|
|
308
333
|
const { keysToOmit } = this.options
|
|
309
334
|
|
|
310
|
-
|
|
311
|
-
if (!
|
|
335
|
+
const transformed = this.transform(node)
|
|
336
|
+
if (!transformed) return null
|
|
312
337
|
|
|
313
338
|
const meta = ast.syncSchemaRef(node)
|
|
314
339
|
|
|
315
|
-
|
|
340
|
+
const base = (() => {
|
|
341
|
+
if (!keysToOmit?.length || meta.primitive !== 'object' || (meta.type === 'union' && meta.discriminatorPropertyName)) return transformed
|
|
316
342
|
// Mirror printerTs `nonNullable: true`: when omitting keys, the resulting
|
|
317
343
|
// schema is a new non-nullable object type — skip optional/nullable/nullish.
|
|
318
344
|
// Discriminated unions (z.discriminatedUnion) do not support .omit(), so skip them.
|
|
319
345
|
|
|
320
346
|
// If this is a lazy reference, apply omit inside the lazy function
|
|
321
|
-
const lazyMatch =
|
|
322
|
-
if (lazyMatch) {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
base = `${base}.omit({ ${keysToOmit.map((k: string) => `"${k}": true`).join(', ')} })`
|
|
326
|
-
}
|
|
327
|
-
}
|
|
347
|
+
const lazyMatch = transformed.match(/^z\.lazy\(\(\)\s*=>\s*(.+)\)$/)
|
|
348
|
+
if (lazyMatch) return `z.lazy(() => ${lazyMatch[1]}.omit({ ${keysToOmit.map((k: string) => `"${k}": true`).join(', ')} }))`
|
|
349
|
+
return `${transformed}.omit({ ${keysToOmit.map((k: string) => `"${k}": true`).join(', ')} })`
|
|
350
|
+
})()
|
|
328
351
|
|
|
329
352
|
return applyModifiers({
|
|
330
353
|
value: base,
|
|
@@ -45,7 +45,7 @@ export type PrinterZodMiniOptions = {
|
|
|
45
45
|
/**
|
|
46
46
|
* Properties to exclude using `.omit({ key: true })`.
|
|
47
47
|
*/
|
|
48
|
-
keysToOmit?: Array<string>
|
|
48
|
+
keysToOmit?: Array<string> | null
|
|
49
49
|
/**
|
|
50
50
|
* Schema names that form circular dependency chains.
|
|
51
51
|
* Properties referencing these emit lazy getters wrapping refs in `z.lazy(() => …)`.
|
|
@@ -61,6 +61,22 @@ export type PrinterZodMiniOptions = {
|
|
|
61
61
|
* Factory options for the Zod Mini printer, defining input/output types and configuration.
|
|
62
62
|
*/
|
|
63
63
|
export type PrinterZodMiniFactory = ast.PrinterFactoryOptions<'zod-mini', PrinterZodMiniOptions, string, string>
|
|
64
|
+
|
|
65
|
+
function strictOneOfMember(member: string, node: ast.SchemaNode): string {
|
|
66
|
+
if (node.type === 'object' && (node.additionalProperties === undefined || node.additionalProperties === false)) {
|
|
67
|
+
return member.replace(/^z\.object\(/, 'z.strictObject(')
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return member
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function getMemberConstraintMini(member: ast.SchemaNode): string | undefined {
|
|
74
|
+
if (member.primitive === 'string') return lengthChecksMini(ast.narrowSchema(member, 'string') ?? {}) || undefined
|
|
75
|
+
if (member.primitive === 'number' || member.primitive === 'integer')
|
|
76
|
+
return numberChecksMini(ast.narrowSchema(member, 'number') ?? ast.narrowSchema(member, 'integer') ?? {}) || undefined
|
|
77
|
+
if (member.primitive === 'array') return lengthChecksMini(ast.narrowSchema(member, 'array') ?? {}) || undefined
|
|
78
|
+
}
|
|
79
|
+
|
|
64
80
|
/**
|
|
65
81
|
* Zod v4 **Mini** printer built with `definePrinter`.
|
|
66
82
|
*
|
|
@@ -144,7 +160,7 @@ export const printerZodMini = ast.definePrinter<PrinterZodMiniFactory>((options)
|
|
|
144
160
|
},
|
|
145
161
|
|
|
146
162
|
ref(node) {
|
|
147
|
-
if (!node.name) return
|
|
163
|
+
if (!node.name) return null
|
|
148
164
|
const refName = node.ref ? (ast.extractRefName(node.ref) ?? node.name) : node.name
|
|
149
165
|
const resolvedName = node.ref ? (this.options.resolver?.default(refName, 'function') ?? refName) : node.name
|
|
150
166
|
|
|
@@ -168,9 +184,12 @@ export const printerZodMini = ast.definePrinter<PrinterZodMiniFactory>((options)
|
|
|
168
184
|
const hasSelfRef = this.options.cyclicSchemas != null && ast.containsCircularRef(schema, { circularSchemas: this.options.cyclicSchemas })
|
|
169
185
|
// Inside a getter the getter itself defers evaluation, so suppress
|
|
170
186
|
// z.lazy() wrapping on nested refs by temporarily clearing cyclicSchemas.
|
|
187
|
+
// Save before clearing: this.options === options (same reference via definePrinter),
|
|
188
|
+
// so reading options.cyclicSchemas after mutation would return undefined.
|
|
189
|
+
const savedCyclicSchemas = this.options.cyclicSchemas
|
|
171
190
|
if (hasSelfRef) this.options.cyclicSchemas = undefined
|
|
172
191
|
const baseOutput = this.transform(schema) ?? this.transform(ast.createSchema({ type: 'unknown' }))!
|
|
173
|
-
if (hasSelfRef) this.options.cyclicSchemas =
|
|
192
|
+
if (hasSelfRef) this.options.cyclicSchemas = savedCyclicSchemas
|
|
174
193
|
|
|
175
194
|
const wrappedOutput = this.options.wrapOutput ? this.options.wrapOutput({ output: baseOutput, schema }) || baseOutput : baseOutput
|
|
176
195
|
|
|
@@ -194,13 +213,9 @@ export const printerZodMini = ast.definePrinter<PrinterZodMiniFactory>((options)
|
|
|
194
213
|
array(node) {
|
|
195
214
|
const items = (node.items ?? []).map((item) => this.transform(item)).filter(Boolean)
|
|
196
215
|
const inner = items.join(', ') || this.transform(ast.createSchema({ type: 'unknown' }))!
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
if (node.unique) {
|
|
200
|
-
result += `.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })`
|
|
201
|
-
}
|
|
216
|
+
const base = `z.array(${inner})${lengthChecksMini(node)}`
|
|
202
217
|
|
|
203
|
-
return
|
|
218
|
+
return node.unique ? `${base}.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })` : base
|
|
204
219
|
},
|
|
205
220
|
tuple(node) {
|
|
206
221
|
const items = (node.items ?? []).map((item) => this.transform(item)).filter(Boolean)
|
|
@@ -209,7 +224,13 @@ export const printerZodMini = ast.definePrinter<PrinterZodMiniFactory>((options)
|
|
|
209
224
|
},
|
|
210
225
|
union(node) {
|
|
211
226
|
const nodeMembers = node.members ?? []
|
|
212
|
-
const members = nodeMembers
|
|
227
|
+
const members = nodeMembers
|
|
228
|
+
.map((memberNode) => {
|
|
229
|
+
const member = this.transform(memberNode)
|
|
230
|
+
|
|
231
|
+
return member && node.strategy === 'one' ? strictOneOfMember(member, memberNode) : member
|
|
232
|
+
})
|
|
233
|
+
.filter(Boolean)
|
|
213
234
|
if (members.length === 0) return ''
|
|
214
235
|
if (members.length === 1) return members[0]!
|
|
215
236
|
if (node.discriminatorPropertyName && !nodeMembers.some((m) => m.type === 'intersection')) {
|
|
@@ -227,61 +248,37 @@ export const printerZodMini = ast.definePrinter<PrinterZodMiniFactory>((options)
|
|
|
227
248
|
const [first, ...rest] = members
|
|
228
249
|
if (!first) return ''
|
|
229
250
|
|
|
230
|
-
|
|
231
|
-
if (!
|
|
251
|
+
const firstBase = this.transform(first)
|
|
252
|
+
if (!firstBase) return ''
|
|
232
253
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
const c = lengthChecksMini(s ?? {})
|
|
237
|
-
if (c) {
|
|
238
|
-
base += c
|
|
239
|
-
continue
|
|
240
|
-
}
|
|
241
|
-
} else if (member.primitive === 'number' || member.primitive === 'integer') {
|
|
242
|
-
const n = ast.narrowSchema(member, 'number') ?? ast.narrowSchema(member, 'integer')
|
|
243
|
-
const c = numberChecksMini(n ?? {})
|
|
244
|
-
if (c) {
|
|
245
|
-
base += c
|
|
246
|
-
continue
|
|
247
|
-
}
|
|
248
|
-
} else if (member.primitive === 'array') {
|
|
249
|
-
const a = ast.narrowSchema(member, 'array')
|
|
250
|
-
const c = lengthChecksMini(a ?? {})
|
|
251
|
-
if (c) {
|
|
252
|
-
base += c
|
|
253
|
-
continue
|
|
254
|
-
}
|
|
255
|
-
}
|
|
254
|
+
return rest.reduce((acc, member) => {
|
|
255
|
+
const constraint = getMemberConstraintMini(member)
|
|
256
|
+
if (constraint) return acc + constraint
|
|
256
257
|
const transformed = this.transform(member)
|
|
257
|
-
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
return base
|
|
258
|
+
return transformed ? `z.intersection(${acc}, ${transformed})` : acc
|
|
259
|
+
}, firstBase)
|
|
261
260
|
},
|
|
262
261
|
...options.nodes,
|
|
263
262
|
},
|
|
264
263
|
print(node) {
|
|
265
264
|
const { keysToOmit } = this.options
|
|
266
265
|
|
|
267
|
-
|
|
268
|
-
if (!
|
|
266
|
+
const transformed = this.transform(node)
|
|
267
|
+
if (!transformed) return null
|
|
269
268
|
|
|
270
269
|
const meta = ast.syncSchemaRef(node)
|
|
271
270
|
|
|
272
|
-
|
|
271
|
+
const base = (() => {
|
|
272
|
+
if (!keysToOmit?.length || meta.primitive !== 'object' || (meta.type === 'union' && meta.discriminatorPropertyName)) return transformed
|
|
273
273
|
// Mirror printerTs `nonNullable: true`: when omitting keys, the resulting
|
|
274
274
|
// schema is a new non-nullable object type — skip optional/nullable/nullish.
|
|
275
275
|
// Discriminated unions (z.discriminatedUnion) do not support .omit(), so skip them.
|
|
276
276
|
|
|
277
277
|
// If this is a lazy reference, apply omit inside the lazy function
|
|
278
|
-
const lazyMatch =
|
|
279
|
-
if (lazyMatch) {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
base = `${base}.omit({ ${keysToOmit.map((k: string) => `"${k}": true`).join(', ')} })`
|
|
283
|
-
}
|
|
284
|
-
}
|
|
278
|
+
const lazyMatch = transformed.match(/^z\.lazy\(\(\)\s*=>\s*(.+)\)$/)
|
|
279
|
+
if (lazyMatch) return `z.lazy(() => ${lazyMatch[1]}.omit({ ${keysToOmit.map((k: string) => `"${k}": true`).join(', ')} }))`
|
|
280
|
+
return `${transformed}.omit({ ${keysToOmit.map((k: string) => `"${k}": true`).join(', ')} })`
|
|
281
|
+
})()
|
|
285
282
|
|
|
286
283
|
return applyMiniModifiers({
|
|
287
284
|
value: base,
|
|
@@ -1,57 +1,69 @@
|
|
|
1
|
-
import { camelCase, pascalCase } from '@internals/utils'
|
|
1
|
+
import { camelCase, ensureValidVarName, pascalCase } from '@internals/utils'
|
|
2
2
|
import { defineResolver } from '@kubb/core'
|
|
3
3
|
import type { PluginZod } from '../types.ts'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* Default resolver used by `@kubb/plugin-zod`. Decides the names and file
|
|
7
|
+
* paths for every generated Zod schema. Schemas use camelCase with a
|
|
8
|
+
* `Schema` suffix (`listPetsSchema`); their inferred types use PascalCase.
|
|
7
9
|
*
|
|
8
|
-
*
|
|
10
|
+
* @example Resolve schema and type names
|
|
11
|
+
* ```ts
|
|
12
|
+
* import { resolverZod } from '@kubb/plugin-zod'
|
|
9
13
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
14
|
+
* resolverZod.default('list pets', 'function') // 'listPetsSchema'
|
|
15
|
+
* resolverZod.resolveSchemaTypeName('pet') // 'PetSchema'
|
|
16
|
+
* ```
|
|
12
17
|
*/
|
|
13
|
-
export const resolverZod = defineResolver<PluginZod>((
|
|
18
|
+
export const resolverZod = defineResolver<PluginZod>(() => {
|
|
14
19
|
return {
|
|
15
20
|
name: 'default',
|
|
16
21
|
pluginName: 'plugin-zod',
|
|
17
22
|
default(name, type) {
|
|
18
|
-
|
|
23
|
+
const resolved = camelCase(name, { isFile: type === 'file', suffix: type ? 'schema' : undefined })
|
|
24
|
+
return type === 'file' ? resolved : ensureValidVarName(resolved)
|
|
19
25
|
},
|
|
20
26
|
resolveSchemaName(name) {
|
|
21
|
-
return camelCase(name, { suffix: 'schema' })
|
|
27
|
+
return ensureValidVarName(camelCase(name, { suffix: 'schema' }))
|
|
22
28
|
},
|
|
23
29
|
resolveSchemaTypeName(name) {
|
|
24
|
-
return pascalCase(name, { suffix: 'schema' })
|
|
30
|
+
return ensureValidVarName(pascalCase(name, { suffix: 'schema' }))
|
|
31
|
+
},
|
|
32
|
+
resolveInputSchemaName(name) {
|
|
33
|
+
return this.resolveSchemaName(`${name} input`)
|
|
34
|
+
},
|
|
35
|
+
resolveInputSchemaTypeName(name) {
|
|
36
|
+
return this.resolveSchemaTypeName(`${name} input`)
|
|
25
37
|
},
|
|
26
38
|
resolveTypeName(name) {
|
|
27
|
-
return pascalCase(name)
|
|
39
|
+
return ensureValidVarName(pascalCase(name))
|
|
28
40
|
},
|
|
29
41
|
resolvePathName(name, type) {
|
|
30
|
-
return
|
|
42
|
+
return this.default(name, type)
|
|
31
43
|
},
|
|
32
44
|
resolveParamName(node, param) {
|
|
33
|
-
return
|
|
45
|
+
return this.resolveSchemaName(`${node.operationId} ${param.in} ${param.name}`)
|
|
34
46
|
},
|
|
35
47
|
resolveResponseStatusName(node, statusCode) {
|
|
36
|
-
return
|
|
48
|
+
return this.resolveSchemaName(`${node.operationId} Status ${statusCode}`)
|
|
37
49
|
},
|
|
38
50
|
resolveDataName(node) {
|
|
39
|
-
return
|
|
51
|
+
return this.resolveSchemaName(`${node.operationId} Data`)
|
|
40
52
|
},
|
|
41
53
|
resolveResponsesName(node) {
|
|
42
|
-
return
|
|
54
|
+
return this.resolveSchemaName(`${node.operationId} Responses`)
|
|
43
55
|
},
|
|
44
56
|
resolveResponseName(node) {
|
|
45
|
-
return
|
|
57
|
+
return this.resolveSchemaName(`${node.operationId} Response`)
|
|
46
58
|
},
|
|
47
59
|
resolvePathParamsName(node, param) {
|
|
48
|
-
return
|
|
60
|
+
return this.resolveParamName(node, param)
|
|
49
61
|
},
|
|
50
62
|
resolveQueryParamsName(node, param) {
|
|
51
|
-
return
|
|
63
|
+
return this.resolveParamName(node, param)
|
|
52
64
|
},
|
|
53
65
|
resolveHeaderParamsName(node, param) {
|
|
54
|
-
return
|
|
66
|
+
return this.resolveParamName(node, param)
|
|
55
67
|
},
|
|
56
68
|
}
|
|
57
69
|
})
|