@kubb/plugin-ts 5.0.0-alpha.8 → 5.0.0-beta.10
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 +26 -7
- package/dist/index.cjs +1526 -4
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +574 -4
- package/dist/index.js +1488 -2
- package/dist/index.js.map +1 -0
- package/extension.yaml +632 -0
- package/package.json +43 -65
- package/src/components/{v2/Enum.tsx → Enum.tsx} +33 -17
- package/src/components/Type.tsx +31 -161
- package/src/constants.ts +15 -5
- package/src/factory.ts +295 -39
- package/src/generators/typeGenerator.tsx +248 -420
- package/src/index.ts +9 -2
- package/src/plugin.ts +67 -205
- package/src/printers/functionPrinter.ts +197 -0
- package/src/printers/printerTs.ts +329 -0
- package/src/resolvers/resolverTs.ts +66 -0
- package/src/types.ts +238 -94
- package/src/utils.ts +129 -0
- 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 -480
- package/dist/generators.js +0 -2
- package/dist/plugin-D5NGPj0v.js +0 -1232
- package/dist/plugin-D5NGPj0v.js.map +0 -1
- package/dist/plugin-MLTxoa8p.cjs +0 -1279
- package/dist/plugin-MLTxoa8p.cjs.map +0 -1
- package/dist/types-CsvB6X5Y.d.ts +0 -167
- package/src/components/index.ts +0 -1
- package/src/components/v2/Type.tsx +0 -59
- package/src/generators/index.ts +0 -2
- package/src/generators/v2/typeGenerator.tsx +0 -171
- package/src/generators/v2/utils.ts +0 -140
- package/src/parser.ts +0 -389
- package/src/printer.ts +0 -368
|
@@ -1,467 +1,295 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import { isKeyword, type OperationSchemas, type OperationSchema as OperationSchemaType, SchemaGenerator, schemaKeywords } from '@kubb/plugin-oas'
|
|
7
|
-
import { createReactGenerator } from '@kubb/plugin-oas/generators'
|
|
8
|
-
import { useOas, useOperationManager, useSchemaManager } from '@kubb/plugin-oas/hooks'
|
|
9
|
-
import { applyParamsCasing, getBanner, getFooter, getImports, isParameterSchema } from '@kubb/plugin-oas/utils'
|
|
10
|
-
import { File } from '@kubb/react-fabric'
|
|
11
|
-
import ts from 'typescript'
|
|
12
|
-
import { Type } from '../components'
|
|
13
|
-
import * as factory from '../factory.ts'
|
|
14
|
-
import { createUrlTemplateType, getUnknownType, keywordTypeNodes } from '../factory.ts'
|
|
15
|
-
import { pluginTsName } from '../plugin.ts'
|
|
1
|
+
import { ast, defineGenerator } from '@kubb/core'
|
|
2
|
+
import { File, jsxRenderer } from '@kubb/renderer-jsx'
|
|
3
|
+
import { Type } from '../components/Type.tsx'
|
|
4
|
+
import { ENUM_TYPES_WITH_KEY_SUFFIX } from '../constants.ts'
|
|
5
|
+
import { printerTs } from '../printers/printerTs.ts'
|
|
16
6
|
import type { PluginTs } from '../types'
|
|
7
|
+
import { buildData, buildResponses, buildResponseUnion } from '../utils.ts'
|
|
8
|
+
|
|
9
|
+
function getContentTypeSuffix(contentType: string): string {
|
|
10
|
+
const baseType = contentType.split(';')[0]!.trim()
|
|
11
|
+
if (baseType === 'application/json') return 'Json'
|
|
12
|
+
if (baseType === 'multipart/form-data') return 'FormData'
|
|
13
|
+
if (baseType === 'application/x-www-form-urlencoded') return 'FormUrlEncoded'
|
|
14
|
+
const subtype = baseType.split('/').pop() ?? baseType
|
|
15
|
+
const parts = subtype.split(/[^a-zA-Z0-9]+/).filter(Boolean)
|
|
16
|
+
if (parts.length === 0) return 'Unknown'
|
|
17
|
+
return parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join('')
|
|
18
|
+
}
|
|
17
19
|
|
|
18
|
-
function
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if (schemas.response) {
|
|
22
|
-
properties['response'] = factory.createUnionDeclaration({
|
|
23
|
-
nodes: schemas.responses.map((res) => {
|
|
24
|
-
const identifier = driver.resolveName({
|
|
25
|
-
name: res.name,
|
|
26
|
-
pluginName: pluginTsName,
|
|
27
|
-
type: 'function',
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
return factory.createTypeReferenceNode(factory.createIdentifier(identifier), undefined)
|
|
31
|
-
}),
|
|
32
|
-
})!
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (schemas.request) {
|
|
36
|
-
const identifier = driver.resolveName({
|
|
37
|
-
name: schemas.request.name,
|
|
38
|
-
pluginName: pluginTsName,
|
|
39
|
-
type: 'function',
|
|
40
|
-
})
|
|
41
|
-
properties['request'] = factory.createTypeReferenceNode(factory.createIdentifier(identifier), undefined)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (schemas.pathParams) {
|
|
45
|
-
const identifier = driver.resolveName({
|
|
46
|
-
name: schemas.pathParams.name,
|
|
47
|
-
pluginName: pluginTsName,
|
|
48
|
-
type: 'function',
|
|
49
|
-
})
|
|
50
|
-
properties['pathParams'] = factory.createTypeReferenceNode(factory.createIdentifier(identifier), undefined)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (schemas.queryParams) {
|
|
54
|
-
const identifier = driver.resolveName({
|
|
55
|
-
name: schemas.queryParams.name,
|
|
56
|
-
pluginName: pluginTsName,
|
|
57
|
-
type: 'function',
|
|
58
|
-
})
|
|
59
|
-
properties['queryParams'] = factory.createTypeReferenceNode(factory.createIdentifier(identifier), undefined)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (schemas.headerParams) {
|
|
63
|
-
const identifier = driver.resolveName({
|
|
64
|
-
name: schemas.headerParams.name,
|
|
65
|
-
pluginName: pluginTsName,
|
|
66
|
-
type: 'function',
|
|
67
|
-
})
|
|
68
|
-
properties['headerParams'] = factory.createTypeReferenceNode(factory.createIdentifier(identifier), undefined)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (schemas.errors) {
|
|
72
|
-
properties['errors'] = factory.createUnionDeclaration({
|
|
73
|
-
nodes: schemas.errors.map((error) => {
|
|
74
|
-
const identifier = driver.resolveName({
|
|
75
|
-
name: error.name,
|
|
76
|
-
pluginName: pluginTsName,
|
|
77
|
-
type: 'function',
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
return factory.createTypeReferenceNode(factory.createIdentifier(identifier), undefined)
|
|
81
|
-
}),
|
|
82
|
-
})!
|
|
20
|
+
function getPerContentTypeName(dataName: string, suffix: string): string {
|
|
21
|
+
if (dataName.endsWith('Data')) {
|
|
22
|
+
return suffix.endsWith('Data') ? dataName.slice(0, -4) + suffix : `${dataName.slice(0, -4)}${suffix}Data`
|
|
83
23
|
}
|
|
84
|
-
|
|
85
|
-
const namespaceNode = factory.createTypeAliasDeclaration({
|
|
86
|
-
name,
|
|
87
|
-
type: factory.createTypeLiteralNode(
|
|
88
|
-
Object.keys(properties)
|
|
89
|
-
.map((key) => {
|
|
90
|
-
const type = properties[key]
|
|
91
|
-
if (!type) {
|
|
92
|
-
return undefined
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return factory.createPropertySignature({
|
|
96
|
-
name: pascalCase(key),
|
|
97
|
-
type,
|
|
98
|
-
})
|
|
99
|
-
})
|
|
100
|
-
.filter(Boolean),
|
|
101
|
-
),
|
|
102
|
-
modifiers: [factory.modifiers.export],
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
return safePrint(namespaceNode)
|
|
24
|
+
return dataName + suffix
|
|
106
25
|
}
|
|
107
26
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
baseName: string
|
|
115
|
-
operation: Operation
|
|
116
|
-
schemas: OperationSchemas
|
|
117
|
-
driver: PluginDriver
|
|
118
|
-
}): string {
|
|
119
|
-
const name = driver.resolveName({
|
|
120
|
-
name: `${baseName} Request`,
|
|
121
|
-
pluginName: pluginTsName,
|
|
122
|
-
type: 'type',
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
const results: string[] = []
|
|
126
|
-
|
|
127
|
-
// Generate DataRequest type
|
|
128
|
-
const dataRequestProperties: ts.PropertySignature[] = []
|
|
129
|
-
|
|
130
|
-
if (schemas.request) {
|
|
131
|
-
const identifier = driver.resolveName({
|
|
132
|
-
name: schemas.request.name,
|
|
133
|
-
pluginName: pluginTsName,
|
|
134
|
-
type: 'type',
|
|
135
|
-
})
|
|
136
|
-
dataRequestProperties.push(
|
|
137
|
-
factory.createPropertySignature({
|
|
138
|
-
name: 'data',
|
|
139
|
-
questionToken: true,
|
|
140
|
-
type: factory.createTypeReferenceNode(factory.createIdentifier(identifier), undefined),
|
|
141
|
-
}),
|
|
142
|
-
)
|
|
143
|
-
} else {
|
|
144
|
-
dataRequestProperties.push(
|
|
145
|
-
factory.createPropertySignature({
|
|
146
|
-
name: 'data',
|
|
147
|
-
questionToken: true,
|
|
148
|
-
type: keywordTypeNodes.never,
|
|
149
|
-
}),
|
|
150
|
-
)
|
|
151
|
-
}
|
|
27
|
+
export const typeGenerator = defineGenerator<PluginTs>({
|
|
28
|
+
name: 'typescript',
|
|
29
|
+
renderer: jsxRenderer,
|
|
30
|
+
schema(node, ctx) {
|
|
31
|
+
const { enumType, enumTypeSuffix, enumKeyCasing, syntaxType, optionalType, arrayType, output, group, printer } = ctx.options
|
|
32
|
+
const { adapter, config, resolver, root } = ctx
|
|
152
33
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
dataRequestProperties.push(
|
|
168
|
-
factory.createPropertySignature({
|
|
169
|
-
name: 'pathParams',
|
|
170
|
-
questionToken: true,
|
|
171
|
-
type: keywordTypeNodes.never,
|
|
172
|
-
}),
|
|
173
|
-
)
|
|
174
|
-
}
|
|
34
|
+
if (!node.name) {
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
const mode = ctx.getMode(output)
|
|
38
|
+
// Build a set of schema names that are enums so the ref handler and getImports
|
|
39
|
+
// callback can use the suffixed type name (e.g. `StatusKey`) for those refs.
|
|
40
|
+
const enumSchemaNames = new Set((adapter.inputNode?.schemas ?? []).filter((s) => ast.narrowSchema(s, ast.schemaTypes.enum) && s.name).map((s) => s.name!))
|
|
41
|
+
|
|
42
|
+
function resolveImportName(schemaName: string): string {
|
|
43
|
+
if (ENUM_TYPES_WITH_KEY_SUFFIX.has(enumType) && enumTypeSuffix && enumSchemaNames.has(schemaName)) {
|
|
44
|
+
return resolver.resolveEnumKeyName({ name: schemaName }, enumTypeSuffix)
|
|
45
|
+
}
|
|
46
|
+
return resolver.resolveTypeName(schemaName)
|
|
47
|
+
}
|
|
175
48
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
49
|
+
const imports = adapter.getImports(node, (schemaName) => ({
|
|
50
|
+
name: resolveImportName(schemaName),
|
|
51
|
+
path: resolver.resolveFile({ name: schemaName, extname: '.ts' }, { root, output, group }).path,
|
|
52
|
+
}))
|
|
53
|
+
|
|
54
|
+
const isEnumSchema = !!ast.narrowSchema(node, ast.schemaTypes.enum)
|
|
55
|
+
|
|
56
|
+
const meta = {
|
|
57
|
+
name: ENUM_TYPES_WITH_KEY_SUFFIX.has(enumType) && isEnumSchema ? resolver.resolveEnumKeyName(node, enumTypeSuffix) : resolver.resolveTypeName(node.name),
|
|
58
|
+
file: resolver.resolveFile({ name: node.name, extname: '.ts' }, { root, output, group }),
|
|
59
|
+
} as const
|
|
60
|
+
|
|
61
|
+
const schemaPrinter = printerTs({
|
|
62
|
+
optionalType,
|
|
63
|
+
arrayType,
|
|
64
|
+
enumType,
|
|
65
|
+
enumTypeSuffix,
|
|
66
|
+
name: meta.name,
|
|
67
|
+
syntaxType,
|
|
68
|
+
description: node.description,
|
|
69
|
+
resolver,
|
|
70
|
+
enumSchemaNames,
|
|
71
|
+
nodes: printer?.nodes,
|
|
182
72
|
})
|
|
183
|
-
dataRequestProperties.push(
|
|
184
|
-
factory.createPropertySignature({
|
|
185
|
-
name: 'queryParams',
|
|
186
|
-
questionToken: true,
|
|
187
|
-
type: factory.createTypeReferenceNode(factory.createIdentifier(identifier), undefined),
|
|
188
|
-
}),
|
|
189
|
-
)
|
|
190
|
-
} else {
|
|
191
|
-
dataRequestProperties.push(
|
|
192
|
-
factory.createPropertySignature({
|
|
193
|
-
name: 'queryParams',
|
|
194
|
-
questionToken: true,
|
|
195
|
-
type: keywordTypeNodes.never,
|
|
196
|
-
}),
|
|
197
|
-
)
|
|
198
|
-
}
|
|
199
73
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
74
|
+
return (
|
|
75
|
+
<File
|
|
76
|
+
baseName={meta.file.baseName}
|
|
77
|
+
path={meta.file.path}
|
|
78
|
+
meta={meta.file.meta}
|
|
79
|
+
banner={resolver.resolveBanner(adapter.inputNode, { output, config })}
|
|
80
|
+
footer={resolver.resolveFooter(adapter.inputNode, { output, config })}
|
|
81
|
+
>
|
|
82
|
+
{mode === 'split' &&
|
|
83
|
+
imports.map((imp) => (
|
|
84
|
+
<File.Import key={[node.name, imp.path, imp.isTypeOnly].join('-')} root={meta.file.path} path={imp.path} name={imp.name} isTypeOnly />
|
|
85
|
+
))}
|
|
86
|
+
<Type
|
|
87
|
+
name={meta.name}
|
|
88
|
+
node={node}
|
|
89
|
+
enumType={enumType}
|
|
90
|
+
enumTypeSuffix={enumTypeSuffix}
|
|
91
|
+
enumKeyCasing={enumKeyCasing}
|
|
92
|
+
resolver={resolver}
|
|
93
|
+
printer={schemaPrinter}
|
|
94
|
+
/>
|
|
95
|
+
</File>
|
|
221
96
|
)
|
|
222
|
-
}
|
|
97
|
+
},
|
|
98
|
+
operation(node, ctx) {
|
|
99
|
+
const { enumType, enumTypeSuffix, enumKeyCasing, optionalType, arrayType, syntaxType, paramsCasing, group, output, printer } = ctx.options
|
|
100
|
+
const { adapter, config, resolver, root } = ctx
|
|
223
101
|
|
|
224
|
-
|
|
225
|
-
dataRequestProperties.push(
|
|
226
|
-
factory.createPropertySignature({
|
|
227
|
-
name: 'url',
|
|
228
|
-
type: createUrlTemplateType(operation.path),
|
|
229
|
-
}),
|
|
230
|
-
)
|
|
102
|
+
const mode = ctx.getMode(output)
|
|
231
103
|
|
|
232
|
-
|
|
233
|
-
name,
|
|
234
|
-
type: factory.createTypeLiteralNode(dataRequestProperties),
|
|
235
|
-
modifiers: [factory.modifiers.export],
|
|
236
|
-
})
|
|
104
|
+
const params = ast.caseParams(node.parameters, paramsCasing)
|
|
237
105
|
|
|
238
|
-
|
|
106
|
+
const meta = {
|
|
107
|
+
file: resolver.resolveFile({ name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path }, { root, output, group }),
|
|
108
|
+
} as const
|
|
239
109
|
|
|
240
|
-
|
|
241
|
-
|
|
110
|
+
// Build a set of schema names that are enums so the ref handler and getImports
|
|
111
|
+
// callback can use the suffixed type name (e.g. `StatusKey`) for those refs.
|
|
112
|
+
const enumSchemaNames = new Set((adapter.inputNode?.schemas ?? []).filter((s) => ast.narrowSchema(s, ast.schemaTypes.enum) && s.name).map((s) => s.name!))
|
|
242
113
|
|
|
243
|
-
function
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
}
|
|
249
|
-
baseName: string
|
|
250
|
-
schemas: OperationSchemas
|
|
251
|
-
driver: PluginDriver
|
|
252
|
-
unknownType: PluginTs['resolvedOptions']['unknownType']
|
|
253
|
-
}): string {
|
|
254
|
-
const results: string[] = []
|
|
255
|
-
|
|
256
|
-
const name = driver.resolveName({
|
|
257
|
-
name: `${baseName} ResponseData`,
|
|
258
|
-
pluginName: pluginTsName,
|
|
259
|
-
type: 'type',
|
|
260
|
-
})
|
|
261
|
-
|
|
262
|
-
// Generate Responses type (mapping status codes to response types)
|
|
263
|
-
if (schemas.responses && schemas.responses.length > 0) {
|
|
264
|
-
const responsesProperties: ts.PropertySignature[] = schemas.responses.map((res) => {
|
|
265
|
-
const identifier = driver.resolveName({
|
|
266
|
-
name: res.name,
|
|
267
|
-
pluginName: pluginTsName,
|
|
268
|
-
type: 'type',
|
|
269
|
-
})
|
|
114
|
+
function resolveImportName(schemaName: string): string {
|
|
115
|
+
if (ENUM_TYPES_WITH_KEY_SUFFIX.has(enumType) && enumTypeSuffix && enumSchemaNames.has(schemaName)) {
|
|
116
|
+
return resolver.resolveEnumKeyName({ name: schemaName }, enumTypeSuffix)
|
|
117
|
+
}
|
|
118
|
+
return resolver.resolveTypeName(schemaName)
|
|
119
|
+
}
|
|
270
120
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
121
|
+
function renderSchemaType({ schema, name, keysToOmit }: { schema: ast.SchemaNode | null; name: string; keysToOmit?: Array<string> }) {
|
|
122
|
+
if (!schema) return null
|
|
123
|
+
|
|
124
|
+
const imports = adapter.getImports(schema, (schemaName) => ({
|
|
125
|
+
name: resolveImportName(schemaName),
|
|
126
|
+
path: resolver.resolveFile({ name: schemaName, extname: '.ts' }, { root, output, group }).path,
|
|
127
|
+
}))
|
|
128
|
+
|
|
129
|
+
const schemaPrinter = printerTs({
|
|
130
|
+
optionalType,
|
|
131
|
+
arrayType,
|
|
132
|
+
enumType,
|
|
133
|
+
enumTypeSuffix,
|
|
134
|
+
name,
|
|
135
|
+
syntaxType,
|
|
136
|
+
description: schema.description,
|
|
137
|
+
keysToOmit,
|
|
138
|
+
resolver,
|
|
139
|
+
enumSchemaNames,
|
|
140
|
+
nodes: printer?.nodes,
|
|
274
141
|
})
|
|
275
|
-
})
|
|
276
|
-
|
|
277
|
-
const responsesNode = factory.createTypeAliasDeclaration({
|
|
278
|
-
name: `${baseName}Responses`,
|
|
279
|
-
type: factory.createTypeLiteralNode(responsesProperties),
|
|
280
|
-
modifiers: [factory.modifiers.export],
|
|
281
|
-
})
|
|
282
|
-
|
|
283
|
-
results.push(safePrint(responsesNode))
|
|
284
|
-
|
|
285
|
-
// Generate Response type (union via indexed access)
|
|
286
|
-
const responseNode = factory.createTypeAliasDeclaration({
|
|
287
|
-
name,
|
|
288
|
-
type: factory.createIndexedAccessTypeNode(
|
|
289
|
-
factory.createTypeReferenceNode(factory.createIdentifier(`${baseName}Responses`), undefined),
|
|
290
|
-
factory.createTypeOperatorNode(
|
|
291
|
-
ts.SyntaxKind.KeyOfKeyword,
|
|
292
|
-
factory.createTypeReferenceNode(factory.createIdentifier(`${baseName}Responses`), undefined),
|
|
293
|
-
),
|
|
294
|
-
),
|
|
295
|
-
modifiers: [factory.modifiers.export],
|
|
296
|
-
})
|
|
297
|
-
|
|
298
|
-
results.push(safePrint(responseNode))
|
|
299
|
-
} else {
|
|
300
|
-
const responseNode = factory.createTypeAliasDeclaration({
|
|
301
|
-
name,
|
|
302
|
-
modifiers: [factory.modifiers.export],
|
|
303
|
-
type: getUnknownType(unknownType),
|
|
304
|
-
})
|
|
305
|
-
|
|
306
|
-
results.push(safePrint(responseNode))
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
return results.join('\n\n')
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
export const typeGenerator = createReactGenerator<PluginTs>({
|
|
313
|
-
name: 'typescript',
|
|
314
|
-
Operation({ operation, generator, plugin }) {
|
|
315
|
-
const {
|
|
316
|
-
options,
|
|
317
|
-
options: { enumType, enumKeyCasing, syntaxType, optionalType, arrayType, unknownType, paramsCasing },
|
|
318
|
-
} = plugin
|
|
319
|
-
|
|
320
|
-
const mode = useMode()
|
|
321
|
-
const driver = usePluginDriver()
|
|
322
|
-
|
|
323
|
-
const oas = useOas()
|
|
324
|
-
const { getSchemas, getFile, getName, getGroup } = useOperationManager(generator)
|
|
325
|
-
const schemaManager = useSchemaManager()
|
|
326
|
-
|
|
327
|
-
const name = getName(operation, { type: 'type', pluginName: pluginTsName })
|
|
328
|
-
|
|
329
|
-
const file = getFile(operation)
|
|
330
|
-
const schemas = getSchemas(operation)
|
|
331
|
-
const schemaGenerator = new SchemaGenerator(options, {
|
|
332
|
-
fabric: generator.context.fabric,
|
|
333
|
-
oas,
|
|
334
|
-
events: generator.context.events,
|
|
335
|
-
plugin,
|
|
336
|
-
driver,
|
|
337
|
-
mode,
|
|
338
|
-
override: options.override,
|
|
339
|
-
})
|
|
340
|
-
|
|
341
|
-
const operationSchemas = [schemas.pathParams, schemas.queryParams, schemas.headerParams, schemas.statusCodes, schemas.request, schemas.response]
|
|
342
|
-
.flat()
|
|
343
|
-
.filter(Boolean)
|
|
344
|
-
|
|
345
|
-
const mapOperationSchema = ({ name, schema, description, keysToOmit, ...options }: OperationSchemaType) => {
|
|
346
|
-
// Apply paramsCasing transformation to pathParams, queryParams, and headerParams (not response)
|
|
347
|
-
const shouldTransform = paramsCasing && isParameterSchema(name)
|
|
348
|
-
const transformedSchema = shouldTransform ? applyParamsCasing(schema, paramsCasing) : schema
|
|
349
|
-
|
|
350
|
-
const tree = schemaGenerator.parse({ schema: transformedSchema, name, parentName: null })
|
|
351
|
-
const imports = getImports(tree)
|
|
352
|
-
const group = options.operation ? getGroup(options.operation) : undefined
|
|
353
|
-
|
|
354
|
-
const type = {
|
|
355
|
-
name: schemaManager.getName(name, { type: 'type' }),
|
|
356
|
-
typedName: schemaManager.getName(name, { type: 'type' }),
|
|
357
|
-
file: schemaManager.getFile(options.operationName || name, { group }),
|
|
358
|
-
}
|
|
359
142
|
|
|
360
143
|
return (
|
|
361
144
|
<>
|
|
362
145
|
{mode === 'split' &&
|
|
363
146
|
imports.map((imp) => (
|
|
364
|
-
<File.Import key={[name, imp.
|
|
147
|
+
<File.Import key={[name, imp.path, imp.isTypeOnly].join('-')} root={meta.file.path} path={imp.path} name={imp.name} isTypeOnly />
|
|
365
148
|
))}
|
|
366
149
|
<Type
|
|
367
|
-
name={
|
|
368
|
-
|
|
369
|
-
description={description}
|
|
370
|
-
tree={tree}
|
|
371
|
-
schema={transformedSchema}
|
|
150
|
+
name={name}
|
|
151
|
+
node={schema}
|
|
372
152
|
enumType={enumType}
|
|
153
|
+
enumTypeSuffix={enumTypeSuffix}
|
|
373
154
|
enumKeyCasing={enumKeyCasing}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
keysToOmit={keysToOmit}
|
|
377
|
-
syntaxType={syntaxType}
|
|
155
|
+
resolver={resolver}
|
|
156
|
+
printer={schemaPrinter}
|
|
378
157
|
/>
|
|
379
158
|
</>
|
|
380
159
|
)
|
|
381
160
|
}
|
|
382
161
|
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
|
|
162
|
+
const paramTypes = params.map((param) =>
|
|
163
|
+
renderSchemaType({
|
|
164
|
+
schema: param.schema,
|
|
165
|
+
name: resolver.resolveParamName(node, param),
|
|
166
|
+
}),
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
const requestBodyContent = node.requestBody?.content ?? []
|
|
170
|
+
|
|
171
|
+
function buildRequestType() {
|
|
172
|
+
if (requestBodyContent.length === 0) return null
|
|
173
|
+
if (requestBodyContent.length === 1) {
|
|
174
|
+
const entry = requestBodyContent[0]!
|
|
175
|
+
if (!entry.schema) return null
|
|
176
|
+
return renderSchemaType({
|
|
177
|
+
schema: {
|
|
178
|
+
...entry.schema,
|
|
179
|
+
description: node.requestBody!.description ?? entry.schema.description,
|
|
180
|
+
},
|
|
181
|
+
name: resolver.resolveDataName(node),
|
|
182
|
+
keysToOmit: entry.keysToOmit,
|
|
183
|
+
})
|
|
184
|
+
}
|
|
185
|
+
// Multiple content types — generate individual types + union alias
|
|
186
|
+
const dataName = resolver.resolveDataName(node)
|
|
187
|
+
const usedNames = new Set<string>()
|
|
188
|
+
const individualItems = requestBodyContent
|
|
189
|
+
.filter((entry) => entry.schema)
|
|
190
|
+
.map((entry) => {
|
|
191
|
+
const baseSuffix = getContentTypeSuffix(entry.contentType)
|
|
192
|
+
let individualName = getPerContentTypeName(dataName, baseSuffix)
|
|
193
|
+
let counter = 2
|
|
194
|
+
while (usedNames.has(individualName)) {
|
|
195
|
+
individualName = getPerContentTypeName(dataName, `${baseSuffix}${counter++}`)
|
|
196
|
+
}
|
|
197
|
+
usedNames.add(individualName)
|
|
198
|
+
return {
|
|
199
|
+
name: individualName,
|
|
200
|
+
rendered: renderSchemaType({
|
|
201
|
+
schema: {
|
|
202
|
+
...entry.schema!,
|
|
203
|
+
description: node.requestBody!.description ?? entry.schema!.description,
|
|
204
|
+
},
|
|
205
|
+
name: individualName,
|
|
206
|
+
keysToOmit: entry.keysToOmit,
|
|
207
|
+
}),
|
|
208
|
+
}
|
|
209
|
+
})
|
|
210
|
+
const unionSchema = ast.createSchema({
|
|
211
|
+
type: 'union',
|
|
212
|
+
members: individualItems.map((item) => ast.createSchema({ type: 'ref', name: item.name })),
|
|
213
|
+
})
|
|
214
|
+
const unionType = renderSchemaType({ schema: unionSchema, name: dataName })
|
|
215
|
+
return (
|
|
216
|
+
<>
|
|
217
|
+
{individualItems.map((item) => item.rendered)}
|
|
218
|
+
{unionType}
|
|
219
|
+
</>
|
|
220
|
+
)
|
|
221
|
+
}
|
|
386
222
|
|
|
387
|
-
const
|
|
223
|
+
const requestType = buildRequestType()
|
|
388
224
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
footer={getFooter({ oas, output: plugin.options.output })}
|
|
396
|
-
>
|
|
397
|
-
{operationSchemas.map(mapOperationSchema)}
|
|
398
|
-
|
|
399
|
-
{generator.context.UNSTABLE_NAMING ? (
|
|
400
|
-
<>
|
|
401
|
-
<File.Source name={`${name}Request`} isExportable isIndexable isTypeOnly>
|
|
402
|
-
{printRequestSchema({ baseName: name, operation, schemas, driver })}
|
|
403
|
-
</File.Source>
|
|
404
|
-
<File.Source name={responseName} isExportable isIndexable isTypeOnly>
|
|
405
|
-
{printResponseSchema({ baseName: name, schemas, driver, unknownType })}
|
|
406
|
-
</File.Source>
|
|
407
|
-
</>
|
|
408
|
-
) : (
|
|
409
|
-
<File.Source name={combinedSchemaName} isExportable isIndexable isTypeOnly>
|
|
410
|
-
{printCombinedSchema({ name: combinedSchemaName, schemas, driver })}
|
|
411
|
-
</File.Source>
|
|
412
|
-
)}
|
|
413
|
-
</File>
|
|
225
|
+
const responseTypes = node.responses.map((res) =>
|
|
226
|
+
renderSchemaType({
|
|
227
|
+
schema: res.schema,
|
|
228
|
+
name: resolver.resolveResponseStatusName(node, res.statusCode),
|
|
229
|
+
keysToOmit: res.keysToOmit,
|
|
230
|
+
}),
|
|
414
231
|
)
|
|
415
|
-
},
|
|
416
|
-
Schema({ schema, plugin }) {
|
|
417
|
-
const {
|
|
418
|
-
options: { enumType, enumKeyCasing, syntaxType, optionalType, arrayType, output },
|
|
419
|
-
} = plugin
|
|
420
|
-
const mode = useMode()
|
|
421
232
|
|
|
422
|
-
const
|
|
423
|
-
|
|
233
|
+
const dataType = renderSchemaType({
|
|
234
|
+
schema: buildData({ ...node, parameters: params }, { resolver }),
|
|
235
|
+
name: resolver.resolveRequestConfigName(node),
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
const responsesType = renderSchemaType({
|
|
239
|
+
schema: buildResponses(node, { resolver }),
|
|
240
|
+
name: resolver.resolveResponsesName(node),
|
|
241
|
+
})
|
|
424
242
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
243
|
+
function buildResponseType() {
|
|
244
|
+
if (!node.responses.some((res) => res.schema)) {
|
|
245
|
+
return null
|
|
246
|
+
}
|
|
428
247
|
|
|
429
|
-
|
|
248
|
+
const responseName = resolver.resolveResponseName(node)
|
|
249
|
+
|
|
250
|
+
const responsesWithSchema = node.responses.filter((res) => res.schema)
|
|
251
|
+
const importedNames = new Set(
|
|
252
|
+
responsesWithSchema.flatMap((res) =>
|
|
253
|
+
res.schema
|
|
254
|
+
? adapter
|
|
255
|
+
.getImports(res.schema, (schemaName) => ({
|
|
256
|
+
name: resolveImportName(schemaName),
|
|
257
|
+
path: '',
|
|
258
|
+
}))
|
|
259
|
+
.flatMap((imp) => (Array.isArray(imp.name) ? imp.name : [imp.name]))
|
|
260
|
+
: [],
|
|
261
|
+
),
|
|
262
|
+
)
|
|
430
263
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
264
|
+
if (importedNames.has(responseName)) {
|
|
265
|
+
return null
|
|
266
|
+
}
|
|
434
267
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
268
|
+
return renderSchemaType({
|
|
269
|
+
schema: {
|
|
270
|
+
...buildResponseUnion(node, { resolver })!,
|
|
271
|
+
description: 'Union of all possible responses',
|
|
272
|
+
},
|
|
273
|
+
name: responseName,
|
|
274
|
+
})
|
|
439
275
|
}
|
|
440
276
|
|
|
277
|
+
const responseType = buildResponseType()
|
|
278
|
+
|
|
441
279
|
return (
|
|
442
280
|
<File
|
|
443
|
-
baseName={
|
|
444
|
-
path={
|
|
445
|
-
meta={
|
|
446
|
-
banner={
|
|
447
|
-
footer={
|
|
281
|
+
baseName={meta.file.baseName}
|
|
282
|
+
path={meta.file.path}
|
|
283
|
+
meta={meta.file.meta}
|
|
284
|
+
banner={resolver.resolveBanner(adapter.inputNode, { output, config })}
|
|
285
|
+
footer={resolver.resolveFooter(adapter.inputNode, { output, config })}
|
|
448
286
|
>
|
|
449
|
-
{
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
typedName={type.typedName}
|
|
456
|
-
description={schema.value.description}
|
|
457
|
-
tree={schema.tree}
|
|
458
|
-
schema={schema.value}
|
|
459
|
-
enumType={enumType}
|
|
460
|
-
enumKeyCasing={enumKeyCasing}
|
|
461
|
-
optionalType={optionalType}
|
|
462
|
-
arrayType={arrayType}
|
|
463
|
-
syntaxType={syntaxType}
|
|
464
|
-
/>
|
|
287
|
+
{paramTypes}
|
|
288
|
+
{responseTypes}
|
|
289
|
+
{requestType}
|
|
290
|
+
{dataType}
|
|
291
|
+
{responsesType}
|
|
292
|
+
{responseType}
|
|
465
293
|
</File>
|
|
466
294
|
)
|
|
467
295
|
},
|