@kubb/plugin-zod 5.0.0-alpha.9 → 5.0.0-beta.4
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/extension.yaml +502 -0
- 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 -508
- 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,35 +1,42 @@
|
|
|
1
1
|
import { stringifyObject } from '@internals/utils'
|
|
2
|
-
import type {
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
|
|
2
|
+
import type { ast } from '@kubb/core'
|
|
3
|
+
import { Const, File, Type } from '@kubb/renderer-jsx'
|
|
4
|
+
import type { KubbReactNode } from '@kubb/renderer-jsx/types'
|
|
5
|
+
|
|
6
|
+
type SchemaNames = {
|
|
7
|
+
request: string | undefined
|
|
8
|
+
parameters: {
|
|
9
|
+
path: string | undefined
|
|
10
|
+
query: string | undefined
|
|
11
|
+
header: string | undefined
|
|
12
|
+
}
|
|
13
|
+
responses: { default?: string } & Record<number | string, string>
|
|
14
|
+
errors: Record<number | string, string>
|
|
15
|
+
}
|
|
6
16
|
|
|
7
17
|
type Props = {
|
|
8
18
|
name: string
|
|
9
|
-
operations: Array<{
|
|
19
|
+
operations: Array<{ node: ast.OperationNode; data: SchemaNames }>
|
|
10
20
|
}
|
|
11
21
|
|
|
12
|
-
export function Operations({ name, operations }: Props):
|
|
13
|
-
const operationsJSON = operations.reduce(
|
|
22
|
+
export function Operations({ name, operations }: Props): KubbReactNode {
|
|
23
|
+
const operationsJSON = operations.reduce<Record<string, unknown>>(
|
|
14
24
|
(prev, acc) => {
|
|
15
|
-
prev[`"${acc.
|
|
25
|
+
prev[`"${acc.node.operationId}"`] = acc.data
|
|
16
26
|
|
|
17
27
|
return prev
|
|
18
28
|
},
|
|
19
29
|
{} as Record<string, unknown>,
|
|
20
30
|
)
|
|
21
31
|
|
|
22
|
-
const pathsJSON = operations.reduce(
|
|
23
|
-
|
|
24
|
-
prev[`"${acc.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
32
|
+
const pathsJSON = operations.reduce<Record<string, Record<string, string>>>((prev, acc) => {
|
|
33
|
+
prev[`"${acc.node.path}"`] = {
|
|
34
|
+
...(prev[`"${acc.node.path}"`] ?? {}),
|
|
35
|
+
[acc.node.method]: `operations["${acc.node.operationId}"]`,
|
|
36
|
+
}
|
|
28
37
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
{} as Record<string, Record<HttpMethod, string>>,
|
|
32
|
-
)
|
|
38
|
+
return prev
|
|
39
|
+
}, {})
|
|
33
40
|
|
|
34
41
|
return (
|
|
35
42
|
<>
|
package/src/components/Zod.tsx
CHANGED
|
@@ -1,140 +1,40 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import type {
|
|
6
|
-
import * as parserZod from '../parser.ts'
|
|
7
|
-
import type { PluginZod } from '../types.ts'
|
|
1
|
+
import type { ast } from '@kubb/core'
|
|
2
|
+
import { Const, File, Type } from '@kubb/renderer-jsx'
|
|
3
|
+
import type { KubbReactNode } from '@kubb/renderer-jsx/types'
|
|
4
|
+
import type { PrinterZodFactory } from '../printers/printerZod.ts'
|
|
5
|
+
import type { PrinterZodMiniFactory } from '../printers/printerZodMini.ts'
|
|
8
6
|
|
|
9
7
|
type Props = {
|
|
10
8
|
name: string
|
|
11
|
-
|
|
9
|
+
node: ast.SchemaNode
|
|
10
|
+
/**
|
|
11
|
+
* Pre-configured printer instance created by the generator.
|
|
12
|
+
* The generator selects `printerZod` or `printerZodMini` based on the `mini` option,
|
|
13
|
+
* then merges in any user-supplied `printer.nodes` overrides.
|
|
14
|
+
*/
|
|
15
|
+
printer: ast.Printer<PrinterZodFactory> | ast.Printer<PrinterZodMiniFactory>
|
|
12
16
|
inferTypeName?: string
|
|
13
|
-
tree: Array<Schema>
|
|
14
|
-
schema: SchemaObject
|
|
15
|
-
description?: string
|
|
16
|
-
coercion: PluginZod['resolvedOptions']['coercion']
|
|
17
|
-
mapper: PluginZod['resolvedOptions']['mapper']
|
|
18
|
-
keysToOmit?: string[]
|
|
19
|
-
wrapOutput?: PluginZod['resolvedOptions']['wrapOutput']
|
|
20
|
-
version: '3' | '4'
|
|
21
|
-
guidType: PluginZod['resolvedOptions']['guidType']
|
|
22
|
-
emptySchemaType: PluginZod['resolvedOptions']['emptySchemaType']
|
|
23
|
-
mini?: boolean
|
|
24
17
|
}
|
|
25
18
|
|
|
26
|
-
export function Zod({
|
|
27
|
-
|
|
28
|
-
typeName,
|
|
29
|
-
tree,
|
|
30
|
-
schema,
|
|
31
|
-
inferTypeName,
|
|
32
|
-
mapper,
|
|
33
|
-
coercion,
|
|
34
|
-
keysToOmit,
|
|
35
|
-
description,
|
|
36
|
-
wrapOutput,
|
|
37
|
-
version,
|
|
38
|
-
guidType,
|
|
39
|
-
emptySchemaType,
|
|
40
|
-
mini = false,
|
|
41
|
-
}: Props): FabricReactNode {
|
|
42
|
-
const hasTuple = !!SchemaGenerator.find(tree, schemaKeywords.tuple)
|
|
19
|
+
export function Zod({ name, node, printer, inferTypeName }: Props): KubbReactNode {
|
|
20
|
+
const output = printer.print(node)
|
|
43
21
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return false
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return true
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
// In mini mode, filter out modifiers from the main schema parsing
|
|
53
|
-
const baseSchemas = mini ? parserZod.filterMiniModifiers(schemas) : schemas
|
|
54
|
-
|
|
55
|
-
const output = baseSchemas
|
|
56
|
-
.map((schemaKeyword, index) => {
|
|
57
|
-
const siblings = baseSchemas.filter((_, i) => i !== index)
|
|
58
|
-
|
|
59
|
-
return parserZod.parse({ schema, parent: undefined, current: schemaKeyword, siblings, name }, { mapper, coercion, wrapOutput, version, guidType, mini })
|
|
60
|
-
})
|
|
61
|
-
.filter(Boolean)
|
|
62
|
-
.join('')
|
|
63
|
-
|
|
64
|
-
let suffix = ''
|
|
65
|
-
const firstSchema = schemas.at(0)
|
|
66
|
-
const lastSchema = schemas.at(-1)
|
|
67
|
-
|
|
68
|
-
if (!mini && lastSchema && isKeyword(lastSchema, schemaKeywords.nullable)) {
|
|
69
|
-
if (firstSchema && isKeyword(firstSchema, schemaKeywords.ref)) {
|
|
70
|
-
if (version === '3') {
|
|
71
|
-
suffix = '.unwrap().schema.unwrap()'
|
|
72
|
-
} else {
|
|
73
|
-
suffix = '.unwrap().unwrap()'
|
|
74
|
-
}
|
|
75
|
-
} else {
|
|
76
|
-
suffix = '.unwrap()'
|
|
77
|
-
}
|
|
78
|
-
} else if (!mini) {
|
|
79
|
-
if (firstSchema && isKeyword(firstSchema, schemaKeywords.ref)) {
|
|
80
|
-
if (version === '3') {
|
|
81
|
-
suffix = '.schema'
|
|
82
|
-
} else {
|
|
83
|
-
suffix = '.unwrap()'
|
|
84
|
-
}
|
|
85
|
-
}
|
|
22
|
+
if (!output) {
|
|
23
|
+
return
|
|
86
24
|
}
|
|
87
25
|
|
|
88
|
-
const emptyValue = parserZod.parse(
|
|
89
|
-
{
|
|
90
|
-
schema,
|
|
91
|
-
parent: undefined,
|
|
92
|
-
current: {
|
|
93
|
-
keyword: schemaKeywords[emptySchemaType],
|
|
94
|
-
},
|
|
95
|
-
siblings: [],
|
|
96
|
-
},
|
|
97
|
-
{ mapper, coercion, wrapOutput, version, guidType, mini },
|
|
98
|
-
)
|
|
99
|
-
|
|
100
|
-
let baseSchemaOutput =
|
|
101
|
-
[output, keysToOmit?.length ? `${suffix}.omit({ ${keysToOmit.map((key) => `'${key}': true`).join(',')} })` : undefined].filter(Boolean).join('') ||
|
|
102
|
-
emptyValue ||
|
|
103
|
-
''
|
|
104
|
-
|
|
105
|
-
// For mini mode, wrap the output with modifiers using the parser function
|
|
106
|
-
if (mini) {
|
|
107
|
-
baseSchemaOutput = parserZod.wrapWithMiniModifiers(baseSchemaOutput, parserZod.extractMiniModifiers(schemas))
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const wrappedSchemaOutput = wrapOutput ? wrapOutput({ output: baseSchemaOutput, schema }) || baseSchemaOutput : baseSchemaOutput
|
|
111
|
-
const finalOutput = typeName ? `${wrappedSchemaOutput} as unknown as ${version === '4' ? 'z.ZodType' : 'ToZod'}<${typeName}>` : wrappedSchemaOutput
|
|
112
|
-
|
|
113
26
|
return (
|
|
114
27
|
<>
|
|
115
28
|
<File.Source name={name} isExportable isIndexable>
|
|
116
|
-
<Const
|
|
117
|
-
|
|
118
|
-
name={name}
|
|
119
|
-
JSDoc={{
|
|
120
|
-
comments: [description ? `@description ${jsStringEscape(description)}` : undefined].filter(Boolean),
|
|
121
|
-
}}
|
|
122
|
-
>
|
|
123
|
-
{finalOutput}
|
|
29
|
+
<Const export name={name}>
|
|
30
|
+
{output}
|
|
124
31
|
</Const>
|
|
125
32
|
</File.Source>
|
|
126
33
|
{inferTypeName && (
|
|
127
34
|
<File.Source name={inferTypeName} isExportable isIndexable isTypeOnly>
|
|
128
|
-
|
|
129
|
-
<
|
|
130
|
-
|
|
131
|
-
</Type>
|
|
132
|
-
)}
|
|
133
|
-
{!typeName && (
|
|
134
|
-
<Type export name={inferTypeName}>
|
|
135
|
-
{`z.infer<typeof ${name}>`}
|
|
136
|
-
</Type>
|
|
137
|
-
)}
|
|
35
|
+
<Type export name={inferTypeName}>
|
|
36
|
+
{`z.infer<typeof ${name}>`}
|
|
37
|
+
</Type>
|
|
138
38
|
</File.Source>
|
|
139
39
|
)}
|
|
140
40
|
</>
|
package/src/constants.ts
ADDED
|
@@ -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'
|