@kubb/plugin-ts 5.0.0-alpha.9 → 5.0.0-beta.100
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/LICENSE +17 -10
- package/README.md +38 -23
- package/dist/index.cjs +2376 -5
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +941 -40
- package/dist/index.js +2321 -2
- package/dist/index.js.map +1 -0
- package/dist/rolldown-runtime-CNktS9qV.js +17 -0
- package/package.json +38 -74
- package/dist/chunk--u3MIqq1.js +0 -8
- package/dist/components-CRu8IKY3.js +0 -729
- package/dist/components-CRu8IKY3.js.map +0 -1
- package/dist/components-DeNDKlzf.cjs +0 -982
- package/dist/components-DeNDKlzf.cjs.map +0 -1
- package/dist/components.cjs +0 -3
- package/dist/components.d.ts +0 -36
- package/dist/components.js +0 -2
- package/dist/generators.cjs +0 -4
- package/dist/generators.d.ts +0 -509
- package/dist/generators.js +0 -2
- package/dist/plugin-BZkBwnEA.js +0 -1269
- package/dist/plugin-BZkBwnEA.js.map +0 -1
- package/dist/plugin-Bunz1oGa.cjs +0 -1322
- package/dist/plugin-Bunz1oGa.cjs.map +0 -1
- package/dist/types-mSXmB8WU.d.ts +0 -298
- package/src/components/Type.tsx +0 -190
- package/src/components/index.ts +0 -1
- package/src/components/v2/Enum.tsx +0 -67
- package/src/components/v2/Type.tsx +0 -59
- package/src/constants.ts +0 -29
- package/src/factory.ts +0 -726
- package/src/generators/index.ts +0 -2
- package/src/generators/typeGenerator.tsx +0 -468
- package/src/generators/v2/typeGenerator.tsx +0 -167
- package/src/generators/v2/utils.ts +0 -140
- package/src/index.ts +0 -3
- package/src/parser.ts +0 -389
- package/src/plugin.ts +0 -232
- package/src/printer.ts +0 -368
- package/src/resolverTs.ts +0 -77
- package/src/types.ts +0 -296
package/src/factory.ts
DELETED
|
@@ -1,726 +0,0 @@
|
|
|
1
|
-
import { camelCase, pascalCase, screamingSnakeCase, snakeCase } from '@internals/utils'
|
|
2
|
-
import { isNumber, sortBy } from 'remeda'
|
|
3
|
-
import ts from 'typescript'
|
|
4
|
-
|
|
5
|
-
const { SyntaxKind, factory } = ts
|
|
6
|
-
|
|
7
|
-
// https://ts-ast-viewer.com/
|
|
8
|
-
|
|
9
|
-
export const modifiers = {
|
|
10
|
-
async: factory.createModifier(ts.SyntaxKind.AsyncKeyword),
|
|
11
|
-
export: factory.createModifier(ts.SyntaxKind.ExportKeyword),
|
|
12
|
-
const: factory.createModifier(ts.SyntaxKind.ConstKeyword),
|
|
13
|
-
static: factory.createModifier(ts.SyntaxKind.StaticKeyword),
|
|
14
|
-
} as const
|
|
15
|
-
|
|
16
|
-
export const syntaxKind = {
|
|
17
|
-
union: SyntaxKind.UnionType as 192,
|
|
18
|
-
literalType: SyntaxKind.LiteralType,
|
|
19
|
-
stringLiteral: SyntaxKind.StringLiteral,
|
|
20
|
-
} as const
|
|
21
|
-
|
|
22
|
-
export function getUnknownType(unknownType: 'any' | 'unknown' | 'void' | undefined) {
|
|
23
|
-
if (unknownType === 'any') {
|
|
24
|
-
return keywordTypeNodes.any
|
|
25
|
-
}
|
|
26
|
-
if (unknownType === 'void') {
|
|
27
|
-
return keywordTypeNodes.void
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return keywordTypeNodes.unknown
|
|
31
|
-
}
|
|
32
|
-
function isValidIdentifier(str: string): boolean {
|
|
33
|
-
if (!str.length || str.trim() !== str) {
|
|
34
|
-
return false
|
|
35
|
-
}
|
|
36
|
-
const node = ts.parseIsolatedEntityName(str, ts.ScriptTarget.Latest)
|
|
37
|
-
|
|
38
|
-
return !!node && node.kind === ts.SyntaxKind.Identifier && ts.identifierToKeywordKind(node.kind as unknown as ts.Identifier) === undefined
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function propertyName(name: string | ts.PropertyName): ts.PropertyName {
|
|
42
|
-
if (typeof name === 'string') {
|
|
43
|
-
const isValid = isValidIdentifier(name)
|
|
44
|
-
return isValid ? factory.createIdentifier(name) : factory.createStringLiteral(name)
|
|
45
|
-
}
|
|
46
|
-
return name
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const questionToken = factory.createToken(ts.SyntaxKind.QuestionToken)
|
|
50
|
-
|
|
51
|
-
export function createQuestionToken(token?: boolean | ts.QuestionToken) {
|
|
52
|
-
if (!token) {
|
|
53
|
-
return undefined
|
|
54
|
-
}
|
|
55
|
-
if (token === true) {
|
|
56
|
-
return questionToken
|
|
57
|
-
}
|
|
58
|
-
return token
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export function createIntersectionDeclaration({ nodes, withParentheses }: { nodes: Array<ts.TypeNode>; withParentheses?: boolean }): ts.TypeNode | null {
|
|
62
|
-
if (!nodes.length) {
|
|
63
|
-
return null
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (nodes.length === 1) {
|
|
67
|
-
return nodes[0] || null
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const node = factory.createIntersectionTypeNode(nodes)
|
|
71
|
-
|
|
72
|
-
if (withParentheses) {
|
|
73
|
-
return factory.createParenthesizedType(node)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return node
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Minimum nodes length of 2
|
|
81
|
-
* @example `string & number`
|
|
82
|
-
*/
|
|
83
|
-
export function createTupleDeclaration({ nodes, withParentheses }: { nodes: Array<ts.TypeNode>; withParentheses?: boolean }): ts.TypeNode | null {
|
|
84
|
-
if (!nodes.length) {
|
|
85
|
-
return null
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (nodes.length === 1) {
|
|
89
|
-
return nodes[0] || null
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const node = factory.createTupleTypeNode(nodes)
|
|
93
|
-
|
|
94
|
-
if (withParentheses) {
|
|
95
|
-
return factory.createParenthesizedType(node)
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return node
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export function createArrayDeclaration({ nodes, arrayType = 'array' }: { nodes: Array<ts.TypeNode>; arrayType?: 'array' | 'generic' }): ts.TypeNode | null {
|
|
102
|
-
if (!nodes.length) {
|
|
103
|
-
return factory.createTupleTypeNode([])
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (nodes.length === 1) {
|
|
107
|
-
const node = nodes[0]
|
|
108
|
-
if (!node) {
|
|
109
|
-
return null
|
|
110
|
-
}
|
|
111
|
-
if (arrayType === 'generic') {
|
|
112
|
-
return factory.createTypeReferenceNode(factory.createIdentifier('Array'), [node])
|
|
113
|
-
}
|
|
114
|
-
return factory.createArrayTypeNode(node)
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// For union types (multiple nodes), respect arrayType preference
|
|
118
|
-
const unionType = factory.createUnionTypeNode(nodes)
|
|
119
|
-
if (arrayType === 'generic') {
|
|
120
|
-
return factory.createTypeReferenceNode(factory.createIdentifier('Array'), [unionType])
|
|
121
|
-
}
|
|
122
|
-
// For array syntax with unions, we need parentheses: (string | number)[]
|
|
123
|
-
return factory.createArrayTypeNode(factory.createParenthesizedType(unionType))
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Minimum nodes length of 2
|
|
128
|
-
* @example `string | number`
|
|
129
|
-
*/
|
|
130
|
-
export function createUnionDeclaration({ nodes, withParentheses }: { nodes: Array<ts.TypeNode>; withParentheses?: boolean }): ts.TypeNode {
|
|
131
|
-
if (!nodes.length) {
|
|
132
|
-
return keywordTypeNodes.any
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if (nodes.length === 1) {
|
|
136
|
-
return nodes[0] as ts.TypeNode
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const node = factory.createUnionTypeNode(nodes)
|
|
140
|
-
|
|
141
|
-
if (withParentheses) {
|
|
142
|
-
return factory.createParenthesizedType(node)
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
return node
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
export function createPropertySignature({
|
|
149
|
-
readOnly,
|
|
150
|
-
modifiers = [],
|
|
151
|
-
name,
|
|
152
|
-
questionToken,
|
|
153
|
-
type,
|
|
154
|
-
}: {
|
|
155
|
-
readOnly?: boolean
|
|
156
|
-
modifiers?: Array<ts.Modifier>
|
|
157
|
-
name: ts.PropertyName | string
|
|
158
|
-
questionToken?: ts.QuestionToken | boolean
|
|
159
|
-
type?: ts.TypeNode
|
|
160
|
-
}) {
|
|
161
|
-
return factory.createPropertySignature(
|
|
162
|
-
[...modifiers, readOnly ? factory.createToken(ts.SyntaxKind.ReadonlyKeyword) : undefined].filter(Boolean),
|
|
163
|
-
propertyName(name),
|
|
164
|
-
createQuestionToken(questionToken),
|
|
165
|
-
type,
|
|
166
|
-
)
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
export function createParameterSignature(
|
|
170
|
-
name: string | ts.BindingName,
|
|
171
|
-
{
|
|
172
|
-
modifiers,
|
|
173
|
-
dotDotDotToken,
|
|
174
|
-
questionToken,
|
|
175
|
-
type,
|
|
176
|
-
initializer,
|
|
177
|
-
}: {
|
|
178
|
-
decorators?: Array<ts.Decorator>
|
|
179
|
-
modifiers?: Array<ts.Modifier>
|
|
180
|
-
dotDotDotToken?: ts.DotDotDotToken
|
|
181
|
-
questionToken?: ts.QuestionToken | boolean
|
|
182
|
-
type?: ts.TypeNode
|
|
183
|
-
initializer?: ts.Expression
|
|
184
|
-
},
|
|
185
|
-
): ts.ParameterDeclaration {
|
|
186
|
-
return factory.createParameterDeclaration(modifiers, dotDotDotToken, name, createQuestionToken(questionToken), type, initializer)
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
export function createJSDoc({ comments }: { comments: string[] }) {
|
|
190
|
-
if (!comments.length) {
|
|
191
|
-
return null
|
|
192
|
-
}
|
|
193
|
-
return factory.createJSDocComment(
|
|
194
|
-
factory.createNodeArray(
|
|
195
|
-
comments.map((comment, i) => {
|
|
196
|
-
if (i === comments.length - 1) {
|
|
197
|
-
return factory.createJSDocText(comment)
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
return factory.createJSDocText(`${comment}\n`)
|
|
201
|
-
}),
|
|
202
|
-
),
|
|
203
|
-
)
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* @link https://github.com/microsoft/TypeScript/issues/44151
|
|
208
|
-
*/
|
|
209
|
-
export function appendJSDocToNode<TNode extends ts.Node>({ node, comments }: { node: TNode; comments: Array<string | undefined> }) {
|
|
210
|
-
const filteredComments = comments.filter(Boolean)
|
|
211
|
-
|
|
212
|
-
if (!filteredComments.length) {
|
|
213
|
-
return node
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const text = filteredComments.reduce((acc = '', comment = '') => {
|
|
217
|
-
return `${acc}\n * ${comment.replaceAll('*/', '*\\/')}`
|
|
218
|
-
}, '*')
|
|
219
|
-
|
|
220
|
-
// Use the node directly instead of spreading to avoid creating Unknown nodes
|
|
221
|
-
// TypeScript's addSyntheticLeadingComment accepts the node as-is
|
|
222
|
-
return ts.addSyntheticLeadingComment(node, ts.SyntaxKind.MultiLineCommentTrivia, `${text || '*'}\n`, true)
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
export function createIndexSignature(
|
|
226
|
-
type: ts.TypeNode,
|
|
227
|
-
{
|
|
228
|
-
modifiers,
|
|
229
|
-
indexName = 'key',
|
|
230
|
-
indexType = factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
|
|
231
|
-
}: {
|
|
232
|
-
indexName?: string
|
|
233
|
-
indexType?: ts.TypeNode
|
|
234
|
-
decorators?: Array<ts.Decorator>
|
|
235
|
-
modifiers?: Array<ts.Modifier>
|
|
236
|
-
} = {},
|
|
237
|
-
) {
|
|
238
|
-
return factory.createIndexSignature(modifiers, [createParameterSignature(indexName, { type: indexType })], type)
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
export function createTypeAliasDeclaration({
|
|
242
|
-
modifiers,
|
|
243
|
-
name,
|
|
244
|
-
typeParameters,
|
|
245
|
-
type,
|
|
246
|
-
}: {
|
|
247
|
-
modifiers?: Array<ts.Modifier>
|
|
248
|
-
name: string | ts.Identifier
|
|
249
|
-
typeParameters?: Array<ts.TypeParameterDeclaration>
|
|
250
|
-
type: ts.TypeNode
|
|
251
|
-
}) {
|
|
252
|
-
return factory.createTypeAliasDeclaration(modifiers, name, typeParameters, type)
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
export function createInterfaceDeclaration({
|
|
256
|
-
modifiers,
|
|
257
|
-
name,
|
|
258
|
-
typeParameters,
|
|
259
|
-
members,
|
|
260
|
-
}: {
|
|
261
|
-
modifiers?: Array<ts.Modifier>
|
|
262
|
-
name: string | ts.Identifier
|
|
263
|
-
typeParameters?: Array<ts.TypeParameterDeclaration>
|
|
264
|
-
members: Array<ts.TypeElement>
|
|
265
|
-
}) {
|
|
266
|
-
return factory.createInterfaceDeclaration(modifiers, name, typeParameters, undefined, members)
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
export function createTypeDeclaration({
|
|
270
|
-
syntax,
|
|
271
|
-
isExportable,
|
|
272
|
-
comments,
|
|
273
|
-
name,
|
|
274
|
-
type,
|
|
275
|
-
}: {
|
|
276
|
-
syntax: 'type' | 'interface'
|
|
277
|
-
comments: Array<string | undefined>
|
|
278
|
-
isExportable?: boolean
|
|
279
|
-
name: string | ts.Identifier
|
|
280
|
-
type: ts.TypeNode
|
|
281
|
-
}) {
|
|
282
|
-
if (syntax === 'interface' && 'members' in type) {
|
|
283
|
-
const node = createInterfaceDeclaration({
|
|
284
|
-
members: type.members as Array<ts.TypeElement>,
|
|
285
|
-
modifiers: isExportable ? [modifiers.export] : [],
|
|
286
|
-
name,
|
|
287
|
-
typeParameters: undefined,
|
|
288
|
-
})
|
|
289
|
-
|
|
290
|
-
return appendJSDocToNode({
|
|
291
|
-
node,
|
|
292
|
-
comments,
|
|
293
|
-
})
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
const node = createTypeAliasDeclaration({
|
|
297
|
-
type,
|
|
298
|
-
modifiers: isExportable ? [modifiers.export] : [],
|
|
299
|
-
name,
|
|
300
|
-
typeParameters: undefined,
|
|
301
|
-
})
|
|
302
|
-
|
|
303
|
-
return appendJSDocToNode({
|
|
304
|
-
node,
|
|
305
|
-
comments,
|
|
306
|
-
})
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
export function createNamespaceDeclaration({ statements, name }: { name: string; statements: ts.Statement[] }) {
|
|
310
|
-
return factory.createModuleDeclaration(
|
|
311
|
-
[factory.createToken(ts.SyntaxKind.ExportKeyword)],
|
|
312
|
-
factory.createIdentifier(name),
|
|
313
|
-
factory.createModuleBlock(statements),
|
|
314
|
-
ts.NodeFlags.Namespace,
|
|
315
|
-
)
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
/**
|
|
319
|
-
* In { propertyName: string; name?: string } is `name` being used to make the type more unique when multiple same names are used.
|
|
320
|
-
* @example `import { Pet as Cat } from './Pet'`
|
|
321
|
-
*/
|
|
322
|
-
export function createImportDeclaration({
|
|
323
|
-
name,
|
|
324
|
-
path,
|
|
325
|
-
isTypeOnly = false,
|
|
326
|
-
isNameSpace = false,
|
|
327
|
-
}: {
|
|
328
|
-
name: string | Array<string | { propertyName: string; name?: string }>
|
|
329
|
-
path: string
|
|
330
|
-
isTypeOnly?: boolean
|
|
331
|
-
isNameSpace?: boolean
|
|
332
|
-
}) {
|
|
333
|
-
if (!Array.isArray(name)) {
|
|
334
|
-
let importPropertyName: ts.Identifier | undefined = factory.createIdentifier(name)
|
|
335
|
-
let importName: ts.NamedImportBindings | undefined
|
|
336
|
-
|
|
337
|
-
if (isNameSpace) {
|
|
338
|
-
importPropertyName = undefined
|
|
339
|
-
importName = factory.createNamespaceImport(factory.createIdentifier(name))
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
return factory.createImportDeclaration(
|
|
343
|
-
undefined,
|
|
344
|
-
factory.createImportClause(isTypeOnly, importPropertyName, importName),
|
|
345
|
-
factory.createStringLiteral(path),
|
|
346
|
-
undefined,
|
|
347
|
-
)
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
// Sort the imports alphabetically for consistent output across platforms
|
|
351
|
-
const sortedName = sortBy(name, [(item) => (typeof item === 'object' ? item.propertyName : item), 'asc'])
|
|
352
|
-
|
|
353
|
-
return factory.createImportDeclaration(
|
|
354
|
-
undefined,
|
|
355
|
-
factory.createImportClause(
|
|
356
|
-
isTypeOnly,
|
|
357
|
-
undefined,
|
|
358
|
-
factory.createNamedImports(
|
|
359
|
-
sortedName.map((item) => {
|
|
360
|
-
if (typeof item === 'object') {
|
|
361
|
-
const obj = item as { propertyName: string; name?: string }
|
|
362
|
-
if (obj.name) {
|
|
363
|
-
return factory.createImportSpecifier(false, factory.createIdentifier(obj.propertyName), factory.createIdentifier(obj.name))
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
return factory.createImportSpecifier(false, undefined, factory.createIdentifier(obj.propertyName))
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
return factory.createImportSpecifier(false, undefined, factory.createIdentifier(item))
|
|
370
|
-
}),
|
|
371
|
-
),
|
|
372
|
-
),
|
|
373
|
-
factory.createStringLiteral(path),
|
|
374
|
-
undefined,
|
|
375
|
-
)
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
export function createExportDeclaration({
|
|
379
|
-
path,
|
|
380
|
-
asAlias,
|
|
381
|
-
isTypeOnly = false,
|
|
382
|
-
name,
|
|
383
|
-
}: {
|
|
384
|
-
path: string
|
|
385
|
-
asAlias?: boolean
|
|
386
|
-
isTypeOnly?: boolean
|
|
387
|
-
name?: string | Array<ts.Identifier | string>
|
|
388
|
-
}) {
|
|
389
|
-
if (name && !Array.isArray(name) && !asAlias) {
|
|
390
|
-
console.warn(`When using name as string, asAlias should be true ${name}`)
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
if (!Array.isArray(name)) {
|
|
394
|
-
const parsedName = name?.match(/^\d/) ? `_${name?.slice(1)}` : name
|
|
395
|
-
|
|
396
|
-
return factory.createExportDeclaration(
|
|
397
|
-
undefined,
|
|
398
|
-
isTypeOnly,
|
|
399
|
-
asAlias && parsedName ? factory.createNamespaceExport(factory.createIdentifier(parsedName)) : undefined,
|
|
400
|
-
factory.createStringLiteral(path),
|
|
401
|
-
undefined,
|
|
402
|
-
)
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
// Sort the exports alphabetically for consistent output across platforms
|
|
406
|
-
const sortedName = sortBy(name, [(propertyName) => (typeof propertyName === 'string' ? propertyName : propertyName.text), 'asc'])
|
|
407
|
-
|
|
408
|
-
return factory.createExportDeclaration(
|
|
409
|
-
undefined,
|
|
410
|
-
isTypeOnly,
|
|
411
|
-
factory.createNamedExports(
|
|
412
|
-
sortedName.map((propertyName) => {
|
|
413
|
-
return factory.createExportSpecifier(false, undefined, typeof propertyName === 'string' ? factory.createIdentifier(propertyName) : propertyName)
|
|
414
|
-
}),
|
|
415
|
-
),
|
|
416
|
-
factory.createStringLiteral(path),
|
|
417
|
-
undefined,
|
|
418
|
-
)
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
/**
|
|
422
|
-
* Apply casing transformation to enum keys
|
|
423
|
-
*/
|
|
424
|
-
function applyEnumKeyCasing(key: string, casing: 'screamingSnakeCase' | 'snakeCase' | 'pascalCase' | 'camelCase' | 'none' = 'none'): string {
|
|
425
|
-
if (casing === 'none') {
|
|
426
|
-
return key
|
|
427
|
-
}
|
|
428
|
-
if (casing === 'screamingSnakeCase') {
|
|
429
|
-
return screamingSnakeCase(key)
|
|
430
|
-
}
|
|
431
|
-
if (casing === 'snakeCase') {
|
|
432
|
-
return snakeCase(key)
|
|
433
|
-
}
|
|
434
|
-
if (casing === 'pascalCase') {
|
|
435
|
-
return pascalCase(key)
|
|
436
|
-
}
|
|
437
|
-
if (casing === 'camelCase') {
|
|
438
|
-
return camelCase(key)
|
|
439
|
-
}
|
|
440
|
-
return key
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
export function createEnumDeclaration({
|
|
444
|
-
type = 'enum',
|
|
445
|
-
name,
|
|
446
|
-
typeName,
|
|
447
|
-
enums,
|
|
448
|
-
enumKeyCasing = 'none',
|
|
449
|
-
}: {
|
|
450
|
-
/**
|
|
451
|
-
* Choose to use `enum`, `asConst`, `asPascalConst`, `constEnum`, or `literal` for enums.
|
|
452
|
-
* - `enum`: TypeScript enum
|
|
453
|
-
* - `asConst`: const with camelCase name (e.g., `petType`)
|
|
454
|
-
* - `asPascalConst`: const with PascalCase name (e.g., `PetType`)
|
|
455
|
-
* - `constEnum`: const enum
|
|
456
|
-
* - `literal`: literal union type
|
|
457
|
-
* @default `'enum'`
|
|
458
|
-
*/
|
|
459
|
-
type?: 'enum' | 'asConst' | 'asPascalConst' | 'constEnum' | 'literal' | 'inlineLiteral'
|
|
460
|
-
/**
|
|
461
|
-
* Enum name in camelCase.
|
|
462
|
-
*/
|
|
463
|
-
name: string
|
|
464
|
-
/**
|
|
465
|
-
* Enum name in PascalCase.
|
|
466
|
-
*/
|
|
467
|
-
typeName: string
|
|
468
|
-
enums: [key: string | number, value: string | number | boolean][]
|
|
469
|
-
/**
|
|
470
|
-
* Choose the casing for enum key names.
|
|
471
|
-
* @default 'none'
|
|
472
|
-
*/
|
|
473
|
-
enumKeyCasing?: 'screamingSnakeCase' | 'snakeCase' | 'pascalCase' | 'camelCase' | 'none'
|
|
474
|
-
}): [name: ts.Node | undefined, type: ts.Node] {
|
|
475
|
-
if (type === 'literal' || type === 'inlineLiteral') {
|
|
476
|
-
return [
|
|
477
|
-
undefined,
|
|
478
|
-
factory.createTypeAliasDeclaration(
|
|
479
|
-
[factory.createToken(ts.SyntaxKind.ExportKeyword)],
|
|
480
|
-
factory.createIdentifier(typeName),
|
|
481
|
-
undefined,
|
|
482
|
-
factory.createUnionTypeNode(
|
|
483
|
-
enums
|
|
484
|
-
.map(([_key, value]) => {
|
|
485
|
-
if (isNumber(value)) {
|
|
486
|
-
if (value < 0) {
|
|
487
|
-
return factory.createLiteralTypeNode(
|
|
488
|
-
factory.createPrefixUnaryExpression(ts.SyntaxKind.MinusToken, factory.createNumericLiteral(Math.abs(value))),
|
|
489
|
-
)
|
|
490
|
-
}
|
|
491
|
-
return factory.createLiteralTypeNode(factory.createNumericLiteral(value?.toString()))
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
if (typeof value === 'boolean') {
|
|
495
|
-
return factory.createLiteralTypeNode(value ? factory.createTrue() : factory.createFalse())
|
|
496
|
-
}
|
|
497
|
-
if (value) {
|
|
498
|
-
return factory.createLiteralTypeNode(factory.createStringLiteral(value.toString()))
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
return undefined
|
|
502
|
-
})
|
|
503
|
-
.filter(Boolean),
|
|
504
|
-
),
|
|
505
|
-
),
|
|
506
|
-
]
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
if (type === 'enum' || type === 'constEnum') {
|
|
510
|
-
return [
|
|
511
|
-
undefined,
|
|
512
|
-
factory.createEnumDeclaration(
|
|
513
|
-
[factory.createToken(ts.SyntaxKind.ExportKeyword), type === 'constEnum' ? factory.createToken(ts.SyntaxKind.ConstKeyword) : undefined].filter(Boolean),
|
|
514
|
-
factory.createIdentifier(typeName),
|
|
515
|
-
enums
|
|
516
|
-
.map(([key, value]) => {
|
|
517
|
-
let initializer: ts.Expression = factory.createStringLiteral(value?.toString())
|
|
518
|
-
const isExactNumber = Number.parseInt(value.toString(), 10) === value
|
|
519
|
-
|
|
520
|
-
if (isExactNumber && isNumber(Number.parseInt(value.toString(), 10))) {
|
|
521
|
-
if ((value as number) < 0) {
|
|
522
|
-
initializer = factory.createPrefixUnaryExpression(ts.SyntaxKind.MinusToken, factory.createNumericLiteral(Math.abs(value as number)))
|
|
523
|
-
} else {
|
|
524
|
-
initializer = factory.createNumericLiteral(value as number)
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
if (typeof value === 'boolean') {
|
|
529
|
-
initializer = value ? factory.createTrue() : factory.createFalse()
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
if (isNumber(Number.parseInt(key.toString(), 10))) {
|
|
533
|
-
const casingKey = applyEnumKeyCasing(`${typeName}_${key}`, enumKeyCasing)
|
|
534
|
-
return factory.createEnumMember(propertyName(casingKey), initializer)
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
if (key) {
|
|
538
|
-
const casingKey = applyEnumKeyCasing(key.toString(), enumKeyCasing)
|
|
539
|
-
return factory.createEnumMember(propertyName(casingKey), initializer)
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
return undefined
|
|
543
|
-
})
|
|
544
|
-
.filter(Boolean),
|
|
545
|
-
),
|
|
546
|
-
]
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
// used when using `as const` instead of an TypeScript enum.
|
|
550
|
-
// name is already PascalCase for asPascalConst and camelCase for asConst (set in Type.tsx)
|
|
551
|
-
// typeName has the Key suffix for type alias, so we use name for the const identifier
|
|
552
|
-
const identifierName = name
|
|
553
|
-
|
|
554
|
-
// When there are no enum items (empty or all-null enum), don't generate a runtime const.
|
|
555
|
-
// Return undefined for nameNode so the barrel won't try to export a non-existent symbol.
|
|
556
|
-
// Use `never` as the type alias to keep references valid without creating a broken const.
|
|
557
|
-
if (enums.length === 0) {
|
|
558
|
-
return [
|
|
559
|
-
undefined,
|
|
560
|
-
factory.createTypeAliasDeclaration(
|
|
561
|
-
[factory.createToken(ts.SyntaxKind.ExportKeyword)],
|
|
562
|
-
factory.createIdentifier(typeName),
|
|
563
|
-
undefined,
|
|
564
|
-
factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword),
|
|
565
|
-
),
|
|
566
|
-
]
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
return [
|
|
570
|
-
factory.createVariableStatement(
|
|
571
|
-
[factory.createToken(ts.SyntaxKind.ExportKeyword)],
|
|
572
|
-
factory.createVariableDeclarationList(
|
|
573
|
-
[
|
|
574
|
-
factory.createVariableDeclaration(
|
|
575
|
-
factory.createIdentifier(identifierName),
|
|
576
|
-
undefined,
|
|
577
|
-
undefined,
|
|
578
|
-
factory.createAsExpression(
|
|
579
|
-
factory.createObjectLiteralExpression(
|
|
580
|
-
enums
|
|
581
|
-
.map(([key, value]) => {
|
|
582
|
-
let initializer: ts.Expression = factory.createStringLiteral(value?.toString())
|
|
583
|
-
|
|
584
|
-
if (isNumber(value)) {
|
|
585
|
-
// Error: Negative numbers should be created in combination with createPrefixUnaryExpression factory.
|
|
586
|
-
// The method createNumericLiteral only accepts positive numbers
|
|
587
|
-
// or those combined with createPrefixUnaryExpression.
|
|
588
|
-
// Therefore, we need to ensure that the number is not negative.
|
|
589
|
-
if (value < 0) {
|
|
590
|
-
initializer = factory.createPrefixUnaryExpression(ts.SyntaxKind.MinusToken, factory.createNumericLiteral(Math.abs(value)))
|
|
591
|
-
} else {
|
|
592
|
-
initializer = factory.createNumericLiteral(value)
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
if (typeof value === 'boolean') {
|
|
597
|
-
initializer = value ? factory.createTrue() : factory.createFalse()
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
if (key) {
|
|
601
|
-
const casingKey = applyEnumKeyCasing(key.toString(), enumKeyCasing)
|
|
602
|
-
return factory.createPropertyAssignment(propertyName(casingKey), initializer)
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
return undefined
|
|
606
|
-
})
|
|
607
|
-
.filter(Boolean),
|
|
608
|
-
true,
|
|
609
|
-
),
|
|
610
|
-
factory.createTypeReferenceNode(factory.createIdentifier('const'), undefined),
|
|
611
|
-
),
|
|
612
|
-
),
|
|
613
|
-
],
|
|
614
|
-
ts.NodeFlags.Const,
|
|
615
|
-
),
|
|
616
|
-
),
|
|
617
|
-
factory.createTypeAliasDeclaration(
|
|
618
|
-
[factory.createToken(ts.SyntaxKind.ExportKeyword)],
|
|
619
|
-
factory.createIdentifier(typeName),
|
|
620
|
-
undefined,
|
|
621
|
-
factory.createIndexedAccessTypeNode(
|
|
622
|
-
factory.createParenthesizedType(factory.createTypeQueryNode(factory.createIdentifier(identifierName), undefined)),
|
|
623
|
-
factory.createTypeOperatorNode(ts.SyntaxKind.KeyOfKeyword, factory.createTypeQueryNode(factory.createIdentifier(identifierName), undefined)),
|
|
624
|
-
),
|
|
625
|
-
),
|
|
626
|
-
]
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
export function createOmitDeclaration({ keys, type, nonNullable }: { keys: Array<string> | string; type: ts.TypeNode; nonNullable?: boolean }) {
|
|
630
|
-
const node = nonNullable ? factory.createTypeReferenceNode(factory.createIdentifier('NonNullable'), [type]) : type
|
|
631
|
-
|
|
632
|
-
if (Array.isArray(keys)) {
|
|
633
|
-
return factory.createTypeReferenceNode(factory.createIdentifier('Omit'), [
|
|
634
|
-
node,
|
|
635
|
-
factory.createUnionTypeNode(
|
|
636
|
-
keys.map((key) => {
|
|
637
|
-
return factory.createLiteralTypeNode(factory.createStringLiteral(key))
|
|
638
|
-
}),
|
|
639
|
-
),
|
|
640
|
-
])
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
return factory.createTypeReferenceNode(factory.createIdentifier('Omit'), [node, factory.createLiteralTypeNode(factory.createStringLiteral(keys))])
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
export const keywordTypeNodes = {
|
|
647
|
-
any: factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword),
|
|
648
|
-
unknown: factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword),
|
|
649
|
-
void: factory.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword),
|
|
650
|
-
number: factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword),
|
|
651
|
-
integer: factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword),
|
|
652
|
-
bigint: factory.createKeywordTypeNode(ts.SyntaxKind.BigIntKeyword),
|
|
653
|
-
object: factory.createKeywordTypeNode(ts.SyntaxKind.ObjectKeyword),
|
|
654
|
-
string: factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
|
|
655
|
-
boolean: factory.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword),
|
|
656
|
-
undefined: factory.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword),
|
|
657
|
-
null: factory.createLiteralTypeNode(factory.createToken(ts.SyntaxKind.NullKeyword)),
|
|
658
|
-
never: factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword),
|
|
659
|
-
} as const
|
|
660
|
-
|
|
661
|
-
/**
|
|
662
|
-
* Converts a path like '/pet/{petId}/uploadImage' to a template literal type
|
|
663
|
-
* like `/pet/${string}/uploadImage`
|
|
664
|
-
*/
|
|
665
|
-
/**
|
|
666
|
-
* Converts an OAS-style path (e.g. `/pets/{petId}`) or an Express-style path
|
|
667
|
-
* (e.g. `/pets/:petId`) to a TypeScript template literal type
|
|
668
|
-
* like `` `/pets/${string}` ``.
|
|
669
|
-
*/
|
|
670
|
-
export function createUrlTemplateType(path: string): ts.TypeNode {
|
|
671
|
-
// normalized Express `:param` → OAS `{param}` so a single regex handles both.
|
|
672
|
-
const normalized = path.replace(/:([^/]+)/g, '{$1}')
|
|
673
|
-
|
|
674
|
-
if (!normalized.includes('{')) {
|
|
675
|
-
return factory.createLiteralTypeNode(factory.createStringLiteral(normalized))
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
const segments = normalized.split(/(\{[^}]+\})/)
|
|
679
|
-
const parts: string[] = []
|
|
680
|
-
const parameterIndices: number[] = []
|
|
681
|
-
|
|
682
|
-
segments.forEach((segment) => {
|
|
683
|
-
if (segment.startsWith('{') && segment.endsWith('}')) {
|
|
684
|
-
parameterIndices.push(parts.length)
|
|
685
|
-
parts.push(segment)
|
|
686
|
-
} else if (segment) {
|
|
687
|
-
parts.push(segment)
|
|
688
|
-
}
|
|
689
|
-
})
|
|
690
|
-
|
|
691
|
-
const head = ts.factory.createTemplateHead(parts[0] || '')
|
|
692
|
-
const templateSpans: ts.TemplateLiteralTypeSpan[] = []
|
|
693
|
-
|
|
694
|
-
parameterIndices.forEach((paramIndex, i) => {
|
|
695
|
-
const isLast = i === parameterIndices.length - 1
|
|
696
|
-
const nextPart = parts[paramIndex + 1] || ''
|
|
697
|
-
const literal = isLast ? ts.factory.createTemplateTail(nextPart) : ts.factory.createTemplateMiddle(nextPart)
|
|
698
|
-
templateSpans.push(ts.factory.createTemplateLiteralTypeSpan(keywordTypeNodes.string, literal))
|
|
699
|
-
})
|
|
700
|
-
|
|
701
|
-
return ts.factory.createTemplateLiteralType(head, templateSpans)
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
export const createTypeLiteralNode = factory.createTypeLiteralNode
|
|
705
|
-
|
|
706
|
-
export const createTypeReferenceNode = factory.createTypeReferenceNode
|
|
707
|
-
export const createNumericLiteral = factory.createNumericLiteral
|
|
708
|
-
export const createStringLiteral = factory.createStringLiteral
|
|
709
|
-
|
|
710
|
-
export const createArrayTypeNode = factory.createArrayTypeNode
|
|
711
|
-
export const createParenthesizedType = factory.createParenthesizedType
|
|
712
|
-
|
|
713
|
-
export const createLiteralTypeNode = factory.createLiteralTypeNode
|
|
714
|
-
export const createNull = factory.createNull
|
|
715
|
-
export const createIdentifier = factory.createIdentifier
|
|
716
|
-
|
|
717
|
-
export const createOptionalTypeNode = factory.createOptionalTypeNode
|
|
718
|
-
export const createTupleTypeNode = factory.createTupleTypeNode
|
|
719
|
-
export const createRestTypeNode = factory.createRestTypeNode
|
|
720
|
-
export const createTrue = factory.createTrue
|
|
721
|
-
export const createFalse = factory.createFalse
|
|
722
|
-
export const createIndexedAccessTypeNode = factory.createIndexedAccessTypeNode
|
|
723
|
-
export const createTypeOperatorNode = factory.createTypeOperatorNode
|
|
724
|
-
export const createPrefixUnaryExpression = factory.createPrefixUnaryExpression
|
|
725
|
-
|
|
726
|
-
export { SyntaxKind }
|