@kubb/plugin-zod 5.0.0-beta.22 → 5.0.0-beta.25

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubb/plugin-zod",
3
- "version": "5.0.0-beta.22",
3
+ "version": "5.0.0-beta.25",
4
4
  "description": "Generate Zod validation schemas from your OpenAPI specification for runtime data parsing and type safety. Pairs perfectly with @kubb/plugin-ts for end-to-end type coverage.",
5
5
  "keywords": [
6
6
  "code-generation",
@@ -48,15 +48,15 @@
48
48
  "registry": "https://registry.npmjs.org/"
49
49
  },
50
50
  "dependencies": {
51
- "@kubb/core": "5.0.0-beta.22",
52
- "@kubb/renderer-jsx": "5.0.0-beta.22",
51
+ "@kubb/core": "5.0.0-beta.25",
52
+ "@kubb/renderer-jsx": "5.0.0-beta.25",
53
53
  "remeda": "^2.34.1"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@internals/utils": "0.0.0"
57
57
  },
58
58
  "peerDependencies": {
59
- "@kubb/renderer-jsx": "5.0.0-beta.22"
59
+ "@kubb/renderer-jsx": "5.0.0-beta.25"
60
60
  },
61
61
  "size-limit": [
62
62
  {
@@ -4,11 +4,11 @@ import { Const, File, Type } from '@kubb/renderer-jsx'
4
4
  import type { KubbReactNode } from '@kubb/renderer-jsx/types'
5
5
 
6
6
  type SchemaNames = {
7
- request: string | undefined
7
+ request: string | null
8
8
  parameters: {
9
- path: string | undefined
10
- query: string | undefined
11
- header: string | undefined
9
+ path: string | null
10
+ query: string | null
11
+ header: string | null
12
12
  }
13
13
  responses: { default?: string } & Record<number | string, string>
14
14
  errors: Record<number | string, string>
@@ -13,7 +13,7 @@ type Props = {
13
13
  * then merges in any user-supplied `printer.nodes` overrides.
14
14
  */
15
15
  printer: ast.Printer<PrinterZodFactory> | ast.Printer<PrinterZodMiniFactory>
16
- inferTypeName?: string
16
+ inferTypeName?: string | null
17
17
  }
18
18
 
19
19
  export function Zod({ name, node, printer, inferTypeName }: Props): KubbReactNode {
@@ -17,6 +17,12 @@ type ZodMiniPrinterEntry = { printer: ReturnType<typeof printerZodMini>; guidTyp
17
17
  const zodPrinterCache = new WeakMap<ResolverZod, ZodPrinterEntry>()
18
18
  const zodMiniPrinterCache = new WeakMap<ResolverZod, ZodMiniPrinterEntry>()
19
19
 
20
+ /**
21
+ * Built-in generator for `@kubb/plugin-zod`. Emits one Zod schema per
22
+ * schema in the spec plus per-operation request/response/parameter schemas.
23
+ * When `mini: true`, schemas use the Zod Mini functional API instead of
24
+ * chainable methods.
25
+ */
20
26
  export const zodGenerator = defineGenerator<PluginZod>({
21
27
  name: 'zod',
22
28
  renderer: jsxRendererSync,
@@ -34,15 +40,15 @@ export const zodGenerator = defineGenerator<PluginZod>({
34
40
 
35
41
  const imports = adapter.getImports(node, (schemaName) => ({
36
42
  name: resolver.resolveSchemaName(schemaName),
37
- path: resolver.resolveFile({ name: schemaName, extname: '.ts' }, { root, output, group }).path,
43
+ path: resolver.resolveFile({ name: schemaName, extname: '.ts' }, { root, output, group: group ?? undefined }).path,
38
44
  }))
39
45
 
40
46
  const meta = {
41
47
  name: resolver.resolveSchemaName(node.name),
42
- file: resolver.resolveFile({ name: node.name, extname: '.ts' }, { root, output, group }),
48
+ file: resolver.resolveFile({ name: node.name, extname: '.ts' }, { root, output, group: group ?? undefined }),
43
49
  } as const
44
50
 
45
- const inferTypeName = inferred ? resolver.resolveSchemaTypeName(node.name) : undefined
51
+ const inferTypeName = inferred ? resolver.resolveSchemaTypeName(node.name) : null
46
52
 
47
53
  const cyclicSchemas = new Set<string>(ctx.meta.circularNames)
48
54
 
@@ -92,7 +98,10 @@ export const zodGenerator = defineGenerator<PluginZod>({
92
98
  const params = ast.caseParams(node.parameters, paramsCasing)
93
99
 
94
100
  const meta = {
95
- file: resolver.resolveFile({ name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path }, { root, output, group }),
101
+ file: resolver.resolveFile(
102
+ { name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
103
+ { root, output, group: group ?? undefined },
104
+ ),
96
105
  } as const
97
106
 
98
107
  const cyclicSchemas = new Set<string>(ctx.meta.circularNames)
@@ -100,11 +109,11 @@ export const zodGenerator = defineGenerator<PluginZod>({
100
109
  function renderSchemaEntry({ schema, name, keysToOmit }: { schema: ast.SchemaNode | null; name: string; keysToOmit?: Array<string> | null }) {
101
110
  if (!schema) return null
102
111
 
103
- const inferTypeName = inferred ? resolver.resolveTypeName(name) : undefined
112
+ const inferTypeName = inferred ? resolver.resolveTypeName(name) : null
104
113
 
105
114
  const imports = adapter.getImports(schema, (schemaName) => ({
106
115
  name: resolver.resolveSchemaName(schemaName),
107
- path: resolver.resolveFile({ name: schemaName, extname: '.ts' }, { root, output, group }).path,
116
+ path: resolver.resolveFile({ name: schemaName, extname: '.ts' }, { root, output, group: group ?? undefined }).path,
108
117
  }))
109
118
 
110
119
  const cachedStd = zodPrinterCache.get(resolver)
@@ -213,7 +222,7 @@ export const zodGenerator = defineGenerator<PluginZod>({
213
222
  const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath as 'zod' | 'zod/mini')
214
223
 
215
224
  const meta = {
216
- file: resolver.resolveFile({ name: 'operations', extname: '.ts' }, { root, output, group }),
225
+ file: resolver.resolveFile({ name: 'operations', extname: '.ts' }, { root, output, group: group ?? undefined }),
217
226
  } as const
218
227
 
219
228
  const transformedOperations = nodes.map((node) => {
@@ -226,8 +235,11 @@ export const zodGenerator = defineGenerator<PluginZod>({
226
235
  })
227
236
 
228
237
  const imports = transformedOperations.flatMap(({ node, data }) => {
229
- const names = [data.request, ...Object.values(data.responses), ...Object.values(data.parameters)].filter(Boolean) as string[]
230
- const opFile = resolver.resolveFile({ name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path }, { root, output, group })
238
+ const names = [data.request, ...Object.values(data.responses), ...Object.values(data.parameters)].filter(Boolean) as Array<string>
239
+ const opFile = resolver.resolveFile(
240
+ { name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
241
+ { root, output, group: group ?? undefined },
242
+ )
231
243
 
232
244
  return names.map((name) => <File.Import key={[name, opFile.path].join('-')} name={[name]} root={meta.file.path} path={opFile.path} />)
233
245
  })
package/src/plugin.ts CHANGED
@@ -5,20 +5,33 @@ import { resolverZod } from './resolvers/resolverZod.ts'
5
5
  import type { PluginZod } from './types.ts'
6
6
 
7
7
  /**
8
- * Canonical plugin name for `@kubb/plugin-zod`, used in driver lookups and warnings.
8
+ * Canonical plugin name for `@kubb/plugin-zod`. Used for driver lookups and
9
+ * cross-plugin dependency references.
9
10
  */
10
11
  export const pluginZodName = 'plugin-zod' satisfies PluginZod['name']
11
12
 
12
13
  /**
13
- * Generates Zod validation schemas from an OpenAPI specification.
14
- * Walks schemas and operations, delegates to generators, and writes barrel files
15
- * based on the configured `barrelType`.
14
+ * Generates Zod v4 schemas from an OpenAPI spec. Use them to validate API
15
+ * responses at runtime, build form schemas, or feed back into router libraries
16
+ * that consume Zod (tRPC, Hono, Elysia). Pair with `@kubb/plugin-client` and
17
+ * set the client's `parser: 'zod'` to validate every response automatically.
16
18
  *
17
- * @example Zod schema generator
19
+ * @example
18
20
  * ```ts
19
- * import pluginZod from '@kubb/plugin-zod'
21
+ * import { defineConfig } from 'kubb'
22
+ * import { pluginTs } from '@kubb/plugin-ts'
23
+ * import { pluginZod } from '@kubb/plugin-zod'
24
+ *
20
25
  * export default defineConfig({
21
- * plugins: [pluginZod({ output: { path: 'zod' } })]
26
+ * input: { path: './petStore.yaml' },
27
+ * output: { path: './src/gen' },
28
+ * plugins: [
29
+ * pluginTs(),
30
+ * pluginZod({
31
+ * output: { path: './zod' },
32
+ * typed: true,
33
+ * }),
34
+ * ],
22
35
  * })
23
36
  * ```
24
37
  */
@@ -54,7 +67,7 @@ export const pluginZod = definePlugin<PluginZod>((options) => {
54
67
  return `${camelCase(ctx.group)}Controller`
55
68
  },
56
69
  } satisfies Group)
57
- : undefined
70
+ : null
58
71
 
59
72
  return {
60
73
  name: pluginZodName,
@@ -3,12 +3,17 @@ import { defineResolver } from '@kubb/core'
3
3
  import type { PluginZod } from '../types.ts'
4
4
 
5
5
  /**
6
- * Naming convention resolver for Zod plugin.
6
+ * Default resolver used by `@kubb/plugin-zod`. Decides the names and file
7
+ * paths for every generated Zod schema. Schemas use camelCase with a
8
+ * `Schema` suffix (`listPetsSchema`); their inferred types use PascalCase.
7
9
  *
8
- * Provides default naming helpers using camelCase with a `Schema` suffix for schemas.
10
+ * @example Resolve schema and type names
11
+ * ```ts
12
+ * import { resolverZod } from '@kubb/plugin-zod'
9
13
  *
10
- * @example
11
- * `resolverZod.default('list pets', 'function') // 'listPetsSchema'`
14
+ * resolverZod.default('list pets', 'function') // 'listPetsSchema'
15
+ * resolverZod.resolveSchemaTypeName('pet') // 'PetSchema'
16
+ * ```
12
17
  */
13
18
  export const resolverZod = defineResolver<PluginZod>(() => {
14
19
  return {
package/src/types.ts CHANGED
@@ -75,85 +75,106 @@ export type ResolverZod = Resolver &
75
75
 
76
76
  export type Options = {
77
77
  /**
78
- * @default 'zod'
78
+ * Where the generated Zod schemas are written and how they are exported.
79
+ *
80
+ * @default { path: 'zod', barrel: { type: 'named' } }
79
81
  */
80
82
  output?: Output
81
83
  /**
82
- * Group the Zod schemas based on the provided name.
84
+ * Split generated files into subfolders based on the operation's tag.
83
85
  */
84
86
  group?: Group
85
87
  /**
86
- * Tags, operations, or paths to exclude from generation.
88
+ * Skip operations matching at least one entry in the list.
87
89
  */
88
90
  exclude?: Array<Exclude>
89
91
  /**
90
- * Tags, operations, or paths to include in generation.
92
+ * Restrict generation to operations matching at least one entry in the list.
91
93
  */
92
94
  include?: Array<Include>
93
95
  /**
94
- * Override options for specific tags, operations, or paths.
96
+ * Apply a different options object to operations matching a pattern.
95
97
  */
96
98
  override?: Array<Override<ResolvedOptions>>
97
99
  /**
98
- * Import path for Zod package.
100
+ * Module specifier used in the `import { z } from '...'` statement.
101
+ * Use `'zod/mini'` for the tree-shakeable bundle.
99
102
  *
100
103
  * @default 'zod'
101
104
  */
102
105
  importPath?: 'zod' | 'zod/mini' | (string & {})
103
106
  /**
104
- * Add TypeScript type annotations to generated schemas.
107
+ * Tie each Zod schema to its TypeScript type from `@kubb/plugin-ts`. Requires
108
+ * `@kubb/plugin-ts` in the plugins list. TypeScript fails compilation when the
109
+ * schema drifts from the type.
105
110
  */
106
111
  typed?: boolean
107
112
  /**
108
- * Return schemas as inferred types using `z.infer`.
113
+ * Export a `z.infer<typeof schema>` type alias next to every generated schema.
114
+ * Lets the Zod schema act as the single source of truth.
109
115
  */
110
116
  inferred?: boolean
111
117
  /**
112
- * Apply coercion to string values or configure coercion per type.
118
+ * Wrap schemas in `z.coerce` so input is coerced before validation. Useful for
119
+ * form data and query params where everything arrives as a string.
120
+ * - `true` coerces strings, numbers, and dates.
121
+ * - Object form picks per-primitive coercion.
122
+ *
123
+ * @default false
124
+ * @see https://zod.dev/?id=coercion-for-primitives
113
125
  */
114
126
  coercion?: boolean | { dates?: boolean; strings?: boolean; numbers?: boolean }
115
127
  /**
116
- * Generate operation-level schemas (grouped by operationId).
128
+ * Emit an `operations.ts` file with request body, query/path params, and per-status
129
+ * response schemas grouped by operation.
117
130
  */
118
131
  operations?: boolean
119
132
  /**
120
- * Validator to use for UUID format: `uuid` or `guid`.
133
+ * Validator for `format: uuid` properties.
134
+ * - `'uuid'` — `z.uuid()`. Standard RFC 4122.
135
+ * - `'guid'` — `z.guid()`. Accepts Microsoft-style GUIDs.
121
136
  *
122
137
  * @default 'uuid'
123
138
  */
124
139
  guidType?: 'uuid' | 'guid'
125
140
  /**
126
- * Use Zod Mini's functional API for better tree-shaking.
141
+ * Switch to Zod Mini's functional API for better tree-shaking. Also defaults
142
+ * `importPath` to `'zod/mini'`.
127
143
  *
128
144
  * @default false
145
+ * @beta
129
146
  */
130
147
  mini?: boolean
131
148
  /**
132
- * Callback to wrap the generated schema output.
133
- *
134
- * Useful for adding metadata like `.openapi()` or extension helpers.
149
+ * Wrap the generated Zod schema string with extra calls. Receives the raw output
150
+ * and the originating `SchemaNode`. Useful for round-tripping OpenAPI metadata
151
+ * back into Zod (e.g. `.openapi(...)`).
135
152
  */
136
153
  wrapOutput?: (arg: { output: string; schema: ast.SchemaNode }) => string | undefined
137
154
  /**
138
- * Apply casing to parameter names.
155
+ * Rename properties inside path/query/header schemas. Body schemas are unaffected.
156
+ *
157
+ * @note Must match the value of `paramsCasing` on `@kubb/plugin-ts`.
139
158
  */
140
159
  paramsCasing?: 'camelcase'
141
160
  /**
142
- * Additional generators alongside the default generators.
161
+ * Custom generators that run alongside the built-in Zod generators.
143
162
  */
144
163
  generators?: Array<Generator<PluginZod>>
145
164
  /**
146
- * Override naming conventions for schema names and types.
165
+ * Override how schema and operation names are built. Methods you omit fall back
166
+ * to the default `resolverZod`.
147
167
  */
148
168
  resolver?: Partial<ResolverZod> & ThisType<ResolverZod>
149
169
  /**
150
- * Override printer node handlers to customize rendering of specific schema types.
170
+ * Replace the Zod handler for a specific schema type (`'integer'`, `'date'`, ...).
171
+ * When `mini: true`, overrides target the Zod Mini printer instead.
151
172
  */
152
173
  printer?: {
153
174
  nodes?: PrinterZodNodes | PrinterZodMiniNodes
154
175
  }
155
176
  /**
156
- * AST visitor to transform schema and operation nodes.
177
+ * AST visitor applied to each schema or operation node before printing.
157
178
  */
158
179
  transformer?: ast.Visitor
159
180
  }
@@ -163,7 +184,7 @@ type ResolvedOptions = {
163
184
  exclude: Array<Exclude>
164
185
  include: Array<Include> | undefined
165
186
  override: Array<Override<ResolvedOptions>>
166
- group: Group | undefined
187
+ group: Group | null
167
188
  typed: NonNullable<Options['typed']>
168
189
  inferred: NonNullable<Options['inferred']>
169
190
  importPath: NonNullable<Options['importPath']>
package/src/utils.ts CHANGED
@@ -39,11 +39,11 @@ export function buildSchemaNames(node: ast.OperationNode, { params, resolver }:
39
39
  responses['default'] = resolver.resolveResponseName(node)
40
40
 
41
41
  return {
42
- request: node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : undefined,
42
+ request: node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : null,
43
43
  parameters: {
44
- path: pathParam ? resolver.resolvePathParamsName(node, pathParam) : undefined,
45
- query: queryParam ? resolver.resolveQueryParamsName(node, queryParam) : undefined,
46
- header: headerParam ? resolver.resolveHeaderParamsName(node, headerParam) : undefined,
44
+ path: pathParam ? resolver.resolvePathParamsName(node, pathParam) : null,
45
+ query: queryParam ? resolver.resolveQueryParamsName(node, queryParam) : null,
46
+ header: headerParam ? resolver.resolveHeaderParamsName(node, headerParam) : null,
47
47
  },
48
48
  responses,
49
49
  errors,
@@ -133,7 +133,7 @@ export function lengthConstraints({ min, max, pattern }: LengthConstraints): str
133
133
  * Build `.check(z.minimum(), z.maximum())` for `zod/mini` numeric constraints.
134
134
  */
135
135
  export function numberChecksMini({ min, max, exclusiveMinimum, exclusiveMaximum, multipleOf }: NumericConstraints): string {
136
- const checks: string[] = []
136
+ const checks: Array<string> = []
137
137
  if (min !== undefined) checks.push(`z.minimum(${min})`)
138
138
  if (max !== undefined) checks.push(`z.maximum(${max})`)
139
139
  if (exclusiveMinimum !== undefined) checks.push(`z.minimum(${exclusiveMinimum}, { exclusive: true })`)
@@ -146,7 +146,7 @@ export function numberChecksMini({ min, max, exclusiveMinimum, exclusiveMaximum,
146
146
  * Build `.check(z.minLength(), z.maxLength(), z.regex())` for `zod/mini` length constraints.
147
147
  */
148
148
  export function lengthChecksMini({ min, max, pattern }: LengthConstraints): string {
149
- const checks: string[] = []
149
+ const checks: Array<string> = []
150
150
  if (min !== undefined) checks.push(`z.minLength(${min})`)
151
151
  if (max !== undefined) checks.push(`z.maxLength(${max})`)
152
152
  if (pattern !== undefined) checks.push(`z.regex(${toRegExpString(pattern, null)})`)