@kubb/swagger-ts 2.0.0-alpha.9 → 2.0.0-beta.2
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/README.md +1 -1
- package/dist/index.cjs +31 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +33 -33
- package/dist/index.js.map +1 -1
- package/package.json +11 -12
- package/src/builders/TypeBuilder.ts +3 -2
- package/src/generators/OperationGenerator.ts +4 -2
- package/src/generators/TypeGenerator.ts +41 -34
- package/src/plugin.ts +9 -13
- package/src/types.ts +1 -1
@@ -1,18 +1,19 @@
|
|
1
1
|
import { SchemaGenerator } from '@kubb/core'
|
2
2
|
import { getUniqueName } from '@kubb/core/utils'
|
3
3
|
import * as factory from '@kubb/parser/factory'
|
4
|
-
import { isReference } from '@kubb/swagger/utils'
|
4
|
+
import { getSchemaFactory, isReference } from '@kubb/swagger/utils'
|
5
5
|
|
6
6
|
import { camelCase } from 'change-case'
|
7
7
|
|
8
8
|
import type { PluginContext } from '@kubb/core'
|
9
9
|
import type { ts } from '@kubb/parser'
|
10
|
-
import type { OpenAPIV3, Refs } from '@kubb/swagger'
|
10
|
+
import type { Oas, OasTypes, OpenAPIV3, OpenAPIV3_1, Refs } from '@kubb/swagger'
|
11
11
|
import type { Options as CaseOptions } from 'change-case'
|
12
12
|
|
13
13
|
// based on https://github.com/cellular/oazapfts/blob/7ba226ebb15374e8483cc53e7532f1663179a22c/src/codegen/generate.ts#L398
|
14
14
|
|
15
15
|
type Options = {
|
16
|
+
oas: Oas
|
16
17
|
usedEnumNames: Record<string, number>
|
17
18
|
|
18
19
|
withJSDocs?: boolean
|
@@ -21,7 +22,8 @@ type Options = {
|
|
21
22
|
dateType: 'string' | 'date'
|
22
23
|
optionalType: 'questionToken' | 'undefined' | 'questionTokenAndUndefined'
|
23
24
|
}
|
24
|
-
|
25
|
+
|
26
|
+
export class TypeGenerator extends SchemaGenerator<Options, OasTypes.SchemaObject, ts.Node[]> {
|
25
27
|
refs: Refs = {}
|
26
28
|
|
27
29
|
extraNodes: ts.Node[] = []
|
@@ -37,14 +39,7 @@ export class TypeGenerator extends SchemaGenerator<Options, OpenAPIV3.SchemaObje
|
|
37
39
|
}
|
38
40
|
|
39
41
|
constructor(
|
40
|
-
options: Options
|
41
|
-
usedEnumNames: {},
|
42
|
-
withJSDocs: true,
|
43
|
-
resolveName: ({ name }) => name,
|
44
|
-
enumType: 'asConst',
|
45
|
-
dateType: 'string',
|
46
|
-
optionalType: 'questionToken',
|
47
|
-
},
|
42
|
+
options: Options,
|
48
43
|
) {
|
49
44
|
super(options)
|
50
45
|
|
@@ -57,7 +52,7 @@ export class TypeGenerator extends SchemaGenerator<Options, OpenAPIV3.SchemaObje
|
|
57
52
|
description,
|
58
53
|
keysToOmit,
|
59
54
|
}: {
|
60
|
-
schema:
|
55
|
+
schema: OasTypes.SchemaObject
|
61
56
|
baseName: string
|
62
57
|
description?: string
|
63
58
|
keysToOmit?: string[]
|
@@ -102,7 +97,7 @@ export class TypeGenerator extends SchemaGenerator<Options, OpenAPIV3.SchemaObje
|
|
102
97
|
* Delegates to getBaseTypeFromSchema internally and
|
103
98
|
* optionally adds a union with null.
|
104
99
|
*/
|
105
|
-
#getTypeFromSchema(schema?:
|
100
|
+
#getTypeFromSchema(schema?: OasTypes.SchemaObject, name?: string): ts.TypeNode | null {
|
106
101
|
const type = this.#getBaseTypeFromSchema(schema, name)
|
107
102
|
|
108
103
|
if (!type) {
|
@@ -119,16 +114,16 @@ export class TypeGenerator extends SchemaGenerator<Options, OpenAPIV3.SchemaObje
|
|
119
114
|
/**
|
120
115
|
* Recursively creates a type literal with the given props.
|
121
116
|
*/
|
122
|
-
#getTypeFromProperties(baseSchema?:
|
117
|
+
#getTypeFromProperties(baseSchema?: OasTypes.SchemaObject, baseName?: string) {
|
123
118
|
const { optionalType } = this.options
|
124
119
|
const properties = baseSchema?.properties || {}
|
125
120
|
const required = baseSchema?.required
|
126
121
|
const additionalProperties = baseSchema?.additionalProperties
|
127
122
|
|
128
123
|
const members: Array<ts.TypeElement | null> = Object.keys(properties).map((name) => {
|
129
|
-
const schema = properties[name] as
|
124
|
+
const schema = properties[name] as OasTypes.SchemaObject
|
130
125
|
|
131
|
-
const isRequired = required
|
126
|
+
const isRequired = Array.isArray(required) ? required.includes(name) : !!required
|
132
127
|
let type = this.#getTypeFromSchema(schema, this.options.resolveName({ name: `${baseName || ''} ${name}` }))
|
133
128
|
|
134
129
|
if (!type) {
|
@@ -149,7 +144,7 @@ export class TypeGenerator extends SchemaGenerator<Options, OpenAPIV3.SchemaObje
|
|
149
144
|
node: propertySignature,
|
150
145
|
comments: [
|
151
146
|
schema.description ? `@description ${schema.description}` : undefined,
|
152
|
-
schema.type ? `@type ${schema.type}${isRequired ? '' : ' | undefined'} ${schema.format || ''}` : undefined,
|
147
|
+
schema.type ? `@type ${schema.type?.toString()}${isRequired ? '' : ' | undefined'} ${schema.format || ''}` : undefined,
|
153
148
|
schema.example ? `@example ${schema.example as string}` : undefined,
|
154
149
|
schema.deprecated ? `@deprecated` : undefined,
|
155
150
|
schema.default !== undefined && typeof schema.default === 'string' ? `@default '${schema.default}'` : undefined,
|
@@ -161,7 +156,7 @@ export class TypeGenerator extends SchemaGenerator<Options, OpenAPIV3.SchemaObje
|
|
161
156
|
return propertySignature
|
162
157
|
})
|
163
158
|
if (additionalProperties) {
|
164
|
-
const type = additionalProperties === true ? factory.keywordTypeNodes.any : this.#getTypeFromSchema(additionalProperties as
|
159
|
+
const type = additionalProperties === true ? factory.keywordTypeNodes.any : this.#getTypeFromSchema(additionalProperties as OasTypes.SchemaObject)
|
165
160
|
|
166
161
|
if (type) {
|
167
162
|
members.push(factory.createIndexSignature(type))
|
@@ -192,11 +187,21 @@ export class TypeGenerator extends SchemaGenerator<Options, OpenAPIV3.SchemaObje
|
|
192
187
|
return factory.createTypeReferenceNode(ref.propertyName, undefined)
|
193
188
|
}
|
194
189
|
|
190
|
+
#getParsedSchema(schema?: OasTypes.SchemaObject) {
|
191
|
+
const parsedSchema = getSchemaFactory(this.options.oas)(schema)
|
192
|
+
return parsedSchema
|
193
|
+
}
|
194
|
+
|
195
195
|
/**
|
196
196
|
* This is the very core of the OpenAPI to TS conversion - it takes a
|
197
197
|
* schema and returns the appropriate type.
|
198
198
|
*/
|
199
|
-
#getBaseTypeFromSchema(
|
199
|
+
#getBaseTypeFromSchema(
|
200
|
+
_schema: OasTypes.SchemaObject | undefined,
|
201
|
+
baseName?: string,
|
202
|
+
): ts.TypeNode | null {
|
203
|
+
const { schema, version } = this.#getParsedSchema(_schema)
|
204
|
+
|
200
205
|
if (!schema) {
|
201
206
|
return factory.keywordTypeNodes.any
|
202
207
|
}
|
@@ -207,13 +212,13 @@ export class TypeGenerator extends SchemaGenerator<Options, OpenAPIV3.SchemaObje
|
|
207
212
|
|
208
213
|
if (schema.oneOf) {
|
209
214
|
// union
|
210
|
-
const schemaWithoutOneOf = { ...schema, oneOf: undefined }
|
215
|
+
const schemaWithoutOneOf = { ...schema, oneOf: undefined } as OasTypes.SchemaObject
|
211
216
|
|
212
217
|
const union = factory.createUnionDeclaration({
|
213
218
|
withParentheses: true,
|
214
219
|
nodes: schema.oneOf
|
215
|
-
.map((item
|
216
|
-
return this.#getBaseTypeFromSchema(item)
|
220
|
+
.map((item) => {
|
221
|
+
return item && this.#getBaseTypeFromSchema(item as OasTypes.SchemaObject)
|
217
222
|
})
|
218
223
|
.filter((item) => {
|
219
224
|
return item && item !== factory.keywordTypeNodes.any
|
@@ -230,13 +235,13 @@ export class TypeGenerator extends SchemaGenerator<Options, OpenAPIV3.SchemaObje
|
|
230
235
|
}
|
231
236
|
|
232
237
|
if (schema.anyOf) {
|
233
|
-
const schemaWithoutAnyOf = { ...schema, anyOf: undefined }
|
238
|
+
const schemaWithoutAnyOf = { ...schema, anyOf: undefined } as OasTypes.SchemaObject
|
234
239
|
|
235
240
|
const union = factory.createUnionDeclaration({
|
236
241
|
withParentheses: true,
|
237
242
|
nodes: schema.anyOf
|
238
|
-
.map((item
|
239
|
-
return this.#getBaseTypeFromSchema(item)
|
243
|
+
.map((item) => {
|
244
|
+
return item && this.#getBaseTypeFromSchema(item as OasTypes.SchemaObject)
|
240
245
|
})
|
241
246
|
.filter((item) => {
|
242
247
|
return item && item !== factory.keywordTypeNodes.any
|
@@ -253,13 +258,13 @@ export class TypeGenerator extends SchemaGenerator<Options, OpenAPIV3.SchemaObje
|
|
253
258
|
}
|
254
259
|
if (schema.allOf) {
|
255
260
|
// intersection/add
|
256
|
-
const schemaWithoutAllOf = { ...schema, allOf: undefined }
|
261
|
+
const schemaWithoutAllOf = { ...schema, allOf: undefined } as OasTypes.SchemaObject
|
257
262
|
|
258
263
|
const and = factory.createIntersectionDeclaration({
|
259
264
|
withParentheses: true,
|
260
265
|
nodes: schema.allOf
|
261
|
-
.map((item
|
262
|
-
return this.#getBaseTypeFromSchema(item)
|
266
|
+
.map((item) => {
|
267
|
+
return item && this.#getBaseTypeFromSchema(item as OasTypes.SchemaObject)
|
263
268
|
})
|
264
269
|
.filter((item) => {
|
265
270
|
return item && item !== factory.keywordTypeNodes.any
|
@@ -285,7 +290,7 @@ export class TypeGenerator extends SchemaGenerator<Options, OpenAPIV3.SchemaObje
|
|
285
290
|
|
286
291
|
if ('x-enumNames' in schema) {
|
287
292
|
enums = [...new Set(schema['x-enumNames'] as string[])].map((key: string, index) => {
|
288
|
-
return [key, schema.enum?.[index]]
|
293
|
+
return [key, schema.enum?.[index] as string]
|
289
294
|
})
|
290
295
|
}
|
291
296
|
|
@@ -302,7 +307,7 @@ export class TypeGenerator extends SchemaGenerator<Options, OpenAPIV3.SchemaObje
|
|
302
307
|
|
303
308
|
if (schema.enum) {
|
304
309
|
return factory.createUnionDeclaration({
|
305
|
-
nodes: schema.enum.map((name
|
310
|
+
nodes: schema.enum.map((name) => {
|
306
311
|
return factory.createLiteralTypeNode(typeof name === 'number' ? factory.createNumericLiteral(name) : factory.createStringLiteral(`${name}`))
|
307
312
|
}) as unknown as Array<ts.TypeNode>,
|
308
313
|
})
|
@@ -310,17 +315,19 @@ export class TypeGenerator extends SchemaGenerator<Options, OpenAPIV3.SchemaObje
|
|
310
315
|
|
311
316
|
if ('items' in schema) {
|
312
317
|
// items -> array
|
313
|
-
const node = this.#getTypeFromSchema(schema.items as
|
318
|
+
const node = this.#getTypeFromSchema(schema.items as OasTypes.SchemaObject, baseName)
|
314
319
|
if (node) {
|
315
320
|
return factory.createArrayTypeNode(node)
|
316
321
|
}
|
317
322
|
}
|
323
|
+
|
318
324
|
/**
|
319
325
|
* OpenAPI 3.1
|
320
326
|
* @link https://json-schema.org/understanding-json-schema/reference/array.html#tuple-validation
|
321
327
|
*/
|
328
|
+
|
322
329
|
if ('prefixItems' in schema) {
|
323
|
-
const prefixItems = schema.prefixItems as
|
330
|
+
const prefixItems = schema.prefixItems as OasTypes.SchemaObject[]
|
324
331
|
|
325
332
|
return factory.createTupleDeclaration({
|
326
333
|
nodes: prefixItems.map((item) => {
|
@@ -338,7 +345,7 @@ export class TypeGenerator extends SchemaGenerator<Options, OpenAPIV3.SchemaObje
|
|
338
345
|
if (schema.type) {
|
339
346
|
if (Array.isArray(schema.type)) {
|
340
347
|
// OPENAPI v3.1.0: https://www.openapis.org/blog/2021/02/16/migrating-from-openapi-3-0-to-3-1-0
|
341
|
-
const [type, nullable] = schema.type as Array<
|
348
|
+
const [type, nullable] = schema.type as Array<OpenAPIV3_1.NonArraySchemaObjectType>
|
342
349
|
|
343
350
|
return factory.createUnionDeclaration({
|
344
351
|
nodes: [
|
@@ -369,7 +376,7 @@ export class TypeGenerator extends SchemaGenerator<Options, OpenAPIV3.SchemaObje
|
|
369
376
|
}
|
370
377
|
|
371
378
|
// detect assertion "const" and define the type property as a Literal
|
372
|
-
if (
|
379
|
+
if (version === '3.1' && typeof schema['const'] === 'string') {
|
373
380
|
return factory.createLiteralTypeNode(factory.createStringLiteral(schema['const']))
|
374
381
|
}
|
375
382
|
|
package/src/plugin.ts
CHANGED
@@ -10,11 +10,11 @@ import { TypeBuilder } from './builders/index.ts'
|
|
10
10
|
import { OperationGenerator } from './generators/index.ts'
|
11
11
|
|
12
12
|
import type { KubbFile, KubbPlugin } from '@kubb/core'
|
13
|
-
import type {
|
13
|
+
import type { OasTypes, PluginOptions as SwaggerPluginOptions } from '@kubb/swagger'
|
14
14
|
import type { PluginOptions } from './types.ts'
|
15
15
|
|
16
16
|
export const pluginName = 'swagger-ts' satisfies PluginOptions['name']
|
17
|
-
export const pluginKey: PluginOptions['key'] = [
|
17
|
+
export const pluginKey: PluginOptions['key'] = [pluginName] satisfies PluginOptions['key']
|
18
18
|
|
19
19
|
export const definePlugin = createPlugin<PluginOptions>((options) => {
|
20
20
|
const {
|
@@ -30,17 +30,11 @@ export const definePlugin = createPlugin<PluginOptions>((options) => {
|
|
30
30
|
exportAs,
|
31
31
|
} = options
|
32
32
|
const template = group?.output ? group.output : `${output}/{{tag}}Controller`
|
33
|
-
let pluginsOptions: [KubbPlugin<SwaggerPluginOptions>]
|
34
33
|
|
35
34
|
return {
|
36
35
|
name: pluginName,
|
37
36
|
options,
|
38
|
-
|
39
|
-
validate(plugins) {
|
40
|
-
pluginsOptions = PluginManager.getDependedPlugins<SwaggerPluginOptions>(plugins, [swaggerPluginName])
|
41
|
-
|
42
|
-
return true
|
43
|
-
},
|
37
|
+
pre: [swaggerPluginName],
|
44
38
|
resolvePath(baseName, directory, options) {
|
45
39
|
const root = path.resolve(this.config.root, this.config.output.path)
|
46
40
|
const mode = FileManager.getMode(path.resolve(root, output))
|
@@ -78,7 +72,7 @@ export const definePlugin = createPlugin<PluginOptions>((options) => {
|
|
78
72
|
return this.fileManager.write(source, writePath)
|
79
73
|
},
|
80
74
|
async buildStart() {
|
81
|
-
const [swaggerPlugin] =
|
75
|
+
const [swaggerPlugin]: [KubbPlugin<SwaggerPluginOptions>] = PluginManager.getDependedPlugins<SwaggerPluginOptions>(this.plugins, [swaggerPluginName])
|
82
76
|
|
83
77
|
const oas = await swaggerPlugin.api.getOas()
|
84
78
|
|
@@ -106,8 +100,9 @@ export const definePlugin = createPlugin<PluginOptions>((options) => {
|
|
106
100
|
enumType,
|
107
101
|
dateType,
|
108
102
|
optionalType,
|
103
|
+
oas,
|
109
104
|
}).configure()
|
110
|
-
Object.entries(schemas).forEach(([name, schema]: [string,
|
105
|
+
Object.entries(schemas).forEach(([name, schema]: [string, OasTypes.SchemaObject]) => {
|
111
106
|
// generate and pass through new code back to the core so it can be write to that file
|
112
107
|
return builder.add({
|
113
108
|
schema,
|
@@ -115,7 +110,7 @@ export const definePlugin = createPlugin<PluginOptions>((options) => {
|
|
115
110
|
})
|
116
111
|
})
|
117
112
|
|
118
|
-
const mapFolderSchema = async ([name]: [string,
|
113
|
+
const mapFolderSchema = async ([name]: [string, OasTypes.SchemaObject]) => {
|
119
114
|
const resolvedPath = this.resolvePath({ baseName: `${this.resolveName({ name, pluginKey: this.plugin.key })}.ts`, pluginKey: this.plugin.key })
|
120
115
|
|
121
116
|
if (!resolvedPath) {
|
@@ -146,8 +141,9 @@ export const definePlugin = createPlugin<PluginOptions>((options) => {
|
|
146
141
|
enumType,
|
147
142
|
dateType,
|
148
143
|
optionalType,
|
144
|
+
oas,
|
149
145
|
}).configure()
|
150
|
-
Object.entries(schemas).forEach(([name, schema]: [string,
|
146
|
+
Object.entries(schemas).forEach(([name, schema]: [string, OasTypes.SchemaObject]) => {
|
151
147
|
// generate and pass through new code back to the core so it can be write to that file
|
152
148
|
return builder.add({
|
153
149
|
schema,
|
package/src/types.ts
CHANGED
@@ -73,7 +73,7 @@ export type FileMeta = {
|
|
73
73
|
tag?: string
|
74
74
|
}
|
75
75
|
|
76
|
-
export type PluginOptions = PluginFactoryOptions<'swagger-ts',
|
76
|
+
export type PluginOptions = PluginFactoryOptions<'swagger-ts', Options, Options, never, ResolvePathOptions>
|
77
77
|
|
78
78
|
declare module '@kubb/core' {
|
79
79
|
export interface _Register {
|