@kubb/plugin-zod 5.0.0-beta.10 → 5.0.0-beta.103

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.
@@ -1,310 +0,0 @@
1
- import { stringify } from '@internals/utils'
2
-
3
- import { ast } from '@kubb/core'
4
- import type { PluginZod, ResolverZod } from '../types.ts'
5
- import { applyMiniModifiers, formatLiteral, lengthChecksMini, numberChecksMini } from '../utils.ts'
6
-
7
- /**
8
- * Partial map of node-type overrides for the Zod Mini printer.
9
- *
10
- * Each key is a `SchemaType` string (e.g. `'date'`, `'string'`). The function
11
- * replaces the built-in handler for that node type. Use `this.transform` to
12
- * recurse into nested schema nodes, and `this.options` to read printer options.
13
- *
14
- * @example Override the `date` handler
15
- * ```ts
16
- * pluginZod({
17
- * mini: true,
18
- * printer: {
19
- * nodes: {
20
- * date(node) {
21
- * return 'z.iso.date()'
22
- * },
23
- * },
24
- * },
25
- * })
26
- * ```
27
- */
28
- export type PrinterZodMiniNodes = ast.PrinterPartial<string, PrinterZodMiniOptions>
29
-
30
- export type PrinterZodMiniOptions = {
31
- /**
32
- * Use `z.guid()` or `z.uuid()` for UUID/GUID validation.
33
- *
34
- * @default 'uuid'
35
- */
36
- guidType?: PluginZod['resolvedOptions']['guidType']
37
- /**
38
- * Hook to transform generated Zod schema before output.
39
- */
40
- wrapOutput?: PluginZod['resolvedOptions']['wrapOutput']
41
- /**
42
- * Transforms raw schema names into valid JavaScript identifiers.
43
- */
44
- resolver?: ResolverZod
45
- /**
46
- * Properties to exclude using `.omit({ key: true })`.
47
- */
48
- keysToOmit?: Array<string>
49
- /**
50
- * Schema names that form circular dependency chains.
51
- * Properties referencing these emit lazy getters wrapping refs in `z.lazy(() => …)`.
52
- */
53
- cyclicSchemas?: ReadonlySet<string>
54
- /**
55
- * Custom handler map for node type overrides.
56
- */
57
- nodes?: PrinterZodMiniNodes
58
- }
59
-
60
- /**
61
- * Factory options for the Zod Mini printer, defining input/output types and configuration.
62
- */
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
- /**
74
- * Zod v4 **Mini** printer built with `definePrinter`.
75
- *
76
- * Converts a `SchemaNode` AST into a Zod v4 code string using the functional API
77
- * (`z.optional(z.string())`) for improved tree-shaking. See {@link printerZod} for the chainable API.
78
- *
79
- * @example Functional Mini API
80
- * ```ts
81
- * const printer = printerZodMini({})
82
- * const code = printer.print(optionalStringNode) // "z.optional(z.string())"
83
- * ```
84
- */
85
- export const printerZodMini = ast.definePrinter<PrinterZodMiniFactory>((options) => {
86
- return {
87
- name: 'zod-mini',
88
- options,
89
- nodes: {
90
- any: () => 'z.any()',
91
- unknown: () => 'z.unknown()',
92
- void: () => 'z.void()',
93
- never: () => 'z.never()',
94
- boolean: () => 'z.boolean()',
95
- null: () => 'z.null()',
96
- string(node) {
97
- return `z.string()${lengthChecksMini(node)}`
98
- },
99
- number(node) {
100
- return `z.number()${numberChecksMini(node)}`
101
- },
102
- integer(node) {
103
- return `z.int()${numberChecksMini(node)}`
104
- },
105
- bigint(node) {
106
- return `z.bigint()${numberChecksMini(node)}`
107
- },
108
- date(node) {
109
- if (node.representation === 'string') {
110
- return 'z.iso.date()'
111
- }
112
-
113
- return 'z.date()'
114
- },
115
- datetime() {
116
- // Mini mode: datetime validation via z.string() (z.iso.datetime not available in mini)
117
- return 'z.string()'
118
- },
119
- time(node) {
120
- if (node.representation === 'string') {
121
- return 'z.iso.time()'
122
- }
123
-
124
- return 'z.date()'
125
- },
126
- uuid(node) {
127
- const base = this.options.guidType === 'guid' ? 'z.guid()' : 'z.uuid()'
128
-
129
- return `${base}${lengthChecksMini(node)}`
130
- },
131
- email(node) {
132
- return `z.email()${lengthChecksMini(node)}`
133
- },
134
- url(node) {
135
- return `z.url()${lengthChecksMini(node)}`
136
- },
137
- ipv4: () => 'z.ipv4()',
138
- ipv6: () => 'z.ipv6()',
139
- blob: () => 'z.instanceof(File)',
140
- enum(node) {
141
- const values = node.namedEnumValues?.map((v) => v.value) ?? node.enumValues ?? []
142
- const nonNullValues = values.filter((v): v is string | number | boolean => v !== null)
143
-
144
- // asConst-style enum: use z.union([z.literal(…), …])
145
- if (node.namedEnumValues?.length) {
146
- const literals = nonNullValues.map((v) => `z.literal(${formatLiteral(v)})`)
147
- if (literals.length === 1) return literals[0]!
148
- return `z.union([${literals.join(', ')}])`
149
- }
150
-
151
- // Regular enum: use z.enum([…])
152
- return `z.enum([${nonNullValues.map(formatLiteral).join(', ')}])`
153
- },
154
-
155
- ref(node) {
156
- if (!node.name) return undefined
157
- const refName = node.ref ? (ast.extractRefName(node.ref) ?? node.name) : node.name
158
- const resolvedName = node.ref ? (this.options.resolver?.default(refName, 'function') ?? refName) : node.name
159
-
160
- if (node.ref && this.options.cyclicSchemas?.has(refName)) {
161
- return `z.lazy(() => ${resolvedName})`
162
- }
163
-
164
- return resolvedName
165
- },
166
- object(node) {
167
- const properties = node.properties
168
- .map((prop) => {
169
- const { name: propName, schema } = prop
170
-
171
- const meta = ast.syncSchemaRef(schema)
172
-
173
- const isNullable = meta.nullable
174
- const isOptional = schema.optional
175
- const isNullish = schema.nullish
176
-
177
- const hasSelfRef = this.options.cyclicSchemas != null && ast.containsCircularRef(schema, { circularSchemas: this.options.cyclicSchemas })
178
- // Inside a getter the getter itself defers evaluation, so suppress
179
- // z.lazy() wrapping on nested refs by temporarily clearing cyclicSchemas.
180
- if (hasSelfRef) this.options.cyclicSchemas = undefined
181
- const baseOutput = this.transform(schema) ?? this.transform(ast.createSchema({ type: 'unknown' }))!
182
- if (hasSelfRef) this.options.cyclicSchemas = options.cyclicSchemas
183
-
184
- const wrappedOutput = this.options.wrapOutput ? this.options.wrapOutput({ output: baseOutput, schema }) || baseOutput : baseOutput
185
-
186
- const value = applyMiniModifiers({
187
- value: wrappedOutput,
188
- nullable: isNullable,
189
- optional: isOptional,
190
- nullish: isNullish,
191
- defaultValue: meta.default,
192
- })
193
-
194
- if (hasSelfRef) {
195
- return `get "${propName}"() { return ${value} }`
196
- }
197
- return `"${propName}": ${value}`
198
- })
199
- .join(',\n ')
200
-
201
- return `z.object({\n ${properties}\n })`
202
- },
203
- array(node) {
204
- const items = (node.items ?? []).map((item) => this.transform(item)).filter(Boolean)
205
- const inner = items.join(', ') || this.transform(ast.createSchema({ type: 'unknown' }))!
206
- let result = `z.array(${inner})${lengthChecksMini(node)}`
207
-
208
- if (node.unique) {
209
- result += `.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })`
210
- }
211
-
212
- return result
213
- },
214
- tuple(node) {
215
- const items = (node.items ?? []).map((item) => this.transform(item)).filter(Boolean)
216
-
217
- return `z.tuple([${items.join(', ')}])`
218
- },
219
- union(node) {
220
- const nodeMembers = node.members ?? []
221
- const members = nodeMembers
222
- .map((memberNode) => {
223
- const member = this.transform(memberNode)
224
-
225
- return member && node.strategy === 'one' ? strictOneOfMember(member, memberNode) : member
226
- })
227
- .filter(Boolean)
228
- if (members.length === 0) return ''
229
- if (members.length === 1) return members[0]!
230
- if (node.discriminatorPropertyName && !nodeMembers.some((m) => m.type === 'intersection')) {
231
- // z.discriminatedUnion requires ZodObject members; intersections (ZodIntersection) are not
232
- // assignable to $ZodDiscriminant, so fall back to z.union when any member is an intersection.
233
- return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)}, [${members.join(', ')}])`
234
- }
235
-
236
- return `z.union([${members.join(', ')}])`
237
- },
238
- intersection(node) {
239
- const members = node.members ?? []
240
- if (members.length === 0) return ''
241
-
242
- const [first, ...rest] = members
243
- if (!first) return ''
244
-
245
- let base = this.transform(first)
246
- if (!base) return ''
247
-
248
- for (const member of rest) {
249
- if (member.primitive === 'string') {
250
- const s = ast.narrowSchema(member, 'string')
251
- const c = lengthChecksMini(s ?? {})
252
- if (c) {
253
- base += c
254
- continue
255
- }
256
- } else if (member.primitive === 'number' || member.primitive === 'integer') {
257
- const n = ast.narrowSchema(member, 'number') ?? ast.narrowSchema(member, 'integer')
258
- const c = numberChecksMini(n ?? {})
259
- if (c) {
260
- base += c
261
- continue
262
- }
263
- } else if (member.primitive === 'array') {
264
- const a = ast.narrowSchema(member, 'array')
265
- const c = lengthChecksMini(a ?? {})
266
- if (c) {
267
- base += c
268
- continue
269
- }
270
- }
271
- const transformed = this.transform(member)
272
- if (transformed) base = `z.intersection(${base}, ${transformed})`
273
- }
274
-
275
- return base
276
- },
277
- ...options.nodes,
278
- },
279
- print(node) {
280
- const { keysToOmit } = this.options
281
-
282
- let base = this.transform(node)
283
- if (!base) return null
284
-
285
- const meta = ast.syncSchemaRef(node)
286
-
287
- if (keysToOmit?.length && meta.primitive === 'object' && !(meta.type === 'union' && meta.discriminatorPropertyName)) {
288
- // Mirror printerTs `nonNullable: true`: when omitting keys, the resulting
289
- // schema is a new non-nullable object type — skip optional/nullable/nullish.
290
- // Discriminated unions (z.discriminatedUnion) do not support .omit(), so skip them.
291
-
292
- // If this is a lazy reference, apply omit inside the lazy function
293
- const lazyMatch = base.match(/^z\.lazy\(\(\)\s*=>\s*(.+)\)$/)
294
- if (lazyMatch) {
295
- base = `z.lazy(() => ${lazyMatch[1]}.omit({ ${keysToOmit.map((k: string) => `"${k}": true`).join(', ')} }))`
296
- } else {
297
- base = `${base}.omit({ ${keysToOmit.map((k: string) => `"${k}": true`).join(', ')} })`
298
- }
299
- }
300
-
301
- return applyMiniModifiers({
302
- value: base,
303
- nullable: meta.nullable,
304
- optional: meta.optional,
305
- nullish: meta.nullish,
306
- defaultValue: meta.default,
307
- })
308
- },
309
- }
310
- })
@@ -1,57 +0,0 @@
1
- import { camelCase, pascalCase } from '@internals/utils'
2
- import { defineResolver } from '@kubb/core'
3
- import type { PluginZod } from '../types.ts'
4
-
5
- /**
6
- * Naming convention resolver for Zod plugin.
7
- *
8
- * Provides default naming helpers using camelCase with a `Schema` suffix for schemas.
9
- *
10
- * @example
11
- * `resolverZod.default('list pets', 'function') // → 'listPetsSchema'`
12
- */
13
- export const resolverZod = defineResolver<PluginZod>(() => {
14
- return {
15
- name: 'default',
16
- pluginName: 'plugin-zod',
17
- default(name, type) {
18
- return camelCase(name, { isFile: type === 'file', suffix: type ? 'schema' : undefined })
19
- },
20
- resolveSchemaName(name) {
21
- return camelCase(name, { suffix: 'schema' })
22
- },
23
- resolveSchemaTypeName(name) {
24
- return pascalCase(name, { suffix: 'schema' })
25
- },
26
- resolveTypeName(name) {
27
- return pascalCase(name)
28
- },
29
- resolvePathName(name, type) {
30
- return this.default(name, type)
31
- },
32
- resolveParamName(node, param) {
33
- return this.resolveSchemaName(`${node.operationId} ${param.in} ${param.name}`)
34
- },
35
- resolveResponseStatusName(node, statusCode) {
36
- return this.resolveSchemaName(`${node.operationId} Status ${statusCode}`)
37
- },
38
- resolveDataName(node) {
39
- return this.resolveSchemaName(`${node.operationId} Data`)
40
- },
41
- resolveResponsesName(node) {
42
- return this.resolveSchemaName(`${node.operationId} Responses`)
43
- },
44
- resolveResponseName(node) {
45
- return this.resolveSchemaName(`${node.operationId} Response`)
46
- },
47
- resolvePathParamsName(node, param) {
48
- return this.resolveParamName(node, param)
49
- },
50
- resolveQueryParamsName(node, param) {
51
- return this.resolveParamName(node, param)
52
- },
53
- resolveHeaderParamsName(node, param) {
54
- return this.resolveParamName(node, param)
55
- },
56
- }
57
- })
package/src/types.ts DELETED
@@ -1,187 +0,0 @@
1
- import type { ast, Exclude, Generator, Group, Include, Output, Override, PluginFactoryOptions, Resolver } from '@kubb/core'
2
- import type { PrinterZodNodes } from './printers/printerZod.ts'
3
- import type { PrinterZodMiniNodes } from './printers/printerZodMini.ts'
4
-
5
- /**
6
- * Resolver for Zod that provides naming methods for schema types.
7
- */
8
- export type ResolverZod = Resolver &
9
- ast.OperationParamsResolver & {
10
- /**
11
- * Resolves a camelCase schema function name with a `Schema` suffix.
12
- */
13
- resolveSchemaName(this: ResolverZod, name: string): string
14
- /**
15
- * Resolves the schema type name (inferred type from schema).
16
- *
17
- * @example Schema type names
18
- * `resolver.resolveSchemaTypeName('pet') // → 'Pet'`
19
- */
20
- resolveSchemaTypeName(this: ResolverZod, name: string): string
21
- /**
22
- * Resolves the generated type name from the schema.
23
- *
24
- * @example Type names
25
- * `resolver.resolveTypeName('pet') // → 'Pet'`
26
- */
27
- resolveTypeName(this: ResolverZod, name: string): string
28
- /**
29
- * Resolves the output file name for a schema.
30
- */
31
- resolvePathName(this: ResolverZod, name: string, type?: 'file' | 'function' | 'type' | 'const'): string
32
- /**
33
- * Resolves the name for an operation response by status code.
34
- *
35
- * @example Response status names
36
- * `resolver.resolveResponseStatusName(node, 200) // → 'listPetsStatus200Schema'`
37
- */
38
- resolveResponseStatusName(this: ResolverZod, node: ast.OperationNode, statusCode: ast.StatusCode): string
39
- /**
40
- * Resolves the name for the collection of all operation responses.
41
- *
42
- * @example Responses collection names
43
- * `resolver.resolveResponsesName(node) // → 'listPetsResponsesSchema'`
44
- */
45
- resolveResponsesName(this: ResolverZod, node: ast.OperationNode): string
46
- /**
47
- * Resolves the name for the union of all operation responses.
48
- *
49
- * @example Response union names
50
- * `resolver.resolveResponseName(node) // → 'listPetsResponseSchema'`
51
- */
52
- resolveResponseName(this: ResolverZod, node: ast.OperationNode): string
53
- /**
54
- * Resolves the name for an operation's grouped path parameters type.
55
- *
56
- * @example Path parameters names
57
- * `resolver.resolvePathParamsName(node, param) // → 'deletePetPathPetIdSchema'`
58
- */
59
- resolvePathParamsName(this: ResolverZod, node: ast.OperationNode, param: ast.ParameterNode): string
60
- /**
61
- * Resolves the name for an operation's grouped query parameters type.
62
- *
63
- * @example Query parameters names
64
- * `resolver.resolveQueryParamsName(node, param) // → 'findPetsByStatusQueryStatusSchema'`
65
- */
66
- resolveQueryParamsName(this: ResolverZod, node: ast.OperationNode, param: ast.ParameterNode): string
67
- /**
68
- * Resolves the name for an operation's grouped header parameters type.
69
- *
70
- * @example Header parameters names
71
- * `resolver.resolveHeaderParamsName(node, param) // → 'deletePetHeaderApiKeySchema'`
72
- */
73
- resolveHeaderParamsName(this: ResolverZod, node: ast.OperationNode, param: ast.ParameterNode): string
74
- }
75
-
76
- export type Options = {
77
- /**
78
- * @default 'zod'
79
- */
80
- output?: Output
81
- /**
82
- * Group the Zod schemas based on the provided name.
83
- */
84
- group?: Group
85
- /**
86
- * Tags, operations, or paths to exclude from generation.
87
- */
88
- exclude?: Array<Exclude>
89
- /**
90
- * Tags, operations, or paths to include in generation.
91
- */
92
- include?: Array<Include>
93
- /**
94
- * Override options for specific tags, operations, or paths.
95
- */
96
- override?: Array<Override<ResolvedOptions>>
97
- /**
98
- * Import path for Zod package.
99
- *
100
- * @default 'zod'
101
- */
102
- importPath?: 'zod' | 'zod/mini' | (string & {})
103
- /**
104
- * Add TypeScript type annotations to generated schemas.
105
- */
106
- typed?: boolean
107
- /**
108
- * Return schemas as inferred types using `z.infer`.
109
- */
110
- inferred?: boolean
111
- /**
112
- * Apply coercion to string values or configure coercion per type.
113
- */
114
- coercion?: boolean | { dates?: boolean; strings?: boolean; numbers?: boolean }
115
- /**
116
- * Generate operation-level schemas (grouped by operationId).
117
- */
118
- operations?: boolean
119
- /**
120
- * Validator to use for UUID format: `uuid` or `guid`.
121
- *
122
- * @default 'uuid'
123
- */
124
- guidType?: 'uuid' | 'guid'
125
- /**
126
- * Use Zod Mini's functional API for better tree-shaking.
127
- *
128
- * @default false
129
- */
130
- mini?: boolean
131
- /**
132
- * Callback to wrap the generated schema output.
133
- *
134
- * Useful for adding metadata like `.openapi()` or extension helpers.
135
- */
136
- wrapOutput?: (arg: { output: string; schema: ast.SchemaNode }) => string | undefined
137
- /**
138
- * Apply casing to parameter names.
139
- */
140
- paramsCasing?: 'camelcase'
141
- /**
142
- * Additional generators alongside the default generators.
143
- */
144
- generators?: Array<Generator<PluginZod>>
145
- /**
146
- * Override naming conventions for schema names and types.
147
- */
148
- resolver?: Partial<ResolverZod> & ThisType<ResolverZod>
149
- /**
150
- * Override printer node handlers to customize rendering of specific schema types.
151
- */
152
- printer?: {
153
- nodes?: PrinterZodNodes | PrinterZodMiniNodes
154
- }
155
- /**
156
- * AST visitor to transform schema and operation nodes.
157
- */
158
- transformer?: ast.Visitor
159
- }
160
-
161
- type ResolvedOptions = {
162
- output: Output
163
- exclude: Array<Exclude>
164
- include: Array<Include> | undefined
165
- override: Array<Override<ResolvedOptions>>
166
- group: Group | undefined
167
- typed: NonNullable<Options['typed']>
168
- inferred: NonNullable<Options['inferred']>
169
- importPath: NonNullable<Options['importPath']>
170
- coercion: NonNullable<Options['coercion']>
171
- operations: NonNullable<Options['operations']>
172
- guidType: NonNullable<Options['guidType']>
173
- mini: NonNullable<Options['mini']>
174
- wrapOutput: Options['wrapOutput']
175
- paramsCasing: Options['paramsCasing']
176
- printer: Options['printer']
177
- }
178
-
179
- export type PluginZod = PluginFactoryOptions<'plugin-zod', Options, ResolvedOptions, ResolverZod>
180
-
181
- declare global {
182
- namespace Kubb {
183
- interface PluginRegistry {
184
- 'plugin-zod': PluginZod
185
- }
186
- }
187
- }