@kubb/plugin-ts 5.0.0-alpha.1 → 5.0.0-alpha.11

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 (41) hide show
  1. package/dist/{components-LmqJfxMv.js → components-CRu8IKY3.js} +19 -11
  2. package/dist/components-CRu8IKY3.js.map +1 -0
  3. package/dist/{components-9wydyqUx.cjs → components-DeNDKlzf.cjs} +144 -10
  4. package/dist/components-DeNDKlzf.cjs.map +1 -0
  5. package/dist/components.cjs +1 -1
  6. package/dist/components.d.ts +1 -3
  7. package/dist/components.js +1 -1
  8. package/dist/generators.cjs +3 -2
  9. package/dist/generators.d.ts +60 -48
  10. package/dist/generators.js +2 -2
  11. package/dist/index.cjs +2 -1
  12. package/dist/index.d.ts +41 -2
  13. package/dist/index.js +2 -2
  14. package/dist/plugin-CJ29AwE2.cjs +1320 -0
  15. package/dist/plugin-CJ29AwE2.cjs.map +1 -0
  16. package/dist/plugin-D60XNJSD.js +1267 -0
  17. package/dist/plugin-D60XNJSD.js.map +1 -0
  18. package/dist/types-mSXmB8WU.d.ts +298 -0
  19. package/package.json +8 -8
  20. package/src/components/Type.tsx +0 -3
  21. package/src/components/v2/Enum.tsx +67 -0
  22. package/src/components/v2/Type.tsx +59 -0
  23. package/src/constants.ts +29 -0
  24. package/src/factory.ts +14 -16
  25. package/src/generators/index.ts +1 -0
  26. package/src/generators/typeGenerator.tsx +46 -48
  27. package/src/generators/v2/typeGenerator.tsx +167 -0
  28. package/src/generators/v2/utils.ts +140 -0
  29. package/src/index.ts +1 -0
  30. package/src/parser.ts +1 -8
  31. package/src/plugin.ts +64 -19
  32. package/src/printer.ts +238 -91
  33. package/src/resolverTs.ts +77 -0
  34. package/src/types.ts +144 -15
  35. package/dist/components-9wydyqUx.cjs.map +0 -1
  36. package/dist/components-LmqJfxMv.js.map +0 -1
  37. package/dist/plugin-BHE4J4aP.cjs +0 -508
  38. package/dist/plugin-BHE4J4aP.cjs.map +0 -1
  39. package/dist/plugin-DnKRpgGK.js +0 -476
  40. package/dist/plugin-DnKRpgGK.js.map +0 -1
  41. package/dist/types-BpeKGgCn.d.ts +0 -170
package/src/printer.ts CHANGED
@@ -1,27 +1,57 @@
1
- import { jsStringEscape, stringify } from '@internals/utils'
1
+ import { jsStringEscape, pascalCase, stringify } from '@internals/utils'
2
+ import { isPlainStringType } from '@kubb/ast'
2
3
  import type { ArraySchemaNode, SchemaNode } from '@kubb/ast/types'
3
4
  import type { PrinterFactoryOptions } from '@kubb/core'
4
5
  import { definePrinter } from '@kubb/core'
5
6
  import type ts from 'typescript'
7
+ import { ENUM_TYPES_WITH_KEY_SUFFIX, OPTIONAL_ADDS_QUESTION_TOKEN, OPTIONAL_ADDS_UNDEFINED } from './constants.ts'
6
8
  import * as factory from './factory.ts'
9
+ import type { PluginTs } from './types.ts'
7
10
 
8
11
  type TsOptions = {
9
12
  /**
10
13
  * @default `'questionToken'`
11
14
  */
12
- optionalType: 'questionToken' | 'undefined' | 'questionTokenAndUndefined'
15
+ optionalType: PluginTs['resolvedOptions']['optionalType']
13
16
  /**
14
17
  * @default `'array'`
15
18
  */
16
- arrayType: 'array' | 'generic'
19
+ arrayType: PluginTs['resolvedOptions']['arrayType']
17
20
  /**
18
21
  * @default `'inlineLiteral'`
19
22
  */
20
- enumType: 'enum' | 'asConst' | 'asPascalConst' | 'constEnum' | 'literal' | 'inlineLiteral'
23
+ enumType: PluginTs['resolvedOptions']['enumType']
24
+ /**
25
+ * Controls whether a `type` alias or `interface` declaration is emitted.
26
+ * @default `'type'`
27
+ */
28
+ syntaxType?: PluginTs['resolvedOptions']['syntaxType']
29
+ /**
30
+ * When set, `printer.print(node)` produces a full `type Name = …` declaration.
31
+ * When omitted, `printer.print(node)` returns the raw type node.
32
+ */
33
+ typeName?: string
34
+
35
+ /**
36
+ * JSDoc `@description` comment added to the generated type or interface declaration.
37
+ */
38
+ description?: string
39
+ /**
40
+ * Property keys to exclude from the generated type via `Omit<Type, Keys>`.
41
+ * Forces type-alias syntax even when `syntaxType` is `'interface'`.
42
+ */
43
+ keysToOmit?: Array<string>
21
44
  }
22
45
 
23
- type TsPrinter = PrinterFactoryOptions<'typescript', TsOptions, ts.TypeNode>
46
+ /**
47
+ * TypeScript printer factory options: maps `SchemaNode` → `ts.TypeNode` (raw) or `ts.Node` (full declaration).
48
+ */
49
+ type TsPrinter = PrinterFactoryOptions<'typescript', TsOptions, ts.TypeNode, ts.Node>
24
50
 
51
+ /**
52
+ * Converts a primitive const value to a TypeScript literal type node.
53
+ * Handles negative numbers via a prefix unary expression.
54
+ */
25
55
  function constToTypeNode(value: string | number | boolean, format: 'string' | 'number' | 'boolean'): ts.TypeNode | undefined {
26
56
  if (format === 'boolean') {
27
57
  return factory.createLiteralTypeNode(value === true ? factory.createTrue() : factory.createFalse())
@@ -35,16 +65,26 @@ function constToTypeNode(value: string | number | boolean, format: 'string' | 'n
35
65
  return factory.createLiteralTypeNode(factory.createStringLiteral(String(value)))
36
66
  }
37
67
 
68
+ /**
69
+ * Returns a `Date` reference type node when `representation` is `'date'`, otherwise falls back to `string`.
70
+ */
38
71
  function dateOrStringNode(node: { representation?: string }): ts.TypeNode {
39
72
  return node.representation === 'date' ? factory.createTypeReferenceNode(factory.createIdentifier('Date')) : factory.keywordTypeNodes.string
40
73
  }
41
74
 
75
+ /**
76
+ * Maps an array of `SchemaNode`s through the printer, filtering out `null` and `undefined` results.
77
+ */
42
78
  function buildMemberNodes(members: Array<SchemaNode> | undefined, print: (node: SchemaNode) => ts.TypeNode | null | undefined): Array<ts.TypeNode> {
43
- return (members ?? []).map(print).filter(Boolean) as Array<ts.TypeNode>
79
+ return (members ?? []).map(print).filter(Boolean)
44
80
  }
45
81
 
82
+ /**
83
+ * Builds a TypeScript tuple type node from an array schema's `items`,
84
+ * applying min/max slice and optional/rest element rules.
85
+ */
46
86
  function buildTupleNode(node: ArraySchemaNode, print: (node: SchemaNode) => ts.TypeNode | null | undefined): ts.TypeNode | undefined {
47
- let items = (node.items ?? []).map(print).filter(Boolean) as Array<ts.TypeNode>
87
+ let items = (node.items ?? []).map(print).filter(Boolean)
48
88
 
49
89
  const restNode = node.rest ? (print(node.rest) ?? undefined) : undefined
50
90
  const { min, max } = node
@@ -67,25 +107,31 @@ function buildTupleNode(node: ArraySchemaNode, print: (node: SchemaNode) => ts.T
67
107
  return factory.createTupleTypeNode(items)
68
108
  }
69
109
 
110
+ /**
111
+ * Applies `nullable` and optional/nullish `| undefined` union modifiers to a property's resolved base type.
112
+ */
70
113
  function buildPropertyType(schema: SchemaNode, baseType: ts.TypeNode, optionalType: TsOptions['optionalType']): ts.TypeNode {
71
- const addsUndefined = ['undefined', 'questionTokenAndUndefined'].includes(optionalType)
114
+ const addsUndefined = OPTIONAL_ADDS_UNDEFINED.has(optionalType)
72
115
 
73
116
  let type = baseType
74
117
 
75
118
  if (schema.nullable) {
76
- type = factory.createUnionDeclaration({ nodes: [type, factory.keywordTypeNodes.null] }) as ts.TypeNode
119
+ type = factory.createUnionDeclaration({ nodes: [type, factory.keywordTypeNodes.null] })
77
120
  }
78
121
 
79
122
  if ((schema.nullish || schema.optional) && addsUndefined) {
80
- type = factory.createUnionDeclaration({ nodes: [type, factory.keywordTypeNodes.undefined] }) as ts.TypeNode
123
+ type = factory.createUnionDeclaration({ nodes: [type, factory.keywordTypeNodes.undefined] })
81
124
  }
82
125
 
83
126
  return type
84
127
  }
85
128
 
129
+ /**
130
+ * Collects JSDoc annotation strings (description, deprecated, min/max, pattern, default, example, type) for a schema node.
131
+ */
86
132
  function buildPropertyJSDocComments(schema: SchemaNode): Array<string | undefined> {
87
133
  return [
88
- 'description' in schema && schema.description ? `@description ${jsStringEscape(schema.description as string)}` : undefined,
134
+ 'description' in schema && schema.description ? `@description ${jsStringEscape(schema.description)}` : undefined,
89
135
  'deprecated' in schema && schema.deprecated ? '@deprecated' : undefined,
90
136
  'min' in schema && schema.min !== undefined ? `@minLength ${schema.min}` : undefined,
91
137
  'max' in schema && schema.max !== undefined ? `@maxLength ${schema.max}` : undefined,
@@ -100,6 +146,9 @@ function buildPropertyJSDocComments(schema: SchemaNode): Array<string | undefine
100
146
  ]
101
147
  }
102
148
 
149
+ /**
150
+ * Creates TypeScript index signatures for `additionalProperties` and `patternProperties` on an object schema node.
151
+ */
103
152
  function buildIndexSignatures(
104
153
  node: { additionalProperties?: SchemaNode | boolean; patternProperties?: Record<string, SchemaNode> },
105
154
  propertyCount: number,
@@ -108,7 +157,8 @@ function buildIndexSignatures(
108
157
  const elements: Array<ts.TypeElement> = []
109
158
 
110
159
  if (node.additionalProperties && node.additionalProperties !== true) {
111
- const additionalType = (print(node.additionalProperties as SchemaNode) ?? factory.keywordTypeNodes.unknown) as ts.TypeNode
160
+ const additionalType = print(node.additionalProperties) ?? factory.keywordTypeNodes.unknown
161
+
112
162
  elements.push(factory.createIndexSignature(propertyCount > 0 ? factory.keywordTypeNodes.unknown : additionalType))
113
163
  } else if (node.additionalProperties === true) {
114
164
  elements.push(factory.createIndexSignature(factory.keywordTypeNodes.unknown))
@@ -117,9 +167,10 @@ function buildIndexSignatures(
117
167
  if (node.patternProperties) {
118
168
  const first = Object.values(node.patternProperties)[0]
119
169
  if (first) {
120
- let patternType = (print(first) ?? factory.keywordTypeNodes.unknown) as ts.TypeNode
170
+ let patternType = print(first) ?? factory.keywordTypeNodes.unknown
171
+
121
172
  if (first.nullable) {
122
- patternType = factory.createUnionDeclaration({ nodes: [patternType, factory.keywordTypeNodes.null] }) as ts.TypeNode
173
+ patternType = factory.createUnionDeclaration({ nodes: [patternType, factory.keywordTypeNodes.null] })
123
174
  }
124
175
  elements.push(factory.createIndexSignature(patternType))
125
176
  }
@@ -129,93 +180,189 @@ function buildIndexSignatures(
129
180
  }
130
181
 
131
182
  /**
132
- * Converts a `SchemaNode` AST node into a TypeScript `ts.TypeNode`.
183
+ * TypeScript type printer built with `definePrinter`.
184
+ *
185
+ * Converts a `SchemaNode` AST node into a TypeScript AST node:
186
+ * - **`printer.print(node)`** — when `options.typeName` is set, returns a full
187
+ * `type Name = …` or `interface Name { … }` declaration (`ts.Node`).
188
+ * Without `typeName`, returns the raw `ts.TypeNode` for the schema.
189
+ *
190
+ * Dispatches on `node.type` to the appropriate handler in `nodes`. Options are closed
191
+ * over per printer instance, so each call to `printerTs(options)` produces an independent printer.
192
+ *
193
+ * @example Raw type node (no `typeName`)
194
+ * ```ts
195
+ * const printer = printerTs({ optionalType: 'questionToken', arrayType: 'array', enumType: 'inlineLiteral' })
196
+ * const typeNode = printer.print(schemaNode) // ts.TypeNode
197
+ * ```
133
198
  *
134
- * Built on `definePrinter` dispatches on `node.type`, with options closed over
135
- * per printer instance. Produces the same `ts.TypeNode` output as the keyword-based
136
- * `parse` in `parser.ts`.
199
+ * @example Full declaration (with `typeName`)
200
+ * ```ts
201
+ * const printer = printerTs({ optionalType: 'questionToken', arrayType: 'array', enumType: 'inlineLiteral', typeName: 'MyType' })
202
+ * const declaration = printer.print(schemaNode) // ts.TypeAliasDeclaration | ts.InterfaceDeclaration
203
+ * ```
137
204
  */
138
- export const printerTs = definePrinter<TsPrinter>((options) => ({
139
- name: 'typescript',
140
- options,
141
- nodes: {
142
- any: () => factory.keywordTypeNodes.any,
143
- unknown: () => factory.keywordTypeNodes.unknown,
144
- void: () => factory.keywordTypeNodes.void,
145
- boolean: () => factory.keywordTypeNodes.boolean,
146
- null: () => factory.keywordTypeNodes.null,
147
- blob: () => factory.createTypeReferenceNode('Blob', []),
148
- string: () => factory.keywordTypeNodes.string,
149
- uuid: () => factory.keywordTypeNodes.string,
150
- email: () => factory.keywordTypeNodes.string,
151
- url: () => factory.keywordTypeNodes.string,
152
- datetime: () => factory.keywordTypeNodes.string,
153
- number: () => factory.keywordTypeNodes.number,
154
- integer: () => factory.keywordTypeNodes.number,
155
- bigint: () => factory.keywordTypeNodes.bigint,
156
- date: (node) => dateOrStringNode(node),
157
- time: (node) => dateOrStringNode(node),
158
- ref(node) {
159
- if (!node.name) {
160
- return undefined
161
- }
162
- return factory.createTypeReferenceNode(node.name, undefined)
163
- },
164
- enum(node) {
165
- const values = node.namedEnumValues?.map((v) => v.value) ?? node.enumValues ?? []
166
-
167
- if (this.options.enumType === 'inlineLiteral' || !node.name) {
168
- const literalNodes = values
169
- .filter((v): v is string | number | boolean => v !== null)
170
- .map((value) => constToTypeNode(value, typeof value === 'number' ? 'number' : typeof value === 'boolean' ? 'boolean' : 'string'))
171
- .filter(Boolean) as Array<ts.TypeNode>
205
+ export const printerTs = definePrinter<TsPrinter>((options) => {
206
+ const addsUndefined = OPTIONAL_ADDS_UNDEFINED.has(options.optionalType)
207
+
208
+ return {
209
+ name: 'typescript',
210
+ options,
211
+ nodes: {
212
+ any: () => factory.keywordTypeNodes.any,
213
+ unknown: () => factory.keywordTypeNodes.unknown,
214
+ void: () => factory.keywordTypeNodes.void,
215
+ never: () => factory.keywordTypeNodes.never,
216
+ boolean: () => factory.keywordTypeNodes.boolean,
217
+ null: () => factory.keywordTypeNodes.null,
218
+ blob: () => factory.createTypeReferenceNode('Blob', []),
219
+ string: () => factory.keywordTypeNodes.string,
220
+ uuid: () => factory.keywordTypeNodes.string,
221
+ email: () => factory.keywordTypeNodes.string,
222
+ url: (node) => {
223
+ if (node.path) {
224
+ return factory.createUrlTemplateType(node.path)
225
+ }
226
+ return factory.keywordTypeNodes.string
227
+ },
228
+ datetime: () => factory.keywordTypeNodes.string,
229
+ number: () => factory.keywordTypeNodes.number,
230
+ integer: () => factory.keywordTypeNodes.number,
231
+ bigint: () => factory.keywordTypeNodes.bigint,
232
+ date: dateOrStringNode,
233
+ time: dateOrStringNode,
234
+ ref(node) {
235
+ if (!node.name) {
236
+ return undefined
237
+ }
238
+ return factory.createTypeReferenceNode(node.name, undefined)
239
+ },
240
+ enum(node) {
241
+ const values = node.namedEnumValues?.map((v) => v.value) ?? node.enumValues ?? []
242
+
243
+ if (this.options.enumType === 'inlineLiteral' || !node.name) {
244
+ const literalNodes = values
245
+ .filter((v): v is string | number | boolean => v !== null)
246
+ .map((value) => constToTypeNode(value, typeof value as 'string' | 'number' | 'boolean'))
247
+ .filter(Boolean)
248
+
249
+ return factory.createUnionDeclaration({ withParentheses: true, nodes: literalNodes }) ?? undefined
250
+ }
251
+
252
+ const resolvedName = pascalCase(node.name)
253
+ const typeName = ENUM_TYPES_WITH_KEY_SUFFIX.has(this.options.enumType) ? `${resolvedName}Key` : resolvedName
254
+
255
+ return factory.createTypeReferenceNode(typeName, undefined)
256
+ },
257
+ union(node) {
258
+ const members = node.members ?? []
259
+
260
+ const hasStringLiteral = members.some((m) => m.type === 'enum' && (m.enumType === 'string' || m.primitive === 'string'))
261
+ const hasPlainString = members.some((m) => isPlainStringType(m))
262
+
263
+ if (hasStringLiteral && hasPlainString) {
264
+ const memberNodes = members
265
+ .map((m) => {
266
+ if (isPlainStringType(m)) {
267
+ return factory.createIntersectionDeclaration({
268
+ nodes: [factory.keywordTypeNodes.string, factory.createTypeLiteralNode([])],
269
+ withParentheses: true,
270
+ })
271
+ }
272
+
273
+ return this.print(m)
274
+ })
275
+ .filter(Boolean)
276
+
277
+ return factory.createUnionDeclaration({ withParentheses: true, nodes: memberNodes }) ?? undefined
278
+ }
279
+
280
+ return factory.createUnionDeclaration({ withParentheses: true, nodes: buildMemberNodes(members, this.print) }) ?? undefined
281
+ },
282
+ intersection(node) {
283
+ return factory.createIntersectionDeclaration({ withParentheses: true, nodes: buildMemberNodes(node.members, this.print) }) ?? undefined
284
+ },
285
+ array(node) {
286
+ const itemNodes = (node.items ?? []).map((item) => this.print(item)).filter(Boolean)
287
+
288
+ return factory.createArrayDeclaration({ nodes: itemNodes, arrayType: this.options.arrayType }) ?? undefined
289
+ },
290
+ tuple(node) {
291
+ return buildTupleNode(node, this.print)
292
+ },
293
+ object(node) {
294
+ const { print, options } = this
295
+ const addsQuestionToken = OPTIONAL_ADDS_QUESTION_TOKEN.has(options.optionalType)
296
+
297
+ const propertyNodes: Array<ts.TypeElement> = node.properties.map((prop) => {
298
+ const baseType = print(prop.schema) ?? factory.keywordTypeNodes.unknown
299
+ const type = buildPropertyType(prop.schema, baseType, options.optionalType)
300
+
301
+ const propertyNode = factory.createPropertySignature({
302
+ questionToken: prop.schema.optional || prop.schema.nullish ? addsQuestionToken : false,
303
+ name: prop.name,
304
+ type,
305
+ readOnly: prop.schema.readOnly,
306
+ })
307
+
308
+ return factory.appendJSDocToNode({ node: propertyNode, comments: buildPropertyJSDocComments(prop.schema) })
309
+ })
172
310
 
173
- return factory.createUnionDeclaration({ withParentheses: true, nodes: literalNodes }) ?? undefined
174
- }
311
+ const allElements = [...propertyNodes, ...buildIndexSignatures(node, propertyNodes.length, print)]
175
312
 
176
- const typeName = ['asConst', 'asPascalConst'].includes(this.options.enumType) ? `${node.name}Key` : node.name
313
+ if (!allElements.length) {
314
+ return factory.keywordTypeNodes.object
315
+ }
177
316
 
178
- return factory.createTypeReferenceNode(typeName, undefined)
317
+ return factory.createTypeLiteralNode(allElements)
318
+ },
179
319
  },
180
- union(node) {
181
- return factory.createUnionDeclaration({ withParentheses: true, nodes: buildMemberNodes(node.members, this.print) }) ?? undefined
182
- },
183
- intersection(node) {
184
- return factory.createIntersectionDeclaration({ withParentheses: true, nodes: buildMemberNodes(node.members, this.print) }) ?? undefined
185
- },
186
- array(node) {
187
- const itemNodes = (node.items ?? []).map((item) => this.print(item)).filter(Boolean) as Array<ts.TypeNode>
320
+ print(node) {
321
+ let type = this.print(node)
188
322
 
189
- return factory.createArrayDeclaration({ nodes: itemNodes, arrayType: this.options.arrayType }) ?? undefined
190
- },
191
- tuple(node) {
192
- return buildTupleNode(node, this.print)
193
- },
194
- object(node) {
195
- const addsQuestionToken = ['questionToken', 'questionTokenAndUndefined'].includes(this.options.optionalType)
196
- const { print } = this
197
-
198
- const propertyNodes: Array<ts.TypeElement> = node.properties.map((prop) => {
199
- const baseType = (print(prop.schema) ?? factory.keywordTypeNodes.unknown) as ts.TypeNode
200
- const type = buildPropertyType(prop.schema, baseType, this.options.optionalType)
201
-
202
- const propertyNode = factory.createPropertySignature({
203
- questionToken: prop.schema.optional || prop.schema.nullish ? addsQuestionToken : false,
204
- name: prop.name,
205
- type,
206
- readOnly: prop.schema.readOnly,
207
- })
323
+ if (!type) {
324
+ return undefined
325
+ }
208
326
 
209
- return factory.appendJSDocToNode({ node: propertyNode, comments: buildPropertyJSDocComments(prop.schema) })
210
- })
327
+ // Apply top-level nullable / optional union modifiers.
328
+ if (node.nullable) {
329
+ type = factory.createUnionDeclaration({ nodes: [type, factory.keywordTypeNodes.null] })
330
+ }
211
331
 
212
- const allElements = [...propertyNodes, ...buildIndexSignatures(node, propertyNodes.length, print)]
332
+ if ((node.nullish || node.optional) && addsUndefined) {
333
+ type = factory.createUnionDeclaration({ nodes: [type, factory.keywordTypeNodes.undefined] })
334
+ }
213
335
 
214
- if (!allElements.length) {
215
- return factory.keywordTypeNodes.object
336
+ // Without typeName, return the type node as-is (no declaration wrapping).
337
+ const { typeName, syntaxType = 'type', description, keysToOmit } = this.options
338
+ if (!typeName) {
339
+ return type
216
340
  }
217
341
 
218
- return factory.createTypeLiteralNode(allElements)
342
+ const useTypeGeneration = syntaxType === 'type' || type.kind === factory.syntaxKind.union || !!keysToOmit?.length
343
+
344
+ return factory.createTypeDeclaration({
345
+ name: typeName,
346
+ isExportable: true,
347
+ type: keysToOmit?.length
348
+ ? factory.createOmitDeclaration({
349
+ keys: keysToOmit,
350
+ type,
351
+ nonNullable: true,
352
+ })
353
+ : type,
354
+ syntax: useTypeGeneration ? 'type' : 'interface',
355
+ comments: [
356
+ node?.title ? jsStringEscape(node.title) : undefined,
357
+ description ? `@description ${jsStringEscape(description)}` : undefined,
358
+ node?.deprecated ? '@deprecated' : undefined,
359
+ node && 'min' in node && node.min !== undefined ? `@minLength ${node.min}` : undefined,
360
+ node && 'max' in node && node.max !== undefined ? `@maxLength ${node.max}` : undefined,
361
+ node && 'pattern' in node && node.pattern ? `@pattern ${node.pattern}` : undefined,
362
+ node?.default ? `@default ${node.default}` : undefined,
363
+ node?.example ? `@example ${node.example}` : undefined,
364
+ ],
365
+ })
219
366
  },
220
- },
221
- }))
367
+ }
368
+ })
@@ -0,0 +1,77 @@
1
+ import { pascalCase } from '@internals/utils'
2
+ import { defineResolver } from '@kubb/core'
3
+ import type { PluginTs } from './types.ts'
4
+
5
+ /**
6
+ * Resolver for `@kubb/plugin-ts` that provides the default naming and path-resolution
7
+ * helpers used by the plugin. Import this in other plugins to resolve the exact names and
8
+ * paths that `plugin-ts` generates without hardcoding the conventions.
9
+ *
10
+ * The `default` method is automatically injected by `defineResolver` — it uses `camelCase`
11
+ * for identifiers/files and `pascalCase` for type names.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * import { resolver } from '@kubb/plugin-ts'
16
+ *
17
+ * resolver.default('list pets', 'type') // → 'ListPets'
18
+ * resolver.resolveName('list pets status 200') // → 'listPetsStatus200'
19
+ * resolver.resolveTypedName('list pets status 200') // → 'ListPetsStatus200'
20
+ * resolver.resolvePathName('list pets', 'file') // → 'listPets'
21
+ * ```
22
+ */
23
+ export const resolverTs = defineResolver<PluginTs>(() => {
24
+ return {
25
+ default(name, type) {
26
+ return pascalCase(name, { isFile: type === 'file' })
27
+ },
28
+ resolveName(name) {
29
+ return this.default(name, 'function')
30
+ },
31
+ resolveTypedName(name) {
32
+ return this.default(name, 'type')
33
+ },
34
+ resolvePathName(name, type) {
35
+ return this.default(name, type)
36
+ },
37
+ resolveParamName(node, param) {
38
+ return this.resolveName(`${node.operationId} ${this.default(param.in)} ${param.name}`)
39
+ },
40
+ resolveParamTypedName(node, param) {
41
+ return this.resolveTypedName(`${node.operationId} ${this.default(param.in)} ${param.name}`)
42
+ },
43
+ resolveResponseStatusName(node, statusCode) {
44
+ return this.resolveName(`${node.operationId} Status ${statusCode}`)
45
+ },
46
+ resolveResponseStatusTypedName(node, statusCode) {
47
+ return this.resolveTypedName(`${node.operationId} Status ${statusCode}`)
48
+ },
49
+ resolveDataName(node) {
50
+ return this.resolveName(`${node.operationId} Data`)
51
+ },
52
+ resolveDataTypedName(node) {
53
+ return this.resolveTypedName(`${node.operationId} Data`)
54
+ },
55
+ resolveRequestConfigName(node) {
56
+ return this.resolveName(`${node.operationId} RequestConfig`)
57
+ },
58
+ resolveRequestConfigTypedName(node) {
59
+ return this.resolveTypedName(`${node.operationId} RequestConfig`)
60
+ },
61
+ resolveResponsesName(node) {
62
+ return this.resolveName(`${node.operationId} Responses`)
63
+ },
64
+ resolveResponsesTypedName(node) {
65
+ return this.resolveTypedName(`${node.operationId} Responses`)
66
+ },
67
+ resolveResponseName(node) {
68
+ return this.resolveName(`${node.operationId} Response`)
69
+ },
70
+ resolveResponseTypedName(node) {
71
+ return this.resolveTypedName(`${node.operationId} Response`)
72
+ },
73
+ resolveEnumKeyTypedName(node) {
74
+ return `${this.resolveTypedName(node.name ?? '')}Key`
75
+ },
76
+ }
77
+ })