@kubb/plugin-zod 5.0.0-beta.42 → 5.0.0-beta.56

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.42",
3
+ "version": "5.0.0-beta.56",
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",
@@ -26,7 +26,6 @@
26
26
  "src",
27
27
  "templates",
28
28
  "dist",
29
- "extension.yaml",
30
29
  "!/**/**.test.**",
31
30
  "!/**/__tests__/**",
32
31
  "!/**/__snapshots__/**"
@@ -48,29 +47,23 @@
48
47
  "registry": "https://registry.npmjs.org/"
49
48
  },
50
49
  "dependencies": {
51
- "@kubb/core": "5.0.0-beta.42",
52
- "@kubb/renderer-jsx": "5.0.0-beta.42"
50
+ "@kubb/ast": "5.0.0-beta.55",
51
+ "@kubb/core": "5.0.0-beta.55",
52
+ "@kubb/renderer-jsx": "5.0.0-beta.55"
53
53
  },
54
54
  "devDependencies": {
55
- "@internals/shared": "0.0.0",
56
- "@internals/utils": "0.0.0"
55
+ "@internals/utils": "0.0.0",
56
+ "@internals/shared": "0.0.0"
57
57
  },
58
58
  "peerDependencies": {
59
- "@kubb/renderer-jsx": "5.0.0-beta.42"
59
+ "@kubb/renderer-jsx": "5.0.0-beta.55"
60
60
  },
61
- "size-limit": [
62
- {
63
- "path": "./dist/*.js",
64
- "limit": "510 KiB",
65
- "gzip": true
66
- }
67
- ],
68
61
  "engines": {
69
62
  "node": ">=22"
70
63
  },
71
64
  "scripts": {
72
- "build": "tsdown && size-limit",
73
- "clean": "npx rimraf ./dist",
65
+ "build": "tsdown",
66
+ "clean": "node -e \"require('node:fs').rmSync('./dist', {recursive:true,force:true})\"",
74
67
  "lint": "oxlint .",
75
68
  "lint:fix": "oxlint --fix .",
76
69
  "release": "pnpm publish --no-git-check",
@@ -1,4 +1,4 @@
1
- import { stringifyObject } from '@internals/utils'
1
+ import { stringifyObject } from '@kubb/ast/utils'
2
2
  import { ast } from '@kubb/core'
3
3
  import { Const, File, Type } from '@kubb/renderer-jsx'
4
4
  import type { KubbReactNode } from '@kubb/renderer-jsx/types'
@@ -1,8 +1,9 @@
1
1
  import { resolveContentTypeVariants } from '@internals/shared'
2
+ import { extractRefName } from '@kubb/ast/utils'
2
3
  import type { Adapter } from '@kubb/core'
3
4
  import { ast, defineGenerator } from '@kubb/core'
4
5
  import type { AdapterOas } from '@kubb/adapter-oas'
5
- import { File, jsxRendererSync } from '@kubb/renderer-jsx'
6
+ import { File, jsxRenderer } from '@kubb/renderer-jsx'
6
7
  import { Operations } from '../components/Operations.tsx'
7
8
  import { Zod } from '../components/Zod.tsx'
8
9
  import { ZOD_NAMESPACE_IMPORTS } from '../constants.ts'
@@ -54,7 +55,7 @@ function getMiniPrinter(resolver: ResolverZod, params: { guidType: unknown; wrap
54
55
  */
55
56
  export const zodGenerator = defineGenerator<PluginZod>({
56
57
  name: 'zod',
57
- renderer: jsxRendererSync,
58
+ renderer: jsxRenderer,
58
59
  schema(node, ctx) {
59
60
  const { adapter, config, resolver, root } = ctx
60
61
  const { output, coercion, guidType, mini, wrapOutput, inferred, importPath, group, printer } = ctx.options
@@ -64,7 +65,6 @@ export const zodGenerator = defineGenerator<PluginZod>({
64
65
  return
65
66
  }
66
67
 
67
- const mode = ctx.getMode(output)
68
68
  const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath as 'zod' | 'zod/mini')
69
69
  const cyclicSchemas = new Set<string>(ctx.meta.circularNames)
70
70
 
@@ -75,7 +75,7 @@ export const zodGenerator = defineGenerator<PluginZod>({
75
75
  const codecRefNames = new Set(
76
76
  hasCodec
77
77
  ? ast.collect<string>(node, {
78
- schema: (n) => (n.type === 'ref' && n.ref && containsCodec(n) ? (ast.extractRefName(n.ref) ?? undefined) : undefined),
78
+ schema: (n) => (n.type === 'ref' && n.ref && containsCodec(n) ? (extractRefName(n.ref) ?? undefined) : undefined),
79
79
  })
80
80
  : [],
81
81
  )
@@ -85,7 +85,7 @@ export const zodGenerator = defineGenerator<PluginZod>({
85
85
  }))
86
86
  const inputImportEntries = hasCodec
87
87
  ? [...codecRefNames].map((schemaName) => ({
88
- name: resolver.resolveInputSchemaName(schemaName),
88
+ name: [resolver.resolveInputSchemaName(schemaName)],
89
89
  path: resolver.resolveFile({ name: schemaName, extname: '.ts' }, { root, output, group: group ?? undefined }).path,
90
90
  }))
91
91
  : []
@@ -116,8 +116,9 @@ export const zodGenerator = defineGenerator<PluginZod>({
116
116
  footer={resolver.resolveFooter(ctx.meta, { output, config, file: { path: meta.file.path, baseName: meta.file.baseName } })}
117
117
  >
118
118
  <File.Import name={isZodImport ? 'z' : ['z']} path={importPath} isNameSpace={isZodImport} />
119
- {mode === 'split' &&
120
- imports.map((imp) => <File.Import key={[node.name, imp.path, imp.name].join('-')} root={meta.file.path} path={imp.path} name={imp.name} />)}
119
+ {imports.map((imp) => (
120
+ <File.Import key={[node.name, imp.path, imp.name].join('-')} root={meta.file.path} path={imp.path} name={imp.name} />
121
+ ))}
121
122
 
122
123
  <Zod name={meta.name} node={node} printer={schemaPrinter} inferTypeName={inferTypeName} />
123
124
  {hasCodec && stdPrinters && (
@@ -137,7 +138,6 @@ export const zodGenerator = defineGenerator<PluginZod>({
137
138
  const { output, coercion, guidType, mini, wrapOutput, inferred, importPath, group, paramsCasing, printer } = ctx.options
138
139
  const dateType = (adapter as Adapter<AdapterOas>).options.dateType
139
140
 
140
- const mode = ctx.getMode(output)
141
141
  const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath as 'zod' | 'zod/mini')
142
142
 
143
143
  const params = ast.caseParams(node.parameters, paramsCasing)
@@ -171,7 +171,7 @@ export const zodGenerator = defineGenerator<PluginZod>({
171
171
  direction === 'input' && !mini
172
172
  ? new Set(
173
173
  ast.collect<string>(schema, {
174
- schema: (n) => (n.type === 'ref' && n.ref && containsCodec(n) ? (ast.extractRefName(n.ref) ?? undefined) : undefined),
174
+ schema: (n) => (n.type === 'ref' && n.ref && containsCodec(n) ? (extractRefName(n.ref) ?? undefined) : undefined),
175
175
  }),
176
176
  )
177
177
  : null
@@ -190,8 +190,9 @@ export const zodGenerator = defineGenerator<PluginZod>({
190
190
 
191
191
  return (
192
192
  <>
193
- {mode === 'split' &&
194
- imports.map((imp) => <File.Import key={[name, imp.path, imp.name].join('-')} root={meta.file.path} path={imp.path} name={imp.name} />)}
193
+ {imports.map((imp) => (
194
+ <File.Import key={[name, imp.path, imp.name].join('-')} root={meta.file.path} path={imp.path} name={imp.name} />
195
+ ))}
195
196
  <Zod name={name} node={schema} printer={schemaPrinter} inferTypeName={inferTypeName} />
196
197
  </>
197
198
  )
package/src/plugin.ts CHANGED
@@ -37,7 +37,7 @@ export const pluginZodName = 'plugin-zod' satisfies PluginZod['name']
37
37
  */
38
38
  export const pluginZod = definePlugin<PluginZod>((options) => {
39
39
  const {
40
- output = { path: 'zod', barrelType: 'named' },
40
+ output = { path: 'zod', barrel: { type: 'named' } },
41
41
  group,
42
42
  exclude = [],
43
43
  include,
@@ -57,7 +57,7 @@ export const pluginZod = definePlugin<PluginZod>((options) => {
57
57
  generators: userGenerators = [],
58
58
  } = options
59
59
 
60
- const groupConfig = createGroupConfig(group, { suffix: 'Controller' })
60
+ const groupConfig = createGroupConfig(group)
61
61
 
62
62
  return {
63
63
  name: pluginZodName,
@@ -1,4 +1,4 @@
1
- import { stringify } from '@internals/utils'
1
+ import { buildList, buildObject, extractRefName, objectKey, stringify } from '@kubb/ast/utils'
2
2
 
3
3
  import { ast } from '@kubb/core'
4
4
  import type { PluginZod, ResolverZod } from '../types.ts'
@@ -203,7 +203,7 @@ export const printerZod = ast.definePrinter<PrinterZodFactory>((options) => {
203
203
  },
204
204
  ref(node) {
205
205
  if (!node.name) return null
206
- const refName = node.ref ? (ast.extractRefName(node.ref) ?? node.name) : node.name
206
+ const refName = node.ref ? (extractRefName(node.ref) ?? node.name) : node.name
207
207
 
208
208
  // In the input direction, a date-bearing component resolves to its `${name}InputSchema`
209
209
  // variant so request bodies encode `Date → string` instead of decoding.
@@ -221,50 +221,48 @@ export const printerZod = ast.definePrinter<PrinterZodFactory>((options) => {
221
221
  return resolvedName
222
222
  },
223
223
  object(node) {
224
- const properties = node.properties
225
- .map((prop) => {
226
- const { name: propName, schema } = prop
227
-
228
- const meta = ast.syncSchemaRef(schema)
229
-
230
- const isNullable = meta.nullable
231
- const isOptional = schema.optional
232
- const isNullish = schema.nullish
233
-
234
- const hasSelfRef = this.options.cyclicSchemas != null && ast.containsCircularRef(schema, { circularSchemas: this.options.cyclicSchemas })
235
- // Inside a getter the getter itself defers evaluation, so suppress
236
- // z.lazy() wrapping on nested refs by temporarily clearing cyclicSchemas.
237
- // Save before clearing: this.options === options (same reference via definePrinter),
238
- // so reading options.cyclicSchemas after mutation would return undefined.
239
- const savedCyclicSchemas = this.options.cyclicSchemas
240
- if (hasSelfRef) this.options.cyclicSchemas = undefined
241
- const baseOutput = this.transform(schema) ?? this.transform(ast.createSchema({ type: 'unknown' }))!
242
- if (hasSelfRef) this.options.cyclicSchemas = savedCyclicSchemas
243
-
244
- const wrappedOutput = this.options.wrapOutput ? this.options.wrapOutput({ output: baseOutput, schema }) || baseOutput : baseOutput
245
-
246
- // When a property schema is not a ref but the metadata is from a ref (e.g., discriminator
247
- // property override), skip applying the description from the ref target to avoid applying
248
- // metadata from a replaced schema.
249
- const descriptionToApply = schema.type !== 'ref' && meta.type === 'ref' ? undefined : meta.description
250
-
251
- const value = applyModifiers({
252
- value: wrappedOutput,
253
- nullable: isNullable,
254
- optional: isOptional,
255
- nullish: isNullish,
256
- defaultValue: meta.default,
257
- description: descriptionToApply,
258
- })
259
-
260
- if (hasSelfRef) {
261
- return `get "${propName}"() { return ${value} }`
262
- }
263
- return `"${propName}": ${value}`
224
+ const entries = node.properties.map((prop) => {
225
+ const { name: propName, schema } = prop
226
+
227
+ const meta = ast.syncSchemaRef(schema)
228
+
229
+ const isNullable = meta.nullable
230
+ const isOptional = schema.optional
231
+ const isNullish = schema.nullish
232
+
233
+ const hasSelfRef = this.options.cyclicSchemas != null && ast.containsCircularRef(schema, { circularSchemas: this.options.cyclicSchemas })
234
+ // Inside a getter the getter itself defers evaluation, so suppress
235
+ // z.lazy() wrapping on nested refs by temporarily clearing cyclicSchemas.
236
+ // Save before clearing: this.options === options (same reference via definePrinter),
237
+ // so reading options.cyclicSchemas after mutation would return undefined.
238
+ const savedCyclicSchemas = this.options.cyclicSchemas
239
+ if (hasSelfRef) this.options.cyclicSchemas = undefined
240
+ const baseOutput = this.transform(schema) ?? this.transform(ast.createSchema({ type: 'unknown' }))!
241
+ if (hasSelfRef) this.options.cyclicSchemas = savedCyclicSchemas
242
+
243
+ const wrappedOutput = this.options.wrapOutput ? this.options.wrapOutput({ output: baseOutput, schema }) || baseOutput : baseOutput
244
+
245
+ // When a property schema is not a ref but the metadata is from a ref (e.g., discriminator
246
+ // property override), skip applying the description from the ref target to avoid applying
247
+ // metadata from a replaced schema.
248
+ const descriptionToApply = schema.type !== 'ref' && meta.type === 'ref' ? undefined : meta.description
249
+
250
+ const value = applyModifiers({
251
+ value: wrappedOutput,
252
+ nullable: isNullable,
253
+ optional: isOptional,
254
+ nullish: isNullish,
255
+ defaultValue: meta.default,
256
+ description: descriptionToApply,
264
257
  })
265
- .join(',\n ')
266
258
 
267
- const objectBase = `z.object({\n ${properties}\n })`
259
+ if (hasSelfRef) {
260
+ return `get ${objectKey(propName)}() { return ${value} }`
261
+ }
262
+ return `${objectKey(propName)}: ${value}`
263
+ })
264
+
265
+ const objectBase = `z.object(${buildObject(entries)})`
268
266
 
269
267
  // Handle additionalProperties as .catchall() or .strict()
270
268
  const result = (() => {
@@ -289,7 +287,7 @@ export const printerZod = ast.definePrinter<PrinterZodFactory>((options) => {
289
287
  tuple(node) {
290
288
  const items = (node.items ?? []).map((item) => this.transform(item)).filter(Boolean)
291
289
 
292
- return `z.tuple([${items.join(', ')}])`
290
+ return `z.tuple(${buildList(items)})`
293
291
  },
294
292
  union(node) {
295
293
  const nodeMembers = node.members ?? []
@@ -305,10 +303,10 @@ export const printerZod = ast.definePrinter<PrinterZodFactory>((options) => {
305
303
  if (node.discriminatorPropertyName && !nodeMembers.some((m) => m.type === 'intersection')) {
306
304
  // z.discriminatedUnion requires ZodObject members; intersections (ZodIntersection) are not
307
305
  // assignable to $ZodDiscriminant, so fall back to z.union when any member is an intersection.
308
- return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)}, [${members.join(', ')}])`
306
+ return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)}, ${buildList(members)})`
309
307
  }
310
308
 
311
- return `z.union([${members.join(', ')}])`
309
+ return `z.union(${buildList(members)})`
312
310
  },
313
311
  intersection(node) {
314
312
  const members = node.members ?? []
@@ -1,4 +1,4 @@
1
- import { stringify } from '@internals/utils'
1
+ import { buildList, buildObject, extractRefName, objectKey, stringify } from '@kubb/ast/utils'
2
2
 
3
3
  import { ast } from '@kubb/core'
4
4
  import type { PluginZod, ResolverZod } from '../types.ts'
@@ -161,7 +161,7 @@ export const printerZodMini = ast.definePrinter<PrinterZodMiniFactory>((options)
161
161
 
162
162
  ref(node) {
163
163
  if (!node.name) return null
164
- const refName = node.ref ? (ast.extractRefName(node.ref) ?? node.name) : node.name
164
+ const refName = node.ref ? (extractRefName(node.ref) ?? node.name) : node.name
165
165
  const resolvedName = node.ref ? (this.options.resolver?.default(refName, 'function') ?? refName) : node.name
166
166
 
167
167
  if (node.ref && this.options.cyclicSchemas?.has(refName)) {
@@ -171,44 +171,42 @@ export const printerZodMini = ast.definePrinter<PrinterZodMiniFactory>((options)
171
171
  return resolvedName
172
172
  },
173
173
  object(node) {
174
- const properties = node.properties
175
- .map((prop) => {
176
- const { name: propName, schema } = prop
177
-
178
- const meta = ast.syncSchemaRef(schema)
179
-
180
- const isNullable = meta.nullable
181
- const isOptional = schema.optional
182
- const isNullish = schema.nullish
183
-
184
- const hasSelfRef = this.options.cyclicSchemas != null && ast.containsCircularRef(schema, { circularSchemas: this.options.cyclicSchemas })
185
- // Inside a getter the getter itself defers evaluation, so suppress
186
- // z.lazy() wrapping on nested refs by temporarily clearing cyclicSchemas.
187
- // Save before clearing: this.options === options (same reference via definePrinter),
188
- // so reading options.cyclicSchemas after mutation would return undefined.
189
- const savedCyclicSchemas = this.options.cyclicSchemas
190
- if (hasSelfRef) this.options.cyclicSchemas = undefined
191
- const baseOutput = this.transform(schema) ?? this.transform(ast.createSchema({ type: 'unknown' }))!
192
- if (hasSelfRef) this.options.cyclicSchemas = savedCyclicSchemas
193
-
194
- const wrappedOutput = this.options.wrapOutput ? this.options.wrapOutput({ output: baseOutput, schema }) || baseOutput : baseOutput
195
-
196
- const value = applyMiniModifiers({
197
- value: wrappedOutput,
198
- nullable: isNullable,
199
- optional: isOptional,
200
- nullish: isNullish,
201
- defaultValue: meta.default,
202
- })
203
-
204
- if (hasSelfRef) {
205
- return `get "${propName}"() { return ${value} }`
206
- }
207
- return `"${propName}": ${value}`
174
+ const entries = node.properties.map((prop) => {
175
+ const { name: propName, schema } = prop
176
+
177
+ const meta = ast.syncSchemaRef(schema)
178
+
179
+ const isNullable = meta.nullable
180
+ const isOptional = schema.optional
181
+ const isNullish = schema.nullish
182
+
183
+ const hasSelfRef = this.options.cyclicSchemas != null && ast.containsCircularRef(schema, { circularSchemas: this.options.cyclicSchemas })
184
+ // Inside a getter the getter itself defers evaluation, so suppress
185
+ // z.lazy() wrapping on nested refs by temporarily clearing cyclicSchemas.
186
+ // Save before clearing: this.options === options (same reference via definePrinter),
187
+ // so reading options.cyclicSchemas after mutation would return undefined.
188
+ const savedCyclicSchemas = this.options.cyclicSchemas
189
+ if (hasSelfRef) this.options.cyclicSchemas = undefined
190
+ const baseOutput = this.transform(schema) ?? this.transform(ast.createSchema({ type: 'unknown' }))!
191
+ if (hasSelfRef) this.options.cyclicSchemas = savedCyclicSchemas
192
+
193
+ const wrappedOutput = this.options.wrapOutput ? this.options.wrapOutput({ output: baseOutput, schema }) || baseOutput : baseOutput
194
+
195
+ const value = applyMiniModifiers({
196
+ value: wrappedOutput,
197
+ nullable: isNullable,
198
+ optional: isOptional,
199
+ nullish: isNullish,
200
+ defaultValue: meta.default,
208
201
  })
209
- .join(',\n ')
210
202
 
211
- return `z.object({\n ${properties}\n })`
203
+ if (hasSelfRef) {
204
+ return `get ${objectKey(propName)}() { return ${value} }`
205
+ }
206
+ return `${objectKey(propName)}: ${value}`
207
+ })
208
+
209
+ return `z.object(${buildObject(entries)})`
212
210
  },
213
211
  array(node) {
214
212
  const items = (node.items ?? []).map((item) => this.transform(item)).filter(Boolean)
@@ -220,7 +218,7 @@ export const printerZodMini = ast.definePrinter<PrinterZodMiniFactory>((options)
220
218
  tuple(node) {
221
219
  const items = (node.items ?? []).map((item) => this.transform(item)).filter(Boolean)
222
220
 
223
- return `z.tuple([${items.join(', ')}])`
221
+ return `z.tuple(${buildList(items)})`
224
222
  },
225
223
  union(node) {
226
224
  const nodeMembers = node.members ?? []
@@ -236,10 +234,10 @@ export const printerZodMini = ast.definePrinter<PrinterZodMiniFactory>((options)
236
234
  if (node.discriminatorPropertyName && !nodeMembers.some((m) => m.type === 'intersection')) {
237
235
  // z.discriminatedUnion requires ZodObject members; intersections (ZodIntersection) are not
238
236
  // assignable to $ZodDiscriminant, so fall back to z.union when any member is an intersection.
239
- return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)}, [${members.join(', ')}])`
237
+ return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)}, ${buildList(members)})`
240
238
  }
241
239
 
242
- return `z.union([${members.join(', ')}])`
240
+ return `z.union(${buildList(members)})`
243
241
  },
244
242
  intersection(node) {
245
243
  const members = node.members ?? []
@@ -1,18 +1,20 @@
1
- import { camelCase, ensureValidVarName, pascalCase } from '@internals/utils'
1
+ import { camelCase, ensureValidVarName, pascalCase, toFilePath } from '@internals/utils'
2
2
  import { defineResolver } from '@kubb/core'
3
3
  import type { PluginZod } from '../types.ts'
4
4
 
5
5
  /**
6
6
  * Default resolver used by `@kubb/plugin-zod`. Decides the names and file
7
7
  * paths for every generated Zod schema. Schemas use camelCase with a
8
- * `Schema` suffix (`listPetsSchema`); their inferred types use PascalCase.
8
+ * `Schema` suffix (`listPetsSchema`); their inferred types use PascalCase
9
+ * with a `SchemaType` suffix (`PetSchemaType`), so the value and the type
10
+ * never share an identifier even when the schema name is all-uppercase.
9
11
  *
10
12
  * @example Resolve schema and type names
11
13
  * ```ts
12
14
  * import { resolverZod } from '@kubb/plugin-zod'
13
15
  *
14
16
  * resolverZod.default('list pets', 'function') // 'listPetsSchema'
15
- * resolverZod.resolveSchemaTypeName('pet') // 'PetSchema'
17
+ * resolverZod.resolveSchemaTypeName('pet') // 'PetSchemaType'
16
18
  * ```
17
19
  */
18
20
  export const resolverZod = defineResolver<PluginZod>(() => {
@@ -20,14 +22,14 @@ export const resolverZod = defineResolver<PluginZod>(() => {
20
22
  name: 'default',
21
23
  pluginName: 'plugin-zod',
22
24
  default(name, type) {
23
- const resolved = camelCase(name, { isFile: type === 'file', suffix: type ? 'schema' : undefined })
24
- return type === 'file' ? resolved : ensureValidVarName(resolved)
25
+ if (type === 'file') return toFilePath(name, (part) => camelCase(part, { suffix: 'schema' }))
26
+ return ensureValidVarName(camelCase(name, { suffix: type ? 'schema' : undefined }))
25
27
  },
26
28
  resolveSchemaName(name) {
27
29
  return ensureValidVarName(camelCase(name, { suffix: 'schema' }))
28
30
  },
29
31
  resolveSchemaTypeName(name) {
30
- return ensureValidVarName(pascalCase(name, { suffix: 'schema' }))
32
+ return ensureValidVarName(pascalCase(name, { suffix: 'schema type' }))
31
33
  },
32
34
  resolveInputSchemaName(name) {
33
35
  return this.resolveSchemaName(`${name} input`)
@@ -36,7 +38,7 @@ export const resolverZod = defineResolver<PluginZod>(() => {
36
38
  return this.resolveSchemaTypeName(`${name} input`)
37
39
  },
38
40
  resolveTypeName(name) {
39
- return ensureValidVarName(pascalCase(name))
41
+ return ensureValidVarName(pascalCase(name, { suffix: 'type' }))
40
42
  },
41
43
  resolvePathName(name, type) {
42
44
  return this.default(name, type)
package/src/types.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ast, Exclude, Generator, Group, Include, Output, Override, PluginFactoryOptions, Resolver } from '@kubb/core'
1
+ import type { ast, Exclude, Generator, Group, Include, Output, OutputOptions, Override, PluginFactoryOptions, Resolver } from '@kubb/core'
2
2
  import type { PrinterZodNodes } from './printers/printerZod.ts'
3
3
  import type { PrinterZodMiniNodes } from './printers/printerZodMini.ts'
4
4
 
@@ -15,7 +15,7 @@ export type ResolverZod = Resolver &
15
15
  * Resolves the schema type name (inferred type from schema).
16
16
  *
17
17
  * @example Schema type names
18
- * `resolver.resolveSchemaTypeName('pet') // → 'Pet'`
18
+ * `resolver.resolveSchemaTypeName('pet') // → 'PetSchemaType'`
19
19
  */
20
20
  resolveSchemaTypeName(this: ResolverZod, name: string): string
21
21
  /**
@@ -30,14 +30,14 @@ export type ResolverZod = Resolver &
30
30
  * Resolves the inferred type name for the request (input) direction variant.
31
31
  *
32
32
  * @example Input schema type names
33
- * `resolver.resolveInputSchemaTypeName('order') // → 'OrderInputSchema'`
33
+ * `resolver.resolveInputSchemaTypeName('order') // → 'OrderInputSchemaType'`
34
34
  */
35
35
  resolveInputSchemaTypeName(this: ResolverZod, name: string): string
36
36
  /**
37
37
  * Resolves the generated type name from the schema.
38
38
  *
39
39
  * @example Type names
40
- * `resolver.resolveTypeName('pet') // → 'Pet'`
40
+ * `resolver.resolveTypeName('petSchema') // → 'PetSchemaType'`
41
41
  */
42
42
  resolveTypeName(this: ResolverZod, name: string): string
43
43
  /**
@@ -88,17 +88,13 @@ export type ResolverZod = Resolver &
88
88
  resolveHeaderParamsName(this: ResolverZod, node: ast.OperationNode, param: ast.ParameterNode): string
89
89
  }
90
90
 
91
- export type Options = {
92
- /**
93
- * Where the generated Zod schemas are written and how they are exported.
94
- *
95
- * @default { path: 'zod', barrel: { type: 'named' } }
96
- */
97
- output?: Output
98
- /**
99
- * Split generated files into subfolders based on the operation's tag.
100
- */
101
- group?: Group
91
+ /**
92
+ * Where the generated Zod schemas are written and how they are exported, plus the optional
93
+ * `group` strategy. The `group` option organizes `output.mode: 'directory'` output into per-tag or per-path subdirectories.
94
+ *
95
+ * @default { path: 'zod', barrel: { type: 'named' } }
96
+ */
97
+ export type Options = OutputOptions & {
102
98
  /**
103
99
  * Skip operations matching at least one entry in the list.
104
100
  */
@@ -115,7 +111,7 @@ export type Options = {
115
111
  * Module specifier used in the `import { z } from '...'` statement.
116
112
  * Use `'zod/mini'` for the tree-shakeable bundle.
117
113
  *
118
- * @default 'zod'
114
+ * @default mini ? 'zod/mini' : 'zod'
119
115
  */
120
116
  importPath?: 'zod' | 'zod/mini' | (string & {})
121
117
  /**
package/src/utils.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { stringify, toRegExpString } from '@internals/utils'
1
+ import { extractRefName, stringify, toRegExpString } from '@kubb/ast/utils'
2
2
  import { ast } from '@kubb/core'
3
3
  import type { PluginZod, ResolverZod } from './types.ts'
4
4
 
@@ -86,7 +86,7 @@ export function containsCodec(node: ast.SchemaNode | undefined, seen: Set<string
86
86
 
87
87
  if (node.type === 'ref') {
88
88
  if (!node.ref) return false
89
- const refName = ast.extractRefName(node.ref)
89
+ const refName = extractRefName(node.ref)
90
90
  if (refName) {
91
91
  if (seen.has(refName)) return false
92
92
  seen.add(refName)