@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/factory.ts
CHANGED
|
@@ -662,43 +662,39 @@ export const keywordTypeNodes = {
|
|
|
662
662
|
* Converts a path like '/pet/{petId}/uploadImage' to a template literal type
|
|
663
663
|
* like `/pet/${string}/uploadImage`
|
|
664
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
|
+
*/
|
|
665
670
|
export function createUrlTemplateType(path: string): ts.TypeNode {
|
|
666
|
-
//
|
|
667
|
-
|
|
668
|
-
return factory.createLiteralTypeNode(factory.createStringLiteral(path))
|
|
669
|
-
}
|
|
671
|
+
// normalized Express `:param` → OAS `{param}` so a single regex handles both.
|
|
672
|
+
const normalized = path.replace(/:([^/]+)/g, '{$1}')
|
|
670
673
|
|
|
671
|
-
|
|
672
|
-
|
|
674
|
+
if (!normalized.includes('{')) {
|
|
675
|
+
return factory.createLiteralTypeNode(factory.createStringLiteral(normalized))
|
|
676
|
+
}
|
|
673
677
|
|
|
674
|
-
|
|
678
|
+
const segments = normalized.split(/(\{[^}]+\})/)
|
|
675
679
|
const parts: string[] = []
|
|
676
680
|
const parameterIndices: number[] = []
|
|
677
681
|
|
|
678
682
|
segments.forEach((segment) => {
|
|
679
683
|
if (segment.startsWith('{') && segment.endsWith('}')) {
|
|
680
|
-
// This is a parameter placeholder
|
|
681
684
|
parameterIndices.push(parts.length)
|
|
682
|
-
parts.push(segment)
|
|
685
|
+
parts.push(segment)
|
|
683
686
|
} else if (segment) {
|
|
684
|
-
// This is a static part
|
|
685
687
|
parts.push(segment)
|
|
686
688
|
}
|
|
687
689
|
})
|
|
688
690
|
|
|
689
|
-
// Build template literal type
|
|
690
|
-
// Template literal structure: head + templateSpans[]
|
|
691
|
-
// For '/pet/{petId}/upload': head = '/pet/', spans = [{ type: string, literal: '/upload' }]
|
|
692
|
-
|
|
693
691
|
const head = ts.factory.createTemplateHead(parts[0] || '')
|
|
694
692
|
const templateSpans: ts.TemplateLiteralTypeSpan[] = []
|
|
695
693
|
|
|
696
694
|
parameterIndices.forEach((paramIndex, i) => {
|
|
697
695
|
const isLast = i === parameterIndices.length - 1
|
|
698
696
|
const nextPart = parts[paramIndex + 1] || ''
|
|
699
|
-
|
|
700
697
|
const literal = isLast ? ts.factory.createTemplateTail(nextPart) : ts.factory.createTemplateMiddle(nextPart)
|
|
701
|
-
|
|
702
698
|
templateSpans.push(ts.factory.createTemplateLiteralTypeSpan(keywordTypeNodes.string, literal))
|
|
703
699
|
})
|
|
704
700
|
|
|
@@ -314,7 +314,7 @@ export const typeGenerator = createReactGenerator<PluginTs>({
|
|
|
314
314
|
Operation({ operation, generator, plugin }) {
|
|
315
315
|
const {
|
|
316
316
|
options,
|
|
317
|
-
options: {
|
|
317
|
+
options: { enumType, enumKeyCasing, syntaxType, optionalType, arrayType, unknownType, paramsCasing },
|
|
318
318
|
} = plugin
|
|
319
319
|
|
|
320
320
|
const mode = useMode()
|
|
@@ -369,7 +369,6 @@ export const typeGenerator = createReactGenerator<PluginTs>({
|
|
|
369
369
|
description={description}
|
|
370
370
|
tree={tree}
|
|
371
371
|
schema={transformedSchema}
|
|
372
|
-
mapper={mapper}
|
|
373
372
|
enumType={enumType}
|
|
374
373
|
enumKeyCasing={enumKeyCasing}
|
|
375
374
|
optionalType={optionalType}
|
|
@@ -416,7 +415,7 @@ export const typeGenerator = createReactGenerator<PluginTs>({
|
|
|
416
415
|
},
|
|
417
416
|
Schema({ schema, plugin }) {
|
|
418
417
|
const {
|
|
419
|
-
options: {
|
|
418
|
+
options: { enumType, enumKeyCasing, syntaxType, optionalType, arrayType, output },
|
|
420
419
|
} = plugin
|
|
421
420
|
const mode = useMode()
|
|
422
421
|
|
|
@@ -457,7 +456,6 @@ export const typeGenerator = createReactGenerator<PluginTs>({
|
|
|
457
456
|
description={schema.value.description}
|
|
458
457
|
tree={schema.tree}
|
|
459
458
|
schema={schema.value}
|
|
460
|
-
mapper={mapper}
|
|
461
459
|
enumType={enumType}
|
|
462
460
|
enumKeyCasing={enumKeyCasing}
|
|
463
461
|
optionalType={optionalType}
|
|
@@ -1,45 +1,55 @@
|
|
|
1
|
+
import { pascalCase } from '@internals/utils'
|
|
2
|
+
import { applyParamsCasing } from '@kubb/ast'
|
|
1
3
|
import type { SchemaNode } from '@kubb/ast/types'
|
|
2
4
|
import { defineGenerator } from '@kubb/core'
|
|
3
5
|
import { useKubb } from '@kubb/core/hooks'
|
|
4
6
|
import { File } from '@kubb/react-fabric'
|
|
5
7
|
import { Type } from '../../components/v2/Type.tsx'
|
|
8
|
+
import { ENUM_TYPES_WITH_KEY_SUFFIX } from '../../constants.ts'
|
|
6
9
|
import type { PluginTs } from '../../types'
|
|
10
|
+
import { buildDataSchemaNode, buildResponsesSchemaNode, buildResponseUnionSchemaNode } from './utils.ts'
|
|
7
11
|
|
|
8
12
|
export const typeGenerator = defineGenerator<PluginTs>({
|
|
9
13
|
name: 'typescript',
|
|
10
14
|
type: 'react',
|
|
11
15
|
Operation({ node, adapter, options }) {
|
|
12
|
-
const { enumType, enumKeyCasing, optionalType, arrayType, syntaxType } = options
|
|
13
|
-
|
|
14
|
-
const { plugin, mode, getFile, resolveName } = useKubb<PluginTs>()
|
|
16
|
+
const { enumType, enumKeyCasing, optionalType, arrayType, syntaxType, paramsCasing, group } = options
|
|
17
|
+
const { mode, getFile, resolveName } = useKubb<PluginTs>()
|
|
15
18
|
|
|
16
19
|
const file = getFile({
|
|
17
20
|
name: node.operationId,
|
|
18
|
-
pluginName: plugin.name,
|
|
19
21
|
extname: '.ts',
|
|
20
22
|
mode,
|
|
23
|
+
options: {
|
|
24
|
+
group: group ? (group.type === 'tag' ? { tag: node.tags[0] } : { path: node.path }) : undefined,
|
|
25
|
+
},
|
|
21
26
|
})
|
|
27
|
+
const params = applyParamsCasing(node.parameters, paramsCasing)
|
|
28
|
+
|
|
29
|
+
function renderSchemaType({
|
|
30
|
+
node: schemaNode,
|
|
31
|
+
name,
|
|
32
|
+
typedName,
|
|
33
|
+
description,
|
|
34
|
+
}: {
|
|
35
|
+
node: SchemaNode | null
|
|
36
|
+
name: string
|
|
37
|
+
typedName: string
|
|
38
|
+
description?: string
|
|
39
|
+
}) {
|
|
40
|
+
if (!schemaNode) {
|
|
41
|
+
return null
|
|
42
|
+
}
|
|
22
43
|
|
|
23
|
-
function renderSchemaType({ node: schemaNode, name, typedName, description }: { node: SchemaNode; name: string; typedName: string; description?: string }) {
|
|
24
44
|
const imports = adapter.getImports(schemaNode, (schemaName) => ({
|
|
25
|
-
name: resolveName({
|
|
26
|
-
|
|
27
|
-
pluginName: plugin.name,
|
|
28
|
-
type: 'type',
|
|
29
|
-
}),
|
|
30
|
-
path: getFile({
|
|
31
|
-
name: schemaName,
|
|
32
|
-
pluginName: plugin.name,
|
|
33
|
-
extname: '.ts',
|
|
34
|
-
mode,
|
|
35
|
-
}).path,
|
|
45
|
+
name: resolveName({ name: schemaName, type: 'type' }),
|
|
46
|
+
path: getFile({ name: schemaName, extname: '.ts', mode }).path,
|
|
36
47
|
}))
|
|
37
48
|
|
|
38
49
|
return (
|
|
39
50
|
<>
|
|
40
51
|
{mode === 'split' &&
|
|
41
52
|
imports.map((imp) => <File.Import key={[name, imp.path, imp.isTypeOnly].join('-')} root={file.path} path={imp.path} name={imp.name} isTypeOnly />)}
|
|
42
|
-
|
|
43
53
|
<Type
|
|
44
54
|
name={name}
|
|
45
55
|
typedName={typedName}
|
|
@@ -55,122 +65,88 @@ export const typeGenerator = defineGenerator<PluginTs>({
|
|
|
55
65
|
)
|
|
56
66
|
}
|
|
57
67
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
name: `${node.operationId} ${param.name}`,
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const typedName = resolveName({
|
|
66
|
-
name: `${node.operationId} ${param.name}`,
|
|
67
|
-
pluginName: plugin.name,
|
|
68
|
-
type: 'type',
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
return renderSchemaType({ node: param.schema, name, typedName })
|
|
72
|
-
})
|
|
68
|
+
const paramTypes = params.map((param) =>
|
|
69
|
+
renderSchemaType({
|
|
70
|
+
node: param.schema,
|
|
71
|
+
name: resolveName({ name: `${node.operationId} ${pascalCase(param.in)} ${param.name}`, type: 'function' }),
|
|
72
|
+
typedName: resolveName({ name: `${node.operationId} ${pascalCase(param.in)} ${param.name}`, type: 'type' }),
|
|
73
|
+
}),
|
|
74
|
+
)
|
|
73
75
|
|
|
74
|
-
// Response types
|
|
75
76
|
const responseTypes = node.responses
|
|
76
77
|
.filter((res) => res.schema)
|
|
77
|
-
.map((res) =>
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
name:
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const typedName = resolveName({
|
|
86
|
-
name: responseName,
|
|
87
|
-
pluginName: plugin.name,
|
|
88
|
-
type: 'type',
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
return renderSchemaType({ node: schemaNode, name: resolvedName, typedName, description: res.description })
|
|
92
|
-
})
|
|
78
|
+
.map((res) =>
|
|
79
|
+
renderSchemaType({
|
|
80
|
+
node: res.schema!,
|
|
81
|
+
name: resolveName({ name: `${node.operationId} Status ${res.statusCode}`, type: 'function' }),
|
|
82
|
+
typedName: resolveName({ name: `${node.operationId} Status ${res.statusCode}`, type: 'type' }),
|
|
83
|
+
description: res.description,
|
|
84
|
+
}),
|
|
85
|
+
)
|
|
93
86
|
|
|
94
|
-
// Request body type
|
|
95
87
|
const requestType = node.requestBody
|
|
96
|
-
? (
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
})
|
|
103
|
-
const typedName = resolveName({
|
|
104
|
-
name: requestName,
|
|
105
|
-
pluginName: plugin.name,
|
|
106
|
-
type: 'type',
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
return renderSchemaType({ node: node.requestBody, name: resolvedName, typedName, description: node.requestBody.description })
|
|
110
|
-
})()
|
|
88
|
+
? renderSchemaType({
|
|
89
|
+
node: node.requestBody,
|
|
90
|
+
name: resolveName({ name: `${node.operationId} Data`, type: 'function' }),
|
|
91
|
+
typedName: resolveName({ name: `${node.operationId} Data`, type: 'type' }),
|
|
92
|
+
description: node.requestBody.description,
|
|
93
|
+
})
|
|
111
94
|
: null
|
|
112
95
|
|
|
96
|
+
const dataType = renderSchemaType({
|
|
97
|
+
node: buildDataSchemaNode({ node: { ...node, parameters: params }, resolveName }),
|
|
98
|
+
name: resolveName({ name: `${node.operationId} RequestConfig`, type: 'function' }),
|
|
99
|
+
typedName: resolveName({ name: `${node.operationId} RequestConfig`, type: 'type' }),
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
const responsesType = renderSchemaType({
|
|
103
|
+
node: buildResponsesSchemaNode({ node, resolveName }),
|
|
104
|
+
name: resolveName({ name: `${node.operationId} Responses`, type: 'function' }),
|
|
105
|
+
typedName: resolveName({ name: `${node.operationId} Responses`, type: 'type' }),
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
const responseType = renderSchemaType({
|
|
109
|
+
node: buildResponseUnionSchemaNode({ node, resolveName }),
|
|
110
|
+
name: resolveName({ name: `${node.operationId} Response`, type: 'function' }),
|
|
111
|
+
typedName: resolveName({ name: `${node.operationId} Response`, type: 'type' }),
|
|
112
|
+
description: 'Union of all possible responses',
|
|
113
|
+
})
|
|
114
|
+
|
|
113
115
|
return (
|
|
114
116
|
<File baseName={file.baseName} path={file.path} meta={file.meta}>
|
|
115
117
|
{paramTypes}
|
|
116
118
|
{responseTypes}
|
|
117
119
|
{requestType}
|
|
120
|
+
{dataType}
|
|
121
|
+
{responsesType}
|
|
122
|
+
{responseType}
|
|
118
123
|
</File>
|
|
119
124
|
)
|
|
120
125
|
},
|
|
121
126
|
Schema({ node, adapter, options }) {
|
|
122
127
|
const { enumType, enumKeyCasing, syntaxType, optionalType, arrayType } = options
|
|
123
|
-
const {
|
|
128
|
+
const { mode, resolveName, getFile } = useKubb<PluginTs>()
|
|
124
129
|
|
|
125
130
|
if (!node.name) {
|
|
126
131
|
return
|
|
127
132
|
}
|
|
128
133
|
|
|
129
134
|
const imports = adapter.getImports(node, (schemaName) => ({
|
|
130
|
-
name: resolveName({
|
|
131
|
-
|
|
132
|
-
pluginName: plugin.name,
|
|
133
|
-
type: 'type',
|
|
134
|
-
}),
|
|
135
|
-
path: getFile({
|
|
136
|
-
name: schemaName,
|
|
137
|
-
pluginName: plugin.name,
|
|
138
|
-
extname: '.ts',
|
|
139
|
-
mode,
|
|
140
|
-
// options: {
|
|
141
|
-
// group
|
|
142
|
-
// },
|
|
143
|
-
}).path,
|
|
135
|
+
name: resolveName({ name: schemaName, type: 'type' }),
|
|
136
|
+
path: getFile({ name: schemaName, extname: '.ts', mode }).path,
|
|
144
137
|
}))
|
|
145
138
|
|
|
146
139
|
const isEnumSchema = node.type === 'enum'
|
|
147
140
|
|
|
148
|
-
let typedName = resolveName({
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
type: 'type',
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
if (['asConst', 'asPascalConst'].includes(enumType) && isEnumSchema) {
|
|
155
|
-
typedName = typedName += 'Key'
|
|
141
|
+
let typedName = resolveName({ name: node.name, type: 'type' })
|
|
142
|
+
if (ENUM_TYPES_WITH_KEY_SUFFIX.has(enumType) && isEnumSchema) {
|
|
143
|
+
typedName += 'Key'
|
|
156
144
|
}
|
|
157
145
|
|
|
158
146
|
const type = {
|
|
159
|
-
name: resolveName({
|
|
160
|
-
name: node.name,
|
|
161
|
-
pluginName: plugin.name,
|
|
162
|
-
type: 'function',
|
|
163
|
-
}),
|
|
147
|
+
name: resolveName({ name: node.name, type: 'function' }),
|
|
164
148
|
typedName,
|
|
165
|
-
file: getFile({
|
|
166
|
-
name: node.name,
|
|
167
|
-
pluginName: plugin.name,
|
|
168
|
-
extname: '.ts',
|
|
169
|
-
mode,
|
|
170
|
-
// options: {
|
|
171
|
-
// group
|
|
172
|
-
// },
|
|
173
|
-
}),
|
|
149
|
+
file: getFile({ name: node.name, extname: '.ts', mode }),
|
|
174
150
|
} as const
|
|
175
151
|
|
|
176
152
|
return (
|
|
@@ -179,7 +155,6 @@ export const typeGenerator = defineGenerator<PluginTs>({
|
|
|
179
155
|
imports.map((imp) => (
|
|
180
156
|
<File.Import key={[node.name, imp.path, imp.isTypeOnly].join('-')} root={type.file.path} path={imp.path} name={imp.name} isTypeOnly />
|
|
181
157
|
))}
|
|
182
|
-
|
|
183
158
|
<Type
|
|
184
159
|
name={type.name}
|
|
185
160
|
typedName={type.typedName}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { pascalCase } from '@internals/utils'
|
|
2
|
+
import { createProperty, createSchema } from '@kubb/ast'
|
|
3
|
+
import type { OperationNode, ParameterNode, SchemaNode } from '@kubb/ast/types'
|
|
4
|
+
|
|
5
|
+
type ResolveName = (opts: { name: string; type: 'type' | 'function' }) => string
|
|
6
|
+
|
|
7
|
+
type BuildParamsSchemaOptions = {
|
|
8
|
+
params: Array<ParameterNode>
|
|
9
|
+
operationId: string
|
|
10
|
+
resolveName: ResolveName
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Builds an `ObjectSchemaNode` for a group of parameters (path/query/header).
|
|
15
|
+
* Each property is a `ref` schema pointing to the individually-resolved parameter type.
|
|
16
|
+
* The ref name includes the parameter location so generated type names follow
|
|
17
|
+
* the `<OperationId><Location><ParamName>` convention.
|
|
18
|
+
*/
|
|
19
|
+
export function buildParamsSchema({ params, operationId, resolveName }: BuildParamsSchemaOptions): SchemaNode {
|
|
20
|
+
return createSchema({
|
|
21
|
+
type: 'object',
|
|
22
|
+
properties: params.map((param) =>
|
|
23
|
+
createProperty({
|
|
24
|
+
name: param.name,
|
|
25
|
+
schema: createSchema({
|
|
26
|
+
type: 'ref',
|
|
27
|
+
name: resolveName({ name: `${operationId} ${pascalCase(param.in)} ${param.name}`, type: 'function' }),
|
|
28
|
+
optional: !param.required,
|
|
29
|
+
}),
|
|
30
|
+
}),
|
|
31
|
+
),
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
type BuildOperationSchemaOptions = {
|
|
36
|
+
node: OperationNode
|
|
37
|
+
resolveName: ResolveName
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Builds an `ObjectSchemaNode` representing the `<OperationId>RequestConfig` type:
|
|
42
|
+
* - `data` → request body ref (optional) or `never`
|
|
43
|
+
* - `pathParams` → inline object of path param refs, or `never`
|
|
44
|
+
* - `queryParams` → inline object of query param refs (optional), or `never`
|
|
45
|
+
* - `headerParams` → inline object of header param refs (optional), or `never`
|
|
46
|
+
* - `url` → Express-style template literal (plugin-ts extension, handled by printer)
|
|
47
|
+
*/
|
|
48
|
+
export function buildDataSchemaNode({ node, resolveName }: BuildOperationSchemaOptions): SchemaNode {
|
|
49
|
+
const pathParams = node.parameters.filter((p) => p.in === 'path')
|
|
50
|
+
const queryParams = node.parameters.filter((p) => p.in === 'query')
|
|
51
|
+
const headerParams = node.parameters.filter((p) => p.in === 'header')
|
|
52
|
+
|
|
53
|
+
return createSchema({
|
|
54
|
+
type: 'object',
|
|
55
|
+
properties: [
|
|
56
|
+
createProperty({
|
|
57
|
+
name: 'data',
|
|
58
|
+
schema: node.requestBody
|
|
59
|
+
? createSchema({
|
|
60
|
+
type: 'ref',
|
|
61
|
+
name: resolveName({ name: `${node.operationId} Data`, type: 'function' }),
|
|
62
|
+
optional: true,
|
|
63
|
+
})
|
|
64
|
+
: createSchema({ type: 'never', optional: true }),
|
|
65
|
+
}),
|
|
66
|
+
createProperty({
|
|
67
|
+
name: 'pathParams',
|
|
68
|
+
schema:
|
|
69
|
+
pathParams.length > 0
|
|
70
|
+
? buildParamsSchema({ params: pathParams, operationId: node.operationId, resolveName })
|
|
71
|
+
: createSchema({ type: 'never', optional: true }),
|
|
72
|
+
}),
|
|
73
|
+
createProperty({
|
|
74
|
+
name: 'queryParams',
|
|
75
|
+
schema:
|
|
76
|
+
queryParams.length > 0
|
|
77
|
+
? createSchema({ ...buildParamsSchema({ params: queryParams, operationId: node.operationId, resolveName }), optional: true })
|
|
78
|
+
: createSchema({ type: 'never', optional: true }),
|
|
79
|
+
}),
|
|
80
|
+
createProperty({
|
|
81
|
+
name: 'headerParams',
|
|
82
|
+
schema:
|
|
83
|
+
headerParams.length > 0
|
|
84
|
+
? createSchema({ ...buildParamsSchema({ params: headerParams, operationId: node.operationId, resolveName }), optional: true })
|
|
85
|
+
: createSchema({ type: 'never', optional: true }),
|
|
86
|
+
}),
|
|
87
|
+
createProperty({
|
|
88
|
+
name: 'url',
|
|
89
|
+
schema: createSchema({ type: 'url', path: node.path }),
|
|
90
|
+
}),
|
|
91
|
+
],
|
|
92
|
+
})
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Builds an `ObjectSchemaNode` representing `<OperationId>Responses` — keyed by HTTP status code.
|
|
97
|
+
* Numeric status codes produce unquoted numeric keys (e.g. `200:`).
|
|
98
|
+
*/
|
|
99
|
+
export function buildResponsesSchemaNode({ node, resolveName }: BuildOperationSchemaOptions): SchemaNode | null {
|
|
100
|
+
const responsesWithSchema = node.responses.filter((res) => res.schema)
|
|
101
|
+
|
|
102
|
+
if (responsesWithSchema.length === 0) {
|
|
103
|
+
return null
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return createSchema({
|
|
107
|
+
type: 'object',
|
|
108
|
+
properties: responsesWithSchema.map((res) =>
|
|
109
|
+
createProperty({
|
|
110
|
+
name: String(res.statusCode),
|
|
111
|
+
schema: createSchema({
|
|
112
|
+
type: 'ref',
|
|
113
|
+
name: resolveName({ name: `${node.operationId} Status ${res.statusCode}`, type: 'function' }),
|
|
114
|
+
}),
|
|
115
|
+
}),
|
|
116
|
+
),
|
|
117
|
+
})
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Builds a `UnionSchemaNode` representing `<OperationId>Response` — all response types in union format.
|
|
122
|
+
* Returns `null` when the operation has no responses with schemas.
|
|
123
|
+
*/
|
|
124
|
+
export function buildResponseUnionSchemaNode({ node, resolveName }: BuildOperationSchemaOptions): SchemaNode | null {
|
|
125
|
+
const responsesWithSchema = node.responses.filter((res) => res.schema)
|
|
126
|
+
|
|
127
|
+
if (responsesWithSchema.length === 0) {
|
|
128
|
+
return null
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return createSchema({
|
|
132
|
+
type: 'union',
|
|
133
|
+
members: responsesWithSchema.map((res) =>
|
|
134
|
+
createSchema({
|
|
135
|
+
type: 'ref',
|
|
136
|
+
name: resolveName({ name: `${node.operationId} Status ${res.statusCode}`, type: 'function' }),
|
|
137
|
+
}),
|
|
138
|
+
),
|
|
139
|
+
})
|
|
140
|
+
}
|
package/src/parser.ts
CHANGED
|
@@ -159,7 +159,6 @@ type ParserOptions = {
|
|
|
159
159
|
* @note In Kubb v5, `inlineLiteral` becomes the default.
|
|
160
160
|
*/
|
|
161
161
|
enumType: 'enum' | 'asConst' | 'asPascalConst' | 'constEnum' | 'literal' | 'inlineLiteral'
|
|
162
|
-
mapper?: Record<string, ts.PropertySignature>
|
|
163
162
|
}
|
|
164
163
|
|
|
165
164
|
/**
|
|
@@ -170,7 +169,7 @@ type ParserOptions = {
|
|
|
170
169
|
* @param current - The schema node to parse.
|
|
171
170
|
* @param siblings - Sibling schema nodes, used for context in certain mappings.
|
|
172
171
|
* @param name - The name of the schema or property being parsed.
|
|
173
|
-
* @param options - Parsing options controlling output style, property handling
|
|
172
|
+
* @param options - Parsing options controlling output style, property handling.
|
|
174
173
|
* @returns The generated TypeScript AST node, or `undefined` if the schema keyword is not mapped.
|
|
175
174
|
*/
|
|
176
175
|
export const parse = createParser<ts.Node | null, ParserOptions>({
|
|
@@ -254,12 +253,6 @@ export const parse = createParser<ts.Node | null, ParserOptions>({
|
|
|
254
253
|
const nameSchema = schemas.find((schema) => schema.keyword === schemaKeywords.name) as SchemaKeywordMapper['name']
|
|
255
254
|
const mappedName = nameSchema?.args || name
|
|
256
255
|
|
|
257
|
-
// custom mapper(pluginOptions)
|
|
258
|
-
// Use Object.hasOwn to avoid matching inherited properties like 'toString', 'valueOf', etc.
|
|
259
|
-
if (options.mapper && Object.hasOwn(options.mapper, mappedName)) {
|
|
260
|
-
return options.mapper[mappedName]
|
|
261
|
-
}
|
|
262
|
-
|
|
263
256
|
const isNullish = schemas.some((schema) => schema.keyword === schemaKeywords.nullish)
|
|
264
257
|
const isNullable = schemas.some((schema) => schema.keyword === schemaKeywords.nullable)
|
|
265
258
|
const isOptional = schemas.some((schema) => schema.keyword === schemaKeywords.optional)
|
package/src/plugin.ts
CHANGED
|
@@ -26,7 +26,6 @@ export const pluginTs = definePlugin<PluginTs>((options) => {
|
|
|
26
26
|
emptySchemaType = unknownType,
|
|
27
27
|
syntaxType = 'type',
|
|
28
28
|
transformers = {},
|
|
29
|
-
mapper = {},
|
|
30
29
|
paramsCasing,
|
|
31
30
|
generators = [typeGenerator, typeGeneratorV2].filter(Boolean),
|
|
32
31
|
contentType,
|
|
@@ -53,7 +52,6 @@ export const pluginTs = definePlugin<PluginTs>((options) => {
|
|
|
53
52
|
syntaxType,
|
|
54
53
|
group,
|
|
55
54
|
override,
|
|
56
|
-
mapper,
|
|
57
55
|
paramsCasing,
|
|
58
56
|
usedEnumNames,
|
|
59
57
|
},
|
|
@@ -167,6 +165,17 @@ export const pluginTs = definePlugin<PluginTs>((options) => {
|
|
|
167
165
|
{ depth: 'shallow' },
|
|
168
166
|
)
|
|
169
167
|
|
|
168
|
+
const barrelFiles = await getBarrelFiles(this.fabric.files, {
|
|
169
|
+
type: output.barrelType ?? 'named',
|
|
170
|
+
root,
|
|
171
|
+
output,
|
|
172
|
+
meta: {
|
|
173
|
+
pluginName: this.plugin.name,
|
|
174
|
+
},
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
await this.upsertFile(...barrelFiles)
|
|
178
|
+
|
|
170
179
|
return
|
|
171
180
|
}
|
|
172
181
|
|