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

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,22 +1,23 @@
1
+ import type { Printer } from '@kubb/ast'
1
2
  import type { SchemaNode } from '@kubb/ast/types'
2
3
  import { Const, File, Type } from '@kubb/react-fabric'
3
4
  import type { FabricReactNode } from '@kubb/react-fabric/types'
4
- import { printerZod } from '../printers/printerZod.ts'
5
- import type { PluginZod, ResolverZod } from '../types.ts'
5
+ import type { PrinterZodFactory } from '../printers/printerZod.ts'
6
+ import type { PrinterZodMiniFactory } from '../printers/printerZodMini.ts'
6
7
 
7
8
  type Props = {
8
9
  name: string
9
10
  node: SchemaNode
10
- coercion: PluginZod['resolvedOptions']['coercion']
11
- guidType: PluginZod['resolvedOptions']['guidType']
12
- wrapOutput: PluginZod['resolvedOptions']['wrapOutput']
11
+ /**
12
+ * Pre-configured printer instance created by the generator.
13
+ * The generator selects `printerZod` or `printerZodMini` based on the `mini` option,
14
+ * then merges in any user-supplied `printer.nodes` overrides.
15
+ */
16
+ printer: Printer<PrinterZodFactory> | Printer<PrinterZodMiniFactory>
13
17
  inferTypeName?: string
14
- resolver?: ResolverZod
15
- keysToOmit?: Array<string>
16
18
  }
17
19
 
18
- export function Zod({ name, node, coercion, guidType, wrapOutput, inferTypeName, resolver, keysToOmit }: Props): FabricReactNode {
19
- const printer = printerZod({ coercion, guidType, wrapOutput, resolver, schemaName: name, keysToOmit })
20
+ export function Zod({ name, node, printer, inferTypeName }: Props): FabricReactNode {
20
21
  const output = printer.print(node)
21
22
 
22
23
  if (!output) {
@@ -1,22 +1,23 @@
1
1
  import path from 'node:path'
2
- import { caseParams, composeTransformers, transform } from '@kubb/ast'
2
+ import { caseParams, transform } from '@kubb/ast'
3
3
  import type { SchemaNode } from '@kubb/ast/types'
4
4
  import { defineGenerator, getMode } from '@kubb/core'
5
5
  import { File } from '@kubb/react-fabric'
6
6
  import { Operations } from '../components/Operations.tsx'
7
7
  import { Zod } from '../components/Zod.tsx'
8
- import { ZodMini } from '../components/ZodMini.tsx'
9
8
  import { ZOD_NAMESPACE_IMPORTS } from '../constants.ts'
9
+ import { printerZod } from '../printers/printerZod.ts'
10
+ import { printerZodMini } from '../printers/printerZodMini.ts'
10
11
  import type { PluginZod } from '../types'
11
12
  import { buildSchemaNames } from '../utils.ts'
12
13
 
13
14
  export const zodGenerator = defineGenerator<PluginZod>({
14
15
  name: 'zod',
15
16
  type: 'react',
16
- Schema({ node, adapter, options, config, resolver }) {
17
- const { output, coercion, guidType, mini, wrapOutput, inferred, importPath, group, transformers = [] } = options
17
+ Schema({ node, adapter, options, config, resolver, plugin }) {
18
+ const { output, coercion, guidType, mini, wrapOutput, inferred, importPath, group, printer } = options
18
19
 
19
- const transformedNode = transform(node, composeTransformers(...transformers))
20
+ const transformedNode = plugin.transformer ? transform(node, plugin.transformer) : node
20
21
 
21
22
  if (!transformedNode.name) {
22
23
  return
@@ -27,17 +28,21 @@ export const zodGenerator = defineGenerator<PluginZod>({
27
28
  const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath as 'zod' | 'zod/mini')
28
29
 
29
30
  const imports = adapter.getImports(transformedNode, (schemaName) => ({
30
- name: resolver.default(schemaName, 'function'),
31
+ name: resolver.resolveSchemaName(schemaName),
31
32
  path: resolver.resolveFile({ name: schemaName, extname: '.ts' }, { root, output, group }).path,
32
33
  }))
33
34
 
34
- const inferTypeName = inferred ? resolver.resolveInferName(resolver.resolveName(transformedNode.name)) : undefined
35
-
36
35
  const meta = {
37
- name: resolver.default(transformedNode.name, 'function'),
36
+ name: resolver.resolveSchemaName(transformedNode.name),
38
37
  file: resolver.resolveFile({ name: transformedNode.name, extname: '.ts' }, { root, output, group }),
39
38
  } as const
40
39
 
40
+ const inferTypeName = inferred ? resolver.resolveSchemaTypeName(transformedNode.name) : undefined
41
+
42
+ const schemaPrinter = mini
43
+ ? printerZodMini({ guidType, wrapOutput, resolver, schemaName: meta.name, nodes: printer?.nodes })
44
+ : printerZod({ coercion, guidType, wrapOutput, resolver, schemaName: meta.name, nodes: printer?.nodes })
45
+
41
46
  return (
42
47
  <File
43
48
  baseName={meta.file.baseName}
@@ -50,26 +55,14 @@ export const zodGenerator = defineGenerator<PluginZod>({
50
55
  {mode === 'split' &&
51
56
  imports.map((imp) => <File.Import key={[transformedNode.name, imp.path].join('-')} root={meta.file.path} path={imp.path} name={imp.name} />)}
52
57
 
53
- {mini ? (
54
- <ZodMini name={meta.name} node={transformedNode} guidType={guidType} wrapOutput={wrapOutput} inferTypeName={inferTypeName} resolver={resolver} />
55
- ) : (
56
- <Zod
57
- name={meta.name}
58
- node={transformedNode}
59
- coercion={coercion}
60
- guidType={guidType}
61
- wrapOutput={wrapOutput}
62
- inferTypeName={inferTypeName}
63
- resolver={resolver}
64
- />
65
- )}
58
+ <Zod name={meta.name} node={transformedNode} printer={schemaPrinter} inferTypeName={inferTypeName} />
66
59
  </File>
67
60
  )
68
61
  },
69
- Operation({ node, adapter, options, config, resolver }) {
70
- const { output, coercion, guidType, mini, wrapOutput, inferred, importPath, group, paramsCasing, transformers } = options
62
+ Operation({ node, adapter, options, config, resolver, plugin }) {
63
+ const { output, coercion, guidType, mini, wrapOutput, inferred, importPath, group, paramsCasing, printer } = options
71
64
 
72
- const transformedNode = transform(node, composeTransformers(...transformers))
65
+ const transformedNode = plugin.transformer ? transform(node, plugin.transformer) : node
73
66
 
74
67
  const root = path.resolve(config.root, config.output.path)
75
68
  const mode = getMode(path.resolve(root, output.path))
@@ -87,39 +80,22 @@ export const zodGenerator = defineGenerator<PluginZod>({
87
80
  function renderSchemaEntry({ schema, name, keysToOmit }: { schema: SchemaNode | null; name: string; keysToOmit?: Array<string> }) {
88
81
  if (!schema) return null
89
82
 
90
- const inferTypeName = inferred ? resolver.resolveInferName(name) : undefined
83
+ const inferTypeName = inferred ? resolver.resolveTypeName(name) : undefined
91
84
 
92
85
  const imports = adapter.getImports(schema, (schemaName) => ({
93
- name: resolver.default(schemaName, 'function'),
86
+ name: resolver.resolveSchemaName(schemaName),
94
87
  path: resolver.resolveFile({ name: schemaName, extname: '.ts' }, { root, output, group }).path,
95
88
  }))
96
89
 
90
+ const schemaPrinter = mini
91
+ ? printerZodMini({ guidType, wrapOutput, resolver, schemaName: name, keysToOmit, nodes: printer?.nodes })
92
+ : printerZod({ coercion, guidType, wrapOutput, resolver, schemaName: name, keysToOmit, nodes: printer?.nodes })
93
+
97
94
  return (
98
95
  <>
99
96
  {mode === 'split' &&
100
97
  imports.map((imp) => <File.Import key={[name, imp.path, imp.name].join('-')} root={meta.file.path} path={imp.path} name={imp.name} />)}
101
- {mini ? (
102
- <ZodMini
103
- name={name}
104
- node={schema}
105
- guidType={guidType}
106
- wrapOutput={wrapOutput}
107
- inferTypeName={inferTypeName}
108
- resolver={resolver}
109
- keysToOmit={keysToOmit}
110
- />
111
- ) : (
112
- <Zod
113
- name={name}
114
- node={schema}
115
- coercion={coercion}
116
- guidType={guidType}
117
- wrapOutput={wrapOutput}
118
- inferTypeName={inferTypeName}
119
- resolver={resolver}
120
- keysToOmit={keysToOmit}
121
- />
122
- )}
98
+ <Zod name={name} node={schema} printer={schemaPrinter} inferTypeName={inferTypeName} />
123
99
  </>
124
100
  )
125
101
  }
@@ -160,8 +136,8 @@ export const zodGenerator = defineGenerator<PluginZod>({
160
136
  </File>
161
137
  )
162
138
  },
163
- Operations({ nodes, adapter, options, config, resolver }) {
164
- const { output, importPath, group, operations, paramsCasing, transformers } = options
139
+ Operations({ nodes, adapter, options, config, resolver, plugin }) {
140
+ const { output, importPath, group, operations, paramsCasing } = options
165
141
 
166
142
  if (!operations) {
167
143
  return
@@ -175,7 +151,7 @@ export const zodGenerator = defineGenerator<PluginZod>({
175
151
  } as const
176
152
 
177
153
  const transformedOperations = nodes.map((node) => {
178
- const transformedNode = transform(node, composeTransformers(...transformers))
154
+ const transformedNode = plugin.transformer ? transform(node, plugin.transformer) : node
179
155
 
180
156
  const params = caseParams(transformedNode.parameters, paramsCasing)
181
157
 
@@ -1,12 +1,13 @@
1
1
  import path from 'node:path'
2
- import { caseParams, composeTransformers, createProperty, createSchema, transform } from '@kubb/ast'
2
+ import { caseParams, createProperty, createSchema, transform } from '@kubb/ast'
3
3
  import type { OperationNode, ParameterNode, SchemaNode } from '@kubb/ast/types'
4
4
  import { defineGenerator, getMode } from '@kubb/core'
5
5
  import { File } from '@kubb/react-fabric'
6
6
  import { Operations } from '../components/Operations.tsx'
7
7
  import { Zod } from '../components/Zod.tsx'
8
- import { ZodMini } from '../components/ZodMini.tsx'
9
8
  import { ZOD_NAMESPACE_IMPORTS } from '../constants.ts'
9
+ import { printerZod } from '../printers/printerZod.ts'
10
+ import { printerZodMini } from '../printers/printerZodMini.ts'
10
11
  import type { PluginZod, ResolverZod } from '../types'
11
12
 
12
13
  type BuildGroupedParamsSchemaOptions = {
@@ -167,9 +168,9 @@ function buildLegacySchemaNames(node: OperationNode, params: Array<ParameterNode
167
168
  export const zodGeneratorLegacy = defineGenerator<PluginZod>({
168
169
  name: 'zod-legacy',
169
170
  type: 'react',
170
- Schema({ node, adapter, options, config, resolver }) {
171
- const { output, coercion, guidType, mini, wrapOutput, inferred, importPath, group, transformers = [] } = options
172
- const transformedNode = transform(node, composeTransformers(...transformers))
171
+ Schema({ node, adapter, options, config, resolver, plugin }) {
172
+ const { output, coercion, guidType, mini, wrapOutput, inferred, importPath, group, printer } = options
173
+ const transformedNode = plugin.transformer ? transform(node, plugin.transformer) : node
173
174
 
174
175
  if (!transformedNode.name) {
175
176
  return
@@ -180,17 +181,21 @@ export const zodGeneratorLegacy = defineGenerator<PluginZod>({
180
181
  const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath as 'zod' | 'zod/mini')
181
182
 
182
183
  const imports = adapter.getImports(transformedNode, (schemaName) => ({
183
- name: resolver.default(schemaName, 'function'),
184
+ name: resolver.resolveSchemaName(schemaName),
184
185
  path: resolver.resolveFile({ name: schemaName, extname: '.ts' }, { root, output, group }).path,
185
186
  }))
186
187
 
187
- const inferTypeName = inferred ? resolver.resolveInferName(resolver.resolveName(transformedNode.name)) : undefined
188
+ const inferTypeName = inferred ? resolver.resolveSchemaTypeName(transformedNode.name) : undefined
188
189
 
189
190
  const meta = {
190
- name: resolver.default(transformedNode.name, 'function'),
191
+ name: resolver.resolveSchemaName(transformedNode.name),
191
192
  file: resolver.resolveFile({ name: transformedNode.name, extname: '.ts' }, { root, output, group }),
192
193
  } as const
193
194
 
195
+ const schemaPrinter = mini
196
+ ? printerZodMini({ guidType, wrapOutput, resolver, schemaName: meta.name, nodes: printer?.nodes })
197
+ : printerZod({ coercion, guidType, wrapOutput, resolver, schemaName: meta.name, nodes: printer?.nodes })
198
+
194
199
  return (
195
200
  <File
196
201
  baseName={meta.file.baseName}
@@ -203,26 +208,14 @@ export const zodGeneratorLegacy = defineGenerator<PluginZod>({
203
208
  {mode === 'split' &&
204
209
  imports.map((imp) => <File.Import key={[transformedNode.name, imp.path].join('-')} root={meta.file.path} path={imp.path} name={imp.name} />)}
205
210
 
206
- {mini ? (
207
- <ZodMini name={meta.name} node={transformedNode} guidType={guidType} wrapOutput={wrapOutput} inferTypeName={inferTypeName} resolver={resolver} />
208
- ) : (
209
- <Zod
210
- name={meta.name}
211
- node={transformedNode}
212
- coercion={coercion}
213
- guidType={guidType}
214
- wrapOutput={wrapOutput}
215
- inferTypeName={inferTypeName}
216
- resolver={resolver}
217
- />
218
- )}
211
+ <Zod name={meta.name} node={transformedNode} printer={schemaPrinter} inferTypeName={inferTypeName} />
219
212
  </File>
220
213
  )
221
214
  },
222
- Operation({ node, adapter, options, config, resolver }) {
223
- const { output, coercion, guidType, mini, wrapOutput, inferred, importPath, group, paramsCasing, transformers } = options
215
+ Operation({ node, adapter, options, config, resolver, plugin }) {
216
+ const { output, coercion, guidType, mini, wrapOutput, inferred, importPath, group, paramsCasing, printer } = options
224
217
 
225
- const transformedNode = transform(node, composeTransformers(...transformers))
218
+ const transformedNode = plugin.transformer ? transform(node, plugin.transformer) : node
226
219
 
227
220
  const root = path.resolve(config.root, config.output.path)
228
221
  const mode = getMode(path.resolve(root, output.path))
@@ -240,39 +233,22 @@ export const zodGeneratorLegacy = defineGenerator<PluginZod>({
240
233
  function renderSchemaEntry({ schema, name, keysToOmit }: { schema: SchemaNode | null; name: string; keysToOmit?: Array<string> }) {
241
234
  if (!schema) return null
242
235
 
243
- const inferTypeName = inferred ? resolver.resolveInferName(name) : undefined
236
+ const inferTypeName = inferred ? resolver.resolveTypeName(name) : undefined
244
237
 
245
238
  const imports = adapter.getImports(schema, (schemaName) => ({
246
- name: resolver.default(schemaName, 'function'),
239
+ name: resolver.resolveSchemaName(schemaName),
247
240
  path: resolver.resolveFile({ name: schemaName, extname: '.ts' }, { root, output, group }).path,
248
241
  }))
249
242
 
243
+ const schemaPrinter = mini
244
+ ? printerZodMini({ guidType, wrapOutput, resolver, schemaName: name, keysToOmit, nodes: printer?.nodes })
245
+ : printerZod({ coercion, guidType, wrapOutput, resolver, schemaName: name, keysToOmit, nodes: printer?.nodes })
246
+
250
247
  return (
251
248
  <>
252
249
  {mode === 'split' &&
253
250
  imports.map((imp) => <File.Import key={[name, imp.path, imp.name].join('-')} root={meta.file.path} path={imp.path} name={imp.name} />)}
254
- {mini ? (
255
- <ZodMini
256
- name={name}
257
- node={schema}
258
- guidType={guidType}
259
- wrapOutput={wrapOutput}
260
- inferTypeName={inferTypeName}
261
- resolver={resolver}
262
- keysToOmit={keysToOmit}
263
- />
264
- ) : (
265
- <Zod
266
- name={name}
267
- node={schema}
268
- coercion={coercion}
269
- guidType={guidType}
270
- wrapOutput={wrapOutput}
271
- inferTypeName={inferTypeName}
272
- resolver={resolver}
273
- keysToOmit={keysToOmit}
274
- />
275
- )}
251
+ <Zod name={name} node={schema} printer={schemaPrinter} inferTypeName={inferTypeName} />
276
252
  </>
277
253
  )
278
254
  }
@@ -352,8 +328,8 @@ export const zodGeneratorLegacy = defineGenerator<PluginZod>({
352
328
  </File>
353
329
  )
354
330
  },
355
- Operations({ nodes, adapter, options, config, resolver }) {
356
- const { output, importPath, group, operations, paramsCasing, transformers } = options
331
+ Operations({ nodes, adapter, options, config, resolver, plugin }) {
332
+ const { output, importPath, group, operations, paramsCasing } = options
357
333
 
358
334
  if (!operations) {
359
335
  return
@@ -367,7 +343,7 @@ export const zodGeneratorLegacy = defineGenerator<PluginZod>({
367
343
  } as const
368
344
 
369
345
  const transformedOperations = nodes.map((node) => {
370
- const transformedNode = transform(node, composeTransformers(...transformers))
346
+ const transformedNode = plugin.transformer ? transform(node, plugin.transformer) : node
371
347
 
372
348
  const params = caseParams(transformedNode.parameters, paramsCasing)
373
349
 
package/src/index.ts CHANGED
@@ -2,8 +2,9 @@ export { zodGenerator } from './generators/zodGenerator.tsx'
2
2
  export { zodGeneratorLegacy } from './generators/zodGeneratorLegacy.tsx'
3
3
 
4
4
  export { pluginZod, pluginZodName } from './plugin.ts'
5
-
5
+ export type { PrinterZodFactory, PrinterZodNodes, PrinterZodOptions } from './printers/printerZod.ts'
6
6
  export { printerZod } from './printers/printerZod.ts'
7
+ export type { PrinterZodMiniFactory, PrinterZodMiniNodes, PrinterZodMiniOptions } from './printers/printerZodMini.ts'
7
8
  export { printerZodMini } from './printers/printerZodMini.ts'
8
9
 
9
10
  export { resolverZod } from './resolvers/resolverZod.ts'
package/src/plugin.ts CHANGED
@@ -44,17 +44,18 @@ export const pluginZod = createPlugin<PluginZod>((options) => {
44
44
  inferred = false,
45
45
  wrapOutput = undefined,
46
46
  paramsCasing,
47
+ printer,
47
48
  compatibilityPreset = 'default',
48
- resolvers: userResolvers = [],
49
- transformers: userTransformers = [],
49
+ resolver: userResolver,
50
+ transformer: userTransformer,
50
51
  generators: userGenerators = [],
51
52
  } = options
52
53
 
53
54
  const preset = getPreset({
54
55
  preset: compatibilityPreset,
55
56
  presets: presets,
56
- resolvers: userResolvers,
57
- transformers: userTransformers,
57
+ resolver: userResolver,
58
+ transformer: userTransformer,
58
59
  generators: userGenerators,
59
60
  })
60
61
 
@@ -66,6 +67,9 @@ export const pluginZod = createPlugin<PluginZod>((options) => {
66
67
  get resolver() {
67
68
  return preset.resolver
68
69
  },
70
+ get transformer() {
71
+ return preset.transformer
72
+ },
69
73
  get options() {
70
74
  return {
71
75
  output,
@@ -90,7 +94,7 @@ export const pluginZod = createPlugin<PluginZod>((options) => {
90
94
  mini,
91
95
  wrapOutput,
92
96
  paramsCasing,
93
- transformers: preset.transformers,
97
+ printer,
94
98
  }
95
99
  },
96
100
  resolvePath(baseName, pathMode, options) {
package/src/presets.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { definePresets } from '@kubb/core'
2
2
  import { zodGenerator } from './generators/zodGenerator.tsx'
3
3
  import { zodGeneratorLegacy } from './generators/zodGeneratorLegacy.tsx'
4
+ import { printerZod } from './printers/printerZod.ts'
4
5
  import { resolverZod } from './resolvers/resolverZod.ts'
5
6
  import { resolverZodLegacy } from './resolvers/resolverZodLegacy.ts'
6
7
  import type { ResolverZod } from './types.ts'
@@ -10,16 +11,20 @@ import type { ResolverZod } from './types.ts'
10
11
  *
11
12
  * - `default` — uses `resolverZod` and `zodGenerator` (current naming conventions).
12
13
  * - `kubbV4` — uses `resolverZodLegacy` and `zodGeneratorLegacy` (Kubb v4 naming conventions).
14
+ *
15
+ * Note: `printerZodMini` is selected at runtime by the generator when `mini: true` is set.
13
16
  */
14
17
  export const presets = definePresets<ResolverZod>({
15
18
  default: {
16
19
  name: 'default',
17
- resolvers: [resolverZod],
20
+ resolver: resolverZod,
18
21
  generators: [zodGenerator],
22
+ printer: printerZod,
19
23
  },
20
24
  kubbV4: {
21
25
  name: 'kubbV4',
22
- resolvers: [resolverZodLegacy],
26
+ resolver: resolverZodLegacy,
23
27
  generators: [zodGeneratorLegacy],
28
+ printer: printerZod,
24
29
  },
25
30
  })
@@ -1,11 +1,33 @@
1
1
  import { stringify } from '@internals/utils'
2
2
  import { createSchema, extractRefName, narrowSchema, syncSchemaRef } from '@kubb/ast'
3
- import type { PrinterFactoryOptions } from '@kubb/core'
3
+ import type { PrinterFactoryOptions, PrinterPartial } from '@kubb/core'
4
4
  import { definePrinter } from '@kubb/core'
5
5
  import type { PluginZod, ResolverZod } from '../types.ts'
6
6
  import { applyModifiers, containsSelfRef, formatLiteral, lengthConstraints, numberConstraints, shouldCoerce } from '../utils.ts'
7
7
 
8
- export type ZodOptions = {
8
+ /**
9
+ * Partial map of node-type overrides for the Zod printer.
10
+ *
11
+ * Each key is a `SchemaType` string (e.g. `'date'`, `'string'`). The function
12
+ * replaces the built-in handler for that node type. Use `this.transform` to
13
+ * recurse into nested schema nodes, and `this.options` to read printer options.
14
+ *
15
+ * @example Override the `date` handler
16
+ * ```ts
17
+ * pluginZod({
18
+ * printer: {
19
+ * nodes: {
20
+ * date(node) {
21
+ * return 'z.string().date()'
22
+ * },
23
+ * },
24
+ * },
25
+ * })
26
+ * ```
27
+ */
28
+ export type PrinterZodNodes = PrinterPartial<string, PrinterZodOptions>
29
+
30
+ export type PrinterZodOptions = {
9
31
  coercion?: PluginZod['resolvedOptions']['coercion']
10
32
  guidType?: PluginZod['resolvedOptions']['guidType']
11
33
  wrapOutput?: PluginZod['resolvedOptions']['wrapOutput']
@@ -15,9 +37,13 @@ export type ZodOptions = {
15
37
  * Property keys to exclude from the generated object schema via `.omit({ key: true })`.
16
38
  */
17
39
  keysToOmit?: Array<string>
40
+ /**
41
+ * Partial map of node-type overrides. Each entry replaces the built-in handler for that node type.
42
+ */
43
+ nodes?: PrinterZodNodes
18
44
  }
19
45
 
20
- type ZodPrinterFactory = PrinterFactoryOptions<'zod', ZodOptions, string, string>
46
+ export type PrinterZodFactory = PrinterFactoryOptions<'zod', PrinterZodOptions, string, string>
21
47
 
22
48
  /**
23
49
  * Zod v4 printer built with `definePrinter`.
@@ -33,7 +59,7 @@ type ZodPrinterFactory = PrinterFactoryOptions<'zod', ZodOptions, string, string
33
59
  * const code = printer.print(stringSchemaNode) // "z.string()"
34
60
  * ```
35
61
  */
36
- export const printerZod = definePrinter<ZodPrinterFactory>((options) => {
62
+ export const printerZod = definePrinter<PrinterZodFactory>((options) => {
37
63
  return {
38
64
  name: 'zod',
39
65
  options,
@@ -242,6 +268,7 @@ export const printerZod = definePrinter<ZodPrinterFactory>((options) => {
242
268
 
243
269
  return base
244
270
  },
271
+ ...options.nodes,
245
272
  },
246
273
  print(node) {
247
274
  const { keysToOmit } = this.options
@@ -1,11 +1,34 @@
1
1
  import { stringify } from '@internals/utils'
2
2
  import { createSchema, extractRefName, narrowSchema, syncSchemaRef } from '@kubb/ast'
3
- import type { PrinterFactoryOptions } from '@kubb/core'
3
+ import type { PrinterFactoryOptions, PrinterPartial } from '@kubb/core'
4
4
  import { definePrinter } from '@kubb/core'
5
5
  import type { PluginZod, ResolverZod } from '../types.ts'
6
6
  import { applyMiniModifiers, containsSelfRef, formatLiteral, lengthChecksMini, numberChecksMini } from '../utils.ts'
7
7
 
8
- export type ZodMiniOptions = {
8
+ /**
9
+ * Partial map of node-type overrides for the Zod Mini printer.
10
+ *
11
+ * Each key is a `SchemaType` string (e.g. `'date'`, `'string'`). The function
12
+ * replaces the built-in handler for that node type. Use `this.transform` to
13
+ * recurse into nested schema nodes, and `this.options` to read printer options.
14
+ *
15
+ * @example Override the `date` handler
16
+ * ```ts
17
+ * pluginZod({
18
+ * mini: true,
19
+ * printer: {
20
+ * nodes: {
21
+ * date(node) {
22
+ * return 'z.iso.date()'
23
+ * },
24
+ * },
25
+ * },
26
+ * })
27
+ * ```
28
+ */
29
+ export type PrinterZodMiniNodes = PrinterPartial<string, PrinterZodMiniOptions>
30
+
31
+ export type PrinterZodMiniOptions = {
9
32
  guidType?: PluginZod['resolvedOptions']['guidType']
10
33
  wrapOutput?: PluginZod['resolvedOptions']['wrapOutput']
11
34
  resolver?: ResolverZod
@@ -14,9 +37,13 @@ export type ZodMiniOptions = {
14
37
  * Property keys to exclude from the generated object schema via `.omit({ key: true })`.
15
38
  */
16
39
  keysToOmit?: Array<string>
40
+ /**
41
+ * Partial map of node-type overrides. Each entry replaces the built-in handler for that node type.
42
+ */
43
+ nodes?: PrinterZodMiniNodes
17
44
  }
18
45
 
19
- type ZodMiniPrinterFactory = PrinterFactoryOptions<'zod-mini', ZodMiniOptions, string, string>
46
+ export type PrinterZodMiniFactory = PrinterFactoryOptions<'zod-mini', PrinterZodMiniOptions, string, string>
20
47
  /**
21
48
  * Zod v4 **Mini** printer built with `definePrinter`.
22
49
  *
@@ -31,7 +58,7 @@ type ZodMiniPrinterFactory = PrinterFactoryOptions<'zod-mini', ZodMiniOptions, s
31
58
  * const code = printer.print(optionalStringNode) // "z.optional(z.string())"
32
59
  * ```
33
60
  */
34
- export const printerZodMini = definePrinter<ZodMiniPrinterFactory>((options) => {
61
+ export const printerZodMini = definePrinter<PrinterZodMiniFactory>((options) => {
35
62
  return {
36
63
  name: 'zod-mini',
37
64
  options,
@@ -217,8 +244,8 @@ export const printerZodMini = definePrinter<ZodMiniPrinterFactory>((options) =>
217
244
 
218
245
  return base
219
246
  },
247
+ ...options.nodes,
220
248
  },
221
-
222
249
  print(node) {
223
250
  const { keysToOmit } = this.options
224
251
 
@@ -2,19 +2,6 @@ import { camelCase, pascalCase } from '@internals/utils'
2
2
  import { defineResolver } from '@kubb/core'
3
3
  import type { PluginZod } from '../types.ts'
4
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
5
  /**
19
6
  * Default resolver for `@kubb/plugin-zod`.
20
7
  *
@@ -32,31 +19,34 @@ export const resolverZod = defineResolver<PluginZod>(() => {
32
19
  name: 'default',
33
20
  pluginName: 'plugin-zod',
34
21
  default(name, type) {
35
- return toSchemaName(name, type)
22
+ return camelCase(name, { isFile: type === 'file', suffix: type ? 'schema' : undefined })
23
+ },
24
+ resolveSchemaName(name) {
25
+ return camelCase(name, { suffix: 'schema' })
36
26
  },
37
- resolveName(name) {
38
- return this.default(name, 'function')
27
+ resolveSchemaTypeName(name) {
28
+ return pascalCase(name, { suffix: 'schema' })
39
29
  },
40
- resolveInferName(name) {
30
+ resolveTypeName(name) {
41
31
  return pascalCase(name)
42
32
  },
43
33
  resolvePathName(name, type) {
44
34
  return this.default(name, type)
45
35
  },
46
36
  resolveParamName(node, param) {
47
- return this.resolveName(`${node.operationId} ${param.in} ${param.name}`)
37
+ return this.resolveSchemaName(`${node.operationId} ${param.in} ${param.name}`)
48
38
  },
49
39
  resolveResponseStatusName(node, statusCode) {
50
- return this.resolveName(`${node.operationId} Status ${statusCode}`)
40
+ return this.resolveSchemaName(`${node.operationId} Status ${statusCode}`)
51
41
  },
52
42
  resolveDataName(node) {
53
- return this.resolveName(`${node.operationId} Data`)
43
+ return this.resolveSchemaName(`${node.operationId} Data`)
54
44
  },
55
45
  resolveResponsesName(node) {
56
- return this.resolveName(`${node.operationId} Responses`)
46
+ return this.resolveSchemaName(`${node.operationId} Responses`)
57
47
  },
58
48
  resolveResponseName(node) {
59
- return this.resolveName(`${node.operationId} Response`)
49
+ return this.resolveSchemaName(`${node.operationId} Response`)
60
50
  },
61
51
  resolvePathParamsName(node, param) {
62
52
  return this.resolveParamName(node, param)
@@ -31,30 +31,30 @@ export const resolverZodLegacy = defineResolver<PluginZod>(() => {
31
31
  pluginName: 'plugin-zod',
32
32
  resolveResponseStatusName(node, statusCode) {
33
33
  if (statusCode === 'default') {
34
- return this.resolveName(`${node.operationId} Error`)
34
+ return this.resolveSchemaName(`${node.operationId} Error`)
35
35
  }
36
- return this.resolveName(`${node.operationId} ${statusCode}`)
36
+ return this.resolveSchemaName(`${node.operationId} ${statusCode}`)
37
37
  },
38
38
  resolveDataName(node) {
39
39
  const suffix = node.method === 'GET' ? 'QueryRequest' : 'MutationRequest'
40
- return this.resolveName(`${node.operationId} ${suffix}`)
40
+ return this.resolveSchemaName(`${node.operationId} ${suffix}`)
41
41
  },
42
42
  resolveResponsesName(node) {
43
43
  const suffix = node.method === 'GET' ? 'Query' : 'Mutation'
44
- return this.resolveName(`${node.operationId} ${suffix}`)
44
+ return this.resolveSchemaName(`${node.operationId} ${suffix}`)
45
45
  },
46
46
  resolveResponseName(node) {
47
47
  const suffix = node.method === 'GET' ? 'QueryResponse' : 'MutationResponse'
48
- return this.resolveName(`${node.operationId} ${suffix}`)
48
+ return this.resolveSchemaName(`${node.operationId} ${suffix}`)
49
49
  },
50
50
  resolvePathParamsName(node, _param) {
51
- return this.resolveName(`${node.operationId} PathParams`)
51
+ return this.resolveSchemaName(`${node.operationId} PathParams`)
52
52
  },
53
53
  resolveQueryParamsName(node, _param) {
54
- return this.resolveName(`${node.operationId} QueryParams`)
54
+ return this.resolveSchemaName(`${node.operationId} QueryParams`)
55
55
  },
56
56
  resolveHeaderParamsName(node, _param) {
57
- return this.resolveName(`${node.operationId} HeaderParams`)
57
+ return this.resolveSchemaName(`${node.operationId} HeaderParams`)
58
58
  },
59
59
  }
60
60
  })