@kubb/plugin-zod 5.0.0-alpha.24 → 5.0.0-alpha.26

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.
Files changed (47) hide show
  1. package/dist/index.cjs +1673 -88
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.ts +317 -2
  4. package/dist/index.js +1646 -88
  5. package/dist/index.js.map +1 -1
  6. package/package.json +5 -33
  7. package/src/components/Operations.tsx +22 -15
  8. package/src/components/Zod.tsx +18 -118
  9. package/src/components/ZodMini.tsx +41 -0
  10. package/src/constants.ts +5 -0
  11. package/src/generators/zodGenerator.tsx +165 -158
  12. package/src/generators/zodGeneratorLegacy.tsx +401 -0
  13. package/src/index.ts +11 -1
  14. package/src/plugin.ts +105 -129
  15. package/src/presets.ts +25 -0
  16. package/src/printers/printerZod.ts +271 -0
  17. package/src/printers/printerZodMini.ts +246 -0
  18. package/src/resolvers/resolverZod.ts +71 -0
  19. package/src/resolvers/resolverZodLegacy.ts +60 -0
  20. package/src/types.ts +121 -92
  21. package/src/utils.ts +248 -0
  22. package/dist/components-DW4Q2yVq.js +0 -868
  23. package/dist/components-DW4Q2yVq.js.map +0 -1
  24. package/dist/components-qFUGs0vU.cjs +0 -916
  25. package/dist/components-qFUGs0vU.cjs.map +0 -1
  26. package/dist/components.cjs +0 -4
  27. package/dist/components.d.ts +0 -56
  28. package/dist/components.js +0 -2
  29. package/dist/generators-C9BCTLXg.cjs +0 -301
  30. package/dist/generators-C9BCTLXg.cjs.map +0 -1
  31. package/dist/generators-maqx12yN.js +0 -290
  32. package/dist/generators-maqx12yN.js.map +0 -1
  33. package/dist/generators.cjs +0 -4
  34. package/dist/generators.d.ts +0 -12
  35. package/dist/generators.js +0 -2
  36. package/dist/templates/ToZod.source.cjs +0 -7
  37. package/dist/templates/ToZod.source.cjs.map +0 -1
  38. package/dist/templates/ToZod.source.d.ts +0 -7
  39. package/dist/templates/ToZod.source.js +0 -6
  40. package/dist/templates/ToZod.source.js.map +0 -1
  41. package/dist/types-CClg-ikj.d.ts +0 -172
  42. package/src/components/index.ts +0 -2
  43. package/src/generators/index.ts +0 -2
  44. package/src/generators/operationsGenerator.tsx +0 -50
  45. package/src/parser.ts +0 -952
  46. package/src/templates/ToZod.source.ts +0 -4
  47. package/templates/ToZod.ts +0 -61
package/src/presets.ts ADDED
@@ -0,0 +1,25 @@
1
+ import { definePresets } from '@kubb/core'
2
+ import { zodGenerator } from './generators/zodGenerator.tsx'
3
+ import { zodGeneratorLegacy } from './generators/zodGeneratorLegacy.tsx'
4
+ import { resolverZod } from './resolvers/resolverZod.ts'
5
+ import { resolverZodLegacy } from './resolvers/resolverZodLegacy.ts'
6
+ import type { ResolverZod } from './types.ts'
7
+
8
+ /**
9
+ * Built-in preset registry for `@kubb/plugin-zod`.
10
+ *
11
+ * - `default` — uses `resolverZod` and `zodGenerator` (current naming conventions).
12
+ * - `kubbV4` — uses `resolverZodLegacy` and `zodGeneratorLegacy` (Kubb v4 naming conventions).
13
+ */
14
+ export const presets = definePresets<ResolverZod>({
15
+ default: {
16
+ name: 'default',
17
+ resolvers: [resolverZod],
18
+ generators: [zodGenerator],
19
+ },
20
+ kubbV4: {
21
+ name: 'kubbV4',
22
+ resolvers: [resolverZodLegacy],
23
+ generators: [zodGeneratorLegacy],
24
+ },
25
+ })
@@ -0,0 +1,271 @@
1
+ import { stringify } from '@internals/utils'
2
+ import { createSchema, extractRefName, narrowSchema, syncSchemaRef } from '@kubb/ast'
3
+ import type { PrinterFactoryOptions } from '@kubb/core'
4
+ import { definePrinter } from '@kubb/core'
5
+ import type { PluginZod, ResolverZod } from '../types.ts'
6
+ import { applyModifiers, containsSelfRef, formatLiteral, lengthConstraints, numberConstraints, shouldCoerce } from '../utils.ts'
7
+
8
+ export type ZodOptions = {
9
+ coercion?: PluginZod['resolvedOptions']['coercion']
10
+ guidType?: PluginZod['resolvedOptions']['guidType']
11
+ wrapOutput?: PluginZod['resolvedOptions']['wrapOutput']
12
+ resolver?: ResolverZod
13
+ schemaName?: string
14
+ /**
15
+ * Property keys to exclude from the generated object schema via `.omit({ key: true })`.
16
+ */
17
+ keysToOmit?: Array<string>
18
+ }
19
+
20
+ type ZodPrinterFactory = PrinterFactoryOptions<'zod', ZodOptions, string, string>
21
+
22
+ /**
23
+ * Zod v4 printer built with `definePrinter`.
24
+ *
25
+ * Converts a `SchemaNode` AST into a **standard** Zod v4 code string
26
+ * using the chainable method API (`.optional()`, `.nullable()`, etc.).
27
+ *
28
+ * For the `zod/mini` functional API, see {@link printerZodMini}.
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * const printer = printerZod({ coercion: false })
33
+ * const code = printer.print(stringSchemaNode) // "z.string()"
34
+ * ```
35
+ */
36
+ export const printerZod = definePrinter<ZodPrinterFactory>((options) => {
37
+ return {
38
+ name: 'zod',
39
+ options,
40
+ nodes: {
41
+ any: () => 'z.any()',
42
+ unknown: () => 'z.unknown()',
43
+ void: () => 'z.void()',
44
+ never: () => 'z.never()',
45
+ boolean: () => 'z.boolean()',
46
+ null: () => 'z.null()',
47
+ string(node) {
48
+ const base = shouldCoerce(this.options.coercion, 'strings') ? 'z.coerce.string()' : 'z.string()'
49
+
50
+ return `${base}${lengthConstraints(node)}`
51
+ },
52
+ number(node) {
53
+ const base = shouldCoerce(this.options.coercion, 'numbers') ? 'z.coerce.number()' : 'z.number()'
54
+
55
+ return `${base}${numberConstraints(node)}`
56
+ },
57
+ integer(node) {
58
+ const base = shouldCoerce(this.options.coercion, 'numbers') ? 'z.coerce.number().int()' : 'z.int()'
59
+
60
+ return `${base}${numberConstraints(node)}`
61
+ },
62
+ bigint() {
63
+ return shouldCoerce(this.options.coercion, 'numbers') ? 'z.coerce.bigint()' : 'z.bigint()'
64
+ },
65
+ date(node) {
66
+ if (node.representation === 'string') {
67
+ return 'z.iso.date()'
68
+ }
69
+
70
+ return shouldCoerce(this.options.coercion, 'dates') ? 'z.coerce.date()' : 'z.date()'
71
+ },
72
+ datetime(node) {
73
+ if (node.offset) return 'z.iso.datetime({ offset: true })'
74
+ if (node.local) return 'z.iso.datetime({ local: true })'
75
+
76
+ return 'z.iso.datetime()'
77
+ },
78
+ time(node) {
79
+ if (node.representation === 'string') {
80
+ return 'z.iso.time()'
81
+ }
82
+
83
+ return shouldCoerce(this.options.coercion, 'dates') ? 'z.coerce.date()' : 'z.date()'
84
+ },
85
+ uuid(node) {
86
+ const base = this.options.guidType === 'guid' ? 'z.guid()' : 'z.uuid()'
87
+
88
+ return `${base}${lengthConstraints(node)}`
89
+ },
90
+ email(node) {
91
+ return `z.email()${lengthConstraints(node)}`
92
+ },
93
+ url(node) {
94
+ return `z.url()${lengthConstraints(node)}`
95
+ },
96
+ ipv4: () => 'z.ipv4()',
97
+ ipv6: () => 'z.ipv6()',
98
+ blob: () => 'z.instanceof(File)',
99
+ enum(node) {
100
+ const values = node.namedEnumValues?.map((v) => v.value) ?? node.enumValues ?? []
101
+ const nonNullValues = values.filter((v): v is string | number | boolean => v !== null)
102
+
103
+ // asConst-style enum: use z.union([z.literal(…), …])
104
+ if (node.namedEnumValues?.length) {
105
+ const literals = nonNullValues.map((v) => `z.literal(${formatLiteral(v)})`)
106
+
107
+ if (literals.length === 1) return literals[0]!
108
+ return `z.union([${literals.join(', ')}])`
109
+ }
110
+
111
+ // Regular enum: use z.enum([…])
112
+ return `z.enum([${nonNullValues.map(formatLiteral).join(', ')}])`
113
+ },
114
+ ref(node) {
115
+ if (!node.name) return undefined
116
+ const refName = node.ref ? (extractRefName(node.ref) ?? node.name) : node.name
117
+ const resolvedName = node.ref ? (this.options.resolver?.default(refName, 'function') ?? refName) : node.name
118
+ const isSelfRef = node.ref && this.options.schemaName != null && resolvedName === this.options.schemaName
119
+
120
+ if (isSelfRef) {
121
+ return `z.lazy(() => ${resolvedName})`
122
+ }
123
+
124
+ return resolvedName
125
+ },
126
+ object(node) {
127
+ const properties = node.properties
128
+ .map((prop) => {
129
+ const { name: propName, schema } = prop
130
+
131
+ const meta = syncSchemaRef(schema)
132
+
133
+ const isNullable = meta.nullable
134
+ const isOptional = schema.optional
135
+ const isNullish = schema.nullish
136
+
137
+ const hasSelfRef =
138
+ this.options.schemaName != null && containsSelfRef(schema, { schemaName: this.options.schemaName, resolver: this.options.resolver })
139
+ const baseOutput = this.transform(schema) ?? this.transform(createSchema({ type: 'unknown' }))!
140
+ // Strip z.lazy() wrappers inside object getters — the getter itself provides deferred evaluation
141
+ const resolvedOutput = hasSelfRef ? baseOutput.replaceAll(`z.lazy(() => ${this.options.schemaName})`, this.options.schemaName!) : baseOutput
142
+
143
+ const wrappedOutput = this.options.wrapOutput ? this.options.wrapOutput({ output: resolvedOutput, schema }) || resolvedOutput : resolvedOutput
144
+
145
+ const value = applyModifiers({
146
+ value: wrappedOutput,
147
+ nullable: isNullable,
148
+ optional: isOptional,
149
+ nullish: isNullish,
150
+ defaultValue: meta.default,
151
+ description: meta.description,
152
+ })
153
+
154
+ if (hasSelfRef) {
155
+ return `get "${propName}"() { return ${value} }`
156
+ }
157
+ return `"${propName}": ${value}`
158
+ })
159
+ .join(',\n ')
160
+
161
+ let result = `z.object({\n ${properties}\n })`
162
+
163
+ // Handle additionalProperties as .catchall() or .strict()
164
+ if (node.additionalProperties && node.additionalProperties !== true) {
165
+ const catchallType = this.transform(node.additionalProperties)
166
+ if (catchallType) {
167
+ result += `.catchall(${catchallType})`
168
+ }
169
+ } else if (node.additionalProperties === true) {
170
+ result += `.catchall(${this.transform(createSchema({ type: 'unknown' }))})`
171
+ } else if (node.additionalProperties === false) {
172
+ result += '.strict()'
173
+ }
174
+
175
+ return result
176
+ },
177
+ array(node) {
178
+ const items = (node.items ?? []).map((item) => this.transform(item)).filter(Boolean)
179
+ const inner = items.join(', ') || this.transform(createSchema({ type: 'unknown' }))!
180
+ let result = `z.array(${inner})${lengthConstraints(node)}`
181
+
182
+ if (node.unique) {
183
+ result += `.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })`
184
+ }
185
+
186
+ return result
187
+ },
188
+ tuple(node) {
189
+ const items = (node.items ?? []).map((item) => this.transform(item)).filter(Boolean)
190
+
191
+ return `z.tuple([${items.join(', ')}])`
192
+ },
193
+ union(node) {
194
+ const nodeMembers = node.members ?? []
195
+ const members = nodeMembers.map((m) => this.transform(m)).filter(Boolean)
196
+ if (members.length === 0) return ''
197
+ if (members.length === 1) return members[0]!
198
+ if (node.discriminatorPropertyName && !nodeMembers.some((m) => m.type === 'intersection')) {
199
+ // z.discriminatedUnion requires ZodObject members; intersections (ZodIntersection) are not
200
+ // assignable to $ZodDiscriminant, so fall back to z.union when any member is an intersection.
201
+ return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)}, [${members.join(', ')}])`
202
+ }
203
+
204
+ return `z.union([${members.join(', ')}])`
205
+ },
206
+ intersection(node) {
207
+ const members = node.members ?? []
208
+ if (members.length === 0) return ''
209
+
210
+ const [first, ...rest] = members
211
+ if (!first) return ''
212
+
213
+ let base = this.transform(first)
214
+ if (!base) return ''
215
+
216
+ for (const member of rest) {
217
+ if (member.primitive === 'string') {
218
+ const s = narrowSchema(member, 'string')
219
+ const c = lengthConstraints(s ?? {})
220
+ if (c) {
221
+ base += c
222
+ continue
223
+ }
224
+ } else if (member.primitive === 'number' || member.primitive === 'integer') {
225
+ const n = narrowSchema(member, 'number') ?? narrowSchema(member, 'integer')
226
+ const c = numberConstraints(n ?? {})
227
+ if (c) {
228
+ base += c
229
+ continue
230
+ }
231
+ } else if (member.primitive === 'array') {
232
+ const a = narrowSchema(member, 'array')
233
+ const c = lengthConstraints(a ?? {})
234
+ if (c) {
235
+ base += c
236
+ continue
237
+ }
238
+ }
239
+ const transformed = this.transform(member)
240
+ if (transformed) base = `${base}.and(${transformed})`
241
+ }
242
+
243
+ return base
244
+ },
245
+ },
246
+ print(node) {
247
+ const { keysToOmit } = this.options
248
+
249
+ let base = this.transform(node)
250
+ if (!base) return null
251
+
252
+ const meta = syncSchemaRef(node)
253
+
254
+ if (keysToOmit?.length && meta.primitive === 'object' && !(meta.type === 'union' && meta.discriminatorPropertyName)) {
255
+ // Mirror printerTs `nonNullable: true`: when omitting keys, the resulting
256
+ // schema is a new non-nullable object type — skip optional/nullable/nullish.
257
+ // Discriminated unions (z.discriminatedUnion) do not support .omit(), so skip them.
258
+ base = `${base}.omit({ ${keysToOmit.map((k) => `"${k}": true`).join(', ')} })`
259
+ }
260
+
261
+ return applyModifiers({
262
+ value: base,
263
+ nullable: meta.nullable,
264
+ optional: meta.optional,
265
+ nullish: meta.nullish,
266
+ defaultValue: meta.default,
267
+ description: meta.description,
268
+ })
269
+ },
270
+ }
271
+ })
@@ -0,0 +1,246 @@
1
+ import { stringify } from '@internals/utils'
2
+ import { createSchema, extractRefName, narrowSchema, syncSchemaRef } from '@kubb/ast'
3
+ import type { PrinterFactoryOptions } from '@kubb/core'
4
+ import { definePrinter } from '@kubb/core'
5
+ import type { PluginZod, ResolverZod } from '../types.ts'
6
+ import { applyMiniModifiers, containsSelfRef, formatLiteral, lengthChecksMini, numberChecksMini } from '../utils.ts'
7
+
8
+ export type ZodMiniOptions = {
9
+ guidType?: PluginZod['resolvedOptions']['guidType']
10
+ wrapOutput?: PluginZod['resolvedOptions']['wrapOutput']
11
+ resolver?: ResolverZod
12
+ schemaName?: string
13
+ /**
14
+ * Property keys to exclude from the generated object schema via `.omit({ key: true })`.
15
+ */
16
+ keysToOmit?: Array<string>
17
+ }
18
+
19
+ type ZodMiniPrinterFactory = PrinterFactoryOptions<'zod-mini', ZodMiniOptions, string, string>
20
+ /**
21
+ * Zod v4 **Mini** printer built with `definePrinter`.
22
+ *
23
+ * Converts a `SchemaNode` AST into a Zod v4 Mini code string using the
24
+ * functional API (`z.optional(z.string())`) for better tree-shaking.
25
+ *
26
+ * For the standard chainable API, see {@link printerZod}.
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * const printer = printerZodMini({})
31
+ * const code = printer.print(optionalStringNode) // "z.optional(z.string())"
32
+ * ```
33
+ */
34
+ export const printerZodMini = definePrinter<ZodMiniPrinterFactory>((options) => {
35
+ return {
36
+ name: 'zod-mini',
37
+ options,
38
+ nodes: {
39
+ any: () => 'z.any()',
40
+ unknown: () => 'z.unknown()',
41
+ void: () => 'z.void()',
42
+ never: () => 'z.never()',
43
+ boolean: () => 'z.boolean()',
44
+ null: () => 'z.null()',
45
+ string(node) {
46
+ return `z.string()${lengthChecksMini(node)}`
47
+ },
48
+ number(node) {
49
+ return `z.number()${numberChecksMini(node)}`
50
+ },
51
+ integer(node) {
52
+ return `z.int()${numberChecksMini(node)}`
53
+ },
54
+ bigint(node) {
55
+ return `z.bigint()${numberChecksMini(node)}`
56
+ },
57
+ date(node) {
58
+ if (node.representation === 'string') {
59
+ return 'z.iso.date()'
60
+ }
61
+
62
+ return 'z.date()'
63
+ },
64
+ datetime() {
65
+ // Mini mode: datetime validation via z.string() (z.iso.datetime not available in mini)
66
+ return 'z.string()'
67
+ },
68
+ time(node) {
69
+ if (node.representation === 'string') {
70
+ return 'z.iso.time()'
71
+ }
72
+
73
+ return 'z.date()'
74
+ },
75
+ uuid(node) {
76
+ const base = this.options.guidType === 'guid' ? 'z.guid()' : 'z.uuid()'
77
+
78
+ return `${base}${lengthChecksMini(node)}`
79
+ },
80
+ email(node) {
81
+ return `z.email()${lengthChecksMini(node)}`
82
+ },
83
+ url(node) {
84
+ return `z.url()${lengthChecksMini(node)}`
85
+ },
86
+ ipv4: () => 'z.ipv4()',
87
+ ipv6: () => 'z.ipv6()',
88
+ blob: () => 'z.instanceof(File)',
89
+ enum(node) {
90
+ const values = node.namedEnumValues?.map((v) => v.value) ?? node.enumValues ?? []
91
+ const nonNullValues = values.filter((v): v is string | number | boolean => v !== null)
92
+
93
+ // asConst-style enum: use z.union([z.literal(…), …])
94
+ if (node.namedEnumValues?.length) {
95
+ const literals = nonNullValues.map((v) => `z.literal(${formatLiteral(v)})`)
96
+ if (literals.length === 1) return literals[0]!
97
+ return `z.union([${literals.join(', ')}])`
98
+ }
99
+
100
+ // Regular enum: use z.enum([…])
101
+ return `z.enum([${nonNullValues.map(formatLiteral).join(', ')}])`
102
+ },
103
+
104
+ ref(node) {
105
+ if (!node.name) return undefined
106
+ const refName = node.ref ? (extractRefName(node.ref) ?? node.name) : node.name
107
+ const resolvedName = node.ref ? (this.options.resolver?.default(refName, 'function') ?? refName) : node.name
108
+ const isSelfRef = node.ref && this.options.schemaName != null && resolvedName === this.options.schemaName
109
+
110
+ if (isSelfRef) {
111
+ return `z.lazy(() => ${resolvedName})`
112
+ }
113
+
114
+ return resolvedName
115
+ },
116
+ object(node) {
117
+ const properties = node.properties
118
+ .map((prop) => {
119
+ const { name: propName, schema } = prop
120
+
121
+ const meta = syncSchemaRef(schema)
122
+
123
+ const isNullable = meta.nullable
124
+ const isOptional = schema.optional
125
+ const isNullish = schema.nullish
126
+
127
+ const hasSelfRef =
128
+ this.options.schemaName != null && containsSelfRef(schema, { schemaName: this.options.schemaName, resolver: this.options.resolver })
129
+ const baseOutput = this.transform(schema) ?? this.transform(createSchema({ type: 'unknown' }))!
130
+ // Strip z.lazy() wrappers inside object getters — the getter itself provides deferred evaluation
131
+ const resolvedOutput = hasSelfRef ? baseOutput.replaceAll(`z.lazy(() => ${this.options.schemaName})`, this.options.schemaName!) : baseOutput
132
+
133
+ const wrappedOutput = this.options.wrapOutput ? this.options.wrapOutput({ output: resolvedOutput, schema }) || resolvedOutput : resolvedOutput
134
+
135
+ const value = applyMiniModifiers({
136
+ value: wrappedOutput,
137
+ nullable: isNullable,
138
+ optional: isOptional,
139
+ nullish: isNullish,
140
+ defaultValue: meta.default,
141
+ })
142
+
143
+ if (hasSelfRef) {
144
+ return `get "${propName}"() { return ${value} }`
145
+ }
146
+ return `"${propName}": ${value}`
147
+ })
148
+ .join(',\n ')
149
+
150
+ return `z.object({\n ${properties}\n })`
151
+ },
152
+ array(node) {
153
+ const items = (node.items ?? []).map((item) => this.transform(item)).filter(Boolean)
154
+ const inner = items.join(', ') || this.transform(createSchema({ type: 'unknown' }))!
155
+ let result = `z.array(${inner})${lengthChecksMini(node)}`
156
+
157
+ if (node.unique) {
158
+ result += `.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })`
159
+ }
160
+
161
+ return result
162
+ },
163
+ tuple(node) {
164
+ const items = (node.items ?? []).map((item) => this.transform(item)).filter(Boolean)
165
+
166
+ return `z.tuple([${items.join(', ')}])`
167
+ },
168
+ union(node) {
169
+ const nodeMembers = node.members ?? []
170
+ const members = nodeMembers.map((m) => this.transform(m)).filter(Boolean)
171
+ if (members.length === 0) return ''
172
+ if (members.length === 1) return members[0]!
173
+ if (node.discriminatorPropertyName && !nodeMembers.some((m) => m.type === 'intersection')) {
174
+ // z.discriminatedUnion requires ZodObject members; intersections (ZodIntersection) are not
175
+ // assignable to $ZodDiscriminant, so fall back to z.union when any member is an intersection.
176
+ return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)}, [${members.join(', ')}])`
177
+ }
178
+
179
+ return `z.union([${members.join(', ')}])`
180
+ },
181
+ intersection(node) {
182
+ const members = node.members ?? []
183
+ if (members.length === 0) return ''
184
+
185
+ const [first, ...rest] = members
186
+ if (!first) return ''
187
+
188
+ let base = this.transform(first)
189
+ if (!base) return ''
190
+
191
+ for (const member of rest) {
192
+ if (member.primitive === 'string') {
193
+ const s = narrowSchema(member, 'string')
194
+ const c = lengthChecksMini(s ?? {})
195
+ if (c) {
196
+ base += c
197
+ continue
198
+ }
199
+ } else if (member.primitive === 'number' || member.primitive === 'integer') {
200
+ const n = narrowSchema(member, 'number') ?? narrowSchema(member, 'integer')
201
+ const c = numberChecksMini(n ?? {})
202
+ if (c) {
203
+ base += c
204
+ continue
205
+ }
206
+ } else if (member.primitive === 'array') {
207
+ const a = narrowSchema(member, 'array')
208
+ const c = lengthChecksMini(a ?? {})
209
+ if (c) {
210
+ base += c
211
+ continue
212
+ }
213
+ }
214
+ const transformed = this.transform(member)
215
+ if (transformed) base = `z.intersection(${base}, ${transformed})`
216
+ }
217
+
218
+ return base
219
+ },
220
+ },
221
+
222
+ print(node) {
223
+ const { keysToOmit } = this.options
224
+
225
+ let base = this.transform(node)
226
+ if (!base) return null
227
+
228
+ const meta = syncSchemaRef(node)
229
+
230
+ if (keysToOmit?.length && meta.primitive === 'object' && !(meta.type === 'union' && meta.discriminatorPropertyName)) {
231
+ // Mirror printerTs `nonNullable: true`: when omitting keys, the resulting
232
+ // schema is a new non-nullable object type — skip optional/nullable/nullish.
233
+ // Discriminated unions (z.discriminatedUnion) do not support .omit(), so skip them.
234
+ base = `${base}.omit({ ${keysToOmit.map((k) => `"${k}": true`).join(', ')} })`
235
+ }
236
+
237
+ return applyMiniModifiers({
238
+ value: base,
239
+ nullable: meta.nullable,
240
+ optional: meta.optional,
241
+ nullish: meta.nullish,
242
+ defaultValue: meta.default,
243
+ })
244
+ },
245
+ }
246
+ })
@@ -0,0 +1,71 @@
1
+ import { camelCase, pascalCase } from '@internals/utils'
2
+ import { defineResolver } from '@kubb/core'
3
+ import type { PluginZod } from '../types.ts'
4
+
5
+ function toSchemaName(name: string, type?: 'file' | 'function' | 'type' | 'const'): string {
6
+ const resolved = camelCase(name, {
7
+ suffix: type ? 'schema' : undefined,
8
+ isFile: type === 'file',
9
+ })
10
+
11
+ if (type === 'type') {
12
+ return pascalCase(resolved)
13
+ }
14
+
15
+ return resolved
16
+ }
17
+
18
+ /**
19
+ * Default resolver for `@kubb/plugin-zod`.
20
+ *
21
+ * Uses `camelCase` naming with a `Schema` suffix for function/type/const names.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * resolverZod.default('list pets', 'function') // → 'listPetsSchema'
26
+ * resolverZod.default('Pet', 'file') // → 'pet'
27
+ * resolverZod.resolveName('list pets') // → 'listPetsSchema'
28
+ * ```
29
+ */
30
+ export const resolverZod = defineResolver<PluginZod>(() => {
31
+ return {
32
+ name: 'default',
33
+ pluginName: 'plugin-zod',
34
+ default(name, type) {
35
+ return toSchemaName(name, type)
36
+ },
37
+ resolveName(name) {
38
+ return this.default(name, 'function')
39
+ },
40
+ resolveInferName(name) {
41
+ return pascalCase(name)
42
+ },
43
+ resolvePathName(name, type) {
44
+ return this.default(name, type)
45
+ },
46
+ resolveParamName(node, param) {
47
+ return this.resolveName(`${node.operationId} ${param.in} ${param.name}`)
48
+ },
49
+ resolveResponseStatusName(node, statusCode) {
50
+ return this.resolveName(`${node.operationId} Status ${statusCode}`)
51
+ },
52
+ resolveDataName(node) {
53
+ return this.resolveName(`${node.operationId} Data`)
54
+ },
55
+ resolveResponsesName(node) {
56
+ return this.resolveName(`${node.operationId} Responses`)
57
+ },
58
+ resolveResponseName(node) {
59
+ return this.resolveName(`${node.operationId} Response`)
60
+ },
61
+ resolvePathParamsName(node, param) {
62
+ return this.resolveParamName(node, param)
63
+ },
64
+ resolveQueryParamsName(node, param) {
65
+ return this.resolveParamName(node, param)
66
+ },
67
+ resolveHeaderParamsName(node, param) {
68
+ return this.resolveParamName(node, param)
69
+ },
70
+ }
71
+ })
@@ -0,0 +1,60 @@
1
+ import { defineResolver } from '@kubb/core'
2
+ import type { PluginZod } from '../types.ts'
3
+ import { resolverZod } from './resolverZod.ts'
4
+
5
+ /**
6
+ * Legacy resolver for `@kubb/plugin-zod` that reproduces the naming conventions
7
+ * used in Kubb v4. Enable via `compatibilityPreset: 'kubbV4'`
8
+ * (or by composing this resolver manually).
9
+ *
10
+ * Key differences from the default resolver:
11
+ * - Response status types: `<operationId><StatusCode>Schema` (e.g. `createPets201Schema`) instead of `<operationId>Status201Schema`
12
+ * - Default/error responses: `<operationId>ErrorSchema` instead of `<operationId>StatusDefaultSchema`
13
+ * - Request body: `<operationId>MutationRequestSchema` (non-GET) / `<operationId>QueryRequestSchema` (GET)
14
+ * - Combined responses type: `<operationId>MutationSchema` / `<operationId>QuerySchema`
15
+ * - Response union: `<operationId>MutationResponseSchema` / `<operationId>QueryResponseSchema`
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * import { resolverZodLegacy } from '@kubb/plugin-zod'
20
+ *
21
+ * resolverZodLegacy.resolveResponseStatusName(node, 201) // → 'createPets201Schema'
22
+ * resolverZodLegacy.resolveResponseStatusName(node, 'default') // → 'createPetsErrorSchema'
23
+ * resolverZodLegacy.resolveDataName(node) // → 'createPetsMutationRequestSchema' (POST)
24
+ * resolverZodLegacy.resolveResponsesName(node) // → 'createPetsMutationSchema' (POST)
25
+ * resolverZodLegacy.resolveResponseName(node) // → 'createPetsMutationResponseSchema' (POST)
26
+ * ```
27
+ */
28
+ export const resolverZodLegacy = defineResolver<PluginZod>(() => {
29
+ return {
30
+ ...resolverZod,
31
+ pluginName: 'plugin-zod',
32
+ resolveResponseStatusName(node, statusCode) {
33
+ if (statusCode === 'default') {
34
+ return this.resolveName(`${node.operationId} Error`)
35
+ }
36
+ return this.resolveName(`${node.operationId} ${statusCode}`)
37
+ },
38
+ resolveDataName(node) {
39
+ const suffix = node.method === 'GET' ? 'QueryRequest' : 'MutationRequest'
40
+ return this.resolveName(`${node.operationId} ${suffix}`)
41
+ },
42
+ resolveResponsesName(node) {
43
+ const suffix = node.method === 'GET' ? 'Query' : 'Mutation'
44
+ return this.resolveName(`${node.operationId} ${suffix}`)
45
+ },
46
+ resolveResponseName(node) {
47
+ const suffix = node.method === 'GET' ? 'QueryResponse' : 'MutationResponse'
48
+ return this.resolveName(`${node.operationId} ${suffix}`)
49
+ },
50
+ resolvePathParamsName(node, _param) {
51
+ return this.resolveName(`${node.operationId} PathParams`)
52
+ },
53
+ resolveQueryParamsName(node, _param) {
54
+ return this.resolveName(`${node.operationId} QueryParams`)
55
+ },
56
+ resolveHeaderParamsName(node, _param) {
57
+ return this.resolveName(`${node.operationId} HeaderParams`)
58
+ },
59
+ }
60
+ })