@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
@@ -0,0 +1,365 @@
1
+ import { caseParams, createProperty, createSchema } from '@kubb/ast'
2
+ import type { OperationNode, ParameterNode, SchemaNode } from '@kubb/ast/types'
3
+ import { defineGenerator } from '@kubb/core'
4
+ import { File } from '@kubb/react-fabric'
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
+ import type { PluginZod, ResolverZod } from '../types'
11
+
12
+ type BuildGroupedParamsSchemaOptions = {
13
+ params: Array<ParameterNode>
14
+ }
15
+
16
+ function buildGroupedParamsSchema({ params, optional }: BuildGroupedParamsSchemaOptions & { optional?: boolean }): SchemaNode {
17
+ return createSchema({
18
+ type: 'object',
19
+ optional,
20
+ primitive: 'object',
21
+ properties: params.map((param) => {
22
+ return createProperty({
23
+ name: param.name,
24
+ required: param.required,
25
+ schema: param.schema,
26
+ })
27
+ }),
28
+ })
29
+ }
30
+
31
+ type BuildOperationSchemaOptions = {
32
+ resolver: ResolverZod
33
+ }
34
+
35
+ function buildLegacyResponsesSchemaNode(node: OperationNode, { resolver }: BuildOperationSchemaOptions): SchemaNode | null {
36
+ const isGet = node.method.toLowerCase() === 'get'
37
+ const successResponses = node.responses.filter((res) => {
38
+ const code = Number(res.statusCode)
39
+ return !Number.isNaN(code) && code >= 200 && code < 300
40
+ })
41
+ const errorResponses = node.responses.filter((res) => res.statusCode === 'default' || Number(res.statusCode) >= 400)
42
+
43
+ const responseSchema =
44
+ successResponses.length > 0
45
+ ? successResponses.length === 1
46
+ ? createSchema({ type: 'ref', name: resolver.resolveResponseStatusName(node, successResponses[0]!.statusCode) })
47
+ : createSchema({
48
+ type: 'union',
49
+ members: successResponses.map((res) => createSchema({ type: 'ref', name: resolver.resolveResponseStatusName(node, res.statusCode) })),
50
+ })
51
+ : createSchema({ type: 'any' })
52
+
53
+ const errorsSchema =
54
+ errorResponses.length > 0
55
+ ? errorResponses.length === 1
56
+ ? createSchema({ type: 'ref', name: resolver.resolveResponseStatusName(node, errorResponses[0]!.statusCode) })
57
+ : createSchema({
58
+ type: 'union',
59
+ members: errorResponses.map((res) => createSchema({ type: 'ref', name: resolver.resolveResponseStatusName(node, res.statusCode) })),
60
+ })
61
+ : createSchema({ type: 'any' })
62
+
63
+ const properties = [createProperty({ name: 'Response', required: true, schema: responseSchema })]
64
+
65
+ if (!isGet && node.requestBody?.schema) {
66
+ properties.push(
67
+ createProperty({
68
+ name: 'Request',
69
+ required: true,
70
+ schema: createSchema({ type: 'ref', name: resolver.resolveDataName(node) }),
71
+ }),
72
+ )
73
+ }
74
+
75
+ const queryParam = node.parameters.find((p) => p.in === 'query')
76
+ if (queryParam) {
77
+ properties.push(
78
+ createProperty({
79
+ name: 'QueryParams',
80
+ required: true,
81
+ schema: createSchema({ type: 'ref', name: resolver.resolveQueryParamsName(node, queryParam) }),
82
+ }),
83
+ )
84
+ }
85
+
86
+ const pathParam = node.parameters.find((p) => p.in === 'path')
87
+ if (pathParam) {
88
+ properties.push(
89
+ createProperty({
90
+ name: 'PathParams',
91
+ required: true,
92
+ schema: createSchema({ type: 'ref', name: resolver.resolvePathParamsName(node, pathParam) }),
93
+ }),
94
+ )
95
+ }
96
+
97
+ const headerParam = node.parameters.find((p) => p.in === 'header')
98
+ if (headerParam) {
99
+ properties.push(
100
+ createProperty({
101
+ name: 'HeaderParams',
102
+ required: true,
103
+ schema: createSchema({ type: 'ref', name: resolver.resolveHeaderParamsName(node, headerParam) }),
104
+ }),
105
+ )
106
+ }
107
+
108
+ properties.push(createProperty({ name: 'Errors', required: true, schema: errorsSchema }))
109
+
110
+ return createSchema({ type: 'object', primitive: 'object', properties })
111
+ }
112
+
113
+ function buildLegacyResponseUnionSchemaNode(node: OperationNode, { resolver }: BuildOperationSchemaOptions): SchemaNode {
114
+ const successResponses = node.responses.filter((res) => {
115
+ const code = Number(res.statusCode)
116
+ return !Number.isNaN(code) && code >= 200 && code < 300
117
+ })
118
+
119
+ if (successResponses.length === 0) {
120
+ return createSchema({ type: 'any' })
121
+ }
122
+
123
+ if (successResponses.length === 1) {
124
+ return createSchema({ type: 'ref', name: resolver.resolveResponseStatusName(node, successResponses[0]!.statusCode) })
125
+ }
126
+
127
+ return createSchema({
128
+ type: 'union',
129
+ members: successResponses.map((res) => createSchema({ type: 'ref', name: resolver.resolveResponseStatusName(node, res.statusCode) })),
130
+ })
131
+ }
132
+
133
+ function buildLegacySchemaNames(node: OperationNode, params: Array<ParameterNode>, resolver: ResolverZod) {
134
+ const pathParam = params.find((p) => p.in === 'path')
135
+ const queryParam = params.find((p) => p.in === 'query')
136
+ const headerParam = params.find((p) => p.in === 'header')
137
+
138
+ const responses: Record<number | string, string> = {}
139
+ const errors: Record<number | string, string> = {}
140
+
141
+ for (const res of node.responses) {
142
+ const name = resolver.resolveResponseStatusName(node, res.statusCode)
143
+ const statusNum = Number(res.statusCode)
144
+
145
+ if (!Number.isNaN(statusNum)) {
146
+ responses[statusNum] = name
147
+ if (statusNum >= 400) {
148
+ errors[statusNum] = name
149
+ }
150
+ }
151
+ }
152
+
153
+ responses['default'] = resolver.resolveResponseName(node)
154
+
155
+ return {
156
+ request: node.requestBody?.schema ? resolver.resolveDataName(node) : undefined,
157
+ parameters: {
158
+ path: pathParam ? resolver.resolvePathParamsName(node, pathParam) : undefined,
159
+ query: queryParam ? resolver.resolveQueryParamsName(node, queryParam) : undefined,
160
+ header: headerParam ? resolver.resolveHeaderParamsName(node, headerParam) : undefined,
161
+ },
162
+ responses,
163
+ errors,
164
+ }
165
+ }
166
+
167
+ export const zodGeneratorLegacy = defineGenerator<PluginZod>({
168
+ name: 'zod-legacy',
169
+ schema(node, options) {
170
+ const { adapter, config, resolver, root } = this
171
+ const { output, coercion, guidType, mini, wrapOutput, inferred, importPath, group, printer } = options
172
+
173
+ if (!node.name) {
174
+ return
175
+ }
176
+
177
+ const mode = this.getMode(output)
178
+ const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath as 'zod' | 'zod/mini')
179
+
180
+ const imports = adapter.getImports(node, (schemaName) => ({
181
+ name: resolver.resolveSchemaName(schemaName),
182
+ path: resolver.resolveFile({ name: schemaName, extname: '.ts' }, { root, output, group }).path,
183
+ }))
184
+
185
+ const inferTypeName = inferred ? resolver.resolveSchemaTypeName(node.name) : undefined
186
+
187
+ const meta = {
188
+ name: resolver.resolveSchemaName(node.name),
189
+ file: resolver.resolveFile({ name: node.name, extname: '.ts' }, { root, output, group }),
190
+ } as const
191
+
192
+ const schemaPrinter = mini
193
+ ? printerZodMini({ guidType, wrapOutput, resolver, schemaName: meta.name, nodes: printer?.nodes })
194
+ : printerZod({ coercion, guidType, wrapOutput, resolver, schemaName: meta.name, nodes: printer?.nodes })
195
+
196
+ return (
197
+ <File
198
+ baseName={meta.file.baseName}
199
+ path={meta.file.path}
200
+ meta={meta.file.meta}
201
+ banner={resolver.resolveBanner(adapter.rootNode, { output, config })}
202
+ footer={resolver.resolveFooter(adapter.rootNode, { output, config })}
203
+ >
204
+ <File.Import name={isZodImport ? 'z' : ['z']} path={importPath} isNameSpace={isZodImport} />
205
+ {mode === 'split' && imports.map((imp) => <File.Import key={[node.name, imp.path].join('-')} root={meta.file.path} path={imp.path} name={imp.name} />)}
206
+
207
+ <Zod name={meta.name} node={node} printer={schemaPrinter} inferTypeName={inferTypeName} />
208
+ </File>
209
+ )
210
+ },
211
+ operation(node, options) {
212
+ const { adapter, config, resolver, root } = this
213
+ const { output, coercion, guidType, mini, wrapOutput, inferred, importPath, group, paramsCasing, printer } = options
214
+
215
+ const mode = this.getMode(output)
216
+ const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath as 'zod' | 'zod/mini')
217
+
218
+ const params = caseParams(node.parameters, paramsCasing)
219
+
220
+ const meta = {
221
+ file: resolver.resolveFile({ name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path }, { root, output, group }),
222
+ } as const
223
+
224
+ function renderSchemaEntry({ schema, name, keysToOmit }: { schema: SchemaNode | null; name: string; keysToOmit?: Array<string> }) {
225
+ if (!schema) return null
226
+
227
+ const inferTypeName = inferred ? resolver.resolveTypeName(name) : undefined
228
+
229
+ const imports = adapter.getImports(schema, (schemaName) => ({
230
+ name: resolver.resolveSchemaName(schemaName),
231
+ path: resolver.resolveFile({ name: schemaName, extname: '.ts' }, { root, output, group }).path,
232
+ }))
233
+
234
+ const schemaPrinter = mini
235
+ ? printerZodMini({ guidType, wrapOutput, resolver, schemaName: name, keysToOmit, nodes: printer?.nodes })
236
+ : printerZod({ coercion, guidType, wrapOutput, resolver, schemaName: name, keysToOmit, nodes: printer?.nodes })
237
+
238
+ return (
239
+ <>
240
+ {mode === 'split' &&
241
+ imports.map((imp) => <File.Import key={[name, imp.path, imp.name].join('-')} root={meta.file.path} path={imp.path} name={imp.name} />)}
242
+ <Zod name={name} node={schema} printer={schemaPrinter} inferTypeName={inferTypeName} />
243
+ </>
244
+ )
245
+ }
246
+
247
+ const pathParams = params.filter((p) => p.in === 'path')
248
+ const queryParams = params.filter((p) => p.in === 'query')
249
+ const headerParams = params.filter((p) => p.in === 'header')
250
+
251
+ const responseSchemas = node.responses.map((res) => {
252
+ const responseName = resolver.resolveResponseStatusName(node, res.statusCode)
253
+ return renderSchemaEntry({
254
+ schema: {
255
+ ...res.schema,
256
+ description: res.description ?? res.schema.description,
257
+ },
258
+ name: responseName,
259
+ keysToOmit: res.keysToOmit,
260
+ })
261
+ })
262
+
263
+ const requestSchema = node.requestBody?.schema
264
+ ? renderSchemaEntry({
265
+ schema: {
266
+ ...node.requestBody.schema,
267
+ description: node.requestBody.description ?? node.requestBody.schema.description,
268
+ },
269
+ name: resolver.resolveDataName(node),
270
+ keysToOmit: node.requestBody.keysToOmit,
271
+ })
272
+ : null
273
+
274
+ const legacyParamTypes = [
275
+ pathParams.length > 0
276
+ ? renderSchemaEntry({
277
+ schema: buildGroupedParamsSchema({ params: pathParams, optional: pathParams.every((p) => !p.required) }),
278
+ name: resolver.resolvePathParamsName(node, pathParams[0]!),
279
+ })
280
+ : null,
281
+ queryParams.length > 0
282
+ ? renderSchemaEntry({
283
+ schema: buildGroupedParamsSchema({ params: queryParams, optional: queryParams.every((p) => !p.required) }),
284
+ name: resolver.resolveQueryParamsName(node, queryParams[0]!),
285
+ })
286
+ : null,
287
+ headerParams.length > 0
288
+ ? renderSchemaEntry({
289
+ schema: buildGroupedParamsSchema({ params: headerParams, optional: headerParams.every((p) => !p.required) }),
290
+ name: resolver.resolveHeaderParamsName(node, headerParams[0]!),
291
+ })
292
+ : null,
293
+ ]
294
+
295
+ const legacyResponsesSchema = renderSchemaEntry({
296
+ schema: buildLegacyResponsesSchemaNode(node, { resolver }),
297
+ name: resolver.resolveResponsesName(node),
298
+ })
299
+
300
+ const legacyResponseSchema = renderSchemaEntry({
301
+ schema: buildLegacyResponseUnionSchemaNode(node, { resolver }),
302
+ name: resolver.resolveResponseName(node),
303
+ })
304
+
305
+ return (
306
+ <File
307
+ baseName={meta.file.baseName}
308
+ path={meta.file.path}
309
+ meta={meta.file.meta}
310
+ banner={resolver.resolveBanner(adapter.rootNode, { output, config })}
311
+ footer={resolver.resolveFooter(adapter.rootNode, { output, config })}
312
+ >
313
+ <File.Import name={isZodImport ? 'z' : ['z']} path={importPath} isNameSpace={isZodImport} />
314
+ {legacyParamTypes}
315
+ {responseSchemas}
316
+ {requestSchema}
317
+ {legacyResponseSchema}
318
+ {legacyResponsesSchema}
319
+ </File>
320
+ )
321
+ },
322
+ operations(nodes, options) {
323
+ const { adapter, config, resolver, root } = this
324
+ const { output, importPath, group, operations, paramsCasing } = options
325
+
326
+ if (!operations) {
327
+ return
328
+ }
329
+ const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath as 'zod' | 'zod/mini')
330
+
331
+ const meta = {
332
+ file: resolver.resolveFile({ name: 'operations', extname: '.ts' }, { root, output, group }),
333
+ } as const
334
+
335
+ const transformedOperations = nodes.map((node) => {
336
+ const params = caseParams(node.parameters, paramsCasing)
337
+
338
+ return {
339
+ node,
340
+ data: buildLegacySchemaNames(node, params, resolver),
341
+ }
342
+ })
343
+
344
+ const imports = transformedOperations.flatMap(({ node, data }) => {
345
+ const names = [data.request, ...Object.values(data.responses), ...Object.values(data.parameters)].filter(Boolean) as string[]
346
+ const opFile = resolver.resolveFile({ name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path }, { root, output, group })
347
+
348
+ return names.map((name) => <File.Import key={[name, opFile.path].join('-')} name={[name]} root={meta.file.path} path={opFile.path} />)
349
+ })
350
+
351
+ return (
352
+ <File
353
+ baseName={meta.file.baseName}
354
+ path={meta.file.path}
355
+ meta={meta.file.meta}
356
+ banner={resolver.resolveBanner(adapter.rootNode, { output, config })}
357
+ footer={resolver.resolveFooter(adapter.rootNode, { output, config })}
358
+ >
359
+ <File.Import isTypeOnly name={isZodImport ? 'z' : ['z']} path={importPath} isNameSpace={isZodImport} />
360
+ {imports}
361
+ <Operations name="operations" operations={transformedOperations} />
362
+ </File>
363
+ )
364
+ },
365
+ })
package/src/index.ts CHANGED
@@ -1,2 +1,13 @@
1
+ export { zodGenerator } from './generators/zodGenerator.tsx'
2
+ export { zodGeneratorLegacy } from './generators/zodGeneratorLegacy.tsx'
3
+
1
4
  export { pluginZod, pluginZodName } from './plugin.ts'
2
- export type { PluginZod } from './types.ts'
5
+ export type { PrinterZodFactory, PrinterZodNodes, PrinterZodOptions } from './printers/printerZod.ts'
6
+ export { printerZod } from './printers/printerZod.ts'
7
+ export type { PrinterZodMiniFactory, PrinterZodMiniNodes, PrinterZodMiniOptions } from './printers/printerZodMini.ts'
8
+ export { printerZodMini } from './printers/printerZodMini.ts'
9
+
10
+ export { resolverZod } from './resolvers/resolverZod.ts'
11
+ export { resolverZodLegacy } from './resolvers/resolverZodLegacy.ts'
12
+
13
+ export type { PluginZod, ResolverZod } from './types.ts'
package/src/plugin.ts CHANGED
@@ -1,183 +1,137 @@
1
- import path from 'node:path'
2
- import { camelCase, pascalCase } from '@internals/utils'
3
- import { definePlugin, 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'
7
- import { zodGenerator } from './generators/zodGenerator.tsx'
8
- import { source as toZodSource } from './templates/ToZod.source.ts'
1
+ import { camelCase } from '@internals/utils'
2
+ import { createPlugin, type Group, getPreset, mergeGenerators } from '@kubb/core'
3
+ import { version } from '../package.json'
4
+ import { presets } from './presets.ts'
9
5
  import type { PluginZod } from './types.ts'
10
6
 
7
+ /**
8
+ * Canonical plugin name for `@kubb/plugin-zod`, used to identify the plugin in driver lookups and warnings.
9
+ */
11
10
  export const pluginZodName = 'plugin-zod' satisfies PluginZod['name']
12
11
 
13
- export const pluginZod = definePlugin<PluginZod>((options) => {
12
+ /**
13
+ * The `@kubb/plugin-zod` plugin factory.
14
+ *
15
+ * Generates Zod validation schemas from an OpenAPI/AST `RootNode`.
16
+ * Walks schemas and operations, delegates rendering to the active generators,
17
+ * and writes barrel files based on `output.barrelType`.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * import { pluginZod } from '@kubb/plugin-zod'
22
+ *
23
+ * export default defineConfig({
24
+ * plugins: [pluginZod({ output: { path: 'zod' } })],
25
+ * })
26
+ * ```
27
+ */
28
+ export const pluginZod = createPlugin<PluginZod>((options) => {
14
29
  const {
15
30
  output = { path: 'zod', barrelType: 'named' },
16
31
  group,
17
32
  exclude = [],
18
33
  include,
19
34
  override = [],
20
- transformers = {},
21
35
  dateType = 'string',
22
- unknownType = 'any',
23
- emptySchemaType = unknownType,
24
- integerType = 'number',
25
36
  typed = false,
26
- mapper = {},
27
37
  operations = false,
28
38
  mini = false,
29
- version = mini ? '4' : new PackageManager().isValidSync('zod', '>=4') ? '4' : '3',
30
39
  guidType = 'uuid',
31
- importPath = mini ? 'zod/mini' : version === '4' ? 'zod/v4' : 'zod',
40
+ importPath = mini ? 'zod/mini' : 'zod',
32
41
  coercion = false,
33
42
  inferred = false,
34
- generators = [zodGenerator, operations ? operationsGenerator : undefined].filter(Boolean),
35
43
  wrapOutput = undefined,
36
- contentType,
44
+ paramsCasing,
45
+ printer,
46
+ compatibilityPreset = 'default',
47
+ resolver: userResolver,
48
+ transformer: userTransformer,
49
+ generators: userGenerators = [],
37
50
  } = options
38
51
 
39
- // @deprecated Will be removed in v5 when collisionDetection defaults to true
40
- const usedEnumNames = {}
52
+ const preset = getPreset({
53
+ preset: compatibilityPreset,
54
+ presets: presets,
55
+ resolver: userResolver,
56
+ transformer: userTransformer,
57
+ generators: userGenerators,
58
+ })
59
+
60
+ const generators = preset.generators ?? []
61
+ const mergedGenerator = mergeGenerators(generators)
62
+
63
+ let resolveNameWarning = false
64
+ let resolvePathWarning = false
41
65
 
42
66
  return {
43
67
  name: pluginZodName,
44
- options: {
45
- output,
46
- transformers,
47
- include,
48
- exclude,
49
- override,
50
- typed,
51
- dateType,
52
- unknownType,
53
- emptySchemaType,
54
- integerType,
55
- mapper,
56
- importPath,
57
- coercion,
58
- operations,
59
- inferred,
60
- group,
61
- wrapOutput,
62
- version,
63
- guidType,
64
- mini,
65
- usedEnumNames,
68
+ version,
69
+ get resolver() {
70
+ return preset.resolver
66
71
  },
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)
72
+ get transformer() {
73
+ return preset.transformer
74
+ },
75
+ get options() {
76
+ return {
77
+ output,
78
+ exclude,
79
+ include,
80
+ override,
81
+ group: group
82
+ ? ({
83
+ ...group,
84
+ name: (ctx) => {
85
+ if (group.type === 'path') {
86
+ return `${ctx.group.split('/')[1]}`
87
+ }
88
+ return `${camelCase(ctx.group)}Controller`
89
+ },
90
+ } satisfies Group)
91
+ : undefined,
92
+ dateType,
93
+ typed,
94
+ importPath,
95
+ coercion,
96
+ operations,
97
+ inferred,
98
+ guidType,
99
+ mini,
100
+ wrapOutput,
101
+ paramsCasing,
102
+ printer,
78
103
  }
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
- )
104
+ },
105
+ resolvePath(baseName, pathMode, options) {
106
+ if (!resolvePathWarning) {
107
+ this.warn('Do not use resolvePath for pluginZod, use resolverZod.resolvePath instead')
108
+ resolvePathWarning = true
98
109
  }
99
110
 
100
- return path.resolve(root, output.path, baseName)
111
+ return this.plugin.resolver.resolvePath(
112
+ { baseName, pathMode, tag: options?.group?.tag, path: options?.group?.path },
113
+ { root: this.root, output, group: this.plugin.options.group },
114
+ )
101
115
  },
102
116
  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
117
+ if (!resolveNameWarning) {
118
+ this.warn('Do not use resolveName for pluginZod, use resolverZod.default instead')
119
+ resolveNameWarning = true
114
120
  }
115
121
 
116
- return resolvedName
122
+ return this.plugin.resolver.default(name, type)
117
123
  },
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: [],
136
- })
137
- }
138
-
139
- const schemaGenerator = new SchemaGenerator(this.plugin.options, {
140
- fabric: this.fabric,
141
- oas,
142
- pluginManager: this.pluginManager,
143
- events: this.events,
144
- plugin: this.plugin,
145
- contentType,
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
- pluginManager: this.pluginManager,
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)
124
+ async schema(node, options) {
125
+ return mergedGenerator.schema?.call(this, node, options)
126
+ },
127
+ async operation(node, options) {
128
+ return mergedGenerator.operation?.call(this, node, options)
129
+ },
130
+ async operations(nodes, options) {
131
+ return mergedGenerator.operations?.call(this, nodes, options)
132
+ },
133
+ async buildStart() {
134
+ await this.openInStudio({ ast: true })
181
135
  },
182
136
  }
183
137
  })