@kubb/plugin-ts 5.0.0-alpha.20 → 5.0.0-alpha.21

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 (70) hide show
  1. package/dist/Type-B6fo0gSk.js +120 -0
  2. package/dist/Type-B6fo0gSk.js.map +1 -0
  3. package/dist/Type-oFwUfkZv.cjs +131 -0
  4. package/dist/Type-oFwUfkZv.cjs.map +1 -0
  5. package/dist/builderTs-Cd3juc2G.cjs +120 -0
  6. package/dist/builderTs-Cd3juc2G.cjs.map +1 -0
  7. package/dist/builderTs-DausqHpc.js +116 -0
  8. package/dist/builderTs-DausqHpc.js.map +1 -0
  9. package/dist/builders.cjs +3 -0
  10. package/dist/builders.d.ts +8 -0
  11. package/dist/builders.js +2 -0
  12. package/dist/{casing-Cp-jbC_k.js → casing-BJHFg-zZ.js} +1 -1
  13. package/dist/{casing-Cp-jbC_k.js.map → casing-BJHFg-zZ.js.map} +1 -1
  14. package/dist/{casing-D2uQKLWS.cjs → casing-DHfdqpLi.cjs} +2 -39
  15. package/dist/{casing-D2uQKLWS.cjs.map → casing-DHfdqpLi.cjs.map} +1 -1
  16. package/dist/chunk-ByKO4r7w.cjs +38 -0
  17. package/dist/components.cjs +1 -1
  18. package/dist/components.d.ts +1 -2
  19. package/dist/components.js +1 -1
  20. package/dist/generators-ByK18qUn.js +551 -0
  21. package/dist/generators-ByK18qUn.js.map +1 -0
  22. package/dist/{generators-xHWQCNd9.cjs → generators-aSsiTfUO.cjs} +296 -293
  23. package/dist/generators-aSsiTfUO.cjs.map +1 -0
  24. package/dist/generators.cjs +2 -1
  25. package/dist/generators.d.ts +5 -2
  26. package/dist/generators.js +2 -2
  27. package/dist/index.cjs +38 -36
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.d.ts +2 -2
  30. package/dist/index.js +36 -35
  31. package/dist/index.js.map +1 -1
  32. package/dist/{Type-CMC7L-38.js → printerTs-BgZucv4T.js} +18 -130
  33. package/dist/printerTs-BgZucv4T.js.map +1 -0
  34. package/dist/{Type-B70QnSzH.cjs → printerTs-CFXc_LpP.cjs} +40 -133
  35. package/dist/printerTs-CFXc_LpP.cjs.map +1 -0
  36. package/dist/printers.cjs +3 -0
  37. package/dist/printers.d.ts +75 -0
  38. package/dist/printers.js +2 -0
  39. package/dist/{resolvers-DsKabI0F.js → resolverTsLegacy-DLl854-P.js} +8 -7
  40. package/dist/resolverTsLegacy-DLl854-P.js.map +1 -0
  41. package/dist/{resolvers-YIpeP5YD.cjs → resolverTsLegacy-sJ16Iqrl.cjs} +9 -7
  42. package/dist/resolverTsLegacy-sJ16Iqrl.cjs.map +1 -0
  43. package/dist/resolvers.cjs +3 -3
  44. package/dist/resolvers.d.ts +1 -1
  45. package/dist/resolvers.js +1 -1
  46. package/dist/{types-zqLMbIqZ.d.ts → types-BcyuFDn9.d.ts} +26 -22
  47. package/package.json +17 -5
  48. package/src/builders/builderTs.ts +92 -0
  49. package/src/builders/index.ts +1 -0
  50. package/src/components/Type.tsx +4 -6
  51. package/src/factory.ts +0 -32
  52. package/src/generators/index.ts +1 -0
  53. package/src/generators/typeGenerator.tsx +49 -140
  54. package/src/generators/typeGeneratorLegacy.tsx +345 -0
  55. package/src/index.ts +1 -1
  56. package/src/plugin.ts +15 -44
  57. package/src/presets.ts +10 -7
  58. package/src/printers/index.ts +1 -0
  59. package/src/{printer.ts → printers/printerTs.ts} +20 -16
  60. package/src/resolvers/resolverTs.ts +7 -4
  61. package/src/resolvers/resolverTsLegacy.ts +1 -1
  62. package/src/types.ts +39 -21
  63. package/dist/Type-B70QnSzH.cjs.map +0 -1
  64. package/dist/Type-CMC7L-38.js.map +0 -1
  65. package/dist/generators-BFkr7ecU.js +0 -556
  66. package/dist/generators-BFkr7ecU.js.map +0 -1
  67. package/dist/generators-xHWQCNd9.cjs.map +0 -1
  68. package/dist/resolvers-DsKabI0F.js.map +0 -1
  69. package/dist/resolvers-YIpeP5YD.cjs.map +0 -1
  70. package/src/generators/utils.ts +0 -308
@@ -0,0 +1,345 @@
1
+ import path from 'node:path'
2
+ import { pascalCase } from '@internals/utils'
3
+ import { caseParams, composeTransformers, createProperty, createSchema, narrowSchema, schemaTypes, transform } from '@kubb/ast'
4
+ import type { OperationNode, ParameterNode, SchemaNode } from '@kubb/ast/types'
5
+ import { defineGenerator, getMode } from '@kubb/core'
6
+ import { File } from '@kubb/react-fabric'
7
+ import { Type } from '../components/Type.tsx'
8
+ import { ENUM_TYPES_WITH_KEY_SUFFIX } from '../constants.ts'
9
+ import { resolverTsLegacy } from '../resolvers/resolverTsLegacy.ts'
10
+ import type { PluginTs, ResolverTs } from '../types'
11
+
12
+ type BuildGroupedParamsSchemaOptions = {
13
+ params: Array<ParameterNode>
14
+ parentName?: string
15
+ }
16
+
17
+ function buildGroupedParamsSchema({ params, parentName }: BuildGroupedParamsSchemaOptions): SchemaNode {
18
+ return createSchema({
19
+ type: 'object',
20
+ properties: params.map((param) => {
21
+ let schema = param.schema
22
+ if (narrowSchema(schema, 'enum') && !schema.name && parentName) {
23
+ schema = { ...schema, name: pascalCase([parentName, param.name, 'enum'].join(' ')) }
24
+ }
25
+ return createProperty({
26
+ name: param.name,
27
+ required: param.required,
28
+ schema,
29
+ })
30
+ }),
31
+ })
32
+ }
33
+
34
+ type BuildOperationSchemaOptions = {
35
+ node: OperationNode
36
+ resolver: ResolverTs
37
+ }
38
+
39
+ function buildLegacyResponsesSchemaNode({ node, resolver }: BuildOperationSchemaOptions): SchemaNode | null {
40
+ const isGet = node.method.toLowerCase() === 'get'
41
+ const successResponses = node.responses.filter((res) => {
42
+ const code = Number(res.statusCode)
43
+ return !Number.isNaN(code) && code >= 200 && code < 300
44
+ })
45
+ const errorResponses = node.responses.filter((res) => res.statusCode === 'default' || Number(res.statusCode) >= 400)
46
+
47
+ const responseSchema =
48
+ successResponses.length > 0
49
+ ? successResponses.length === 1
50
+ ? createSchema({ type: 'ref', name: resolver.resolveResponseStatusTypedName(node, successResponses[0]!.statusCode) })
51
+ : createSchema({
52
+ type: 'union',
53
+ members: successResponses.map((res) => createSchema({ type: 'ref', name: resolver.resolveResponseStatusTypedName(node, res.statusCode) })),
54
+ })
55
+ : createSchema({ type: 'any' })
56
+
57
+ const errorsSchema =
58
+ errorResponses.length > 0
59
+ ? errorResponses.length === 1
60
+ ? createSchema({ type: 'ref', name: resolver.resolveResponseStatusTypedName(node, errorResponses[0]!.statusCode) })
61
+ : createSchema({
62
+ type: 'union',
63
+ members: errorResponses.map((res) => createSchema({ type: 'ref', name: resolver.resolveResponseStatusTypedName(node, res.statusCode) })),
64
+ })
65
+ : createSchema({ type: 'any' })
66
+
67
+ const properties = [createProperty({ name: 'Response', required: true, schema: responseSchema })]
68
+
69
+ if (!isGet && node.requestBody?.schema) {
70
+ properties.push(
71
+ createProperty({
72
+ name: 'Request',
73
+ required: true,
74
+ schema: createSchema({ type: 'ref', name: resolver.resolveDataTypedName(node) }),
75
+ }),
76
+ )
77
+ }
78
+
79
+ if (node.parameters.some((p) => p.in === 'query') && resolver.resolveQueryParamsTypedName) {
80
+ properties.push(
81
+ createProperty({
82
+ name: 'QueryParams',
83
+ required: true,
84
+ schema: createSchema({ type: 'ref', name: resolver.resolveQueryParamsTypedName(node) }),
85
+ }),
86
+ )
87
+ }
88
+
89
+ if (node.parameters.some((p) => p.in === 'path') && resolver.resolvePathParamsTypedName) {
90
+ properties.push(
91
+ createProperty({
92
+ name: 'PathParams',
93
+ required: true,
94
+ schema: createSchema({ type: 'ref', name: resolver.resolvePathParamsTypedName(node) }),
95
+ }),
96
+ )
97
+ }
98
+
99
+ if (node.parameters.some((p) => p.in === 'header') && resolver.resolveHeaderParamsTypedName) {
100
+ properties.push(
101
+ createProperty({
102
+ name: 'HeaderParams',
103
+ required: true,
104
+ schema: createSchema({ type: 'ref', name: resolver.resolveHeaderParamsTypedName(node) }),
105
+ }),
106
+ )
107
+ }
108
+
109
+ properties.push(createProperty({ name: 'Errors', required: true, schema: errorsSchema }))
110
+
111
+ return createSchema({ type: 'object', properties })
112
+ }
113
+
114
+ function buildLegacyResponseUnionSchemaNode({ node, resolver }: BuildOperationSchemaOptions): SchemaNode {
115
+ const successResponses = node.responses.filter((res) => {
116
+ const code = Number(res.statusCode)
117
+ return !Number.isNaN(code) && code >= 200 && code < 300
118
+ })
119
+
120
+ if (successResponses.length === 0) {
121
+ return createSchema({ type: 'any' })
122
+ }
123
+
124
+ if (successResponses.length === 1) {
125
+ return createSchema({ type: 'ref', name: resolver.resolveResponseStatusTypedName(node, successResponses[0]!.statusCode) })
126
+ }
127
+
128
+ return createSchema({
129
+ type: 'union',
130
+ members: successResponses.map((res) => createSchema({ type: 'ref', name: resolver.resolveResponseStatusTypedName(node, res.statusCode) })),
131
+ })
132
+ }
133
+
134
+ function nameUnnamedEnums(node: SchemaNode, parentName: string): SchemaNode {
135
+ return transform(node, {
136
+ schema(n) {
137
+ const enumNode = narrowSchema(n, 'enum')
138
+ if (enumNode && !enumNode.name) {
139
+ return { ...enumNode, name: pascalCase([parentName, 'enum'].join(' ')) }
140
+ }
141
+ return undefined
142
+ },
143
+ property(p) {
144
+ const enumNode = narrowSchema(p.schema, 'enum')
145
+ if (enumNode && !enumNode.name) {
146
+ return {
147
+ ...p,
148
+ schema: { ...enumNode, name: pascalCase([parentName, p.name, 'enum'].join(' ')) },
149
+ }
150
+ }
151
+ return undefined
152
+ },
153
+ })
154
+ }
155
+
156
+ export const typeGeneratorLegacy = defineGenerator<PluginTs>({
157
+ name: 'typescript-legacy',
158
+ type: 'react',
159
+ Operation({ node, adapter, options, config }) {
160
+ const { enumType, enumKeyCasing, optionalType, arrayType, syntaxType, paramsCasing, group, output, resolver, transformers = [] } = options
161
+
162
+ const root = path.resolve(config.root, config.output.path)
163
+ const mode = getMode(path.resolve(root, output.path))
164
+
165
+ const file = resolver.resolveFile({ name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path }, { root, output, group })
166
+ const params = caseParams(node.parameters, paramsCasing)
167
+
168
+ function renderSchemaType({
169
+ node: schemaNode,
170
+ name,
171
+ typedName,
172
+ description,
173
+ keysToOmit,
174
+ }: {
175
+ node: SchemaNode | null
176
+ name: string
177
+ typedName: string
178
+ description?: string
179
+ keysToOmit?: Array<string>
180
+ }) {
181
+ if (!schemaNode) {
182
+ return null
183
+ }
184
+
185
+ const transformedNode = transform(schemaNode, composeTransformers(...transformers))
186
+
187
+ const imports = adapter.getImports(transformedNode, (schemaName) => ({
188
+ name: resolver.default(schemaName, 'type'),
189
+ path: resolver.resolveFile({ name: schemaName, extname: '.ts' }, { root, output, group }).path,
190
+ }))
191
+
192
+ return (
193
+ <>
194
+ {mode === 'split' &&
195
+ imports.map((imp) => <File.Import key={[name, imp.path, imp.isTypeOnly].join('-')} root={file.path} path={imp.path} name={imp.name} isTypeOnly />)}
196
+ <Type
197
+ name={name}
198
+ typedName={typedName}
199
+ node={transformedNode}
200
+ description={description}
201
+ enumType={enumType}
202
+ enumKeyCasing={enumKeyCasing}
203
+ optionalType={optionalType}
204
+ arrayType={arrayType}
205
+ syntaxType={syntaxType}
206
+ resolver={resolver}
207
+ keysToOmit={keysToOmit}
208
+ />
209
+ </>
210
+ )
211
+ }
212
+
213
+ const pathParams = params.filter((p) => p.in === 'path')
214
+ const queryParams = params.filter((p) => p.in === 'query')
215
+ const headerParams = params.filter((p) => p.in === 'header')
216
+
217
+ const responseTypes = node.responses.map((res) => {
218
+ const responseName = resolver.resolveResponseStatusName(node, res.statusCode)
219
+ const baseResponseName = resolverTsLegacy.resolveResponseStatusName(node, res.statusCode)
220
+
221
+ return renderSchemaType({
222
+ node: res.schema ? nameUnnamedEnums(res.schema, baseResponseName) : res.schema,
223
+ name: responseName,
224
+ typedName: resolver.resolveResponseStatusTypedName(node, res.statusCode),
225
+ description: res.description,
226
+ keysToOmit: res.keysToOmit,
227
+ })
228
+ })
229
+
230
+ const requestType = node.requestBody?.schema
231
+ ? renderSchemaType({
232
+ node: nameUnnamedEnums(node.requestBody.schema, resolverTsLegacy.resolveDataName(node)),
233
+ name: resolver.resolveDataName(node),
234
+ typedName: resolver.resolveDataTypedName(node),
235
+ description: node.requestBody.description ?? node.requestBody.schema.description,
236
+ keysToOmit: node.requestBody.keysToOmit,
237
+ })
238
+ : null
239
+
240
+ const legacyParamTypes = [
241
+ pathParams.length > 0
242
+ ? renderSchemaType({
243
+ node: buildGroupedParamsSchema({ params: pathParams, parentName: resolverTsLegacy.resolvePathParamsName!(node) }),
244
+ name: resolver.resolvePathParamsName!(node),
245
+ typedName: resolver.resolvePathParamsTypedName!(node),
246
+ })
247
+ : null,
248
+ queryParams.length > 0
249
+ ? renderSchemaType({
250
+ node: buildGroupedParamsSchema({ params: queryParams, parentName: resolverTsLegacy.resolveQueryParamsName!(node) }),
251
+ name: resolver.resolveQueryParamsName!(node),
252
+ typedName: resolver.resolveQueryParamsTypedName!(node),
253
+ })
254
+ : null,
255
+ headerParams.length > 0
256
+ ? renderSchemaType({
257
+ node: buildGroupedParamsSchema({ params: headerParams, parentName: resolverTsLegacy.resolveHeaderParamsName!(node) }),
258
+ name: resolver.resolveHeaderParamsName!(node),
259
+ typedName: resolver.resolveHeaderParamsTypedName!(node),
260
+ })
261
+ : null,
262
+ ]
263
+
264
+ const legacyResponsesType = renderSchemaType({
265
+ node: buildLegacyResponsesSchemaNode({ node, resolver }),
266
+ name: resolver.resolveResponsesName(node),
267
+ typedName: resolver.resolveResponsesTypedName(node),
268
+ })
269
+
270
+ const legacyResponseType = renderSchemaType({
271
+ node: buildLegacyResponseUnionSchemaNode({ node, resolver }),
272
+ name: resolver.resolveResponseName(node),
273
+ typedName: resolver.resolveResponseTypedName(node),
274
+ })
275
+
276
+ return (
277
+ <File
278
+ baseName={file.baseName}
279
+ path={file.path}
280
+ meta={file.meta}
281
+ banner={resolver.resolveBanner(adapter.rootNode, { output, config })}
282
+ footer={resolver.resolveFooter(adapter.rootNode, { output, config })}
283
+ >
284
+ {legacyParamTypes}
285
+ {responseTypes}
286
+ {requestType}
287
+ {legacyResponseType}
288
+ {legacyResponsesType}
289
+ </File>
290
+ )
291
+ },
292
+ Schema({ node, adapter, options, config }) {
293
+ const { enumType, enumKeyCasing, syntaxType, optionalType, arrayType, output, group, resolver, transformers = [] } = options
294
+
295
+ const root = path.resolve(config.root, config.output.path)
296
+ const mode = getMode(path.resolve(root, output.path))
297
+
298
+ if (!node.name) {
299
+ return
300
+ }
301
+
302
+ const transformedNode = transform(node, composeTransformers(...transformers))
303
+
304
+ const imports = adapter.getImports(transformedNode, (schemaName) => ({
305
+ name: resolver.default(schemaName, 'type'),
306
+ path: resolver.resolveFile({ name: schemaName, extname: '.ts' }, { root, output, group }).path,
307
+ }))
308
+
309
+ const isEnumSchema = !!narrowSchema(node, schemaTypes.enum)
310
+
311
+ const typedName = ENUM_TYPES_WITH_KEY_SUFFIX.has(enumType) && isEnumSchema ? resolver.resolveEnumKeyTypedName(node) : resolver.resolveTypedName(node.name)
312
+
313
+ const type = {
314
+ name: resolver.resolveName(node.name),
315
+ typedName,
316
+ file: resolver.resolveFile({ name: node.name, extname: '.ts' }, { root, output, group }),
317
+ } as const
318
+
319
+ return (
320
+ <File
321
+ baseName={type.file.baseName}
322
+ path={type.file.path}
323
+ meta={type.file.meta}
324
+ banner={resolver.resolveBanner(adapter.rootNode, { output, config })}
325
+ footer={resolver.resolveFooter(adapter.rootNode, { output, config })}
326
+ >
327
+ {mode === 'split' &&
328
+ imports.map((imp) => (
329
+ <File.Import key={[node.name, imp.path, imp.isTypeOnly].join('-')} root={type.file.path} path={imp.path} name={imp.name} isTypeOnly />
330
+ ))}
331
+ <Type
332
+ name={type.name}
333
+ typedName={type.typedName}
334
+ node={transformedNode}
335
+ enumType={enumType}
336
+ enumKeyCasing={enumKeyCasing}
337
+ optionalType={optionalType}
338
+ arrayType={arrayType}
339
+ syntaxType={syntaxType}
340
+ resolver={resolver}
341
+ />
342
+ </File>
343
+ )
344
+ },
345
+ })
package/src/index.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  export { pluginTs, pluginTsName } from './plugin.ts'
2
- export type { PluginTs, ResolverTs } from './types.ts'
2
+ export type { PluginTs } from './types.ts'
package/src/plugin.ts CHANGED
@@ -1,8 +1,6 @@
1
1
  import path from 'node:path'
2
- import { camelCase } from '@internals/utils'
3
2
  import { walk } from '@kubb/ast'
4
- import { createPlugin, type Group, getBarrelFiles, getMode, renderOperation, renderSchema } from '@kubb/core'
5
- import { typeGenerator } from './generators/index.ts'
3
+ import { createPlugin, getBarrelFiles, renderOperation, renderSchema } from '@kubb/core'
6
4
  import { getPreset } from './presets.ts'
7
5
  import type { PluginTs } from './types.ts'
8
6
 
@@ -21,73 +19,49 @@ export const pluginTs = createPlugin<PluginTs>((options) => {
21
19
  arrayType = 'array',
22
20
  syntaxType = 'type',
23
21
  paramsCasing,
24
- generators = [typeGenerator].filter(Boolean),
25
22
  compatibilityPreset = 'default',
26
- resolvers: userResolvers,
23
+ resolvers: userResolvers = [],
27
24
  transformers: userTransformers = [],
25
+ generators: userGenerators = [],
28
26
  } = options
29
27
 
30
- const { baseResolver, resolver, transformers } = getPreset(compatibilityPreset, {
28
+ const { resolver, transformers, generators } = getPreset(compatibilityPreset, {
31
29
  resolvers: userResolvers,
32
30
  transformers: userTransformers,
31
+ generators: userGenerators,
33
32
  })
34
33
 
35
34
  let resolveNameWarning = false
35
+ let resolvePathWarning = false
36
36
 
37
37
  return {
38
38
  name: pluginTsName,
39
39
  options: {
40
40
  output,
41
41
  optionalType,
42
+ group,
42
43
  arrayType,
43
44
  enumType,
44
45
  enumKeyCasing,
45
46
  syntaxType,
46
- group,
47
- override,
48
47
  paramsCasing,
49
- compatibilityPreset,
50
- baseResolver,
51
48
  resolver,
52
49
  transformers,
53
50
  },
54
51
  resolvePath(baseName, pathMode, options) {
55
- const root = path.resolve(this.config.root, this.config.output.path)
56
- const mode = pathMode ?? getMode(path.resolve(root, output.path))
57
-
58
- if (mode === 'single') {
59
- /**
60
- * when output is a file then we will always append to the same file(output file), see fileManager.addOrAppend
61
- * Other plugins then need to call addOrAppend instead of just add from the fileManager class
62
- */
63
- return path.resolve(root, output.path)
64
- }
65
-
66
- if (group && (options?.group?.path || options?.group?.tag)) {
67
- const groupName: Group['name'] = group?.name
68
- ? group.name
69
- : (ctx) => {
70
- if (group?.type === 'path') {
71
- return `${ctx.group.split('/')[1]}`
72
- }
73
- return `${camelCase(ctx.group)}Controller`
74
- }
75
-
76
- return path.resolve(
77
- root,
78
- output.path,
79
- groupName({
80
- group: group.type === 'path' ? options.group.path! : options.group.tag!,
81
- }),
82
- baseName,
83
- )
52
+ if (!resolvePathWarning) {
53
+ this.driver.events.emit('warn', 'Do not use resolvePath for pluginTs, use resolverTs.resolvePath instead')
54
+ resolvePathWarning = true
84
55
  }
85
56
 
86
- return path.resolve(root, output.path, baseName)
57
+ return resolver.resolvePath(
58
+ { baseName, pathMode, tag: options?.group?.tag, path: options?.group?.path },
59
+ { root: path.resolve(this.config.root, this.config.output.path), output, group },
60
+ )
87
61
  },
88
62
  resolveName(name, type) {
89
63
  if (!resolveNameWarning) {
90
- this.driver.events.emit('warn', 'Do not use resolveName for pluginTs, use resolverTs instead')
64
+ this.driver.events.emit('warn', 'Do not use resolveName for pluginTs, use resolverTs.default instead')
91
65
  resolveNameWarning = true
92
66
  }
93
67
 
@@ -97,7 +71,6 @@ export const pluginTs = createPlugin<PluginTs>((options) => {
97
71
  const { config, fabric, plugin, adapter, rootNode, driver, openInStudio } = this
98
72
 
99
73
  const root = path.resolve(config.root, config.output.path)
100
- const mode = getMode(path.resolve(root, output.path))
101
74
 
102
75
  if (!adapter) {
103
76
  throw new Error('Plugin cannot work without adapter being set')
@@ -124,7 +97,6 @@ export const pluginTs = createPlugin<PluginTs>((options) => {
124
97
  Component: generator.Schema,
125
98
  plugin,
126
99
  driver,
127
- mode,
128
100
  })
129
101
  }
130
102
  })
@@ -148,7 +120,6 @@ export const pluginTs = createPlugin<PluginTs>((options) => {
148
120
  Component: generator.Operation,
149
121
  plugin,
150
122
  driver,
151
- mode,
152
123
  })
153
124
  }
154
125
  })
package/src/presets.ts CHANGED
@@ -1,23 +1,26 @@
1
1
  import type { Visitor } from '@kubb/ast/types'
2
- import { type CompatibilityPreset, definePreset, definePresets, getPreset as getCorePreset } from '@kubb/core'
2
+ import { type CompatibilityPreset, definePreset, definePresets, type Generator, getPreset as getCorePreset } from '@kubb/core'
3
+ import { typeGenerator, typeGeneratorLegacy } from './generators/index.ts'
3
4
  import { resolverTs, resolverTsLegacy } from './resolvers/index.ts'
4
- import type { ResolverTs } from './types.ts'
5
+ import type { PluginTs, ResolverTs } from './types.ts'
5
6
 
6
7
  export const presets = definePresets<ResolverTs>({
7
- default: definePreset('default', { resolvers: [resolverTs] }),
8
- kubbV4: definePreset('kubbV4', { resolvers: [resolverTsLegacy] }),
8
+ default: definePreset('default', { resolvers: [resolverTs], generators: [typeGenerator] }),
9
+ kubbV4: definePreset('kubbV4', { resolvers: [resolverTsLegacy], generators: [typeGeneratorLegacy] }),
9
10
  })
10
11
 
11
12
  type GetPresetOptions = {
12
- resolvers?: Array<ResolverTs>
13
- transformers?: Array<Visitor>
13
+ resolvers: Array<ResolverTs>
14
+ transformers: Array<Visitor>
15
+ generators: Array<Generator<PluginTs>>
14
16
  }
15
17
 
16
- export function getPreset(preset: CompatibilityPreset, { resolvers, transformers }: GetPresetOptions = {}) {
18
+ export function getPreset(preset: CompatibilityPreset, { resolvers, transformers, generators }: GetPresetOptions) {
17
19
  return getCorePreset({
18
20
  preset,
19
21
  presets,
20
22
  resolvers: [resolverTs, ...(resolvers ?? [])],
21
23
  transformers,
24
+ generators,
22
25
  })
23
26
  }
@@ -0,0 +1 @@
1
+ export { printerTs } from './printerTs.ts'
@@ -3,10 +3,11 @@ import { isStringType, narrowSchema, schemaTypes } from '@kubb/ast'
3
3
  import type { ArraySchemaNode, SchemaNode } from '@kubb/ast/types'
4
4
  import type { PrinterFactoryOptions } from '@kubb/core'
5
5
  import { definePrinter } from '@kubb/core'
6
+ import { safePrint } from '@kubb/fabric-core/parsers/typescript'
6
7
  import type ts from 'typescript'
7
- import { ENUM_TYPES_WITH_KEY_SUFFIX, OPTIONAL_ADDS_QUESTION_TOKEN, OPTIONAL_ADDS_UNDEFINED } from './constants.ts'
8
- import * as factory from './factory.ts'
9
- import type { PluginTs, ResolverTs } from './types.ts'
8
+ import { ENUM_TYPES_WITH_KEY_SUFFIX, OPTIONAL_ADDS_QUESTION_TOKEN, OPTIONAL_ADDS_UNDEFINED } from '../constants.ts'
9
+ import * as factory from '../factory.ts'
10
+ import type { PluginTs, ResolverTs } from '../types.ts'
10
11
 
11
12
  type TsOptions = {
12
13
  /**
@@ -50,7 +51,7 @@ type TsOptions = {
50
51
  /**
51
52
  * TypeScript printer factory options: maps `SchemaNode` → `ts.TypeNode` (raw) or `ts.Node` (full declaration).
52
53
  */
53
- type TsPrinter = PrinterFactoryOptions<'typescript', TsOptions, ts.TypeNode, ts.Node>
54
+ type TsPrinter = PrinterFactoryOptions<'typescript', TsOptions, ts.TypeNode, string>
54
55
 
55
56
  /**
56
57
  * Converts a primitive const value to a TypeScript literal type node.
@@ -287,32 +288,33 @@ export const printerTs = definePrinter<TsPrinter>((options) => {
287
288
  })
288
289
  }
289
290
 
290
- return this.print(m)
291
+ return this.transform(m)
291
292
  })
292
293
  .filter(Boolean)
293
294
 
294
295
  return factory.createUnionDeclaration({ withParentheses: true, nodes: memberNodes }) ?? undefined
295
296
  }
296
297
 
297
- return factory.createUnionDeclaration({ withParentheses: true, nodes: buildMemberNodes(members, this.print) }) ?? undefined
298
+ return factory.createUnionDeclaration({ withParentheses: true, nodes: buildMemberNodes(members, this.transform) }) ?? undefined
298
299
  },
299
300
  intersection(node) {
300
- return factory.createIntersectionDeclaration({ withParentheses: true, nodes: buildMemberNodes(node.members, this.print) }) ?? undefined
301
+ return factory.createIntersectionDeclaration({ withParentheses: true, nodes: buildMemberNodes(node.members, this.transform) }) ?? undefined
301
302
  },
302
303
  array(node) {
303
- const itemNodes = (node.items ?? []).map((item) => this.print(item)).filter(Boolean)
304
+ const itemNodes = (node.items ?? []).map((item) => this.transform(item)).filter(Boolean)
304
305
 
305
306
  return factory.createArrayDeclaration({ nodes: itemNodes, arrayType: this.options.arrayType }) ?? undefined
306
307
  },
307
308
  tuple(node) {
308
- return buildTupleNode(node, this.print)
309
+ return buildTupleNode(node, this.transform)
309
310
  },
310
311
  object(node) {
311
- const { print, options } = this
312
+ const { transform, options } = this
313
+
312
314
  const addsQuestionToken = OPTIONAL_ADDS_QUESTION_TOKEN.has(options.optionalType)
313
315
 
314
316
  const propertyNodes: Array<ts.TypeElement> = node.properties.map((prop) => {
315
- const baseType = print(prop.schema) ?? factory.keywordTypeNodes.unknown
317
+ const baseType = transform(prop.schema) ?? factory.keywordTypeNodes.unknown
316
318
  const type = buildPropertyType(prop.schema, baseType, options.optionalType)
317
319
 
318
320
  const propertyNode = factory.createPropertySignature({
@@ -325,7 +327,7 @@ export const printerTs = definePrinter<TsPrinter>((options) => {
325
327
  return factory.appendJSDocToNode({ node: propertyNode, comments: buildPropertyJSDocComments(prop.schema) })
326
328
  })
327
329
 
328
- const allElements = [...propertyNodes, ...buildIndexSignatures(node, propertyNodes.length, print)]
330
+ const allElements = [...propertyNodes, ...buildIndexSignatures(node, propertyNodes.length, transform)]
329
331
 
330
332
  if (!allElements.length) {
331
333
  return factory.keywordTypeNodes.object
@@ -335,10 +337,10 @@ export const printerTs = definePrinter<TsPrinter>((options) => {
335
337
  },
336
338
  },
337
339
  print(node) {
338
- let type = this.print(node)
340
+ let type = this.transform(node)
339
341
 
340
342
  if (!type) {
341
- return undefined
343
+ return null
342
344
  }
343
345
 
344
346
  // Apply top-level nullable / optional union modifiers.
@@ -353,12 +355,12 @@ export const printerTs = definePrinter<TsPrinter>((options) => {
353
355
  // Without typeName, return the type node as-is (no declaration wrapping).
354
356
  const { typeName, syntaxType = 'type', description, keysToOmit } = this.options
355
357
  if (!typeName) {
356
- return type
358
+ return safePrint(type)
357
359
  }
358
360
 
359
361
  const useTypeGeneration = syntaxType === 'type' || type.kind === factory.syntaxKind.union || !!keysToOmit?.length
360
362
 
361
- return factory.createTypeDeclaration({
363
+ const typeNode = factory.createTypeDeclaration({
362
364
  name: typeName,
363
365
  isExportable: true,
364
366
  type: keysToOmit?.length
@@ -380,6 +382,8 @@ export const printerTs = definePrinter<TsPrinter>((options) => {
380
382
  node?.example ? `@example ${node.example}` : undefined,
381
383
  ],
382
384
  })
385
+
386
+ return safePrint(typeNode)
383
387
  },
384
388
  }
385
389
  })
@@ -27,6 +27,7 @@ function resolveName(name: string, type?: 'file' | 'function' | 'type' | 'const'
27
27
  export const resolverTs = defineResolver<PluginTs>(() => {
28
28
  return {
29
29
  name: 'default',
30
+ pluginName: 'plugin-ts',
30
31
  default(name, type) {
31
32
  return resolveName(name, type)
32
33
  },
@@ -86,11 +87,13 @@ export const resolverTs = defineResolver<PluginTs>(() => {
86
87
  "resolvePathParamsTypedName is only available with compatibilityPreset: 'kubbV4'. Use resolveParamTypedName per individual parameter instead.",
87
88
  )
88
89
  },
89
- resolveQueryParamsName(node) {
90
- return this.resolveName(`${node.operationId} QueryParams`)
90
+ resolveQueryParamsName(_node) {
91
+ throw new Error("resolveQueryParamsName is only available with compatibilityPreset: 'kubbV4'. Use resolveParamName per individual parameter instead.")
91
92
  },
92
- resolveQueryParamsTypedName(node) {
93
- return this.resolveTypedName(`${node.operationId} QueryParams`)
93
+ resolveQueryParamsTypedName(_node) {
94
+ throw new Error(
95
+ "resolveQueryParamsTypedName is only available with compatibilityPreset: 'kubbV4'. Use resolveParamTypedName per individual parameter instead.",
96
+ )
94
97
  },
95
98
  resolveHeaderParamsName(_node) {
96
99
  throw new Error("resolveHeaderParamsName is only available with compatibilityPreset: 'kubbV4'. Use resolveParamName per individual parameter instead.")
@@ -28,7 +28,7 @@ import { resolverTs } from './resolverTs.ts'
28
28
  export const resolverTsLegacy = defineResolver<PluginTs>(() => {
29
29
  return {
30
30
  ...resolverTs,
31
- name: 'legacy',
31
+ pluginName: 'plugin-ts',
32
32
  resolveResponseStatusName(node, statusCode) {
33
33
  if (statusCode === 'default') {
34
34
  return this.resolveName(`${node.operationId} Error`)