@kubb/plugin-zod 5.0.0-alpha.3 → 5.0.0-alpha.30

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.
Files changed (46) hide show
  1. package/dist/index.cjs +1619 -100
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.ts +418 -4
  4. package/dist/index.js +1614 -100
  5. package/dist/index.js.map +1 -1
  6. package/package.json +6 -34
  7. package/src/components/Operations.tsx +22 -15
  8. package/src/components/Zod.tsx +20 -119
  9. package/src/constants.ts +5 -0
  10. package/src/generators/zodGenerator.tsx +129 -159
  11. package/src/generators/zodGeneratorLegacy.tsx +365 -0
  12. package/src/index.ts +12 -1
  13. package/src/plugin.ts +102 -148
  14. package/src/presets.ts +30 -0
  15. package/src/printers/printerZod.ts +298 -0
  16. package/src/printers/printerZodMini.ts +273 -0
  17. package/src/resolvers/resolverZod.ts +61 -0
  18. package/src/resolvers/resolverZodLegacy.ts +60 -0
  19. package/src/types.ts +172 -93
  20. package/src/utils.ts +248 -0
  21. package/dist/components-B7zUFnAm.cjs +0 -890
  22. package/dist/components-B7zUFnAm.cjs.map +0 -1
  23. package/dist/components-eECfXVou.js +0 -842
  24. package/dist/components-eECfXVou.js.map +0 -1
  25. package/dist/components.cjs +0 -4
  26. package/dist/components.d.ts +0 -56
  27. package/dist/components.js +0 -2
  28. package/dist/generators-CRKtFRi1.js +0 -290
  29. package/dist/generators-CRKtFRi1.js.map +0 -1
  30. package/dist/generators-CzSLRVqQ.cjs +0 -301
  31. package/dist/generators-CzSLRVqQ.cjs.map +0 -1
  32. package/dist/generators.cjs +0 -4
  33. package/dist/generators.d.ts +0 -503
  34. package/dist/generators.js +0 -2
  35. package/dist/templates/ToZod.source.cjs +0 -7
  36. package/dist/templates/ToZod.source.cjs.map +0 -1
  37. package/dist/templates/ToZod.source.d.ts +0 -7
  38. package/dist/templates/ToZod.source.js +0 -6
  39. package/dist/templates/ToZod.source.js.map +0 -1
  40. package/dist/types-D0wsPC6Y.d.ts +0 -172
  41. package/src/components/index.ts +0 -2
  42. package/src/generators/index.ts +0 -2
  43. package/src/generators/operationsGenerator.tsx +0 -50
  44. package/src/parser.ts +0 -909
  45. package/src/templates/ToZod.source.ts +0 -4
  46. package/templates/ToZod.ts +0 -61
@@ -1,201 +1,171 @@
1
- import path from 'node:path'
2
- import { useMode, usePluginManager } from '@kubb/core/hooks'
3
- import { type OperationSchema as OperationSchemaType, SchemaGenerator, schemaKeywords } from '@kubb/plugin-oas'
4
- import { createReactGenerator } from '@kubb/plugin-oas/generators'
5
- import { useOas, useOperationManager, useSchemaManager } from '@kubb/plugin-oas/hooks'
6
- import { getBanner, getFooter, getImports } from '@kubb/plugin-oas/utils'
7
- import { pluginTsName } from '@kubb/plugin-ts'
1
+ import { caseParams } from '@kubb/ast'
2
+ import type { SchemaNode } from '@kubb/ast/types'
3
+ import { defineGenerator } from '@kubb/core'
8
4
  import { File } from '@kubb/react-fabric'
9
- import { Zod } from '../components'
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 = createReactGenerator<PluginZod>({
13
+ export const zodGenerator = defineGenerator<PluginZod>({
13
14
  name: 'zod',
14
- Operation({ config, operation, generator, plugin }) {
15
- const {
16
- options,
17
- options: { coercion: globalCoercion, inferred, typed, mapper, wrapOutput, version, guidType, mini },
18
- } = plugin
19
-
20
- const mode = useMode()
21
- const pluginManager = usePluginManager()
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
- pluginManager,
34
- events: generator.context.events,
35
- mode,
36
- override: options.override,
37
- })
15
+ schema(node, options) {
16
+ const { adapter, config, resolver, root } = this
17
+ const { output, coercion, guidType, mini, wrapOutput, inferred, importPath, group, printer } = options
38
18
 
39
- const operationSchemas = [schemas.pathParams, schemas.queryParams, schemas.headerParams, schemas.statusCodes, schemas.request, schemas.response]
40
- .flat()
41
- .filter(Boolean)
42
- const toZodPath = path.resolve(config.root, config.output.path, '.kubb/ToZod.ts')
19
+ if (!node.name) {
20
+ return
21
+ }
43
22
 
44
- const mapOperationSchema = ({ name, schema: schemaOriginal, description, keysToOmit: keysToOmitOriginal, ...options }: OperationSchemaType) => {
45
- let schemaObject = schemaOriginal
46
- let keysToOmit = keysToOmitOriginal
23
+ const mode = this.getMode(output)
24
+ const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath as 'zod' | 'zod/mini')
47
25
 
48
- if ((schemaOriginal.anyOf || schemaOriginal.oneOf) && keysToOmitOriginal && keysToOmitOriginal.length > 0) {
49
- schemaObject = structuredClone(schemaOriginal)
26
+ const imports = adapter.getImports(node, (schemaName) => ({
27
+ name: resolver.resolveSchemaName(schemaName),
28
+ path: resolver.resolveFile({ name: schemaName, extname: '.ts' }, { root, output, group }).path,
29
+ }))
50
30
 
51
- // Remove $ref so the schema parser generates inline schema instead of a reference
52
- delete schemaObject.$ref
31
+ const meta = {
32
+ name: resolver.resolveSchemaName(node.name),
33
+ file: resolver.resolveFile({ name: node.name, extname: '.ts' }, { root, output, group }),
34
+ } as const
53
35
 
54
- for (const key of keysToOmitOriginal) {
55
- delete schemaObject.properties?.[key]
56
- }
36
+ const inferTypeName = inferred ? resolver.resolveSchemaTypeName(node.name) : undefined
57
37
 
58
- if (Array.isArray(schemaObject.required)) {
59
- schemaObject.required = schemaObject.required.filter((key) => !keysToOmitOriginal.includes(key))
60
- }
38
+ const schemaPrinter = mini
39
+ ? printerZodMini({ guidType, wrapOutput, resolver, schemaName: meta.name, nodes: printer?.nodes })
40
+ : printerZod({ coercion, guidType, wrapOutput, resolver, schemaName: meta.name, nodes: printer?.nodes })
61
41
 
62
- keysToOmit = undefined
63
- }
42
+ return (
43
+ <File
44
+ baseName={meta.file.baseName}
45
+ path={meta.file.path}
46
+ meta={meta.file.meta}
47
+ banner={resolver.resolveBanner(adapter.rootNode, { output, config })}
48
+ footer={resolver.resolveFooter(adapter.rootNode, { output, config })}
49
+ >
50
+ <File.Import name={isZodImport ? 'z' : ['z']} path={importPath} isNameSpace={isZodImport} />
51
+ {mode === 'split' && imports.map((imp) => <File.Import key={[node.name, imp.path].join('-')} root={meta.file.path} path={imp.path} name={imp.name} />)}
64
52
 
65
- const hasProperties = Object.keys(schemaObject || {}).length > 0
66
- const hasDefaults = Object.values(schemaObject.properties || {}).some((prop) => prop && Object.hasOwn(prop, 'default'))
53
+ <Zod name={meta.name} node={node} printer={schemaPrinter} inferTypeName={inferTypeName} />
54
+ </File>
55
+ )
56
+ },
57
+ operation(node, options) {
58
+ const { adapter, config, resolver, root } = this
59
+ const { output, coercion, guidType, mini, wrapOutput, inferred, importPath, group, paramsCasing, printer } = options
67
60
 
68
- const required = Array.isArray(schemaObject?.required) ? schemaObject.required.length > 0 : !!schemaObject?.required
69
- const optional = !required && !hasDefaults && hasProperties && name.includes('Params')
61
+ const mode = this.getMode(output)
62
+ const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath as 'zod' | 'zod/mini')
70
63
 
71
- if (!optional && Array.isArray(schemaObject.required) && !schemaObject.required.length) {
72
- schemaObject.required = Object.entries(schemaObject.properties || {})
73
- .filter(([_key, value]) => value && Object.hasOwn(value, 'default'))
74
- .map(([key]) => key)
75
- }
64
+ const params = caseParams(node.parameters, paramsCasing)
76
65
 
77
- const tree = [
78
- ...schemaGenerator.parse({ schema: schemaObject, name, parentName: null }),
79
- optional ? { keyword: schemaKeywords.optional } : undefined,
80
- ].filter(Boolean)
81
- const imports = getImports(tree)
82
- const group = options.operation ? getGroup(options.operation) : undefined
66
+ const meta = {
67
+ file: resolver.resolveFile({ name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path }, { root, output, group }),
68
+ } as const
83
69
 
84
- const coercion = name.includes('Params') ? { numbers: true, strings: false, dates: true } : globalCoercion
70
+ function renderSchemaEntry({ schema, name, keysToOmit }: { schema: SchemaNode | null; name: string; keysToOmit?: Array<string> }) {
71
+ if (!schema) return null
85
72
 
86
- const zod = {
87
- name: schemaManager.getName(name, { type: 'function' }),
88
- inferTypeName: schemaManager.getName(name, { type: 'type' }),
89
- file: schemaManager.getFile(name),
90
- }
73
+ const inferTypeName = inferred ? resolver.resolveTypeName(name) : undefined
91
74
 
92
- const type = {
93
- name: schemaManager.getName(name, {
94
- type: 'type',
95
- pluginName: pluginTsName,
96
- }),
97
- file: schemaManager.getFile(options.operationName || name, {
98
- pluginName: pluginTsName,
99
- group,
100
- }),
101
- }
75
+ const imports = adapter.getImports(schema, (schemaName) => ({
76
+ name: resolver.resolveSchemaName(schemaName),
77
+ path: resolver.resolveFile({ name: schemaName, extname: '.ts' }, { root, output, group }).path,
78
+ }))
79
+
80
+ const schemaPrinter = mini
81
+ ? printerZodMini({ guidType, wrapOutput, resolver, schemaName: name, keysToOmit, nodes: printer?.nodes })
82
+ : printerZod({ coercion, guidType, wrapOutput, resolver, schemaName: name, keysToOmit, nodes: printer?.nodes })
102
83
 
103
84
  return (
104
85
  <>
105
- {typed && <File.Import isTypeOnly root={file.path} path={type.file.path} name={[type.name]} />}
106
- {imports.map((imp) => (
107
- <File.Import key={[imp.path, imp.name, imp.isTypeOnly].join('-')} root={file.path} path={imp.path} name={imp.name} />
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
- />
86
+ {mode === 'split' &&
87
+ imports.map((imp) => <File.Import key={[name, imp.path, imp.name].join('-')} root={meta.file.path} path={imp.path} name={imp.name} />)}
88
+ <Zod name={name} node={schema} printer={schemaPrinter} inferTypeName={inferTypeName} />
125
89
  </>
126
90
  )
127
91
  }
128
92
 
129
- const isZodImport = plugin.options.importPath === 'zod' || plugin.options.importPath === 'zod/mini'
93
+ const paramSchemas = params.map((param) => renderSchemaEntry({ schema: param.schema, name: resolver.resolveParamName(node, param) }))
94
+
95
+ const responseSchemas = node.responses.map((res) =>
96
+ renderSchemaEntry({
97
+ schema: res.schema,
98
+ name: resolver.resolveResponseStatusName(node, res.statusCode),
99
+ keysToOmit: res.keysToOmit,
100
+ }),
101
+ )
102
+
103
+ const requestSchema = node.requestBody?.schema
104
+ ? renderSchemaEntry({
105
+ schema: {
106
+ ...node.requestBody.schema,
107
+ description: node.requestBody.description ?? node.requestBody.schema.description,
108
+ },
109
+ name: resolver.resolveDataName(node),
110
+ keysToOmit: node.requestBody.keysToOmit,
111
+ })
112
+ : null
130
113
 
131
114
  return (
132
115
  <File
133
- baseName={file.baseName}
134
- path={file.path}
135
- meta={file.meta}
136
- banner={getBanner({ oas, output: plugin.options.output, config: pluginManager.config })}
137
- footer={getFooter({ oas, output: plugin.options.output })}
116
+ baseName={meta.file.baseName}
117
+ path={meta.file.path}
118
+ meta={meta.file.meta}
119
+ banner={resolver.resolveBanner(adapter.rootNode, { output, config })}
120
+ footer={resolver.resolveFooter(adapter.rootNode, { output, config })}
138
121
  >
139
- <File.Import name={isZodImport ? 'z' : ['z']} path={plugin.options.importPath} isNameSpace={isZodImport} />
140
- {typed && version === '3' && <File.Import name={['ToZod']} isTypeOnly root={file.path} path={toZodPath} />}
141
- {operationSchemas.map(mapOperationSchema)}
122
+ <File.Import name={isZodImport ? 'z' : ['z']} path={importPath} isNameSpace={isZodImport} />
123
+ {paramSchemas}
124
+ {responseSchemas}
125
+ {requestSchema}
142
126
  </File>
143
127
  )
144
128
  },
145
- Schema({ config, schema, plugin }) {
146
- const { getName, getFile } = useSchemaManager()
147
- const {
148
- options: { output, emptySchemaType, coercion, inferred, typed, mapper, importPath, wrapOutput, version, guidType, mini },
149
- } = plugin
150
- const pluginManager = usePluginManager()
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
- }
129
+ operations(nodes, options) {
130
+ const { adapter, config, resolver, root } = this
131
+ const { output, importPath, group, operations, paramsCasing } = options
160
132
 
161
- const type = {
162
- name: getName(schema.name, { type: 'type', pluginName: pluginTsName }),
163
- file: getFile(schema.name, { pluginName: pluginTsName }),
133
+ if (!operations) {
134
+ return
164
135
  }
136
+ const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath as 'zod' | 'zod/mini')
137
+
138
+ const meta = {
139
+ file: resolver.resolveFile({ name: 'operations', extname: '.ts' }, { root, output, group }),
140
+ } as const
165
141
 
166
- const isZodImport = importPath === 'zod' || importPath === 'zod/mini'
167
- const toZodPath = path.resolve(config.root, config.output.path, '.kubb/ToZod.ts')
142
+ const transformedOperations = nodes.map((node) => {
143
+ const params = caseParams(node.parameters, paramsCasing)
144
+
145
+ return {
146
+ node,
147
+ data: buildSchemaNames(node, { params, resolver }),
148
+ }
149
+ })
150
+
151
+ const imports = transformedOperations.flatMap(({ node, data }) => {
152
+ const names = [data.request, ...Object.values(data.responses), ...Object.values(data.parameters)].filter(Boolean) as string[]
153
+ const opFile = resolver.resolveFile({ name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path }, { root, output, group })
154
+
155
+ return names.map((name) => <File.Import key={[name, opFile.path].join('-')} name={[name]} root={meta.file.path} path={opFile.path} />)
156
+ })
168
157
 
169
158
  return (
170
159
  <File
171
- baseName={zod.file.baseName}
172
- path={zod.file.path}
173
- meta={zod.file.meta}
174
- banner={getBanner({ oas, output, config: pluginManager.config })}
175
- footer={getFooter({ oas, output })}
160
+ baseName={meta.file.baseName}
161
+ path={meta.file.path}
162
+ meta={meta.file.meta}
163
+ banner={resolver.resolveBanner(adapter.rootNode, { output, config })}
164
+ footer={resolver.resolveFooter(adapter.rootNode, { output, config })}
176
165
  >
177
- <File.Import name={isZodImport ? 'z' : ['z']} path={importPath} isNameSpace={isZodImport} />
178
- {typed && <File.Import isTypeOnly root={zod.file.path} path={type.file.path} name={[type.name]} />}
179
- {typed && version === '3' && <File.Import name={['ToZod']} isTypeOnly root={zod.file.path} path={toZodPath} />}
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
- />
166
+ <File.Import isTypeOnly name={isZodImport ? 'z' : ['z']} path={importPath} isNameSpace={isZodImport} />
167
+ {imports}
168
+ <Operations name="operations" operations={transformedOperations} />
199
169
  </File>
200
170
  )
201
171
  },