@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/dist/index.cjs +75 -139
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +16 -18
- package/dist/index.js +69 -133
- package/dist/index.js.map +1 -1
- package/package.json +9 -16
- package/src/components/Operations.tsx +1 -1
- package/src/generators/zodGenerator.tsx +12 -11
- package/src/plugin.ts +2 -2
- package/src/printers/printerZod.ts +45 -47
- package/src/printers/printerZodMini.ts +39 -41
- package/src/resolvers/resolverZod.ts +9 -7
- package/src/types.ts +12 -16
- package/src/utils.ts +2 -2
- package/extension.yaml +0 -970
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kubb/plugin-zod",
|
|
3
|
-
"version": "5.0.0-beta.
|
|
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/
|
|
52
|
-
"@kubb/
|
|
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/
|
|
56
|
-
"@internals/
|
|
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.
|
|
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
|
|
73
|
-
"clean": "
|
|
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,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,
|
|
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:
|
|
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) ? (
|
|
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
|
-
{
|
|
120
|
-
|
|
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) ? (
|
|
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
|
-
{
|
|
194
|
-
|
|
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',
|
|
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
|
|
60
|
+
const groupConfig = createGroupConfig(group)
|
|
61
61
|
|
|
62
62
|
return {
|
|
63
63
|
name: pluginZodName,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { stringify } from '@
|
|
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 ? (
|
|
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
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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
|
-
|
|
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(
|
|
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)},
|
|
306
|
+
return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)}, ${buildList(members)})`
|
|
309
307
|
}
|
|
310
308
|
|
|
311
|
-
return `z.union(
|
|
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 '@
|
|
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 ? (
|
|
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
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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
|
-
|
|
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(
|
|
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)},
|
|
237
|
+
return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)}, ${buildList(members)})`
|
|
240
238
|
}
|
|
241
239
|
|
|
242
|
-
return `z.union(
|
|
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') // '
|
|
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
|
-
|
|
24
|
-
return type
|
|
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') // → '
|
|
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') // → '
|
|
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('
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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 '@
|
|
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 =
|
|
89
|
+
const refName = extractRefName(node.ref)
|
|
90
90
|
if (refName) {
|
|
91
91
|
if (seen.has(refName)) return false
|
|
92
92
|
seen.add(refName)
|