@kubb/plugin-zod 5.0.0-alpha.8 → 5.0.0-beta.3
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/LICENSE +17 -10
- package/README.md +1 -3
- package/dist/index.cjs +1061 -105
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +369 -4
- package/dist/index.js +1053 -104
- package/dist/index.js.map +1 -1
- package/package.json +44 -70
- package/src/components/Operations.tsx +25 -18
- package/src/components/Zod.tsx +21 -121
- package/src/constants.ts +5 -0
- package/src/generators/zodGenerator.tsx +174 -160
- package/src/index.ts +11 -2
- package/src/plugin.ts +67 -156
- package/src/printers/printerZod.ts +339 -0
- package/src/printers/printerZodMini.ts +295 -0
- package/src/resolvers/resolverZod.ts +57 -0
- package/src/types.ts +130 -115
- package/src/utils.ts +222 -0
- package/dist/components-B7zUFnAm.cjs +0 -890
- package/dist/components-B7zUFnAm.cjs.map +0 -1
- package/dist/components-eECfXVou.js +0 -842
- package/dist/components-eECfXVou.js.map +0 -1
- package/dist/components.cjs +0 -4
- package/dist/components.d.ts +0 -56
- package/dist/components.js +0 -2
- package/dist/generators-BjPDdJUz.cjs +0 -301
- package/dist/generators-BjPDdJUz.cjs.map +0 -1
- package/dist/generators-lTWPS6oN.js +0 -290
- package/dist/generators-lTWPS6oN.js.map +0 -1
- package/dist/generators.cjs +0 -4
- package/dist/generators.d.ts +0 -479
- package/dist/generators.js +0 -2
- package/dist/templates/ToZod.source.cjs +0 -7
- package/dist/templates/ToZod.source.cjs.map +0 -1
- package/dist/templates/ToZod.source.d.ts +0 -7
- package/dist/templates/ToZod.source.js +0 -6
- package/dist/templates/ToZod.source.js.map +0 -1
- package/dist/types-CoCoOc2u.d.ts +0 -172
- package/src/components/index.ts +0 -2
- package/src/generators/index.ts +0 -2
- package/src/generators/operationsGenerator.tsx +0 -50
- package/src/parser.ts +0 -909
- package/src/templates/ToZod.source.ts +0 -4
- package/templates/ToZod.ts +0 -61
|
@@ -1,201 +1,215 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
1
|
+
import type { Adapter } from '@kubb/core'
|
|
2
|
+
import { ast, defineGenerator } from '@kubb/core'
|
|
3
|
+
import type { AdapterOas } from '@kubb/adapter-oas'
|
|
4
|
+
import { File, jsxRenderer } from '@kubb/renderer-jsx'
|
|
5
|
+
import { Operations } from '../components/Operations.tsx'
|
|
6
|
+
import { Zod } from '../components/Zod.tsx'
|
|
7
|
+
import { ZOD_NAMESPACE_IMPORTS } from '../constants.ts'
|
|
8
|
+
import { printerZod } from '../printers/printerZod.ts'
|
|
9
|
+
import { printerZodMini } from '../printers/printerZodMini.ts'
|
|
10
10
|
import type { PluginZod } from '../types'
|
|
11
|
+
import { buildSchemaNames } from '../utils.ts'
|
|
11
12
|
|
|
12
|
-
export const zodGenerator =
|
|
13
|
+
export const zodGenerator = defineGenerator<PluginZod>({
|
|
13
14
|
name: 'zod',
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const oas = useOas()
|
|
24
|
-
const { getSchemas, getFile, getGroup } = useOperationManager(generator)
|
|
25
|
-
const schemaManager = useSchemaManager()
|
|
26
|
-
|
|
27
|
-
const file = getFile(operation)
|
|
28
|
-
const schemas = getSchemas(operation)
|
|
29
|
-
const schemaGenerator = new SchemaGenerator(options, {
|
|
30
|
-
fabric: generator.context.fabric,
|
|
31
|
-
oas,
|
|
32
|
-
plugin,
|
|
33
|
-
driver,
|
|
34
|
-
events: generator.context.events,
|
|
35
|
-
mode,
|
|
36
|
-
override: options.override,
|
|
37
|
-
})
|
|
15
|
+
renderer: jsxRenderer,
|
|
16
|
+
schema(node, ctx) {
|
|
17
|
+
const { adapter, config, resolver, root } = ctx
|
|
18
|
+
const { output, coercion, guidType, mini, wrapOutput, inferred, importPath, group, printer } = ctx.options
|
|
19
|
+
const dateType = (adapter as Adapter<AdapterOas>).options.dateType
|
|
20
|
+
|
|
21
|
+
if (!node.name) {
|
|
22
|
+
return
|
|
23
|
+
}
|
|
38
24
|
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
.filter(Boolean)
|
|
42
|
-
const toZodPath = path.resolve(config.root, config.output.path, '.kubb/ToZod.ts')
|
|
25
|
+
const mode = ctx.getMode(output)
|
|
26
|
+
const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath as 'zod' | 'zod/mini')
|
|
43
27
|
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
28
|
+
const imports = adapter.getImports(node, (schemaName) => ({
|
|
29
|
+
name: resolver.resolveSchemaName(schemaName),
|
|
30
|
+
path: resolver.resolveFile({ name: schemaName, extname: '.ts' }, { root, output, group }).path,
|
|
31
|
+
}))
|
|
47
32
|
|
|
48
|
-
|
|
49
|
-
|
|
33
|
+
const meta = {
|
|
34
|
+
name: resolver.resolveSchemaName(node.name),
|
|
35
|
+
file: resolver.resolveFile({ name: node.name, extname: '.ts' }, { root, output, group }),
|
|
36
|
+
} as const
|
|
50
37
|
|
|
51
|
-
|
|
52
|
-
delete schemaObject.$ref
|
|
38
|
+
const inferTypeName = inferred ? resolver.resolveSchemaTypeName(node.name) : undefined
|
|
53
39
|
|
|
54
|
-
|
|
55
|
-
delete schemaObject.properties?.[key]
|
|
56
|
-
}
|
|
40
|
+
const cyclicSchemas = adapter.inputNode ? ast.findCircularSchemas(adapter.inputNode.schemas) : undefined
|
|
57
41
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
42
|
+
const schemaPrinter = mini
|
|
43
|
+
? printerZodMini({ guidType, wrapOutput, resolver, cyclicSchemas, nodes: printer?.nodes })
|
|
44
|
+
: printerZod({ coercion, guidType, dateType, wrapOutput, resolver, cyclicSchemas, nodes: printer?.nodes })
|
|
61
45
|
|
|
62
|
-
|
|
63
|
-
|
|
46
|
+
return (
|
|
47
|
+
<File
|
|
48
|
+
baseName={meta.file.baseName}
|
|
49
|
+
path={meta.file.path}
|
|
50
|
+
meta={meta.file.meta}
|
|
51
|
+
banner={resolver.resolveBanner(adapter.inputNode, { output, config })}
|
|
52
|
+
footer={resolver.resolveFooter(adapter.inputNode, { output, config })}
|
|
53
|
+
>
|
|
54
|
+
<File.Import name={isZodImport ? 'z' : ['z']} path={importPath} isNameSpace={isZodImport} />
|
|
55
|
+
{mode === 'split' && imports.map((imp) => <File.Import key={[node.name, imp.path].join('-')} root={meta.file.path} path={imp.path} name={imp.name} />)}
|
|
64
56
|
|
|
65
|
-
|
|
66
|
-
|
|
57
|
+
<Zod name={meta.name} node={node} printer={schemaPrinter} inferTypeName={inferTypeName} />
|
|
58
|
+
</File>
|
|
59
|
+
)
|
|
60
|
+
},
|
|
61
|
+
operation(node, ctx) {
|
|
62
|
+
const { adapter, config, resolver, root } = ctx
|
|
63
|
+
const { output, coercion, guidType, mini, wrapOutput, inferred, importPath, group, paramsCasing, printer } = ctx.options
|
|
64
|
+
const dateType = (adapter as Adapter<AdapterOas>).options.dateType
|
|
67
65
|
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
const mode = ctx.getMode(output)
|
|
67
|
+
const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath as 'zod' | 'zod/mini')
|
|
70
68
|
|
|
71
|
-
|
|
72
|
-
schemaObject.required = Object.entries(schemaObject.properties || {})
|
|
73
|
-
.filter(([_key, value]) => value && Object.hasOwn(value, 'default'))
|
|
74
|
-
.map(([key]) => key)
|
|
75
|
-
}
|
|
69
|
+
const params = ast.caseParams(node.parameters, paramsCasing)
|
|
76
70
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
].filter(Boolean)
|
|
81
|
-
const imports = getImports(tree)
|
|
82
|
-
const group = options.operation ? getGroup(options.operation) : undefined
|
|
71
|
+
const meta = {
|
|
72
|
+
file: resolver.resolveFile({ name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path }, { root, output, group }),
|
|
73
|
+
} as const
|
|
83
74
|
|
|
84
|
-
|
|
75
|
+
const cyclicSchemas = adapter.inputNode ? ast.findCircularSchemas(adapter.inputNode.schemas) : undefined
|
|
85
76
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
inferTypeName: schemaManager.getName(name, { type: 'type' }),
|
|
89
|
-
file: schemaManager.getFile(name),
|
|
90
|
-
}
|
|
77
|
+
function renderSchemaEntry({ schema, name, keysToOmit }: { schema: ast.SchemaNode | null; name: string; keysToOmit?: Array<string> }) {
|
|
78
|
+
if (!schema) return null
|
|
91
79
|
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}),
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
})
|
|
101
|
-
|
|
80
|
+
const inferTypeName = inferred ? resolver.resolveTypeName(name) : undefined
|
|
81
|
+
|
|
82
|
+
const imports = adapter.getImports(schema, (schemaName) => ({
|
|
83
|
+
name: resolver.resolveSchemaName(schemaName),
|
|
84
|
+
path: resolver.resolveFile({ name: schemaName, extname: '.ts' }, { root, output, group }).path,
|
|
85
|
+
}))
|
|
86
|
+
|
|
87
|
+
const schemaPrinter = mini
|
|
88
|
+
? printerZodMini({ guidType, wrapOutput, resolver, keysToOmit, cyclicSchemas, nodes: printer?.nodes })
|
|
89
|
+
: printerZod({ coercion, guidType, dateType, wrapOutput, resolver, keysToOmit, cyclicSchemas, nodes: printer?.nodes })
|
|
102
90
|
|
|
103
91
|
return (
|
|
104
92
|
<>
|
|
105
|
-
{
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
))}
|
|
109
|
-
<Zod
|
|
110
|
-
name={zod.name}
|
|
111
|
-
typeName={typed ? type.name : undefined}
|
|
112
|
-
inferTypeName={inferred ? zod.inferTypeName : undefined}
|
|
113
|
-
description={description}
|
|
114
|
-
tree={tree}
|
|
115
|
-
schema={schemaObject}
|
|
116
|
-
mapper={mapper}
|
|
117
|
-
coercion={coercion}
|
|
118
|
-
keysToOmit={keysToOmit}
|
|
119
|
-
wrapOutput={wrapOutput}
|
|
120
|
-
version={plugin.options.version}
|
|
121
|
-
guidType={guidType}
|
|
122
|
-
emptySchemaType={plugin.options.emptySchemaType}
|
|
123
|
-
mini={mini}
|
|
124
|
-
/>
|
|
93
|
+
{mode === 'split' &&
|
|
94
|
+
imports.map((imp) => <File.Import key={[name, imp.path, imp.name].join('-')} root={meta.file.path} path={imp.path} name={imp.name} />)}
|
|
95
|
+
<Zod name={name} node={schema} printer={schemaPrinter} inferTypeName={inferTypeName} />
|
|
125
96
|
</>
|
|
126
97
|
)
|
|
127
98
|
}
|
|
128
99
|
|
|
129
|
-
const
|
|
100
|
+
const paramSchemas = params.map((param) => renderSchemaEntry({ schema: param.schema, name: resolver.resolveParamName(node, param) }))
|
|
101
|
+
|
|
102
|
+
const responseSchemas = node.responses.map((res) =>
|
|
103
|
+
renderSchemaEntry({
|
|
104
|
+
schema: res.schema,
|
|
105
|
+
name: resolver.resolveResponseStatusName(node, res.statusCode),
|
|
106
|
+
keysToOmit: res.keysToOmit,
|
|
107
|
+
}),
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
const responsesWithSchema = node.responses.filter((res) => res.schema)
|
|
111
|
+
const responseUnionSchema =
|
|
112
|
+
responsesWithSchema.length > 0
|
|
113
|
+
? (() => {
|
|
114
|
+
const responseUnionName = resolver.resolveResponseName(node)
|
|
115
|
+
|
|
116
|
+
// Collect all import names from response schemas to detect naming collisions.
|
|
117
|
+
// When a response is a $ref to a component schema whose resolved name matches
|
|
118
|
+
// the response union name, skip generation to avoid redeclaration errors.
|
|
119
|
+
const importedNames = new Set(
|
|
120
|
+
responsesWithSchema.flatMap((res) =>
|
|
121
|
+
res.schema
|
|
122
|
+
? adapter
|
|
123
|
+
.getImports(res.schema, (schemaName) => ({
|
|
124
|
+
name: resolver.resolveSchemaName(schemaName),
|
|
125
|
+
path: '',
|
|
126
|
+
}))
|
|
127
|
+
.flatMap((imp) => (Array.isArray(imp.name) ? imp.name : [imp.name]))
|
|
128
|
+
: [],
|
|
129
|
+
),
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
if (importedNames.has(responseUnionName)) {
|
|
133
|
+
return null
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const members = responsesWithSchema.map((res) => ast.createSchema({ type: 'ref', name: resolver.resolveResponseStatusName(node, res.statusCode) }))
|
|
137
|
+
const unionNode = members.length === 1 ? members[0]! : ast.createSchema({ type: 'union', members })
|
|
138
|
+
|
|
139
|
+
return renderSchemaEntry({
|
|
140
|
+
schema: unionNode,
|
|
141
|
+
name: responseUnionName,
|
|
142
|
+
})
|
|
143
|
+
})()
|
|
144
|
+
: null
|
|
145
|
+
|
|
146
|
+
const requestSchema = node.requestBody?.content?.[0]?.schema
|
|
147
|
+
? renderSchemaEntry({
|
|
148
|
+
schema: {
|
|
149
|
+
...node.requestBody.content![0]!.schema!,
|
|
150
|
+
description: node.requestBody.description ?? node.requestBody.content![0]!.schema!.description,
|
|
151
|
+
},
|
|
152
|
+
name: resolver.resolveDataName(node),
|
|
153
|
+
keysToOmit: node.requestBody.content![0]!.keysToOmit,
|
|
154
|
+
})
|
|
155
|
+
: null
|
|
130
156
|
|
|
131
157
|
return (
|
|
132
158
|
<File
|
|
133
|
-
baseName={file.baseName}
|
|
134
|
-
path={file.path}
|
|
135
|
-
meta={file.meta}
|
|
136
|
-
banner={
|
|
137
|
-
footer={
|
|
159
|
+
baseName={meta.file.baseName}
|
|
160
|
+
path={meta.file.path}
|
|
161
|
+
meta={meta.file.meta}
|
|
162
|
+
banner={resolver.resolveBanner(adapter.inputNode, { output, config })}
|
|
163
|
+
footer={resolver.resolveFooter(adapter.inputNode, { output, config })}
|
|
138
164
|
>
|
|
139
|
-
<File.Import name={isZodImport ? 'z' : ['z']} path={
|
|
140
|
-
{
|
|
141
|
-
{
|
|
165
|
+
<File.Import name={isZodImport ? 'z' : ['z']} path={importPath} isNameSpace={isZodImport} />
|
|
166
|
+
{paramSchemas}
|
|
167
|
+
{responseSchemas}
|
|
168
|
+
{responseUnionSchema}
|
|
169
|
+
{requestSchema}
|
|
142
170
|
</File>
|
|
143
171
|
)
|
|
144
172
|
},
|
|
145
|
-
|
|
146
|
-
const {
|
|
147
|
-
const {
|
|
148
|
-
options: { output, emptySchemaType, coercion, inferred, typed, mapper, importPath, wrapOutput, version, guidType, mini },
|
|
149
|
-
} = plugin
|
|
150
|
-
const driver = usePluginDriver()
|
|
151
|
-
const oas = useOas()
|
|
152
|
-
|
|
153
|
-
const imports = getImports(schema.tree)
|
|
154
|
-
|
|
155
|
-
const zod = {
|
|
156
|
-
name: getName(schema.name, { type: 'function' }),
|
|
157
|
-
inferTypeName: getName(schema.name, { type: 'type' }),
|
|
158
|
-
file: getFile(schema.name),
|
|
159
|
-
}
|
|
173
|
+
operations(nodes, ctx) {
|
|
174
|
+
const { adapter, config, resolver, root } = ctx
|
|
175
|
+
const { output, importPath, group, operations, paramsCasing } = ctx.options
|
|
160
176
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
file: getFile(schema.name, { pluginName: pluginTsName }),
|
|
177
|
+
if (!operations) {
|
|
178
|
+
return
|
|
164
179
|
}
|
|
180
|
+
const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath as 'zod' | 'zod/mini')
|
|
181
|
+
|
|
182
|
+
const meta = {
|
|
183
|
+
file: resolver.resolveFile({ name: 'operations', extname: '.ts' }, { root, output, group }),
|
|
184
|
+
} as const
|
|
165
185
|
|
|
166
|
-
const
|
|
167
|
-
|
|
186
|
+
const transformedOperations = nodes.map((node) => {
|
|
187
|
+
const params = ast.caseParams(node.parameters, paramsCasing)
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
node,
|
|
191
|
+
data: buildSchemaNames(node, { params, resolver }),
|
|
192
|
+
}
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
const imports = transformedOperations.flatMap(({ node, data }) => {
|
|
196
|
+
const names = [data.request, ...Object.values(data.responses), ...Object.values(data.parameters)].filter(Boolean) as string[]
|
|
197
|
+
const opFile = resolver.resolveFile({ name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path }, { root, output, group })
|
|
198
|
+
|
|
199
|
+
return names.map((name) => <File.Import key={[name, opFile.path].join('-')} name={[name]} root={meta.file.path} path={opFile.path} />)
|
|
200
|
+
})
|
|
168
201
|
|
|
169
202
|
return (
|
|
170
203
|
<File
|
|
171
|
-
baseName={
|
|
172
|
-
path={
|
|
173
|
-
meta={
|
|
174
|
-
banner={
|
|
175
|
-
footer={
|
|
204
|
+
baseName={meta.file.baseName}
|
|
205
|
+
path={meta.file.path}
|
|
206
|
+
meta={meta.file.meta}
|
|
207
|
+
banner={resolver.resolveBanner(adapter.inputNode, { output, config })}
|
|
208
|
+
footer={resolver.resolveFooter(adapter.inputNode, { output, config })}
|
|
176
209
|
>
|
|
177
|
-
<File.Import name={isZodImport ? 'z' : ['z']} path={importPath} isNameSpace={isZodImport} />
|
|
178
|
-
{
|
|
179
|
-
|
|
180
|
-
{imports.map((imp) => (
|
|
181
|
-
<File.Import key={[imp.path, imp.name, imp.isTypeOnly].join('-')} root={zod.file.path} path={imp.path} name={imp.name} />
|
|
182
|
-
))}
|
|
183
|
-
|
|
184
|
-
<Zod
|
|
185
|
-
name={zod.name}
|
|
186
|
-
typeName={typed ? type.name : undefined}
|
|
187
|
-
inferTypeName={inferred ? zod.inferTypeName : undefined}
|
|
188
|
-
description={schema.value.description}
|
|
189
|
-
tree={schema.tree}
|
|
190
|
-
schema={schema.value}
|
|
191
|
-
mapper={mapper}
|
|
192
|
-
coercion={coercion}
|
|
193
|
-
wrapOutput={wrapOutput}
|
|
194
|
-
version={version}
|
|
195
|
-
guidType={guidType}
|
|
196
|
-
emptySchemaType={emptySchemaType}
|
|
197
|
-
mini={mini}
|
|
198
|
-
/>
|
|
210
|
+
<File.Import isTypeOnly name={isZodImport ? 'z' : ['z']} path={importPath} isNameSpace={isZodImport} />
|
|
211
|
+
{imports}
|
|
212
|
+
<Operations name="operations" operations={transformedOperations} />
|
|
199
213
|
</File>
|
|
200
214
|
)
|
|
201
215
|
},
|
package/src/index.ts
CHANGED
|
@@ -1,2 +1,11 @@
|
|
|
1
|
-
export {
|
|
2
|
-
|
|
1
|
+
export { zodGenerator } from './generators/zodGenerator.tsx'
|
|
2
|
+
|
|
3
|
+
export { default, pluginZod, pluginZodName } from './plugin.ts'
|
|
4
|
+
export type { PrinterZodFactory, PrinterZodNodes, PrinterZodOptions } from './printers/printerZod.ts'
|
|
5
|
+
export { printerZod } from './printers/printerZod.ts'
|
|
6
|
+
export type { PrinterZodMiniFactory, PrinterZodMiniNodes, PrinterZodMiniOptions } from './printers/printerZodMini.ts'
|
|
7
|
+
export { printerZodMini } from './printers/printerZodMini.ts'
|
|
8
|
+
|
|
9
|
+
export { resolverZod } from './resolvers/resolverZod.ts'
|
|
10
|
+
|
|
11
|
+
export type { PluginZod, ResolverZod } from './types.ts'
|
package/src/plugin.ts
CHANGED
|
@@ -1,183 +1,94 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import { createPlugin, type Group, getBarrelFiles, getMode, PackageManager } from '@kubb/core'
|
|
4
|
-
import { OperationGenerator, pluginOasName, SchemaGenerator } from '@kubb/plugin-oas'
|
|
5
|
-
import { pluginTsName } from '@kubb/plugin-ts'
|
|
6
|
-
import { operationsGenerator } from './generators'
|
|
1
|
+
import { camelCase } from '@internals/utils'
|
|
2
|
+
import { definePlugin, type Group } from '@kubb/core'
|
|
7
3
|
import { zodGenerator } from './generators/zodGenerator.tsx'
|
|
8
|
-
import {
|
|
4
|
+
import { resolverZod } from './resolvers/resolverZod.ts'
|
|
9
5
|
import type { PluginZod } from './types.ts'
|
|
10
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Canonical plugin name for `@kubb/plugin-zod`, used in driver lookups and warnings.
|
|
9
|
+
*/
|
|
11
10
|
export const pluginZodName = 'plugin-zod' satisfies PluginZod['name']
|
|
12
11
|
|
|
13
|
-
|
|
12
|
+
/**
|
|
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`.
|
|
16
|
+
*
|
|
17
|
+
* @example Zod schema generator
|
|
18
|
+
* ```ts
|
|
19
|
+
* import pluginZod from '@kubb/plugin-zod'
|
|
20
|
+
* export default defineConfig({
|
|
21
|
+
* plugins: [pluginZod({ output: { path: 'zod' } })]
|
|
22
|
+
* })
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export const pluginZod = definePlugin<PluginZod>((options) => {
|
|
14
26
|
const {
|
|
15
27
|
output = { path: 'zod', barrelType: 'named' },
|
|
16
28
|
group,
|
|
17
29
|
exclude = [],
|
|
18
30
|
include,
|
|
19
31
|
override = [],
|
|
20
|
-
transformers = {},
|
|
21
|
-
dateType = 'string',
|
|
22
|
-
unknownType = 'any',
|
|
23
|
-
emptySchemaType = unknownType,
|
|
24
|
-
integerType = 'number',
|
|
25
32
|
typed = false,
|
|
26
|
-
mapper = {},
|
|
27
33
|
operations = false,
|
|
28
34
|
mini = false,
|
|
29
|
-
version = mini ? '4' : new PackageManager().isValidSync('zod', '>=4') ? '4' : '3',
|
|
30
35
|
guidType = 'uuid',
|
|
31
|
-
importPath = mini ? 'zod/mini' :
|
|
36
|
+
importPath = mini ? 'zod/mini' : 'zod',
|
|
32
37
|
coercion = false,
|
|
33
38
|
inferred = false,
|
|
34
|
-
generators = [zodGenerator, operations ? operationsGenerator : undefined].filter(Boolean),
|
|
35
39
|
wrapOutput = undefined,
|
|
36
|
-
|
|
40
|
+
paramsCasing,
|
|
41
|
+
printer,
|
|
42
|
+
resolver: userResolver,
|
|
43
|
+
transformer: userTransformer,
|
|
44
|
+
generators: userGenerators = [],
|
|
37
45
|
} = options
|
|
38
46
|
|
|
39
|
-
|
|
40
|
-
|
|
47
|
+
const groupConfig = group
|
|
48
|
+
? ({
|
|
49
|
+
...group,
|
|
50
|
+
name: (ctx) => {
|
|
51
|
+
if (group.type === 'path') {
|
|
52
|
+
return `${ctx.group.split('/')[1]}`
|
|
53
|
+
}
|
|
54
|
+
return `${camelCase(ctx.group)}Controller`
|
|
55
|
+
},
|
|
56
|
+
} satisfies Group)
|
|
57
|
+
: undefined
|
|
41
58
|
|
|
42
59
|
return {
|
|
43
60
|
name: pluginZodName,
|
|
44
|
-
options
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
guidType,
|
|
64
|
-
mini,
|
|
65
|
-
usedEnumNames,
|
|
66
|
-
},
|
|
67
|
-
pre: [pluginOasName, typed ? pluginTsName : undefined].filter(Boolean),
|
|
68
|
-
resolvePath(baseName, pathMode, options) {
|
|
69
|
-
const root = path.resolve(this.config.root, this.config.output.path)
|
|
70
|
-
const mode = pathMode ?? getMode(path.resolve(root, output.path))
|
|
71
|
-
|
|
72
|
-
if (mode === 'single') {
|
|
73
|
-
/**
|
|
74
|
-
* when output is a file then we will always append to the same file(output file), see fileManager.addOrAppend
|
|
75
|
-
* Other plugins then need to call addOrAppend instead of just add from the fileManager class
|
|
76
|
-
*/
|
|
77
|
-
return path.resolve(root, output.path)
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (group && (options?.group?.path || options?.group?.tag)) {
|
|
81
|
-
const groupName: Group['name'] = group?.name
|
|
82
|
-
? group.name
|
|
83
|
-
: (ctx) => {
|
|
84
|
-
if (group?.type === 'path') {
|
|
85
|
-
return `${ctx.group.split('/')[1]}`
|
|
86
|
-
}
|
|
87
|
-
return `${camelCase(ctx.group)}Controller`
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return path.resolve(
|
|
91
|
-
root,
|
|
92
|
-
output.path,
|
|
93
|
-
groupName({
|
|
94
|
-
group: group.type === 'path' ? options.group.path! : options.group.tag!,
|
|
95
|
-
}),
|
|
96
|
-
baseName,
|
|
97
|
-
)
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return path.resolve(root, output.path, baseName)
|
|
101
|
-
},
|
|
102
|
-
resolveName(name, type) {
|
|
103
|
-
let resolvedName = camelCase(name, {
|
|
104
|
-
suffix: type ? 'schema' : undefined,
|
|
105
|
-
isFile: type === 'file',
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
if (type === 'type') {
|
|
109
|
-
resolvedName = pascalCase(resolvedName)
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
if (type) {
|
|
113
|
-
return transformers?.name?.(resolvedName, type) || resolvedName
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
return resolvedName
|
|
117
|
-
},
|
|
118
|
-
async install() {
|
|
119
|
-
const root = path.resolve(this.config.root, this.config.output.path)
|
|
120
|
-
const mode = getMode(path.resolve(root, output.path))
|
|
121
|
-
const oas = await this.getOas()
|
|
122
|
-
|
|
123
|
-
if (this.plugin.options.typed && this.plugin.options.version === '3') {
|
|
124
|
-
// pre add bundled
|
|
125
|
-
await this.addFile({
|
|
126
|
-
baseName: 'ToZod.ts',
|
|
127
|
-
path: path.resolve(root, '.kubb/ToZod.ts'),
|
|
128
|
-
sources: [
|
|
129
|
-
{
|
|
130
|
-
name: 'ToZod',
|
|
131
|
-
value: toZodSource,
|
|
132
|
-
},
|
|
133
|
-
],
|
|
134
|
-
imports: [],
|
|
135
|
-
exports: [],
|
|
61
|
+
options,
|
|
62
|
+
hooks: {
|
|
63
|
+
'kubb:plugin:setup'(ctx) {
|
|
64
|
+
ctx.setOptions({
|
|
65
|
+
output,
|
|
66
|
+
exclude,
|
|
67
|
+
include,
|
|
68
|
+
override,
|
|
69
|
+
group: groupConfig,
|
|
70
|
+
typed,
|
|
71
|
+
importPath,
|
|
72
|
+
coercion,
|
|
73
|
+
operations,
|
|
74
|
+
inferred,
|
|
75
|
+
guidType,
|
|
76
|
+
mini,
|
|
77
|
+
wrapOutput,
|
|
78
|
+
paramsCasing,
|
|
79
|
+
printer,
|
|
136
80
|
})
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
include: undefined,
|
|
147
|
-
override,
|
|
148
|
-
mode,
|
|
149
|
-
output: output.path,
|
|
150
|
-
})
|
|
151
|
-
|
|
152
|
-
const schemaFiles = await schemaGenerator.build(...generators)
|
|
153
|
-
await this.upsertFile(...schemaFiles)
|
|
154
|
-
|
|
155
|
-
const operationGenerator = new OperationGenerator(this.plugin.options, {
|
|
156
|
-
fabric: this.fabric,
|
|
157
|
-
oas,
|
|
158
|
-
driver: this.driver,
|
|
159
|
-
events: this.events,
|
|
160
|
-
plugin: this.plugin,
|
|
161
|
-
contentType,
|
|
162
|
-
exclude,
|
|
163
|
-
include,
|
|
164
|
-
override,
|
|
165
|
-
mode,
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
const operationFiles = await operationGenerator.build(...generators)
|
|
169
|
-
await this.upsertFile(...operationFiles)
|
|
170
|
-
|
|
171
|
-
const barrelFiles = await getBarrelFiles(this.fabric.files, {
|
|
172
|
-
type: output.barrelType ?? 'named',
|
|
173
|
-
root,
|
|
174
|
-
output,
|
|
175
|
-
meta: {
|
|
176
|
-
pluginName: this.plugin.name,
|
|
177
|
-
},
|
|
178
|
-
})
|
|
179
|
-
|
|
180
|
-
await this.upsertFile(...barrelFiles)
|
|
81
|
+
ctx.setResolver(userResolver ? { ...resolverZod, ...userResolver } : resolverZod)
|
|
82
|
+
if (userTransformer) {
|
|
83
|
+
ctx.setTransformer(userTransformer)
|
|
84
|
+
}
|
|
85
|
+
ctx.addGenerator(zodGenerator)
|
|
86
|
+
for (const gen of userGenerators) {
|
|
87
|
+
ctx.addGenerator(gen)
|
|
88
|
+
}
|
|
89
|
+
},
|
|
181
90
|
},
|
|
182
91
|
}
|
|
183
92
|
})
|
|
93
|
+
|
|
94
|
+
export default pluginZod
|