@kubb/plugin-ts 5.0.0-alpha.2 → 5.0.0-alpha.20
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/Type-B70QnSzH.cjs +688 -0
- package/dist/Type-B70QnSzH.cjs.map +1 -0
- package/dist/Type-CMC7L-38.js +671 -0
- package/dist/Type-CMC7L-38.js.map +1 -0
- package/dist/casing-Cp-jbC_k.js +84 -0
- package/dist/casing-Cp-jbC_k.js.map +1 -0
- package/dist/casing-D2uQKLWS.cjs +144 -0
- package/dist/casing-D2uQKLWS.cjs.map +1 -0
- package/dist/components.cjs +3 -2
- package/dist/components.d.ts +41 -11
- package/dist/components.js +2 -2
- package/dist/generators-BFkr7ecU.js +556 -0
- package/dist/generators-BFkr7ecU.js.map +1 -0
- package/dist/generators-xHWQCNd9.cjs +560 -0
- package/dist/generators-xHWQCNd9.cjs.map +1 -0
- package/dist/generators.cjs +2 -2
- package/dist/generators.d.ts +3 -491
- package/dist/generators.js +1 -1
- package/dist/index.cjs +146 -3
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +145 -1
- package/dist/index.js.map +1 -0
- package/dist/resolvers-DsKabI0F.js +184 -0
- package/dist/resolvers-DsKabI0F.js.map +1 -0
- package/dist/resolvers-YIpeP5YD.cjs +194 -0
- package/dist/resolvers-YIpeP5YD.cjs.map +1 -0
- package/dist/resolvers.cjs +4 -0
- package/dist/resolvers.d.ts +52 -0
- package/dist/resolvers.js +2 -0
- package/dist/types-zqLMbIqZ.d.ts +340 -0
- package/package.json +15 -8
- package/src/components/Enum.tsx +83 -0
- package/src/components/Type.tsx +25 -144
- package/src/components/index.ts +1 -0
- package/src/constants.ts +29 -0
- package/src/factory.ts +14 -16
- package/src/generators/typeGenerator.tsx +221 -414
- package/src/generators/utils.ts +308 -0
- package/src/index.ts +1 -1
- package/src/plugin.ts +74 -87
- package/src/presets.ts +23 -0
- package/src/printer.ts +256 -92
- package/src/resolvers/index.ts +2 -0
- package/src/resolvers/resolverTs.ts +104 -0
- package/src/resolvers/resolverTsLegacy.ts +87 -0
- package/src/types.ts +234 -63
- package/dist/components-9wydyqUx.cjs +0 -848
- package/dist/components-9wydyqUx.cjs.map +0 -1
- package/dist/components-LmqJfxMv.js +0 -721
- package/dist/components-LmqJfxMv.js.map +0 -1
- package/dist/plugin-CNkzbtpl.cjs +0 -508
- package/dist/plugin-CNkzbtpl.cjs.map +0 -1
- package/dist/plugin-DoLrDl9P.js +0 -476
- package/dist/plugin-DoLrDl9P.js.map +0 -1
- package/dist/types-BpeKGgCn.d.ts +0 -170
- package/src/parser.ts +0 -396
package/src/components/Type.tsx
CHANGED
|
@@ -1,25 +1,23 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { collect, narrowSchema, schemaTypes } from '@kubb/ast'
|
|
2
|
+
import type { EnumSchemaNode, SchemaNode } from '@kubb/ast/types'
|
|
2
3
|
import { safePrint } from '@kubb/fabric-core/parsers/typescript'
|
|
3
|
-
import type { SchemaObject } from '@kubb/oas'
|
|
4
|
-
import { isKeyword, type Schema, SchemaGenerator, schemaKeywords } from '@kubb/plugin-oas'
|
|
5
4
|
import { File } from '@kubb/react-fabric'
|
|
6
5
|
import type { FabricReactNode } from '@kubb/react-fabric/types'
|
|
7
|
-
import
|
|
8
|
-
import * as factory from '../factory.ts'
|
|
9
|
-
import { parse, typeKeywordMapper } from '../parser.ts'
|
|
6
|
+
import { printerTs } from '../printer.ts'
|
|
10
7
|
import type { PluginTs } from '../types.ts'
|
|
8
|
+
import { Enum, getEnumNames } from './Enum.tsx'
|
|
11
9
|
|
|
12
10
|
type Props = {
|
|
13
11
|
name: string
|
|
14
12
|
typedName: string
|
|
15
|
-
|
|
16
|
-
tree: Array<Schema>
|
|
13
|
+
node: SchemaNode
|
|
17
14
|
optionalType: PluginTs['resolvedOptions']['optionalType']
|
|
18
15
|
arrayType: PluginTs['resolvedOptions']['arrayType']
|
|
19
16
|
enumType: PluginTs['resolvedOptions']['enumType']
|
|
20
17
|
enumKeyCasing: PluginTs['resolvedOptions']['enumKeyCasing']
|
|
21
|
-
mapper: PluginTs['resolvedOptions']['mapper']
|
|
22
18
|
syntaxType: PluginTs['resolvedOptions']['syntaxType']
|
|
19
|
+
resolver: PluginTs['resolvedOptions']['resolver']
|
|
20
|
+
legacy?: boolean
|
|
23
21
|
description?: string
|
|
24
22
|
keysToOmit?: string[]
|
|
25
23
|
}
|
|
@@ -27,133 +25,35 @@ type Props = {
|
|
|
27
25
|
export function Type({
|
|
28
26
|
name,
|
|
29
27
|
typedName,
|
|
30
|
-
|
|
28
|
+
node,
|
|
31
29
|
keysToOmit,
|
|
32
|
-
schema,
|
|
33
30
|
optionalType,
|
|
34
31
|
arrayType,
|
|
35
32
|
syntaxType,
|
|
36
33
|
enumType,
|
|
37
34
|
enumKeyCasing,
|
|
38
|
-
mapper,
|
|
39
35
|
description,
|
|
36
|
+
resolver,
|
|
40
37
|
}: Props): FabricReactNode {
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const enumSchemas = SchemaGenerator.deepSearch(tree, schemaKeywords.enum)
|
|
49
|
-
|
|
50
|
-
let type =
|
|
51
|
-
(tree
|
|
52
|
-
.map((current, _index, siblings) =>
|
|
53
|
-
parse(
|
|
54
|
-
{ name, schema, parent: undefined, current, siblings },
|
|
55
|
-
{
|
|
56
|
-
optionalType,
|
|
57
|
-
arrayType,
|
|
58
|
-
enumType,
|
|
59
|
-
mapper,
|
|
60
|
-
},
|
|
61
|
-
),
|
|
62
|
-
)
|
|
63
|
-
.filter(Boolean)
|
|
64
|
-
.at(0) as ts.TypeNode) || typeKeywordMapper.undefined()
|
|
65
|
-
|
|
66
|
-
// Add a "Key" suffix to avoid collisions where necessary
|
|
67
|
-
if (['asConst', 'asPascalConst'].includes(enumType) && enumSchemas.length > 0) {
|
|
68
|
-
const isDirectEnum = schema.type === 'array' && schema.items !== undefined
|
|
69
|
-
const isEnumOnly = 'enum' in schema && schema.enum
|
|
70
|
-
|
|
71
|
-
if (isDirectEnum || isEnumOnly) {
|
|
72
|
-
const enumSchema = enumSchemas[0]!
|
|
73
|
-
const typeNameWithKey = `${enumSchema.args.typeName}Key`
|
|
74
|
-
|
|
75
|
-
type = factory.createTypeReferenceNode(typeNameWithKey)
|
|
76
|
-
|
|
77
|
-
if (schema.type === 'array') {
|
|
78
|
-
if (arrayType === 'generic') {
|
|
79
|
-
type = factory.createTypeReferenceNode(factory.createIdentifier('Array'), [type])
|
|
80
|
-
} else {
|
|
81
|
-
type = factory.createArrayTypeNode(type)
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (schemaFromTree && isKeyword(schemaFromTree, schemaKeywords.schema)) {
|
|
88
|
-
const isNullish = tree.some((item) => item.keyword === schemaKeywords.nullish)
|
|
89
|
-
const isNullable = tree.some((item) => item.keyword === schemaKeywords.nullable)
|
|
90
|
-
const isOptional = tree.some((item) => item.keyword === schemaKeywords.optional)
|
|
38
|
+
const resolvedDescription = description || node?.description
|
|
39
|
+
const enumSchemaNodes = collect<EnumSchemaNode>(node, {
|
|
40
|
+
schema(n): EnumSchemaNode | undefined {
|
|
41
|
+
const enumNode = narrowSchema(n, schemaTypes.enum)
|
|
42
|
+
if (enumNode?.name) return enumNode
|
|
43
|
+
},
|
|
44
|
+
})
|
|
91
45
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
nodes: [type, factory.keywordTypeNodes.null],
|
|
95
|
-
}) as ts.TypeNode
|
|
96
|
-
}
|
|
46
|
+
const printer = printerTs({ optionalType, arrayType, enumType, typeName: name, syntaxType, description: resolvedDescription, keysToOmit, resolver })
|
|
47
|
+
const typeNode = printer.print(node)
|
|
97
48
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
nodes: [type, factory.keywordTypeNodes.undefined],
|
|
101
|
-
}) as ts.TypeNode
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (isOptional && ['undefined', 'questionTokenAndUndefined'].includes(optionalType as string)) {
|
|
105
|
-
type = factory.createUnionDeclaration({
|
|
106
|
-
nodes: [type, factory.keywordTypeNodes.undefined],
|
|
107
|
-
}) as ts.TypeNode
|
|
108
|
-
}
|
|
49
|
+
if (!typeNode) {
|
|
50
|
+
return
|
|
109
51
|
}
|
|
110
52
|
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
typeNodes.push(
|
|
114
|
-
factory.createTypeDeclaration({
|
|
115
|
-
name,
|
|
116
|
-
isExportable: true,
|
|
117
|
-
type: keysToOmit?.length
|
|
118
|
-
? factory.createOmitDeclaration({
|
|
119
|
-
keys: keysToOmit,
|
|
120
|
-
type,
|
|
121
|
-
nonNullable: true,
|
|
122
|
-
})
|
|
123
|
-
: type,
|
|
124
|
-
syntax: useTypeGeneration ? 'type' : 'interface',
|
|
125
|
-
comments: [
|
|
126
|
-
schema.title ? `${jsStringEscape(schema.title)}` : undefined,
|
|
127
|
-
description ? `@description ${jsStringEscape(description)}` : undefined,
|
|
128
|
-
schema.deprecated ? '@deprecated' : undefined,
|
|
129
|
-
schema.minLength ? `@minLength ${schema.minLength}` : undefined,
|
|
130
|
-
schema.maxLength ? `@maxLength ${schema.maxLength}` : undefined,
|
|
131
|
-
schema.pattern ? `@pattern ${schema.pattern}` : undefined,
|
|
132
|
-
schema.default ? `@default ${schema.default}` : undefined,
|
|
133
|
-
schema.example ? `@example ${schema.example}` : undefined,
|
|
134
|
-
],
|
|
135
|
-
}),
|
|
136
|
-
)
|
|
137
|
-
|
|
138
|
-
const enums = [...new Set(enumSchemas)].map((enumSchema) => {
|
|
139
|
-
const name = enumType === 'asPascalConst' ? pascalCase(enumSchema.args.name) : camelCase(enumSchema.args.name)
|
|
140
|
-
const typeName = ['asConst', 'asPascalConst'].includes(enumType) ? `${enumSchema.args.typeName}Key` : enumSchema.args.typeName
|
|
141
|
-
|
|
142
|
-
const [nameNode, typeNode] = factory.createEnumDeclaration({
|
|
143
|
-
name,
|
|
144
|
-
typeName,
|
|
145
|
-
enums: enumSchema.args.items
|
|
146
|
-
.map((item) => (item.value === undefined ? undefined : [trimQuotes(item.name?.toString()), item.value]))
|
|
147
|
-
.filter(Boolean) as unknown as Array<[string, string]>,
|
|
148
|
-
type: enumType,
|
|
149
|
-
enumKeyCasing,
|
|
150
|
-
})
|
|
151
|
-
|
|
53
|
+
const enums = [...new Map(enumSchemaNodes.map((n) => [n.name, n])).values()].map((node) => {
|
|
152
54
|
return {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
name,
|
|
156
|
-
typeName,
|
|
55
|
+
node,
|
|
56
|
+
...getEnumNames({ node, enumType, resolver }),
|
|
157
57
|
}
|
|
158
58
|
})
|
|
159
59
|
|
|
@@ -163,29 +63,10 @@ export function Type({
|
|
|
163
63
|
|
|
164
64
|
return (
|
|
165
65
|
<>
|
|
166
|
-
{shouldExportEnums &&
|
|
167
|
-
enums.map(({ name, nameNode, typeName, typeNode }) => (
|
|
168
|
-
<>
|
|
169
|
-
{nameNode && (
|
|
170
|
-
<File.Source name={name} isExportable isIndexable isTypeOnly={false}>
|
|
171
|
-
{safePrint(nameNode)}
|
|
172
|
-
</File.Source>
|
|
173
|
-
)}
|
|
174
|
-
{
|
|
175
|
-
<File.Source
|
|
176
|
-
name={typeName}
|
|
177
|
-
isIndexable
|
|
178
|
-
isExportable={['enum', 'asConst', 'asPascalConst', 'constEnum', 'literal', undefined].includes(enumType)}
|
|
179
|
-
isTypeOnly={['asConst', 'asPascalConst', 'literal', undefined].includes(enumType)}
|
|
180
|
-
>
|
|
181
|
-
{safePrint(typeNode)}
|
|
182
|
-
</File.Source>
|
|
183
|
-
}
|
|
184
|
-
</>
|
|
185
|
-
))}
|
|
66
|
+
{shouldExportEnums && enums.map(({ node }) => <Enum node={node} enumType={enumType} enumKeyCasing={enumKeyCasing} resolver={resolver} />)}
|
|
186
67
|
{shouldExportType && (
|
|
187
68
|
<File.Source name={typedName} isTypeOnly isExportable isIndexable>
|
|
188
|
-
{safePrint(
|
|
69
|
+
{safePrint(typeNode)}
|
|
189
70
|
</File.Source>
|
|
190
71
|
)}
|
|
191
72
|
</>
|
package/src/components/index.ts
CHANGED
package/src/constants.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { PluginTs } from './types.ts'
|
|
2
|
+
|
|
3
|
+
type OptionalType = PluginTs['resolvedOptions']['optionalType']
|
|
4
|
+
type EnumType = PluginTs['resolvedOptions']['enumType']
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* `optionalType` values that cause a property's type to include `| undefined`.
|
|
8
|
+
*/
|
|
9
|
+
export const OPTIONAL_ADDS_UNDEFINED = new Set<OptionalType>(['undefined', 'questionTokenAndUndefined'] as const)
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* `optionalType` values that render the property key with a `?` token.
|
|
13
|
+
*/
|
|
14
|
+
export const OPTIONAL_ADDS_QUESTION_TOKEN = new Set<OptionalType>(['questionToken', 'questionTokenAndUndefined'] as const)
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* `enumType` values that append a `Key` suffix to the generated enum type alias.
|
|
18
|
+
*/
|
|
19
|
+
export const ENUM_TYPES_WITH_KEY_SUFFIX = new Set<EnumType>(['asConst', 'asPascalConst'] as const)
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* `enumType` values that require a runtime value declaration (object, enum, or literal).
|
|
23
|
+
*/
|
|
24
|
+
export const ENUM_TYPES_WITH_RUNTIME_VALUE = new Set<EnumType | undefined>(['enum', 'asConst', 'asPascalConst', 'constEnum', 'literal', undefined] as const)
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* `enumType` values whose type declaration is type-only (no runtime value emitted for the type alias).
|
|
28
|
+
*/
|
|
29
|
+
export const ENUM_TYPES_WITH_TYPE_ONLY = new Set<EnumType | undefined>(['asConst', 'asPascalConst', 'literal', undefined] as const)
|
package/src/factory.ts
CHANGED
|
@@ -15,6 +15,8 @@ export const modifiers = {
|
|
|
15
15
|
|
|
16
16
|
export const syntaxKind = {
|
|
17
17
|
union: SyntaxKind.UnionType as 192,
|
|
18
|
+
literalType: SyntaxKind.LiteralType,
|
|
19
|
+
stringLiteral: SyntaxKind.StringLiteral,
|
|
18
20
|
} as const
|
|
19
21
|
|
|
20
22
|
export function getUnknownType(unknownType: 'any' | 'unknown' | 'void' | undefined) {
|
|
@@ -660,43 +662,39 @@ export const keywordTypeNodes = {
|
|
|
660
662
|
* Converts a path like '/pet/{petId}/uploadImage' to a template literal type
|
|
661
663
|
* like `/pet/${string}/uploadImage`
|
|
662
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
|
+
*/
|
|
663
670
|
export function createUrlTemplateType(path: string): ts.TypeNode {
|
|
664
|
-
//
|
|
665
|
-
|
|
666
|
-
return factory.createLiteralTypeNode(factory.createStringLiteral(path))
|
|
667
|
-
}
|
|
671
|
+
// normalized Express `:param` → OAS `{param}` so a single regex handles both.
|
|
672
|
+
const normalized = path.replace(/:([^/]+)/g, '{$1}')
|
|
668
673
|
|
|
669
|
-
|
|
670
|
-
|
|
674
|
+
if (!normalized.includes('{')) {
|
|
675
|
+
return factory.createLiteralTypeNode(factory.createStringLiteral(normalized))
|
|
676
|
+
}
|
|
671
677
|
|
|
672
|
-
|
|
678
|
+
const segments = normalized.split(/(\{[^}]+\})/)
|
|
673
679
|
const parts: string[] = []
|
|
674
680
|
const parameterIndices: number[] = []
|
|
675
681
|
|
|
676
682
|
segments.forEach((segment) => {
|
|
677
683
|
if (segment.startsWith('{') && segment.endsWith('}')) {
|
|
678
|
-
// This is a parameter placeholder
|
|
679
684
|
parameterIndices.push(parts.length)
|
|
680
|
-
parts.push(segment)
|
|
685
|
+
parts.push(segment)
|
|
681
686
|
} else if (segment) {
|
|
682
|
-
// This is a static part
|
|
683
687
|
parts.push(segment)
|
|
684
688
|
}
|
|
685
689
|
})
|
|
686
690
|
|
|
687
|
-
// Build template literal type
|
|
688
|
-
// Template literal structure: head + templateSpans[]
|
|
689
|
-
// For '/pet/{petId}/upload': head = '/pet/', spans = [{ type: string, literal: '/upload' }]
|
|
690
|
-
|
|
691
691
|
const head = ts.factory.createTemplateHead(parts[0] || '')
|
|
692
692
|
const templateSpans: ts.TemplateLiteralTypeSpan[] = []
|
|
693
693
|
|
|
694
694
|
parameterIndices.forEach((paramIndex, i) => {
|
|
695
695
|
const isLast = i === parameterIndices.length - 1
|
|
696
696
|
const nextPart = parts[paramIndex + 1] || ''
|
|
697
|
-
|
|
698
697
|
const literal = isLast ? ts.factory.createTemplateTail(nextPart) : ts.factory.createTemplateMiddle(nextPart)
|
|
699
|
-
|
|
700
698
|
templateSpans.push(ts.factory.createTemplateLiteralTypeSpan(keywordTypeNodes.string, literal))
|
|
701
699
|
})
|
|
702
700
|
|