@kubb/plugin-zod 5.0.0-beta.15 → 5.0.0-beta.22
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/index.cjs +82 -109
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +82 -109
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/generators/zodGenerator.tsx +12 -12
- package/src/printers/printerZod.ts +36 -61
- package/src/printers/printerZodMini.ts +26 -47
- package/src/utils.ts +14 -30
|
@@ -53,7 +53,7 @@ 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(() => …)`.
|
|
@@ -90,6 +90,13 @@ function strictOneOfMember(member: string, node: ast.SchemaNode): string {
|
|
|
90
90
|
return member
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
function getMemberConstraint(member: ast.SchemaNode): string | undefined {
|
|
94
|
+
if (member.primitive === 'string') return lengthConstraints(ast.narrowSchema(member, 'string') ?? {}) || undefined
|
|
95
|
+
if (member.primitive === 'number' || member.primitive === 'integer')
|
|
96
|
+
return numberConstraints(ast.narrowSchema(member, 'number') ?? ast.narrowSchema(member, 'integer') ?? {}) || undefined
|
|
97
|
+
if (member.primitive === 'array') return lengthConstraints(ast.narrowSchema(member, 'array') ?? {}) || undefined
|
|
98
|
+
}
|
|
99
|
+
|
|
93
100
|
/**
|
|
94
101
|
* Zod v4 printer built with `definePrinter`.
|
|
95
102
|
*
|
|
@@ -184,7 +191,7 @@ export const printerZod = ast.definePrinter<PrinterZodFactory>((options) => {
|
|
|
184
191
|
return `z.enum([${nonNullValues.map(formatLiteral).join(', ')}])`
|
|
185
192
|
},
|
|
186
193
|
ref(node) {
|
|
187
|
-
if (!node.name) return
|
|
194
|
+
if (!node.name) return null
|
|
188
195
|
const refName = node.ref ? (ast.extractRefName(node.ref) ?? node.name) : node.name
|
|
189
196
|
const resolvedName = node.ref ? (this.options.resolver?.default(refName, 'function') ?? refName) : node.name
|
|
190
197
|
|
|
@@ -220,10 +227,7 @@ export const printerZod = ast.definePrinter<PrinterZodFactory>((options) => {
|
|
|
220
227
|
// When a property schema is not a ref but the metadata is from a ref (e.g., discriminator
|
|
221
228
|
// property override), skip applying the description from the ref target to avoid applying
|
|
222
229
|
// metadata from a replaced schema.
|
|
223
|
-
|
|
224
|
-
if (schema.type !== 'ref' && meta.type === 'ref') {
|
|
225
|
-
descriptionToApply = undefined
|
|
226
|
-
}
|
|
230
|
+
const descriptionToApply = schema.type !== 'ref' && meta.type === 'ref' ? undefined : meta.description
|
|
227
231
|
|
|
228
232
|
const value = applyModifiers({
|
|
229
233
|
value: wrappedOutput,
|
|
@@ -241,32 +245,27 @@ export const printerZod = ast.definePrinter<PrinterZodFactory>((options) => {
|
|
|
241
245
|
})
|
|
242
246
|
.join(',\n ')
|
|
243
247
|
|
|
244
|
-
|
|
248
|
+
const objectBase = `z.object({\n ${properties}\n })`
|
|
245
249
|
|
|
246
250
|
// Handle additionalProperties as .catchall() or .strict()
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
+
const result = (() => {
|
|
252
|
+
if (node.additionalProperties && node.additionalProperties !== true) {
|
|
253
|
+
const catchallType = this.transform(node.additionalProperties)
|
|
254
|
+
return catchallType ? `${objectBase}.catchall(${catchallType})` : objectBase
|
|
251
255
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
}
|
|
256
|
+
if (node.additionalProperties === true) return `${objectBase}.catchall(${this.transform(ast.createSchema({ type: 'unknown' }))})`
|
|
257
|
+
if (node.additionalProperties === false) return `${objectBase}.strict()`
|
|
258
|
+
return objectBase
|
|
259
|
+
})()
|
|
257
260
|
|
|
258
261
|
return result
|
|
259
262
|
},
|
|
260
263
|
array(node) {
|
|
261
264
|
const items = (node.items ?? []).map((item) => this.transform(item)).filter(Boolean)
|
|
262
265
|
const inner = items.join(', ') || this.transform(ast.createSchema({ type: 'unknown' }))!
|
|
263
|
-
|
|
266
|
+
const base = `z.array(${inner})${lengthConstraints(node)}`
|
|
264
267
|
|
|
265
|
-
|
|
266
|
-
result += `.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })`
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
return result
|
|
268
|
+
return node.unique ? `${base}.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })` : base
|
|
270
269
|
},
|
|
271
270
|
tuple(node) {
|
|
272
271
|
const items = (node.items ?? []).map((item) => this.transform(item)).filter(Boolean)
|
|
@@ -299,61 +298,37 @@ export const printerZod = ast.definePrinter<PrinterZodFactory>((options) => {
|
|
|
299
298
|
const [first, ...rest] = members
|
|
300
299
|
if (!first) return ''
|
|
301
300
|
|
|
302
|
-
|
|
303
|
-
if (!
|
|
301
|
+
const firstBase = this.transform(first)
|
|
302
|
+
if (!firstBase) return ''
|
|
304
303
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
const c = lengthConstraints(s ?? {})
|
|
309
|
-
if (c) {
|
|
310
|
-
base += c
|
|
311
|
-
continue
|
|
312
|
-
}
|
|
313
|
-
} else if (member.primitive === 'number' || member.primitive === 'integer') {
|
|
314
|
-
const n = ast.narrowSchema(member, 'number') ?? ast.narrowSchema(member, 'integer')
|
|
315
|
-
const c = numberConstraints(n ?? {})
|
|
316
|
-
if (c) {
|
|
317
|
-
base += c
|
|
318
|
-
continue
|
|
319
|
-
}
|
|
320
|
-
} else if (member.primitive === 'array') {
|
|
321
|
-
const a = ast.narrowSchema(member, 'array')
|
|
322
|
-
const c = lengthConstraints(a ?? {})
|
|
323
|
-
if (c) {
|
|
324
|
-
base += c
|
|
325
|
-
continue
|
|
326
|
-
}
|
|
327
|
-
}
|
|
304
|
+
return rest.reduce((acc, member) => {
|
|
305
|
+
const constraint = getMemberConstraint(member)
|
|
306
|
+
if (constraint) return acc + constraint
|
|
328
307
|
const transformed = this.transform(member)
|
|
329
|
-
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
return base
|
|
308
|
+
return transformed ? `${acc}.and(${transformed})` : acc
|
|
309
|
+
}, firstBase)
|
|
333
310
|
},
|
|
334
311
|
...options.nodes,
|
|
335
312
|
},
|
|
336
313
|
print(node) {
|
|
337
314
|
const { keysToOmit } = this.options
|
|
338
315
|
|
|
339
|
-
|
|
340
|
-
if (!
|
|
316
|
+
const transformed = this.transform(node)
|
|
317
|
+
if (!transformed) return null
|
|
341
318
|
|
|
342
319
|
const meta = ast.syncSchemaRef(node)
|
|
343
320
|
|
|
344
|
-
|
|
321
|
+
const base = (() => {
|
|
322
|
+
if (!keysToOmit?.length || meta.primitive !== 'object' || (meta.type === 'union' && meta.discriminatorPropertyName)) return transformed
|
|
345
323
|
// Mirror printerTs `nonNullable: true`: when omitting keys, the resulting
|
|
346
324
|
// schema is a new non-nullable object type — skip optional/nullable/nullish.
|
|
347
325
|
// Discriminated unions (z.discriminatedUnion) do not support .omit(), so skip them.
|
|
348
326
|
|
|
349
327
|
// If this is a lazy reference, apply omit inside the lazy function
|
|
350
|
-
const lazyMatch =
|
|
351
|
-
if (lazyMatch) {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
base = `${base}.omit({ ${keysToOmit.map((k: string) => `"${k}": true`).join(', ')} })`
|
|
355
|
-
}
|
|
356
|
-
}
|
|
328
|
+
const lazyMatch = transformed.match(/^z\.lazy\(\(\)\s*=>\s*(.+)\)$/)
|
|
329
|
+
if (lazyMatch) return `z.lazy(() => ${lazyMatch[1]}.omit({ ${keysToOmit.map((k: string) => `"${k}": true`).join(', ')} }))`
|
|
330
|
+
return `${transformed}.omit({ ${keysToOmit.map((k: string) => `"${k}": true`).join(', ')} })`
|
|
331
|
+
})()
|
|
357
332
|
|
|
358
333
|
return applyModifiers({
|
|
359
334
|
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(() => …)`.
|
|
@@ -70,6 +70,13 @@ function strictOneOfMember(member: string, node: ast.SchemaNode): string {
|
|
|
70
70
|
return member
|
|
71
71
|
}
|
|
72
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
|
+
|
|
73
80
|
/**
|
|
74
81
|
* Zod v4 **Mini** printer built with `definePrinter`.
|
|
75
82
|
*
|
|
@@ -153,7 +160,7 @@ export const printerZodMini = ast.definePrinter<PrinterZodMiniFactory>((options)
|
|
|
153
160
|
},
|
|
154
161
|
|
|
155
162
|
ref(node) {
|
|
156
|
-
if (!node.name) return
|
|
163
|
+
if (!node.name) return null
|
|
157
164
|
const refName = node.ref ? (ast.extractRefName(node.ref) ?? node.name) : node.name
|
|
158
165
|
const resolvedName = node.ref ? (this.options.resolver?.default(refName, 'function') ?? refName) : node.name
|
|
159
166
|
|
|
@@ -206,13 +213,9 @@ export const printerZodMini = ast.definePrinter<PrinterZodMiniFactory>((options)
|
|
|
206
213
|
array(node) {
|
|
207
214
|
const items = (node.items ?? []).map((item) => this.transform(item)).filter(Boolean)
|
|
208
215
|
const inner = items.join(', ') || this.transform(ast.createSchema({ type: 'unknown' }))!
|
|
209
|
-
|
|
216
|
+
const base = `z.array(${inner})${lengthChecksMini(node)}`
|
|
210
217
|
|
|
211
|
-
|
|
212
|
-
result += `.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })`
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
return result
|
|
218
|
+
return node.unique ? `${base}.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })` : base
|
|
216
219
|
},
|
|
217
220
|
tuple(node) {
|
|
218
221
|
const items = (node.items ?? []).map((item) => this.transform(item)).filter(Boolean)
|
|
@@ -245,61 +248,37 @@ export const printerZodMini = ast.definePrinter<PrinterZodMiniFactory>((options)
|
|
|
245
248
|
const [first, ...rest] = members
|
|
246
249
|
if (!first) return ''
|
|
247
250
|
|
|
248
|
-
|
|
249
|
-
if (!
|
|
251
|
+
const firstBase = this.transform(first)
|
|
252
|
+
if (!firstBase) return ''
|
|
250
253
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
const c = lengthChecksMini(s ?? {})
|
|
255
|
-
if (c) {
|
|
256
|
-
base += c
|
|
257
|
-
continue
|
|
258
|
-
}
|
|
259
|
-
} else if (member.primitive === 'number' || member.primitive === 'integer') {
|
|
260
|
-
const n = ast.narrowSchema(member, 'number') ?? ast.narrowSchema(member, 'integer')
|
|
261
|
-
const c = numberChecksMini(n ?? {})
|
|
262
|
-
if (c) {
|
|
263
|
-
base += c
|
|
264
|
-
continue
|
|
265
|
-
}
|
|
266
|
-
} else if (member.primitive === 'array') {
|
|
267
|
-
const a = ast.narrowSchema(member, 'array')
|
|
268
|
-
const c = lengthChecksMini(a ?? {})
|
|
269
|
-
if (c) {
|
|
270
|
-
base += c
|
|
271
|
-
continue
|
|
272
|
-
}
|
|
273
|
-
}
|
|
254
|
+
return rest.reduce((acc, member) => {
|
|
255
|
+
const constraint = getMemberConstraintMini(member)
|
|
256
|
+
if (constraint) return acc + constraint
|
|
274
257
|
const transformed = this.transform(member)
|
|
275
|
-
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
return base
|
|
258
|
+
return transformed ? `z.intersection(${acc}, ${transformed})` : acc
|
|
259
|
+
}, firstBase)
|
|
279
260
|
},
|
|
280
261
|
...options.nodes,
|
|
281
262
|
},
|
|
282
263
|
print(node) {
|
|
283
264
|
const { keysToOmit } = this.options
|
|
284
265
|
|
|
285
|
-
|
|
286
|
-
if (!
|
|
266
|
+
const transformed = this.transform(node)
|
|
267
|
+
if (!transformed) return null
|
|
287
268
|
|
|
288
269
|
const meta = ast.syncSchemaRef(node)
|
|
289
270
|
|
|
290
|
-
|
|
271
|
+
const base = (() => {
|
|
272
|
+
if (!keysToOmit?.length || meta.primitive !== 'object' || (meta.type === 'union' && meta.discriminatorPropertyName)) return transformed
|
|
291
273
|
// Mirror printerTs `nonNullable: true`: when omitting keys, the resulting
|
|
292
274
|
// schema is a new non-nullable object type — skip optional/nullable/nullish.
|
|
293
275
|
// Discriminated unions (z.discriminatedUnion) do not support .omit(), so skip them.
|
|
294
276
|
|
|
295
277
|
// If this is a lazy reference, apply omit inside the lazy function
|
|
296
|
-
const lazyMatch =
|
|
297
|
-
if (lazyMatch) {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
base = `${base}.omit({ ${keysToOmit.map((k: string) => `"${k}": true`).join(', ')} })`
|
|
301
|
-
}
|
|
302
|
-
}
|
|
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
|
+
})()
|
|
303
282
|
|
|
304
283
|
return applyMiniModifiers({
|
|
305
284
|
value: base,
|
package/src/utils.ts
CHANGED
|
@@ -158,21 +158,14 @@ export function lengthChecksMini({ min, max, pattern }: LengthConstraints): stri
|
|
|
158
158
|
* to a schema value string using the chainable Zod v4 API.
|
|
159
159
|
*/
|
|
160
160
|
export function applyModifiers({ value, nullable, optional, nullish, defaultValue, description }: ModifierOptions): string {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
}
|
|
169
|
-
if (defaultValue !== undefined) {
|
|
170
|
-
result = `${result}.default(${formatDefault(defaultValue)})`
|
|
171
|
-
}
|
|
172
|
-
if (description) {
|
|
173
|
-
result = `${result}.describe(${stringify(description)})`
|
|
174
|
-
}
|
|
175
|
-
return result
|
|
161
|
+
const withModifier = (() => {
|
|
162
|
+
if (nullish || (nullable && optional)) return `${value}.nullish()`
|
|
163
|
+
if (optional) return `${value}.optional()`
|
|
164
|
+
if (nullable) return `${value}.nullable()`
|
|
165
|
+
return value
|
|
166
|
+
})()
|
|
167
|
+
const withDefault = defaultValue !== undefined ? `${withModifier}.default(${formatDefault(defaultValue)})` : withModifier
|
|
168
|
+
return description ? `${withDefault}.describe(${stringify(description)})` : withDefault
|
|
176
169
|
}
|
|
177
170
|
|
|
178
171
|
/**
|
|
@@ -180,21 +173,12 @@ export function applyModifiers({ value, nullable, optional, nullish, defaultValu
|
|
|
180
173
|
* (`z.nullable()`, `z.optional()`, `z.nullish()`).
|
|
181
174
|
*/
|
|
182
175
|
export function applyMiniModifiers({ value, nullable, optional, nullish, defaultValue }: Omit<ModifierOptions, 'description'>): string {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
if (optional) {
|
|
191
|
-
result = `z.optional(${result})`
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
if (defaultValue !== undefined) {
|
|
195
|
-
result = `z._default(${result}, ${formatDefault(defaultValue)})`
|
|
196
|
-
}
|
|
197
|
-
return result
|
|
176
|
+
const withModifier = (() => {
|
|
177
|
+
if (nullish) return `z.nullish(${value})`
|
|
178
|
+
const withNullable = nullable ? `z.nullable(${value})` : value
|
|
179
|
+
return optional ? `z.optional(${withNullable})` : withNullable
|
|
180
|
+
})()
|
|
181
|
+
return defaultValue !== undefined ? `z._default(${withModifier}, ${formatDefault(defaultValue)})` : withModifier
|
|
198
182
|
}
|
|
199
183
|
|
|
200
184
|
type BuildGroupedParamsSchemaOptions = {
|