@kubb/plugin-ts 5.0.0-alpha.4 → 5.0.0-alpha.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{components-CRjwjdyE.js → components-CRu8IKY3.js} +12 -8
- package/dist/components-CRu8IKY3.js.map +1 -0
- package/dist/{components-DI0aTIBg.cjs → components-DeNDKlzf.cjs} +12 -8
- package/dist/components-DeNDKlzf.cjs.map +1 -0
- package/dist/components.cjs +1 -1
- package/dist/components.d.ts +1 -3
- package/dist/components.js +1 -1
- package/dist/generators.cjs +1 -1
- package/dist/generators.d.ts +2 -3
- package/dist/generators.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/{plugin-Bgm8TNUt.js → plugin-BcK4SBA0.js} +510 -243
- package/dist/plugin-BcK4SBA0.js.map +1 -0
- package/dist/{plugin-DvK-Uhvv.cjs → plugin-BrQcApyO.cjs} +510 -244
- package/dist/plugin-BrQcApyO.cjs.map +1 -0
- package/dist/{types-aotMcdUB.d.ts → types-CsvB6X5Y.d.ts} +11 -14
- package/package.json +8 -8
- package/src/components/Type.tsx +0 -3
- package/src/components/v2/Enum.tsx +67 -0
- package/src/components/v2/Type.tsx +22 -117
- package/src/constants.ts +29 -0
- package/src/factory.ts +12 -16
- package/src/generators/typeGenerator.tsx +2 -4
- package/src/generators/v2/typeGenerator.tsx +78 -103
- package/src/generators/v2/utils.ts +140 -0
- package/src/parser.ts +1 -8
- package/src/plugin.ts +11 -2
- package/src/printer.ts +235 -111
- package/src/types.ts +10 -13
- package/dist/components-CRjwjdyE.js.map +0 -1
- package/dist/components-DI0aTIBg.cjs.map +0 -1
- package/dist/plugin-Bgm8TNUt.js.map +0 -1
- package/dist/plugin-DvK-Uhvv.cjs.map +0 -1
package/src/printer.ts
CHANGED
|
@@ -1,28 +1,57 @@
|
|
|
1
|
-
import { jsStringEscape, stringify } from '@internals/utils'
|
|
1
|
+
import { jsStringEscape, pascalCase, stringify } from '@internals/utils'
|
|
2
2
|
import { isPlainStringType } 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
6
|
import type ts from 'typescript'
|
|
7
|
+
import { ENUM_TYPES_WITH_KEY_SUFFIX, OPTIONAL_ADDS_QUESTION_TOKEN, OPTIONAL_ADDS_UNDEFINED } from './constants.ts'
|
|
7
8
|
import * as factory from './factory.ts'
|
|
9
|
+
import type { PluginTs } from './types.ts'
|
|
8
10
|
|
|
9
11
|
type TsOptions = {
|
|
10
12
|
/**
|
|
11
13
|
* @default `'questionToken'`
|
|
12
14
|
*/
|
|
13
|
-
optionalType: '
|
|
15
|
+
optionalType: PluginTs['resolvedOptions']['optionalType']
|
|
14
16
|
/**
|
|
15
17
|
* @default `'array'`
|
|
16
18
|
*/
|
|
17
|
-
arrayType: '
|
|
19
|
+
arrayType: PluginTs['resolvedOptions']['arrayType']
|
|
18
20
|
/**
|
|
19
21
|
* @default `'inlineLiteral'`
|
|
20
22
|
*/
|
|
21
|
-
enumType: '
|
|
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>
|
|
22
44
|
}
|
|
23
45
|
|
|
24
|
-
|
|
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>
|
|
25
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Converts a primitive const value to a TypeScript literal type node.
|
|
53
|
+
* Handles negative numbers via a prefix unary expression.
|
|
54
|
+
*/
|
|
26
55
|
function constToTypeNode(value: string | number | boolean, format: 'string' | 'number' | 'boolean'): ts.TypeNode | undefined {
|
|
27
56
|
if (format === 'boolean') {
|
|
28
57
|
return factory.createLiteralTypeNode(value === true ? factory.createTrue() : factory.createFalse())
|
|
@@ -36,16 +65,26 @@ function constToTypeNode(value: string | number | boolean, format: 'string' | 'n
|
|
|
36
65
|
return factory.createLiteralTypeNode(factory.createStringLiteral(String(value)))
|
|
37
66
|
}
|
|
38
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Returns a `Date` reference type node when `representation` is `'date'`, otherwise falls back to `string`.
|
|
70
|
+
*/
|
|
39
71
|
function dateOrStringNode(node: { representation?: string }): ts.TypeNode {
|
|
40
72
|
return node.representation === 'date' ? factory.createTypeReferenceNode(factory.createIdentifier('Date')) : factory.keywordTypeNodes.string
|
|
41
73
|
}
|
|
42
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Maps an array of `SchemaNode`s through the printer, filtering out `null` and `undefined` results.
|
|
77
|
+
*/
|
|
43
78
|
function buildMemberNodes(members: Array<SchemaNode> | undefined, print: (node: SchemaNode) => ts.TypeNode | null | undefined): Array<ts.TypeNode> {
|
|
44
|
-
return (members ?? []).map(print).filter(Boolean)
|
|
79
|
+
return (members ?? []).map(print).filter(Boolean)
|
|
45
80
|
}
|
|
46
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
|
+
*/
|
|
47
86
|
function buildTupleNode(node: ArraySchemaNode, print: (node: SchemaNode) => ts.TypeNode | null | undefined): ts.TypeNode | undefined {
|
|
48
|
-
let items = (node.items ?? []).map(print).filter(Boolean)
|
|
87
|
+
let items = (node.items ?? []).map(print).filter(Boolean)
|
|
49
88
|
|
|
50
89
|
const restNode = node.rest ? (print(node.rest) ?? undefined) : undefined
|
|
51
90
|
const { min, max } = node
|
|
@@ -68,25 +107,31 @@ function buildTupleNode(node: ArraySchemaNode, print: (node: SchemaNode) => ts.T
|
|
|
68
107
|
return factory.createTupleTypeNode(items)
|
|
69
108
|
}
|
|
70
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Applies `nullable` and optional/nullish `| undefined` union modifiers to a property's resolved base type.
|
|
112
|
+
*/
|
|
71
113
|
function buildPropertyType(schema: SchemaNode, baseType: ts.TypeNode, optionalType: TsOptions['optionalType']): ts.TypeNode {
|
|
72
|
-
const addsUndefined =
|
|
114
|
+
const addsUndefined = OPTIONAL_ADDS_UNDEFINED.has(optionalType)
|
|
73
115
|
|
|
74
116
|
let type = baseType
|
|
75
117
|
|
|
76
118
|
if (schema.nullable) {
|
|
77
|
-
type = factory.createUnionDeclaration({ nodes: [type, factory.keywordTypeNodes.null] })
|
|
119
|
+
type = factory.createUnionDeclaration({ nodes: [type, factory.keywordTypeNodes.null] })
|
|
78
120
|
}
|
|
79
121
|
|
|
80
122
|
if ((schema.nullish || schema.optional) && addsUndefined) {
|
|
81
|
-
type = factory.createUnionDeclaration({ nodes: [type, factory.keywordTypeNodes.undefined] })
|
|
123
|
+
type = factory.createUnionDeclaration({ nodes: [type, factory.keywordTypeNodes.undefined] })
|
|
82
124
|
}
|
|
83
125
|
|
|
84
126
|
return type
|
|
85
127
|
}
|
|
86
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Collects JSDoc annotation strings (description, deprecated, min/max, pattern, default, example, type) for a schema node.
|
|
131
|
+
*/
|
|
87
132
|
function buildPropertyJSDocComments(schema: SchemaNode): Array<string | undefined> {
|
|
88
133
|
return [
|
|
89
|
-
'description' in schema && schema.description ? `@description ${jsStringEscape(schema.description
|
|
134
|
+
'description' in schema && schema.description ? `@description ${jsStringEscape(schema.description)}` : undefined,
|
|
90
135
|
'deprecated' in schema && schema.deprecated ? '@deprecated' : undefined,
|
|
91
136
|
'min' in schema && schema.min !== undefined ? `@minLength ${schema.min}` : undefined,
|
|
92
137
|
'max' in schema && schema.max !== undefined ? `@maxLength ${schema.max}` : undefined,
|
|
@@ -101,6 +146,9 @@ function buildPropertyJSDocComments(schema: SchemaNode): Array<string | undefine
|
|
|
101
146
|
]
|
|
102
147
|
}
|
|
103
148
|
|
|
149
|
+
/**
|
|
150
|
+
* Creates TypeScript index signatures for `additionalProperties` and `patternProperties` on an object schema node.
|
|
151
|
+
*/
|
|
104
152
|
function buildIndexSignatures(
|
|
105
153
|
node: { additionalProperties?: SchemaNode | boolean; patternProperties?: Record<string, SchemaNode> },
|
|
106
154
|
propertyCount: number,
|
|
@@ -109,7 +157,8 @@ function buildIndexSignatures(
|
|
|
109
157
|
const elements: Array<ts.TypeElement> = []
|
|
110
158
|
|
|
111
159
|
if (node.additionalProperties && node.additionalProperties !== true) {
|
|
112
|
-
const additionalType =
|
|
160
|
+
const additionalType = print(node.additionalProperties) ?? factory.keywordTypeNodes.unknown
|
|
161
|
+
|
|
113
162
|
elements.push(factory.createIndexSignature(propertyCount > 0 ? factory.keywordTypeNodes.unknown : additionalType))
|
|
114
163
|
} else if (node.additionalProperties === true) {
|
|
115
164
|
elements.push(factory.createIndexSignature(factory.keywordTypeNodes.unknown))
|
|
@@ -118,9 +167,10 @@ function buildIndexSignatures(
|
|
|
118
167
|
if (node.patternProperties) {
|
|
119
168
|
const first = Object.values(node.patternProperties)[0]
|
|
120
169
|
if (first) {
|
|
121
|
-
let patternType =
|
|
170
|
+
let patternType = print(first) ?? factory.keywordTypeNodes.unknown
|
|
171
|
+
|
|
122
172
|
if (first.nullable) {
|
|
123
|
-
patternType = factory.createUnionDeclaration({ nodes: [patternType, factory.keywordTypeNodes.null] })
|
|
173
|
+
patternType = factory.createUnionDeclaration({ nodes: [patternType, factory.keywordTypeNodes.null] })
|
|
124
174
|
}
|
|
125
175
|
elements.push(factory.createIndexSignature(patternType))
|
|
126
176
|
}
|
|
@@ -130,115 +180,189 @@ function buildIndexSignatures(
|
|
|
130
180
|
}
|
|
131
181
|
|
|
132
182
|
/**
|
|
133
|
-
*
|
|
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.
|
|
134
192
|
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
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
|
+
* ```
|
|
198
|
+
*
|
|
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
|
+
* ```
|
|
138
204
|
*/
|
|
139
|
-
export const printerTs = definePrinter<TsPrinter>((options) =>
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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')
|
|
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
|
+
})
|
|
167
307
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
.filter((v): v is string | number | boolean => v !== null)
|
|
171
|
-
.map((value) => constToTypeNode(value, typeof value === 'number' ? 'number' : typeof value === 'boolean' ? 'boolean' : 'string'))
|
|
172
|
-
.filter(Boolean) as Array<ts.TypeNode>
|
|
308
|
+
return factory.appendJSDocToNode({ node: propertyNode, comments: buildPropertyJSDocComments(prop.schema) })
|
|
309
|
+
})
|
|
173
310
|
|
|
174
|
-
|
|
175
|
-
}
|
|
311
|
+
const allElements = [...propertyNodes, ...buildIndexSignatures(node, propertyNodes.length, print)]
|
|
176
312
|
|
|
177
|
-
|
|
313
|
+
if (!allElements.length) {
|
|
314
|
+
return factory.keywordTypeNodes.object
|
|
315
|
+
}
|
|
178
316
|
|
|
179
|
-
|
|
317
|
+
return factory.createTypeLiteralNode(allElements)
|
|
318
|
+
},
|
|
180
319
|
},
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
const hasStringLiteral = members.some((m) => m.type === 'enum' && m.enumType === 'string')
|
|
185
|
-
const hasPlainString = members.some((m) => isPlainStringType(m))
|
|
186
|
-
|
|
187
|
-
if (hasStringLiteral && hasPlainString) {
|
|
188
|
-
const nodes = members
|
|
189
|
-
.map((m) => {
|
|
190
|
-
if (isPlainStringType(m)) {
|
|
191
|
-
return factory.createIntersectionDeclaration({
|
|
192
|
-
nodes: [factory.keywordTypeNodes.string, factory.createTypeLiteralNode([])],
|
|
193
|
-
withParentheses: true,
|
|
194
|
-
}) as ts.TypeNode
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
return this.print(m)
|
|
198
|
-
})
|
|
199
|
-
.filter(Boolean) as Array<ts.TypeNode>
|
|
320
|
+
print(node) {
|
|
321
|
+
let type = this.print(node)
|
|
200
322
|
|
|
201
|
-
|
|
323
|
+
if (!type) {
|
|
324
|
+
return undefined
|
|
202
325
|
}
|
|
203
326
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
},
|
|
209
|
-
array(node) {
|
|
210
|
-
const itemNodes = (node.items ?? []).map((item) => this.print(item)).filter(Boolean) as Array<ts.TypeNode>
|
|
211
|
-
|
|
212
|
-
return factory.createArrayDeclaration({ nodes: itemNodes, arrayType: this.options.arrayType }) ?? undefined
|
|
213
|
-
},
|
|
214
|
-
tuple(node) {
|
|
215
|
-
return buildTupleNode(node, this.print)
|
|
216
|
-
},
|
|
217
|
-
object(node) {
|
|
218
|
-
const addsQuestionToken = ['questionToken', 'questionTokenAndUndefined'].includes(this.options.optionalType)
|
|
219
|
-
const { print } = this
|
|
220
|
-
|
|
221
|
-
const propertyNodes: Array<ts.TypeElement> = node.properties.map((prop) => {
|
|
222
|
-
const baseType = (print(prop.schema) ?? factory.keywordTypeNodes.unknown) as ts.TypeNode
|
|
223
|
-
const type = buildPropertyType(prop.schema, baseType, this.options.optionalType)
|
|
224
|
-
|
|
225
|
-
const propertyNode = factory.createPropertySignature({
|
|
226
|
-
questionToken: prop.schema.optional || prop.schema.nullish ? addsQuestionToken : false,
|
|
227
|
-
name: prop.name,
|
|
228
|
-
type,
|
|
229
|
-
readOnly: prop.schema.readOnly,
|
|
230
|
-
})
|
|
231
|
-
|
|
232
|
-
return factory.appendJSDocToNode({ node: propertyNode, comments: buildPropertyJSDocComments(prop.schema) })
|
|
233
|
-
})
|
|
327
|
+
// Apply top-level nullable / optional union modifiers.
|
|
328
|
+
if (node.nullable) {
|
|
329
|
+
type = factory.createUnionDeclaration({ nodes: [type, factory.keywordTypeNodes.null] })
|
|
330
|
+
}
|
|
234
331
|
|
|
235
|
-
|
|
332
|
+
if ((node.nullish || node.optional) && addsUndefined) {
|
|
333
|
+
type = factory.createUnionDeclaration({ nodes: [type, factory.keywordTypeNodes.undefined] })
|
|
334
|
+
}
|
|
236
335
|
|
|
237
|
-
|
|
238
|
-
|
|
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
|
|
239
340
|
}
|
|
240
341
|
|
|
241
|
-
|
|
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
|
+
})
|
|
242
366
|
},
|
|
243
|
-
}
|
|
244
|
-
})
|
|
367
|
+
}
|
|
368
|
+
})
|
package/src/types.ts
CHANGED
|
@@ -2,7 +2,6 @@ import type { Group, Output, PluginFactoryOptions, ResolveNameParams } from '@ku
|
|
|
2
2
|
import type { contentType, Oas } from '@kubb/oas'
|
|
3
3
|
import type { Exclude, Include, Override, ResolvePathOptions } from '@kubb/plugin-oas'
|
|
4
4
|
import type { Generator } from '@kubb/plugin-oas/generators'
|
|
5
|
-
import type ts from 'typescript'
|
|
6
5
|
|
|
7
6
|
export type Options = {
|
|
8
7
|
/**
|
|
@@ -63,6 +62,8 @@ export type Options = {
|
|
|
63
62
|
/**
|
|
64
63
|
* Set a suffix for the generated enums.
|
|
65
64
|
* @default 'enum'
|
|
65
|
+
* @deprecated Set `enumSuffix` on the adapter (`adapterOas({ enumSuffix })`) instead.
|
|
66
|
+
* In v5, the adapter owns this decision at parse time; the plugin option is ignored.
|
|
66
67
|
*/
|
|
67
68
|
enumSuffix?: string
|
|
68
69
|
/**
|
|
@@ -70,6 +71,8 @@ export type Options = {
|
|
|
70
71
|
* - 'string' represents dates as string values.
|
|
71
72
|
* - 'date' represents dates as JavaScript Date objects.
|
|
72
73
|
* @default 'string'
|
|
74
|
+
* @deprecated Set `dateType` on the adapter (`adapterOas({ dateType })`) instead.
|
|
75
|
+
* In v5, the adapter owns this decision at parse time; the plugin option is ignored.
|
|
73
76
|
*/
|
|
74
77
|
dateType?: 'string' | 'date'
|
|
75
78
|
/**
|
|
@@ -78,6 +81,8 @@ export type Options = {
|
|
|
78
81
|
* - 'bigint' uses the TypeScript `bigint` type (accurate for values exceeding Number.MAX_SAFE_INTEGER).
|
|
79
82
|
* @note in v5 of Kubb 'bigint' will become the default to better align with OpenAPI's int64 specification.
|
|
80
83
|
* @default 'number'
|
|
84
|
+
* @deprecated Set `integerType` on the adapter (`adapterOas({ integerType })`) instead.
|
|
85
|
+
* In v5, the adapter owns this decision at parse time; the plugin option is ignored.
|
|
81
86
|
*/
|
|
82
87
|
integerType?: 'number' | 'bigint'
|
|
83
88
|
/**
|
|
@@ -86,6 +91,8 @@ export type Options = {
|
|
|
86
91
|
* - 'unknown' requires type narrowing before use.
|
|
87
92
|
* - 'void' represents no value.
|
|
88
93
|
* @default 'any'
|
|
94
|
+
* @deprecated Set `unknownType` on the adapter (`adapterOas({ unknownType })`) instead.
|
|
95
|
+
* In v5, the adapter owns this decision at parse time; the plugin option is ignored.
|
|
89
96
|
*/
|
|
90
97
|
unknownType?: 'any' | 'unknown' | 'void'
|
|
91
98
|
/**
|
|
@@ -94,6 +101,8 @@ export type Options = {
|
|
|
94
101
|
* - 'unknown' requires type narrowing before use.
|
|
95
102
|
* - 'void' represents no value.
|
|
96
103
|
* @default `unknownType`
|
|
104
|
+
* @deprecated Set `emptySchemaType` on the adapter (`adapterOas({ emptySchemaType })`) instead.
|
|
105
|
+
* In v5, the adapter owns this decision at parse time; the plugin option is ignored.
|
|
97
106
|
*/
|
|
98
107
|
emptySchemaType?: 'any' | 'unknown' | 'void'
|
|
99
108
|
/**
|
|
@@ -117,17 +126,6 @@ export type Options = {
|
|
|
117
126
|
*/
|
|
118
127
|
name?: (name: ResolveNameParams['name'], type?: ResolveNameParams['type']) => string
|
|
119
128
|
}
|
|
120
|
-
/**
|
|
121
|
-
* @example
|
|
122
|
-
* Use https://ts-ast-viewer.com to generate factory code(see createPropertySignature)
|
|
123
|
-
* category: factory.createPropertySignature(
|
|
124
|
-
* undefined,
|
|
125
|
-
* factory.createIdentifier("category"),
|
|
126
|
-
* factory.createToken(ts.SyntaxKind.QuestionToken),
|
|
127
|
-
* factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)
|
|
128
|
-
* )
|
|
129
|
-
*/
|
|
130
|
-
mapper?: Record<string, ts.PropertySignature>
|
|
131
129
|
/**
|
|
132
130
|
* How to style your params, by default no casing is applied
|
|
133
131
|
* - 'camelcase' uses camelCase for pathParams, queryParams and headerParams property names
|
|
@@ -160,7 +158,6 @@ type ResolvedOptions = {
|
|
|
160
158
|
arrayType: NonNullable<Options['arrayType']>
|
|
161
159
|
transformers: NonNullable<Options['transformers']>
|
|
162
160
|
syntaxType: NonNullable<Options['syntaxType']>
|
|
163
|
-
mapper: Record<string, any>
|
|
164
161
|
paramsCasing: Options['paramsCasing']
|
|
165
162
|
}
|
|
166
163
|
|