@kubb/plugin-zod 3.0.0-alpha.9 → 3.0.0-beta.10
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/README.md +14 -5
- package/dist/chunk-37OP7TSO.cjs +354 -0
- package/dist/chunk-37OP7TSO.cjs.map +1 -0
- package/dist/chunk-OOAUU32I.js +235 -0
- package/dist/chunk-OOAUU32I.js.map +1 -0
- package/dist/chunk-QEYWJ6VH.cjs +244 -0
- package/dist/chunk-QEYWJ6VH.cjs.map +1 -0
- package/dist/chunk-SSOO3TXR.js +347 -0
- package/dist/chunk-SSOO3TXR.js.map +1 -0
- package/dist/components.cjs +11 -10
- package/dist/components.cjs.map +1 -1
- package/dist/components.d.cts +20 -19
- package/dist/components.d.ts +20 -19
- package/dist/components.js +2 -14
- package/dist/components.js.map +1 -1
- package/dist/generators.cjs +17 -0
- package/dist/generators.cjs.map +1 -0
- package/dist/generators.d.cts +10 -0
- package/dist/generators.d.ts +10 -0
- package/dist/generators.js +4 -0
- package/dist/generators.js.map +1 -0
- package/dist/index.cjs +12 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -137
- package/dist/index.d.ts +4 -137
- package/dist/index.js +3 -12
- package/dist/index.js.map +1 -1
- package/dist/types-L1fXCZAs.d.cts +100 -0
- package/dist/types-L1fXCZAs.d.ts +100 -0
- package/package.json +19 -21
- package/src/components/Operations.tsx +12 -98
- package/src/components/Zod.tsx +68 -0
- package/src/components/index.ts +1 -2
- package/src/generators/__snapshots__/anyof.ts +3 -0
- package/src/generators/__snapshots__/coercion.ts +3 -0
- package/src/generators/__snapshots__/coercionDates.ts +3 -0
- package/src/generators/__snapshots__/coercionNumbers.ts +3 -0
- package/src/generators/__snapshots__/coercionStrings.ts +3 -0
- package/src/generators/__snapshots__/createPet.ts +15 -0
- package/src/generators/__snapshots__/createPetWithUnknownTypeAny.ts +13 -0
- package/src/generators/__snapshots__/createPetWithUnknownTypeUnknown.ts +15 -0
- package/src/generators/__snapshots__/deletePet.ts +3 -0
- package/src/generators/__snapshots__/discriminator.ts +3 -0
- package/src/generators/__snapshots__/enumBooleanLiteral.ts +3 -0
- package/src/generators/__snapshots__/enumNamesType.ts +3 -0
- package/src/generators/__snapshots__/enumNullable.ts +3 -0
- package/src/generators/__snapshots__/enumSingleLiteral.ts +3 -0
- package/src/generators/__snapshots__/enumVarNamesType.ts +3 -0
- package/src/generators/__snapshots__/example.ts +3 -0
- package/src/generators/__snapshots__/getPets.ts +15 -0
- package/src/generators/__snapshots__/mixedValueTypeConst.ts +6 -0
- package/src/generators/__snapshots__/nullableString.ts +3 -0
- package/src/generators/__snapshots__/nullableStringUuid.ts +3 -0
- package/src/generators/__snapshots__/nullableStringWithAnyOf.ts +3 -0
- package/src/generators/__snapshots__/numberValueConst.ts +6 -0
- package/src/generators/__snapshots__/oneof.ts +3 -0
- package/src/generators/__snapshots__/operations.ts +43 -0
- package/src/generators/__snapshots__/optionalPetInfer.ts +5 -0
- package/src/generators/__snapshots__/optionalPetTyped.ts +3 -0
- package/src/generators/__snapshots__/order.ts +3 -0
- package/src/generators/__snapshots__/orderDateTyeString.ts +10 -0
- package/src/generators/__snapshots__/orderDateTypeFalse.ts +3 -0
- package/src/generators/__snapshots__/orderDateTypeString.ts +3 -0
- package/src/generators/__snapshots__/pet.ts +3 -0
- package/src/generators/__snapshots__/petArray.ts +6 -0
- package/src/generators/__snapshots__/petCoercion.ts +3 -0
- package/src/generators/__snapshots__/petTupleObject.ts +6 -0
- package/src/generators/__snapshots__/petWithMapper.ts +3 -0
- package/src/generators/__snapshots__/pets.ts +3 -0
- package/src/generators/__snapshots__/recursive.ts +3 -0
- package/src/generators/__snapshots__/showPetById.ts +15 -0
- package/src/generators/__snapshots__/stringValueConst.ts +6 -0
- package/src/generators/__snapshots__/uuidSchema.ts +3 -0
- package/src/generators/index.ts +2 -0
- package/src/generators/operationsGenerator.tsx +39 -0
- package/src/generators/zodGenerator.tsx +121 -0
- package/src/parser/index.ts +59 -31
- package/src/plugin.ts +30 -39
- package/src/types.ts +50 -89
- package/dist/Operations-BG26e_MW.d.cts +0 -47
- package/dist/Operations-BG26e_MW.d.ts +0 -47
- package/dist/chunk-57AHBVYK.cjs +0 -3502
- package/dist/chunk-57AHBVYK.cjs.map +0 -1
- package/dist/chunk-CUPQVLRZ.js +0 -3502
- package/dist/chunk-CUPQVLRZ.js.map +0 -1
- package/src/SchemaGenerator.tsx +0 -22
- package/src/components/OperationSchema.tsx +0 -60
- package/src/components/Schema.tsx +0 -166
- package/src/components/__snapshots__/operations.ts +0 -53
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { type OperationSchema as OperationSchemaType, SchemaGenerator, createReactGenerator, schemaKeywords } from '@kubb/plugin-oas'
|
|
2
|
+
import { Oas } from '@kubb/plugin-oas/components'
|
|
3
|
+
import { useOas, useOperationManager, useSchemaManager } from '@kubb/plugin-oas/hooks'
|
|
4
|
+
import { pluginTsName } from '@kubb/plugin-ts'
|
|
5
|
+
import { File, useApp } from '@kubb/react'
|
|
6
|
+
import { Zod } from '../components'
|
|
7
|
+
import type { PluginZod } from '../types'
|
|
8
|
+
|
|
9
|
+
export const zodGenerator = createReactGenerator<PluginZod>({
|
|
10
|
+
name: 'zod',
|
|
11
|
+
Operation({ operation, options }) {
|
|
12
|
+
const { coercion, inferred, typed, mapper } = options
|
|
13
|
+
|
|
14
|
+
const { plugin, pluginManager, mode } = useApp<PluginZod>()
|
|
15
|
+
const oas = useOas()
|
|
16
|
+
const { getSchemas, getFile } = useOperationManager()
|
|
17
|
+
const schemaManager = useSchemaManager()
|
|
18
|
+
|
|
19
|
+
const file = getFile(operation)
|
|
20
|
+
const schemas = getSchemas(operation)
|
|
21
|
+
const schemaGenerator = new SchemaGenerator(options, {
|
|
22
|
+
oas,
|
|
23
|
+
plugin,
|
|
24
|
+
pluginManager,
|
|
25
|
+
mode,
|
|
26
|
+
override: options.override,
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
const operationSchemas = [schemas.pathParams, schemas.queryParams, schemas.headerParams, schemas.statusCodes, schemas.request, schemas.response]
|
|
30
|
+
.flat()
|
|
31
|
+
.filter(Boolean)
|
|
32
|
+
|
|
33
|
+
const mapOperationSchema = ({ name, schema, description, keysToOmit, ...options }: OperationSchemaType, i: number) => {
|
|
34
|
+
// hack so Params can be optional when needed
|
|
35
|
+
const required = Array.isArray(schema?.required) ? !!schema.required.length : !!schema?.required
|
|
36
|
+
const optional = !required && !!name.includes('Params')
|
|
37
|
+
const tree = [...schemaGenerator.parse({ schema, name }), optional ? { keyword: schemaKeywords.optional } : undefined].filter(Boolean)
|
|
38
|
+
const imports = schemaManager.getImports(tree)
|
|
39
|
+
|
|
40
|
+
const zod = {
|
|
41
|
+
name: schemaManager.getName(name, { type: 'function' }),
|
|
42
|
+
inferTypeName: schemaManager.getName(name, { type: 'type' }),
|
|
43
|
+
file: schemaManager.getFile(name),
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const type = {
|
|
47
|
+
name: schemaManager.getName(name, { type: 'type', pluginKey: [pluginTsName] }),
|
|
48
|
+
file: schemaManager.getFile(options.operationName || name, { pluginKey: [pluginTsName], tag: options.operation?.getTags()[0]?.name }),
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<Oas.Schema key={i} name={name} value={schema} tree={tree}>
|
|
53
|
+
{typed && <File.Import isTypeOnly root={file.path} path={type.file.path} name={[type.name]} />}
|
|
54
|
+
{imports.map((imp, index) => (
|
|
55
|
+
<File.Import key={index} root={file.path} path={imp.path} name={imp.name} />
|
|
56
|
+
))}
|
|
57
|
+
<Zod
|
|
58
|
+
name={zod.name}
|
|
59
|
+
typeName={typed ? type.name : undefined}
|
|
60
|
+
inferTypeName={inferred ? zod.inferTypeName : undefined}
|
|
61
|
+
description={description}
|
|
62
|
+
tree={tree}
|
|
63
|
+
mapper={mapper}
|
|
64
|
+
coercion={coercion}
|
|
65
|
+
keysToOmit={keysToOmit}
|
|
66
|
+
/>
|
|
67
|
+
</Oas.Schema>
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<File baseName={file.baseName} path={file.path} meta={file.meta} banner={plugin.options.output?.banner} footer={plugin.options.output?.footer}>
|
|
73
|
+
<File.Import name={['z']} path={plugin.options.importPath} />
|
|
74
|
+
{operationSchemas.map(mapOperationSchema)}
|
|
75
|
+
</File>
|
|
76
|
+
)
|
|
77
|
+
},
|
|
78
|
+
Schema({ schema, options }) {
|
|
79
|
+
const { coercion, inferred, typed, mapper, importPath } = options
|
|
80
|
+
|
|
81
|
+
const { getName, getFile, getImports } = useSchemaManager()
|
|
82
|
+
const {
|
|
83
|
+
plugin: {
|
|
84
|
+
options: { output },
|
|
85
|
+
},
|
|
86
|
+
} = useApp<PluginZod>()
|
|
87
|
+
|
|
88
|
+
const imports = getImports(schema.tree)
|
|
89
|
+
|
|
90
|
+
const zod = {
|
|
91
|
+
name: getName(schema.name, { type: 'function' }),
|
|
92
|
+
inferTypeName: getName(schema.name, { type: 'type' }),
|
|
93
|
+
file: getFile(schema.name),
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const type = {
|
|
97
|
+
name: getName(schema.name, { type: 'type', pluginKey: [pluginTsName] }),
|
|
98
|
+
file: getFile(schema.name, { pluginKey: [pluginTsName] }),
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<File baseName={zod.file.baseName} path={zod.file.path} meta={zod.file.meta} banner={output?.banner} footer={output?.footer}>
|
|
103
|
+
<File.Import name={['z']} path={importPath} />
|
|
104
|
+
{typed && <File.Import isTypeOnly root={zod.file.path} path={type.file.path} name={[type.name]} />}
|
|
105
|
+
{imports.map((imp, index) => (
|
|
106
|
+
<File.Import key={index} root={zod.file.path} path={imp.path} name={imp.name} />
|
|
107
|
+
))}
|
|
108
|
+
|
|
109
|
+
<Zod
|
|
110
|
+
name={zod.name}
|
|
111
|
+
typeName={typed ? type.name : undefined}
|
|
112
|
+
inferTypeName={inferred ? zod.inferTypeName : undefined}
|
|
113
|
+
description={schema.value.description}
|
|
114
|
+
tree={schema.tree}
|
|
115
|
+
mapper={mapper}
|
|
116
|
+
coercion={coercion}
|
|
117
|
+
/>
|
|
118
|
+
</File>
|
|
119
|
+
)
|
|
120
|
+
},
|
|
121
|
+
})
|
package/src/parser/index.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import transformers, { createJSDocBlockText } from '@kubb/core/transformers'
|
|
2
|
-
import { type SchemaKeywordMapper, isKeyword, schemaKeywords } from '@kubb/plugin-oas'
|
|
2
|
+
import { type SchemaKeywordMapper, type SchemaTree, isKeyword, schemaKeywords } from '@kubb/plugin-oas'
|
|
3
3
|
|
|
4
4
|
import type { Schema, SchemaKeywordBase, SchemaMapper } from '@kubb/plugin-oas'
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
const zodKeywordMapper = {
|
|
7
7
|
any: () => 'z.any()',
|
|
8
8
|
unknown: () => 'z.unknown()',
|
|
9
9
|
number: (coercion?: boolean, min?: number, max?: number) => {
|
|
@@ -39,7 +39,7 @@ export const zodKeywordMapper = {
|
|
|
39
39
|
tuple: (items: string[] = []) => `z.tuple([${items?.join(', ')}])`,
|
|
40
40
|
enum: (items: string[] = []) => `z.enum([${items?.join(', ')}])`,
|
|
41
41
|
union: (items: string[] = []) => `z.union([${items?.join(', ')}])`,
|
|
42
|
-
const: (value?: string | number) => `z.literal(${value ?? ''})`,
|
|
42
|
+
const: (value?: string | number | boolean) => `z.literal(${value ?? ''})`,
|
|
43
43
|
/**
|
|
44
44
|
* ISO 8601
|
|
45
45
|
*/
|
|
@@ -102,6 +102,7 @@ export const zodKeywordMapper = {
|
|
|
102
102
|
password: undefined,
|
|
103
103
|
phone: undefined,
|
|
104
104
|
readOnly: undefined,
|
|
105
|
+
writeOnly: undefined,
|
|
105
106
|
ref: (value?: string) => (value ? `z.lazy(() => ${value})` : undefined),
|
|
106
107
|
blob: () => 'z.string()',
|
|
107
108
|
deprecated: undefined,
|
|
@@ -149,17 +150,27 @@ export function sort(items?: Schema[]): Schema[] {
|
|
|
149
150
|
return transformers.orderBy(items, [(v) => order.indexOf(v.keyword)], ['asc'])
|
|
150
151
|
}
|
|
151
152
|
|
|
153
|
+
const shouldCoerce = (coercion: ParserOptions['coercion'] | undefined, type: 'dates' | 'strings' | 'numbers'): boolean => {
|
|
154
|
+
if (coercion === undefined) {
|
|
155
|
+
return false
|
|
156
|
+
}
|
|
157
|
+
if (typeof coercion === 'boolean') {
|
|
158
|
+
return coercion
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return !!coercion[type]
|
|
162
|
+
}
|
|
163
|
+
|
|
152
164
|
type ParserOptions = {
|
|
153
165
|
name: string
|
|
154
166
|
typeName?: string
|
|
155
167
|
description?: string
|
|
156
|
-
|
|
157
168
|
keysToOmit?: string[]
|
|
158
169
|
mapper?: Record<string, string>
|
|
159
|
-
coercion?: boolean
|
|
170
|
+
coercion?: boolean | { dates?: boolean; strings?: boolean; numbers?: boolean }
|
|
160
171
|
}
|
|
161
172
|
|
|
162
|
-
export function parse(parent
|
|
173
|
+
export function parse({ parent, current, siblings }: SchemaTree, options: ParserOptions): string | undefined {
|
|
163
174
|
const value = zodKeywordMapper[current.keyword as keyof typeof zodKeywordMapper]
|
|
164
175
|
|
|
165
176
|
if (!value) {
|
|
@@ -169,7 +180,7 @@ export function parse(parent: Schema | undefined, current: Schema, options: Pars
|
|
|
169
180
|
if (isKeyword(current, schemaKeywords.union)) {
|
|
170
181
|
// zod union type needs at least 2 items
|
|
171
182
|
if (Array.isArray(current.args) && current.args.length === 1) {
|
|
172
|
-
return parse(parent, current.args[0] as Schema, options)
|
|
183
|
+
return parse({ parent, current: current.args[0] as Schema, siblings }, options)
|
|
173
184
|
}
|
|
174
185
|
if (Array.isArray(current.args) && !current.args.length) {
|
|
175
186
|
return ''
|
|
@@ -177,7 +188,7 @@ export function parse(parent: Schema | undefined, current: Schema, options: Pars
|
|
|
177
188
|
|
|
178
189
|
return zodKeywordMapper.union(
|
|
179
190
|
sort(current.args)
|
|
180
|
-
.map((schema) => parse(current, schema, options))
|
|
191
|
+
.map((schema, _index, siblings) => parse({ parent: current, current: schema, siblings }, options))
|
|
181
192
|
.filter(Boolean),
|
|
182
193
|
)
|
|
183
194
|
}
|
|
@@ -187,7 +198,7 @@ export function parse(parent: Schema | undefined, current: Schema, options: Pars
|
|
|
187
198
|
.filter((schema: Schema) => {
|
|
188
199
|
return ![schemaKeywords.optional, schemaKeywords.describe].includes(schema.keyword as typeof schemaKeywords.describe)
|
|
189
200
|
})
|
|
190
|
-
.map((schema: Schema) => parse(current, schema, options))
|
|
201
|
+
.map((schema: Schema, _index, siblings) => parse({ parent: current, current: schema, siblings }, options))
|
|
191
202
|
.filter(Boolean)
|
|
192
203
|
|
|
193
204
|
return `${items.slice(0, 1)}${zodKeywordMapper.and(items.slice(1))}`
|
|
@@ -196,7 +207,7 @@ export function parse(parent: Schema | undefined, current: Schema, options: Pars
|
|
|
196
207
|
if (isKeyword(current, schemaKeywords.array)) {
|
|
197
208
|
return zodKeywordMapper.array(
|
|
198
209
|
sort(current.args.items)
|
|
199
|
-
.map((schemas) => parse(current, schemas, options))
|
|
210
|
+
.map((schemas, _index, siblings) => parse({ parent: current, current: schemas, siblings }, options))
|
|
200
211
|
.filter(Boolean),
|
|
201
212
|
current.args.min,
|
|
202
213
|
current.args.max,
|
|
@@ -205,17 +216,22 @@ export function parse(parent: Schema | undefined, current: Schema, options: Pars
|
|
|
205
216
|
|
|
206
217
|
if (isKeyword(current, schemaKeywords.enum)) {
|
|
207
218
|
if (current.args.asConst) {
|
|
219
|
+
if (current.args.items.length === 1) {
|
|
220
|
+
const child = {
|
|
221
|
+
keyword: schemaKeywords.const,
|
|
222
|
+
args: current.args.items[0],
|
|
223
|
+
}
|
|
224
|
+
return parse({ parent: current, current: child, siblings: [child] }, options)
|
|
225
|
+
}
|
|
226
|
+
|
|
208
227
|
return zodKeywordMapper.union(
|
|
209
228
|
current.args.items
|
|
210
|
-
.map((schema) => {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
},
|
|
217
|
-
options,
|
|
218
|
-
)
|
|
229
|
+
.map((schema) => ({
|
|
230
|
+
keyword: schemaKeywords.const,
|
|
231
|
+
args: schema,
|
|
232
|
+
}))
|
|
233
|
+
.map((schema, _index, siblings) => {
|
|
234
|
+
return parse({ parent: current, current: schema, siblings }, options)
|
|
219
235
|
})
|
|
220
236
|
.filter(Boolean),
|
|
221
237
|
)
|
|
@@ -223,6 +239,10 @@ export function parse(parent: Schema | undefined, current: Schema, options: Pars
|
|
|
223
239
|
|
|
224
240
|
return zodKeywordMapper.enum(
|
|
225
241
|
current.args.items.map((schema) => {
|
|
242
|
+
if (schema.format === 'boolean') {
|
|
243
|
+
return transformers.stringify(schema.value)
|
|
244
|
+
}
|
|
245
|
+
|
|
226
246
|
if (schema.format === 'number') {
|
|
227
247
|
return transformers.stringify(schema.value)
|
|
228
248
|
}
|
|
@@ -251,8 +271,8 @@ export function parse(parent: Schema | undefined, current: Schema, options: Pars
|
|
|
251
271
|
}
|
|
252
272
|
|
|
253
273
|
return `"${name}": ${sort(schemas)
|
|
254
|
-
.map((schema, array) => {
|
|
255
|
-
return parse(current, schema, options)
|
|
274
|
+
.map((schema, array, siblings) => {
|
|
275
|
+
return parse({ parent: current, current: schema, siblings }, options)
|
|
256
276
|
})
|
|
257
277
|
.filter(Boolean)
|
|
258
278
|
.join('')}`
|
|
@@ -261,9 +281,9 @@ export function parse(parent: Schema | undefined, current: Schema, options: Pars
|
|
|
261
281
|
|
|
262
282
|
const additionalProperties = current.args?.additionalProperties?.length
|
|
263
283
|
? current.args.additionalProperties
|
|
264
|
-
.map((schema) => parse(current, schema, options))
|
|
284
|
+
.map((schema, _index, siblings) => parse({ parent: current, current: schema, siblings }, options))
|
|
265
285
|
.filter(Boolean)
|
|
266
|
-
.
|
|
286
|
+
.join('')
|
|
267
287
|
: undefined
|
|
268
288
|
|
|
269
289
|
const text = [
|
|
@@ -277,9 +297,7 @@ export function parse(parent: Schema | undefined, current: Schema, options: Pars
|
|
|
277
297
|
|
|
278
298
|
if (isKeyword(current, schemaKeywords.tuple)) {
|
|
279
299
|
return zodKeywordMapper.tuple(
|
|
280
|
-
|
|
281
|
-
.map((schema) => parse(current, schema, options))
|
|
282
|
-
.filter(Boolean),
|
|
300
|
+
current.args.items.map((schema, _index, siblings) => parse({ parent: current, current: schema, siblings }, options)).filter(Boolean),
|
|
283
301
|
)
|
|
284
302
|
}
|
|
285
303
|
|
|
@@ -287,6 +305,10 @@ export function parse(parent: Schema | undefined, current: Schema, options: Pars
|
|
|
287
305
|
if (current.args.format === 'number' && current.args.value !== undefined) {
|
|
288
306
|
return zodKeywordMapper.const(Number.parseInt(current.args.value?.toString()))
|
|
289
307
|
}
|
|
308
|
+
|
|
309
|
+
if (current.args.format === 'boolean' && current.args.value !== undefined) {
|
|
310
|
+
return zodKeywordMapper.const(current.args.value)
|
|
311
|
+
}
|
|
290
312
|
return zodKeywordMapper.const(transformers.stringify(current.args.value))
|
|
291
313
|
}
|
|
292
314
|
|
|
@@ -309,15 +331,15 @@ export function parse(parent: Schema | undefined, current: Schema, options: Pars
|
|
|
309
331
|
}
|
|
310
332
|
|
|
311
333
|
if (isKeyword(current, schemaKeywords.string)) {
|
|
312
|
-
return zodKeywordMapper.string(options.coercion)
|
|
334
|
+
return zodKeywordMapper.string(shouldCoerce(options.coercion, 'strings'))
|
|
313
335
|
}
|
|
314
336
|
|
|
315
337
|
if (isKeyword(current, schemaKeywords.number)) {
|
|
316
|
-
return zodKeywordMapper.number(options.coercion)
|
|
338
|
+
return zodKeywordMapper.number(shouldCoerce(options.coercion, 'numbers'))
|
|
317
339
|
}
|
|
318
340
|
|
|
319
341
|
if (isKeyword(current, schemaKeywords.integer)) {
|
|
320
|
-
return zodKeywordMapper.integer(options.coercion)
|
|
342
|
+
return zodKeywordMapper.integer(shouldCoerce(options.coercion, 'numbers'))
|
|
321
343
|
}
|
|
322
344
|
|
|
323
345
|
if (isKeyword(current, schemaKeywords.min)) {
|
|
@@ -332,11 +354,11 @@ export function parse(parent: Schema | undefined, current: Schema, options: Pars
|
|
|
332
354
|
}
|
|
333
355
|
|
|
334
356
|
if (isKeyword(current, schemaKeywords.date)) {
|
|
335
|
-
return zodKeywordMapper.date(current.args.type, options.coercion)
|
|
357
|
+
return zodKeywordMapper.date(current.args.type, shouldCoerce(options.coercion, 'dates'))
|
|
336
358
|
}
|
|
337
359
|
|
|
338
360
|
if (isKeyword(current, schemaKeywords.time)) {
|
|
339
|
-
return zodKeywordMapper.time(current.args.type, options.coercion)
|
|
361
|
+
return zodKeywordMapper.time(current.args.type, shouldCoerce(options.coercion, 'dates'))
|
|
340
362
|
}
|
|
341
363
|
|
|
342
364
|
if (current.keyword in zodKeywordMapper && 'args' in current) {
|
|
@@ -345,6 +367,12 @@ export function parse(parent: Schema | undefined, current: Schema, options: Pars
|
|
|
345
367
|
return value((current as SchemaKeywordBase<unknown>).args as any)
|
|
346
368
|
}
|
|
347
369
|
|
|
370
|
+
if (isKeyword(current, schemaKeywords.optional)) {
|
|
371
|
+
if (siblings.some((schema) => isKeyword(schema, schemaKeywords.default))) return ''
|
|
372
|
+
|
|
373
|
+
return value()
|
|
374
|
+
}
|
|
375
|
+
|
|
348
376
|
if (current.keyword in zodKeywordMapper) {
|
|
349
377
|
return value()
|
|
350
378
|
}
|
package/src/plugin.ts
CHANGED
|
@@ -1,23 +1,22 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
2
|
|
|
3
|
-
import { FileManager, PluginManager, createPlugin } from '@kubb/core'
|
|
3
|
+
import { FileManager, type Group, PluginManager, createPlugin } from '@kubb/core'
|
|
4
4
|
import { camelCase, pascalCase } from '@kubb/core/transformers'
|
|
5
|
-
import { renderTemplate } from '@kubb/core/utils'
|
|
6
5
|
import { OperationGenerator, SchemaGenerator, pluginOasName } from '@kubb/plugin-oas'
|
|
7
6
|
|
|
8
7
|
import { pluginTsName } from '@kubb/plugin-ts'
|
|
9
8
|
|
|
10
9
|
import type { Plugin } from '@kubb/core'
|
|
11
10
|
import type { PluginOas as SwaggerPluginOptions } from '@kubb/plugin-oas'
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
11
|
+
import { operationsGenerator } from './generators'
|
|
12
|
+
import { zodGenerator } from './generators/zodGenerator.tsx'
|
|
14
13
|
import type { PluginZod } from './types.ts'
|
|
15
14
|
|
|
16
15
|
export const pluginZodName = 'plugin-zod' satisfies PluginZod['name']
|
|
17
16
|
|
|
18
17
|
export const pluginZod = createPlugin<PluginZod>((options) => {
|
|
19
18
|
const {
|
|
20
|
-
output = { path: 'zod' },
|
|
19
|
+
output = { path: 'zod', barrelType: 'named' },
|
|
21
20
|
group,
|
|
22
21
|
exclude = [],
|
|
23
22
|
include,
|
|
@@ -26,43 +25,42 @@ export const pluginZod = createPlugin<PluginZod>((options) => {
|
|
|
26
25
|
dateType = 'string',
|
|
27
26
|
unknownType = 'any',
|
|
28
27
|
typed = false,
|
|
29
|
-
typedSchema = false,
|
|
30
28
|
mapper = {},
|
|
31
|
-
|
|
29
|
+
operations = false,
|
|
32
30
|
importPath = 'zod',
|
|
33
31
|
coercion = false,
|
|
32
|
+
inferred = false,
|
|
33
|
+
generators = [zodGenerator, operations ? operationsGenerator : undefined].filter(Boolean),
|
|
34
34
|
} = options
|
|
35
|
-
const template = group?.output ? group.output : `${output.path}/{{tag}}Controller`
|
|
36
35
|
|
|
37
36
|
return {
|
|
38
37
|
name: pluginZodName,
|
|
39
|
-
output: {
|
|
40
|
-
exportType: 'barrelNamed',
|
|
41
|
-
...output,
|
|
42
|
-
},
|
|
43
38
|
options: {
|
|
44
|
-
|
|
39
|
+
output,
|
|
45
40
|
transformers,
|
|
46
41
|
include,
|
|
47
42
|
exclude,
|
|
48
43
|
override,
|
|
49
44
|
typed,
|
|
50
|
-
typedSchema,
|
|
51
45
|
dateType,
|
|
52
46
|
unknownType,
|
|
53
47
|
mapper,
|
|
54
48
|
importPath,
|
|
55
49
|
coercion,
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
...templates,
|
|
59
|
-
},
|
|
50
|
+
operations,
|
|
51
|
+
inferred,
|
|
60
52
|
},
|
|
61
53
|
pre: [pluginOasName, typed ? pluginTsName : undefined].filter(Boolean),
|
|
62
54
|
resolvePath(baseName, pathMode, options) {
|
|
63
55
|
const root = path.resolve(this.config.root, this.config.output.path)
|
|
64
56
|
const mode = pathMode ?? FileManager.getMode(path.resolve(root, output.path))
|
|
65
57
|
|
|
58
|
+
if (options?.tag && group?.type === 'tag') {
|
|
59
|
+
const groupName: Group['name'] = group?.name ? group.name : (ctx) => `${ctx.group}Controller`
|
|
60
|
+
|
|
61
|
+
return path.resolve(root, output.path, groupName({ group: camelCase(options.tag) }), baseName)
|
|
62
|
+
}
|
|
63
|
+
|
|
66
64
|
if (mode === 'single') {
|
|
67
65
|
/**
|
|
68
66
|
* when output is a file then we will always append to the same file(output file), see fileManager.addOrAppend
|
|
@@ -71,12 +69,6 @@ export const pluginZod = createPlugin<PluginZod>((options) => {
|
|
|
71
69
|
return path.resolve(root, output.path)
|
|
72
70
|
}
|
|
73
71
|
|
|
74
|
-
if (options?.tag && group?.type === 'tag') {
|
|
75
|
-
const tag = camelCase(options.tag)
|
|
76
|
-
|
|
77
|
-
return path.resolve(root, renderTemplate(template, { tag }), baseName)
|
|
78
|
-
}
|
|
79
|
-
|
|
80
72
|
return path.resolve(root, output.path, baseName)
|
|
81
73
|
},
|
|
82
74
|
resolveName(name, type) {
|
|
@@ -113,7 +105,7 @@ export const pluginZod = createPlugin<PluginZod>((options) => {
|
|
|
113
105
|
output: output.path,
|
|
114
106
|
})
|
|
115
107
|
|
|
116
|
-
const schemaFiles = await schemaGenerator.build(
|
|
108
|
+
const schemaFiles = await schemaGenerator.build(...generators)
|
|
117
109
|
await this.addFile(...schemaFiles)
|
|
118
110
|
|
|
119
111
|
const operationGenerator = new OperationGenerator(this.plugin.options, {
|
|
@@ -127,22 +119,21 @@ export const pluginZod = createPlugin<PluginZod>((options) => {
|
|
|
127
119
|
mode,
|
|
128
120
|
})
|
|
129
121
|
|
|
130
|
-
const operationFiles = await operationGenerator.build(
|
|
122
|
+
const operationFiles = await operationGenerator.build(...generators)
|
|
131
123
|
await this.addFile(...operationFiles)
|
|
132
124
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
125
|
+
const barrelFiles = await this.fileManager.getBarrelFiles({
|
|
126
|
+
type: output.barrelType ?? 'named',
|
|
127
|
+
root,
|
|
128
|
+
output,
|
|
129
|
+
files: this.fileManager.files,
|
|
130
|
+
meta: {
|
|
131
|
+
pluginKey: this.plugin.key,
|
|
132
|
+
},
|
|
133
|
+
logger: this.logger,
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
await this.addFile(...barrelFiles)
|
|
146
137
|
},
|
|
147
138
|
}
|
|
148
139
|
})
|
package/src/types.ts
CHANGED
|
@@ -1,57 +1,16 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type * as KubbFile from '@kubb/fs/types'
|
|
1
|
+
import type { Group, Output, PluginFactoryOptions, ResolveNameParams } from '@kubb/core'
|
|
3
2
|
import type { SchemaObject } from '@kubb/oas'
|
|
4
|
-
import type { Exclude, Include, Override, ResolvePathOptions, Schema } from '@kubb/plugin-oas'
|
|
5
|
-
import type { Operations } from './components/Operations'
|
|
6
|
-
|
|
7
|
-
type Templates = {
|
|
8
|
-
operations?: typeof Operations.templates | false
|
|
9
|
-
}
|
|
3
|
+
import type { Exclude, Generator, Include, Override, ResolvePathOptions, Schema } from '@kubb/plugin-oas'
|
|
10
4
|
|
|
11
5
|
export type Options = {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
* @default 'zod'
|
|
17
|
-
*/
|
|
18
|
-
path: string
|
|
19
|
-
/**
|
|
20
|
-
* Name to be used for the `export * as {{exportAs}} from './'`
|
|
21
|
-
*/
|
|
22
|
-
exportAs?: string
|
|
23
|
-
/**
|
|
24
|
-
* Add an extension to the generated imports and exports, default it will not use an extension
|
|
25
|
-
*/
|
|
26
|
-
extName?: KubbFile.Extname
|
|
27
|
-
/**
|
|
28
|
-
* Define what needs to exported, here you can also disable the export of barrel files
|
|
29
|
-
* @default `'barrel'`
|
|
30
|
-
*/
|
|
31
|
-
exportType?: 'barrel' | 'barrelNamed' | false
|
|
32
|
-
}
|
|
6
|
+
/**
|
|
7
|
+
* @default 'zod'
|
|
8
|
+
*/
|
|
9
|
+
output?: Output
|
|
33
10
|
/**
|
|
34
11
|
* Group the Zod schemas based on the provided name.
|
|
35
12
|
*/
|
|
36
|
-
group?:
|
|
37
|
-
/**
|
|
38
|
-
* Tag will group based on the operation tag inside the Swagger file
|
|
39
|
-
*/
|
|
40
|
-
type: 'tag'
|
|
41
|
-
/**
|
|
42
|
-
* Relative path to save the grouped Zod schemas.
|
|
43
|
-
*
|
|
44
|
-
* `{{tag}}` will be replaced by the current tagName.
|
|
45
|
-
* @example `${output}/{{tag}}Controller` => `zod/PetController`
|
|
46
|
-
* @default `${output}/{{tag}}Controller`
|
|
47
|
-
*/
|
|
48
|
-
output?: string
|
|
49
|
-
/**
|
|
50
|
-
* Name to be used for the `export * as {{exportAs}} from './`
|
|
51
|
-
* @default `"{{tag}}Schemas"`
|
|
52
|
-
*/
|
|
53
|
-
exportAs?: string
|
|
54
|
-
}
|
|
13
|
+
group?: Group
|
|
55
14
|
/**
|
|
56
15
|
* Array containing exclude parameters to exclude/skip tags/operations/methods/paths.
|
|
57
16
|
*/
|
|
@@ -64,30 +23,15 @@ export type Options = {
|
|
|
64
23
|
* Array containing override parameters to override `options` based on tags/operations/methods/paths.
|
|
65
24
|
*/
|
|
66
25
|
override?: Array<Override<ResolvedOptions>>
|
|
67
|
-
transformers?: {
|
|
68
|
-
/**
|
|
69
|
-
* Customize the names based on the type that is provided by the plugin.
|
|
70
|
-
*/
|
|
71
|
-
name?: (name: ResolveNameParams['name'], type?: ResolveNameParams['type']) => string
|
|
72
|
-
/**
|
|
73
|
-
* Receive schema and baseName(propertName) and return FakerMeta array
|
|
74
|
-
* TODO TODO add docs
|
|
75
|
-
* @beta
|
|
76
|
-
*/
|
|
77
|
-
schema?: (
|
|
78
|
-
props: {
|
|
79
|
-
schema?: SchemaObject
|
|
80
|
-
name?: string
|
|
81
|
-
parentName?: string
|
|
82
|
-
},
|
|
83
|
-
defaultSchemas: Schema[],
|
|
84
|
-
) => Schema[] | undefined
|
|
85
|
-
}
|
|
86
|
-
mapper?: Record<string, string>
|
|
87
26
|
/**
|
|
88
|
-
*
|
|
27
|
+
* Path to Zod
|
|
28
|
+
* It will be used as `import { z } from '${importPath}'`.
|
|
29
|
+
* It allows both relative and absolute path.
|
|
30
|
+
* the path will be applied as is, so relative path should be based on the file being generated.
|
|
31
|
+
* @default 'zod'
|
|
89
32
|
*/
|
|
90
|
-
|
|
33
|
+
importPath?: string
|
|
34
|
+
|
|
91
35
|
/**
|
|
92
36
|
* Choose to use `date` or `datetime` as JavaScript `Date` instead of `string`.
|
|
93
37
|
* False will fallback on a simple z.string() format
|
|
@@ -106,40 +50,57 @@ export type Options = {
|
|
|
106
50
|
/**
|
|
107
51
|
* Return Zod generated schema as type with z.infer<TYPE>
|
|
108
52
|
*/
|
|
109
|
-
|
|
53
|
+
inferred?: boolean
|
|
110
54
|
/**
|
|
111
55
|
* Use of z.coerce.string() instead of z.string()
|
|
56
|
+
* can also be an object to enable coercion for dates, strings, and numbers
|
|
112
57
|
*/
|
|
113
|
-
coercion?:
|
|
58
|
+
coercion?:
|
|
59
|
+
| boolean
|
|
60
|
+
| {
|
|
61
|
+
dates?: boolean
|
|
62
|
+
strings?: boolean
|
|
63
|
+
numbers?: boolean
|
|
64
|
+
}
|
|
65
|
+
operations?: boolean
|
|
66
|
+
mapper?: Record<string, string>
|
|
67
|
+
transformers?: {
|
|
68
|
+
/**
|
|
69
|
+
* Customize the names based on the type that is provided by the plugin.
|
|
70
|
+
*/
|
|
71
|
+
name?: (name: ResolveNameParams['name'], type?: ResolveNameParams['type']) => string
|
|
72
|
+
/**
|
|
73
|
+
* Receive schema and baseName(propertName) and return FakerMeta array
|
|
74
|
+
* TODO TODO add docs
|
|
75
|
+
* @beta
|
|
76
|
+
*/
|
|
77
|
+
schema?: (
|
|
78
|
+
props: {
|
|
79
|
+
schema?: SchemaObject
|
|
80
|
+
name?: string
|
|
81
|
+
parentName?: string
|
|
82
|
+
},
|
|
83
|
+
defaultSchemas: Schema[],
|
|
84
|
+
) => Schema[] | undefined
|
|
85
|
+
}
|
|
114
86
|
/**
|
|
115
|
-
*
|
|
116
|
-
* It will be used as `import { z } from '${importPath}'`.
|
|
117
|
-
* It allows both relative and absolute path.
|
|
118
|
-
* the path will be applied as is, so relative path should be based on the file being generated.
|
|
119
|
-
* @default 'zod'
|
|
87
|
+
* Define some generators next to the zod generators
|
|
120
88
|
*/
|
|
121
|
-
|
|
89
|
+
generators?: Array<Generator<PluginZod>>
|
|
122
90
|
}
|
|
123
91
|
|
|
124
92
|
type ResolvedOptions = {
|
|
125
|
-
|
|
93
|
+
output: Output
|
|
94
|
+
override: NonNullable<Options['override']>
|
|
126
95
|
transformers: NonNullable<Options['transformers']>
|
|
127
|
-
exclude: Options['exclude']
|
|
128
|
-
include: Options['include']
|
|
129
|
-
override: Options['override']
|
|
130
96
|
dateType: NonNullable<Options['dateType']>
|
|
131
97
|
unknownType: NonNullable<Options['unknownType']>
|
|
132
98
|
typed: NonNullable<Options['typed']>
|
|
133
|
-
|
|
134
|
-
templates: NonNullable<Templates>
|
|
99
|
+
inferred: NonNullable<Options['inferred']>
|
|
135
100
|
mapper: NonNullable<Options['mapper']>
|
|
136
101
|
importPath: NonNullable<Options['importPath']>
|
|
137
102
|
coercion: NonNullable<Options['coercion']>
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
export type FileMeta = {
|
|
141
|
-
pluginKey?: Plugin['key']
|
|
142
|
-
tag?: string
|
|
103
|
+
operations: NonNullable<Options['operations']>
|
|
143
104
|
}
|
|
144
105
|
|
|
145
106
|
export type PluginZod = PluginFactoryOptions<'plugin-zod', Options, ResolvedOptions, never, ResolvePathOptions>
|