@kubb/plugin-oas 3.0.0-alpha.9 → 3.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/README.md +14 -5
- package/dist/OperationGenerator-Dko8Xtd7.d.cts +561 -0
- package/dist/OperationGenerator-Dko8Xtd7.d.ts +561 -0
- package/dist/{Schema-B1vcPGiK.d.ts → Schema-C2cbPYX1.d.cts} +1 -12
- package/dist/{Schema-DoSFh7Qd.d.cts → Schema-CbtIvprh.d.ts} +1 -12
- package/dist/chunk-2TGWPVZN.cjs +92 -0
- package/dist/chunk-2TGWPVZN.cjs.map +1 -0
- package/dist/chunk-A3ROGKLW.cjs +752 -0
- package/dist/chunk-A3ROGKLW.cjs.map +1 -0
- package/dist/chunk-ABOQ73FL.cjs +36 -0
- package/dist/chunk-ABOQ73FL.cjs.map +1 -0
- package/dist/chunk-BG77DP54.js +30 -0
- package/dist/chunk-BG77DP54.js.map +1 -0
- package/dist/chunk-GF26SDHQ.js +28 -0
- package/dist/chunk-GF26SDHQ.js.map +1 -0
- package/dist/chunk-JA75IPYU.js +744 -0
- package/dist/chunk-JA75IPYU.js.map +1 -0
- package/dist/chunk-PADR76WZ.cjs +4 -0
- package/dist/chunk-PADR76WZ.cjs.map +1 -0
- package/dist/chunk-QAFBZLJA.cjs +48 -0
- package/dist/{chunk-SZDO532A.js.map → chunk-QAFBZLJA.cjs.map} +1 -1
- package/dist/chunk-R47XMJ32.js +3 -0
- package/dist/chunk-R47XMJ32.js.map +1 -0
- package/dist/chunk-TNWNNVQW.js +88 -0
- package/dist/chunk-TNWNNVQW.js.map +1 -0
- package/dist/chunk-XNCEFOE6.js +45 -0
- package/dist/chunk-XNCEFOE6.js.map +1 -0
- package/dist/chunk-ZWHQ54JM.cjs +32 -0
- package/dist/chunk-ZWHQ54JM.cjs.map +1 -0
- package/dist/components.cjs +20 -12
- package/dist/components.cjs.map +1 -1
- package/dist/components.d.cts +3 -6
- package/dist/components.d.ts +3 -6
- package/dist/components.js +3 -12
- package/dist/components.js.map +1 -1
- package/dist/generators.cjs +14 -0
- package/dist/generators.cjs.map +1 -0
- package/dist/generators.d.cts +9 -0
- package/dist/generators.d.ts +9 -0
- package/dist/generators.js +5 -0
- package/dist/generators.js.map +1 -0
- package/dist/hooks.cjs +90 -57
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.d.cts +37 -10
- package/dist/hooks.d.ts +37 -10
- package/dist/hooks.js +79 -53
- package/dist/hooks.js.map +1 -1
- package/dist/index.cjs +142 -189
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -5
- package/dist/index.d.ts +2 -5
- package/dist/index.js +82 -156
- package/dist/index.js.map +1 -1
- package/dist/utils.cjs +32 -45
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.cts +4 -13
- package/dist/utils.d.ts +4 -13
- package/dist/utils.js +8 -44
- package/dist/utils.js.map +1 -1
- package/package.json +20 -14
- package/src/OperationGenerator.ts +30 -39
- package/src/SchemaGenerator.ts +70 -11
- package/src/SchemaMapper.ts +24 -5
- package/src/components/Schema.tsx +1 -99
- package/src/generator.tsx +22 -18
- package/src/generators/index.ts +1 -0
- package/src/generators/jsonGenerator.ts +32 -0
- package/src/hooks/index.ts +2 -0
- package/src/hooks/useOperationManager.ts +56 -31
- package/src/hooks/useSchemaManager.ts +77 -0
- package/src/index.ts +2 -4
- package/src/plugin.ts +69 -56
- package/src/types.ts +29 -22
- package/src/utils/getParams.ts +1 -1
- package/src/utils/getSchemaFactory.ts +1 -1
- package/src/utils/index.ts +0 -1
- package/src/utils/parseFromConfig.ts +7 -7
- package/dist/OperationGenerator-By5WOmWB.d.ts +0 -165
- package/dist/OperationGenerator-Gd1X7wUz.d.cts +0 -165
- package/dist/SchemaMapper-sGcY1xL5.d.cts +0 -247
- package/dist/SchemaMapper-sGcY1xL5.d.ts +0 -247
- package/dist/chunk-75BIOXB7.cjs +0 -7
- package/dist/chunk-75BIOXB7.cjs.map +0 -1
- package/dist/chunk-IAUV3UKH.cjs +0 -3965
- package/dist/chunk-IAUV3UKH.cjs.map +0 -1
- package/dist/chunk-M347763D.js +0 -3965
- package/dist/chunk-M347763D.js.map +0 -1
- package/dist/chunk-N7EEVJA6.js +0 -35
- package/dist/chunk-N7EEVJA6.js.map +0 -1
- package/dist/chunk-NU4F7G47.cjs +0 -89
- package/dist/chunk-NU4F7G47.cjs.map +0 -1
- package/dist/chunk-O76YQFZB.cjs +0 -35
- package/dist/chunk-O76YQFZB.cjs.map +0 -1
- package/dist/chunk-SQ64ESS4.js +0 -7
- package/dist/chunk-SQ64ESS4.js.map +0 -1
- package/dist/chunk-SZDO532A.js +0 -89
- package/dist/types-CZTUCaE5.d.cts +0 -145
- package/dist/types-CZTUCaE5.d.ts +0 -145
- package/src/utils/refSorter.ts +0 -13
|
@@ -5,15 +5,10 @@ import type { PluginFactoryOptions, PluginManager } from '@kubb/core'
|
|
|
5
5
|
import type * as KubbFile from '@kubb/fs/types'
|
|
6
6
|
|
|
7
7
|
import type { Plugin } from '@kubb/core'
|
|
8
|
-
import type { HttpMethod, Oas, OasTypes, Operation, contentType } from '@kubb/oas'
|
|
8
|
+
import type { HttpMethod, Oas, OasTypes, Operation, SchemaObject, contentType } from '@kubb/oas'
|
|
9
9
|
import type { Generator } from './generator.tsx'
|
|
10
10
|
import type { Exclude, Include, OperationSchemas, OperationsByMethod, Override } from './types.ts'
|
|
11
11
|
|
|
12
|
-
/**
|
|
13
|
-
* @deprecated
|
|
14
|
-
*/
|
|
15
|
-
export type GetOperationGeneratorOptions<T extends OperationGenerator<any, any, any>> = T extends OperationGenerator<infer Options, any, any> ? Options : never
|
|
16
|
-
|
|
17
12
|
export type OperationMethodResult<TFileMeta extends FileMetaBase> = Promise<KubbFile.File<TFileMeta> | Array<KubbFile.File<TFileMeta>> | null>
|
|
18
13
|
|
|
19
14
|
type Context<TOptions, TPluginOptions extends PluginFactoryOptions> = {
|
|
@@ -31,10 +26,9 @@ type Context<TOptions, TPluginOptions extends PluginFactoryOptions> = {
|
|
|
31
26
|
}
|
|
32
27
|
|
|
33
28
|
export class OperationGenerator<
|
|
34
|
-
TOptions = unknown,
|
|
35
29
|
TPluginOptions extends PluginFactoryOptions = PluginFactoryOptions,
|
|
36
30
|
TFileMeta extends FileMetaBase = FileMetaBase,
|
|
37
|
-
> extends BaseGenerator<
|
|
31
|
+
> extends BaseGenerator<TPluginOptions['resolvedOptions'], Context<TPluginOptions['resolvedOptions'], TPluginOptions>> {
|
|
38
32
|
#operationsByMethod: OperationsByMethod = {}
|
|
39
33
|
get operationsByMethod(): OperationsByMethod {
|
|
40
34
|
return this.#operationsByMethod
|
|
@@ -44,7 +38,7 @@ export class OperationGenerator<
|
|
|
44
38
|
this.#operationsByMethod = paths
|
|
45
39
|
}
|
|
46
40
|
|
|
47
|
-
#getOptions(operation: Operation, method: HttpMethod): Partial<
|
|
41
|
+
#getOptions(operation: Operation, method: HttpMethod): Partial<TPluginOptions['resolvedOptions']> {
|
|
48
42
|
const { override = [] } = this.context
|
|
49
43
|
|
|
50
44
|
return (
|
|
@@ -70,10 +64,6 @@ export class OperationGenerator<
|
|
|
70
64
|
)
|
|
71
65
|
}
|
|
72
66
|
|
|
73
|
-
/**
|
|
74
|
-
*
|
|
75
|
-
* @deprecated
|
|
76
|
-
*/
|
|
77
67
|
#isExcluded(operation: Operation, method: HttpMethod): boolean {
|
|
78
68
|
const { exclude = [] } = this.context
|
|
79
69
|
let matched = false
|
|
@@ -99,10 +89,6 @@ export class OperationGenerator<
|
|
|
99
89
|
return matched
|
|
100
90
|
}
|
|
101
91
|
|
|
102
|
-
/**
|
|
103
|
-
*
|
|
104
|
-
* @deprecated
|
|
105
|
-
*/
|
|
106
92
|
#isIncluded(operation: Operation, method: HttpMethod): boolean {
|
|
107
93
|
const { include = [] } = this.context
|
|
108
94
|
let matched = false
|
|
@@ -131,10 +117,8 @@ export class OperationGenerator<
|
|
|
131
117
|
getSchemas(
|
|
132
118
|
operation: Operation,
|
|
133
119
|
{
|
|
134
|
-
forStatusCode,
|
|
135
120
|
resolveName = (name) => name,
|
|
136
121
|
}: {
|
|
137
|
-
forStatusCode?: string | number
|
|
138
122
|
resolveName?: (name: string) => string
|
|
139
123
|
} = {},
|
|
140
124
|
): OperationSchemas {
|
|
@@ -142,9 +126,6 @@ export class OperationGenerator<
|
|
|
142
126
|
const queryParamsSchema = this.context.oas.getParametersSchema(operation, 'query')
|
|
143
127
|
const headerParamsSchema = this.context.oas.getParametersSchema(operation, 'header')
|
|
144
128
|
const requestSchema = this.context.oas.getRequestSchema(operation)
|
|
145
|
-
const responseStatusCode =
|
|
146
|
-
forStatusCode || (operation.schema.responses && Object.keys(operation.schema.responses).find((key) => key.startsWith('2'))) || 200
|
|
147
|
-
const responseSchema = this.context.oas.getResponseSchema(operation, responseStatusCode)
|
|
148
129
|
const statusCodes = operation.getResponseStatusCodes().map((statusCode) => {
|
|
149
130
|
let name = statusCode
|
|
150
131
|
if (name === 'default') {
|
|
@@ -161,8 +142,15 @@ export class OperationGenerator<
|
|
|
161
142
|
operationName: transformers.pascalCase(`${operation.getOperationId()}`),
|
|
162
143
|
statusCode: name === 'error' ? undefined : Number(statusCode),
|
|
163
144
|
keys: schema?.properties ? Object.keys(schema.properties) : undefined,
|
|
145
|
+
keysToOmit: schema?.properties
|
|
146
|
+
? Object.keys(schema.properties).filter((key) => {
|
|
147
|
+
const item = schema.properties?.[key] as OasTypes.SchemaObject
|
|
148
|
+
return item?.writeOnly
|
|
149
|
+
})
|
|
150
|
+
: undefined,
|
|
164
151
|
}
|
|
165
152
|
})
|
|
153
|
+
const hasResponses = statusCodes.some((item) => item.statusCode?.toString().startsWith('2'))
|
|
166
154
|
|
|
167
155
|
return {
|
|
168
156
|
pathParams: pathParamsSchema
|
|
@@ -211,19 +199,22 @@ export class OperationGenerator<
|
|
|
211
199
|
: undefined,
|
|
212
200
|
response: {
|
|
213
201
|
name: resolveName(transformers.pascalCase(`${operation.getOperationId()} ${operation.method === 'get' ? 'queryResponse' : 'mutationResponse'}`)),
|
|
214
|
-
description: operation.getResponseAsJSONSchema(responseStatusCode)?.at(0)?.description,
|
|
215
202
|
operation,
|
|
216
203
|
operationName: transformers.pascalCase(`${operation.getOperationId()}`),
|
|
217
|
-
schema:
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
204
|
+
schema: {
|
|
205
|
+
oneOf: hasResponses
|
|
206
|
+
? statusCodes
|
|
207
|
+
.filter((item) => item.statusCode?.toString().startsWith('2'))
|
|
208
|
+
.map((item) => {
|
|
209
|
+
return {
|
|
210
|
+
...item.schema,
|
|
211
|
+
$ref: resolveName(transformers.pascalCase(`${operation.getOperationId()} ${item.statusCode}`)),
|
|
212
|
+
}
|
|
213
|
+
})
|
|
214
|
+
: undefined,
|
|
215
|
+
} as SchemaObject,
|
|
226
216
|
},
|
|
217
|
+
responses: statusCodes.filter((item) => item.statusCode?.toString().startsWith('2')),
|
|
227
218
|
errors: statusCodes.filter((item) => item.statusCode?.toString().startsWith('4') || item.statusCode?.toString().startsWith('5')),
|
|
228
219
|
statusCodes,
|
|
229
220
|
}
|
|
@@ -231,7 +222,7 @@ export class OperationGenerator<
|
|
|
231
222
|
|
|
232
223
|
#methods = ['get', 'post', 'patch', 'put', 'delete']
|
|
233
224
|
|
|
234
|
-
async build(...generators: Array<Generator<
|
|
225
|
+
async build(...generators: Array<Generator<TPluginOptions>>): Promise<Array<KubbFile.File<TFileMeta>>> {
|
|
235
226
|
const { oas } = this.context
|
|
236
227
|
|
|
237
228
|
const paths = oas.getPaths()
|
|
@@ -336,41 +327,41 @@ export class OperationGenerator<
|
|
|
336
327
|
/**
|
|
337
328
|
* Operation
|
|
338
329
|
*/
|
|
339
|
-
async operation(operation: Operation, options:
|
|
330
|
+
async operation(operation: Operation, options: TPluginOptions['resolvedOptions']): OperationMethodResult<TFileMeta> {
|
|
340
331
|
return []
|
|
341
332
|
}
|
|
342
333
|
|
|
343
334
|
/**
|
|
344
335
|
* GET
|
|
345
336
|
*/
|
|
346
|
-
async get(operation: Operation, options:
|
|
337
|
+
async get(operation: Operation, options: TPluginOptions['resolvedOptions']): OperationMethodResult<TFileMeta> {
|
|
347
338
|
return []
|
|
348
339
|
}
|
|
349
340
|
|
|
350
341
|
/**
|
|
351
342
|
* POST
|
|
352
343
|
*/
|
|
353
|
-
async post(operation: Operation, options:
|
|
344
|
+
async post(operation: Operation, options: TPluginOptions['resolvedOptions']): OperationMethodResult<TFileMeta> {
|
|
354
345
|
return []
|
|
355
346
|
}
|
|
356
347
|
/**
|
|
357
348
|
* PATCH
|
|
358
349
|
*/
|
|
359
|
-
async patch(operation: Operation, options:
|
|
350
|
+
async patch(operation: Operation, options: TPluginOptions['resolvedOptions']): OperationMethodResult<TFileMeta> {
|
|
360
351
|
return []
|
|
361
352
|
}
|
|
362
353
|
|
|
363
354
|
/**
|
|
364
355
|
* PUT
|
|
365
356
|
*/
|
|
366
|
-
async put(operation: Operation, options:
|
|
357
|
+
async put(operation: Operation, options: TPluginOptions['resolvedOptions']): OperationMethodResult<TFileMeta> {
|
|
367
358
|
return []
|
|
368
359
|
}
|
|
369
360
|
|
|
370
361
|
/**
|
|
371
362
|
* DELETE
|
|
372
363
|
*/
|
|
373
|
-
async delete(operation: Operation, options:
|
|
364
|
+
async delete(operation: Operation, options: TPluginOptions['resolvedOptions']): OperationMethodResult<TFileMeta> {
|
|
374
365
|
return []
|
|
375
366
|
}
|
|
376
367
|
|
package/src/SchemaGenerator.ts
CHANGED
|
@@ -302,7 +302,7 @@ export class SchemaGenerator<
|
|
|
302
302
|
|
|
303
303
|
if (additionalProperties) {
|
|
304
304
|
additionalPropertiesSchemas =
|
|
305
|
-
additionalProperties === true
|
|
305
|
+
additionalProperties === true || !Object.keys(additionalProperties).length
|
|
306
306
|
? [{ keyword: this.#getUnknownReturn({ schema, name }) }]
|
|
307
307
|
: this.parse({ schema: additionalProperties as SchemaObject, parentName: name })
|
|
308
308
|
}
|
|
@@ -336,7 +336,7 @@ export class SchemaGenerator<
|
|
|
336
336
|
return [
|
|
337
337
|
{
|
|
338
338
|
keyword: schemaKeywords.ref,
|
|
339
|
-
args: { name: ref.propertyName, path: ref.path },
|
|
339
|
+
args: { name: ref.propertyName, path: ref.path, isImportable: !!this.context.oas.get($ref) },
|
|
340
340
|
},
|
|
341
341
|
]
|
|
342
342
|
}
|
|
@@ -349,7 +349,7 @@ export class SchemaGenerator<
|
|
|
349
349
|
const file = this.context.pluginManager.getFile({
|
|
350
350
|
name: fileName,
|
|
351
351
|
pluginKey: this.context.plugin.key,
|
|
352
|
-
|
|
352
|
+
extname: '.ts',
|
|
353
353
|
})
|
|
354
354
|
|
|
355
355
|
ref = this.refs[$ref] = {
|
|
@@ -361,7 +361,7 @@ export class SchemaGenerator<
|
|
|
361
361
|
return [
|
|
362
362
|
{
|
|
363
363
|
keyword: schemaKeywords.ref,
|
|
364
|
-
args: { name: ref.propertyName, path: ref?.path },
|
|
364
|
+
args: { name: ref.propertyName, path: ref?.path, isImportable: !!this.context.oas.get($ref) },
|
|
365
365
|
},
|
|
366
366
|
]
|
|
367
367
|
}
|
|
@@ -453,10 +453,16 @@ export class SchemaGenerator<
|
|
|
453
453
|
baseItems.push({ keyword: schemaKeywords.readOnly })
|
|
454
454
|
}
|
|
455
455
|
|
|
456
|
+
if (schema.writeOnly) {
|
|
457
|
+
baseItems.push({ keyword: schemaKeywords.writeOnly })
|
|
458
|
+
}
|
|
459
|
+
|
|
456
460
|
if (isReference(schema)) {
|
|
457
461
|
return [
|
|
458
462
|
...this.#getRefAlias(schema),
|
|
459
463
|
nullable && { keyword: schemaKeywords.nullable },
|
|
464
|
+
schema.readOnly && { keyword: schemaKeywords.readOnly },
|
|
465
|
+
schema.writeOnly && { keyword: schemaKeywords.writeOnly },
|
|
460
466
|
{
|
|
461
467
|
keyword: schemaKeywords.schema,
|
|
462
468
|
args: {
|
|
@@ -483,7 +489,20 @@ export class SchemaGenerator<
|
|
|
483
489
|
}),
|
|
484
490
|
}
|
|
485
491
|
if (schemaWithoutOneOf.properties) {
|
|
486
|
-
|
|
492
|
+
const propertySchemas = this.parse({ schema: schemaWithoutOneOf, name, parentName })
|
|
493
|
+
|
|
494
|
+
return [
|
|
495
|
+
{
|
|
496
|
+
...union,
|
|
497
|
+
args: union.args.map((arg) => {
|
|
498
|
+
return {
|
|
499
|
+
keyword: schemaKeywords.and,
|
|
500
|
+
args: [arg, ...propertySchemas],
|
|
501
|
+
}
|
|
502
|
+
}),
|
|
503
|
+
},
|
|
504
|
+
...baseItems,
|
|
505
|
+
]
|
|
487
506
|
}
|
|
488
507
|
|
|
489
508
|
return [union, ...baseItems]
|
|
@@ -618,6 +637,35 @@ export class SchemaGenerator<
|
|
|
618
637
|
]
|
|
619
638
|
}
|
|
620
639
|
|
|
640
|
+
if (schema.type === 'boolean') {
|
|
641
|
+
// we cannot use z.enum when enum type is boolean
|
|
642
|
+
const enumNames = extensionEnums[0]?.find((item) => isKeyword(item, schemaKeywords.enum)) as unknown as SchemaKeywordMapper['enum']
|
|
643
|
+
return [
|
|
644
|
+
{
|
|
645
|
+
keyword: schemaKeywords.enum,
|
|
646
|
+
args: {
|
|
647
|
+
name: enumName,
|
|
648
|
+
typeName,
|
|
649
|
+
asConst: true,
|
|
650
|
+
items: enumNames?.args?.items
|
|
651
|
+
? [...new Set(enumNames.args.items)].map(({ name, value }) => ({
|
|
652
|
+
name,
|
|
653
|
+
value,
|
|
654
|
+
format: 'boolean',
|
|
655
|
+
}))
|
|
656
|
+
: [...new Set(filteredValues)].map((value: string) => {
|
|
657
|
+
return {
|
|
658
|
+
name: value,
|
|
659
|
+
value,
|
|
660
|
+
format: 'boolean',
|
|
661
|
+
}
|
|
662
|
+
}),
|
|
663
|
+
},
|
|
664
|
+
},
|
|
665
|
+
...baseItems.filter((item) => item.keyword !== schemaKeywords.matches),
|
|
666
|
+
]
|
|
667
|
+
}
|
|
668
|
+
|
|
621
669
|
if (extensionEnums.length > 0 && extensionEnums[0]) {
|
|
622
670
|
return extensionEnums[0]
|
|
623
671
|
}
|
|
@@ -808,7 +856,7 @@ export class SchemaGenerator<
|
|
|
808
856
|
].filter(Boolean)
|
|
809
857
|
}
|
|
810
858
|
|
|
811
|
-
if (!['boolean', 'object', 'number', 'string', 'integer'].includes(schema.type)) {
|
|
859
|
+
if (!['boolean', 'object', 'number', 'string', 'integer', 'null'].includes(schema.type)) {
|
|
812
860
|
this.context.pluginManager.logger.emit('warning', `Schema type '${schema.type}' is not valid for schema ${parentName}.${name}`)
|
|
813
861
|
}
|
|
814
862
|
|
|
@@ -819,14 +867,20 @@ export class SchemaGenerator<
|
|
|
819
867
|
return [{ keyword: unknownReturn }]
|
|
820
868
|
}
|
|
821
869
|
|
|
822
|
-
async build(...generators: Array<Generator<
|
|
870
|
+
async build(...generators: Array<Generator<TPluginOptions>>): Promise<Array<KubbFile.File<TFileMeta>>> {
|
|
823
871
|
const { oas, contentType, include } = this.context
|
|
824
872
|
|
|
873
|
+
oas.resolveDiscriminators()
|
|
874
|
+
|
|
825
875
|
const schemas = getSchemas({ oas, contentType, includes: include })
|
|
826
876
|
|
|
827
|
-
const promises = Object.entries(schemas).reduce((acc, [name,
|
|
877
|
+
const promises = Object.entries(schemas).reduce((acc, [name, value]) => {
|
|
878
|
+
if (!value) {
|
|
879
|
+
return acc
|
|
880
|
+
}
|
|
881
|
+
|
|
828
882
|
const options = this.#getOptions({ name })
|
|
829
|
-
const promiseOperation = this.schema.call(this, name,
|
|
883
|
+
const promiseOperation = this.schema.call(this, name, value, {
|
|
830
884
|
...this.options,
|
|
831
885
|
...options,
|
|
832
886
|
})
|
|
@@ -836,10 +890,15 @@ export class SchemaGenerator<
|
|
|
836
890
|
}
|
|
837
891
|
|
|
838
892
|
generators?.forEach((generator) => {
|
|
893
|
+
const tree = this.parse({ schema: value, name: name })
|
|
894
|
+
|
|
839
895
|
const promise = generator.schema?.({
|
|
840
896
|
instance: this,
|
|
841
|
-
|
|
842
|
-
|
|
897
|
+
schema: {
|
|
898
|
+
name,
|
|
899
|
+
value,
|
|
900
|
+
tree,
|
|
901
|
+
},
|
|
843
902
|
options: {
|
|
844
903
|
...this.options,
|
|
845
904
|
...options,
|
package/src/SchemaMapper.ts
CHANGED
|
@@ -12,6 +12,7 @@ export type SchemaKeywordMapper = {
|
|
|
12
12
|
strict: { keyword: 'strict' }
|
|
13
13
|
url: { keyword: 'url' }
|
|
14
14
|
readOnly: { keyword: 'readOnly' }
|
|
15
|
+
writeOnly: { keyword: 'writeOnly' }
|
|
15
16
|
uuid: { keyword: 'uuid' }
|
|
16
17
|
email: { keyword: 'email' }
|
|
17
18
|
firstName: { keyword: 'firstName' }
|
|
@@ -34,8 +35,8 @@ export type SchemaKeywordMapper = {
|
|
|
34
35
|
asConst: boolean
|
|
35
36
|
items: Array<{
|
|
36
37
|
name: string | number
|
|
37
|
-
format: 'string' | 'number'
|
|
38
|
-
value?: string | number
|
|
38
|
+
format: 'string' | 'number' | 'boolean'
|
|
39
|
+
value?: string | number | boolean
|
|
39
40
|
}>
|
|
40
41
|
}
|
|
41
42
|
}
|
|
@@ -44,14 +45,25 @@ export type SchemaKeywordMapper = {
|
|
|
44
45
|
keyword: 'const'
|
|
45
46
|
args: {
|
|
46
47
|
name: string | number
|
|
47
|
-
format: 'string' | 'number'
|
|
48
|
-
value?: string | number
|
|
48
|
+
format: 'string' | 'number' | 'boolean'
|
|
49
|
+
value?: string | number | boolean
|
|
49
50
|
}
|
|
50
51
|
}
|
|
51
52
|
union: { keyword: 'union'; args: Schema[] }
|
|
52
53
|
ref: {
|
|
53
54
|
keyword: 'ref'
|
|
54
|
-
args: {
|
|
55
|
+
args: {
|
|
56
|
+
name: string
|
|
57
|
+
/**
|
|
58
|
+
* Full qualified path.
|
|
59
|
+
*/
|
|
60
|
+
path: KubbFile.OptionalPath
|
|
61
|
+
/**
|
|
62
|
+
* When true `File.Import` will be used.
|
|
63
|
+
* When false a reference will be used inside the current file.
|
|
64
|
+
*/
|
|
65
|
+
isImportable: boolean
|
|
66
|
+
}
|
|
55
67
|
}
|
|
56
68
|
matches: { keyword: 'matches'; args?: string }
|
|
57
69
|
boolean: { keyword: 'boolean' }
|
|
@@ -107,6 +119,7 @@ export const schemaKeywords = {
|
|
|
107
119
|
max: 'max',
|
|
108
120
|
optional: 'optional',
|
|
109
121
|
readOnly: 'readOnly',
|
|
122
|
+
writeOnly: 'writeOnly',
|
|
110
123
|
|
|
111
124
|
// custom ones
|
|
112
125
|
object: 'object',
|
|
@@ -140,6 +153,12 @@ export type SchemaKeywordBase<T> = {
|
|
|
140
153
|
|
|
141
154
|
export type Schema = { keyword: string } | SchemaKeywordMapper[keyof SchemaKeywordMapper]
|
|
142
155
|
|
|
156
|
+
export type SchemaTree = {
|
|
157
|
+
parent: Schema | undefined
|
|
158
|
+
current: Schema
|
|
159
|
+
siblings: Schema[]
|
|
160
|
+
}
|
|
161
|
+
|
|
143
162
|
export function isKeyword<T extends Schema, K extends keyof SchemaKeywordMapper>(meta: T, keyword: K): meta is Extract<T, SchemaKeywordMapper[K]> {
|
|
144
163
|
return meta.keyword === keyword
|
|
145
164
|
}
|
|
@@ -1,15 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createContext } from '@kubb/react'
|
|
2
2
|
|
|
3
|
-
import { schemaKeywords } from '../SchemaMapper.ts'
|
|
4
|
-
import { useSchema } from '../hooks/useSchema.ts'
|
|
5
|
-
|
|
6
|
-
import type * as KubbFile from '@kubb/fs/types'
|
|
7
3
|
import type { SchemaObject } from '@kubb/oas'
|
|
8
4
|
import type { KubbNode } from '@kubb/react/types'
|
|
9
|
-
import type { ReactNode } from 'react'
|
|
10
|
-
import { SchemaGenerator } from '../SchemaGenerator.ts'
|
|
11
5
|
import type { Schema as SchemaType } from '../SchemaMapper.ts'
|
|
12
|
-
import type { PluginOas } from '../types.ts'
|
|
13
6
|
|
|
14
7
|
export type SchemaContextProps = {
|
|
15
8
|
name: string
|
|
@@ -33,95 +26,4 @@ export function Schema({ name, value, tree = [], children }: Props): KubbNode {
|
|
|
33
26
|
return <SchemaContext.Provider value={{ name, schema: value, tree }}>{children}</SchemaContext.Provider>
|
|
34
27
|
}
|
|
35
28
|
|
|
36
|
-
type FileProps = {
|
|
37
|
-
isTypeOnly?: boolean
|
|
38
|
-
output: string | undefined
|
|
39
|
-
children?: KubbNode
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
Schema.File = function ({ output, isTypeOnly, children }: FileProps): ReactNode {
|
|
43
|
-
const { plugin, pluginManager, mode } = useApp<PluginOas>()
|
|
44
|
-
const { name } = useSchema()
|
|
45
|
-
|
|
46
|
-
if (mode === 'single') {
|
|
47
|
-
const baseName = `${pluginManager.resolveName({
|
|
48
|
-
name,
|
|
49
|
-
pluginKey: plugin.key,
|
|
50
|
-
type: 'file',
|
|
51
|
-
})}.ts` as const
|
|
52
|
-
|
|
53
|
-
const resolvedPath = pluginManager.resolvePath({
|
|
54
|
-
baseName: '',
|
|
55
|
-
pluginKey: plugin.key,
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
if (!resolvedPath) {
|
|
59
|
-
return null
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return (
|
|
63
|
-
<File
|
|
64
|
-
baseName={baseName}
|
|
65
|
-
path={resolvedPath}
|
|
66
|
-
meta={{
|
|
67
|
-
pluginKey: plugin.key,
|
|
68
|
-
}}
|
|
69
|
-
>
|
|
70
|
-
{children}
|
|
71
|
-
</File>
|
|
72
|
-
)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const baseName = `${pluginManager.resolveName({
|
|
76
|
-
name,
|
|
77
|
-
pluginKey: plugin.key,
|
|
78
|
-
type: 'file',
|
|
79
|
-
})}.ts` as const
|
|
80
|
-
const resolvedPath = pluginManager.resolvePath({
|
|
81
|
-
baseName,
|
|
82
|
-
pluginKey: plugin.key,
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
if (!resolvedPath) {
|
|
86
|
-
return null
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return (
|
|
90
|
-
<File
|
|
91
|
-
baseName={baseName}
|
|
92
|
-
path={resolvedPath}
|
|
93
|
-
meta={{
|
|
94
|
-
pluginKey: plugin.key,
|
|
95
|
-
}}
|
|
96
|
-
>
|
|
97
|
-
<Schema.Imports isTypeOnly={isTypeOnly} />
|
|
98
|
-
{children}
|
|
99
|
-
</File>
|
|
100
|
-
)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
type SchemaImportsProps = {
|
|
104
|
-
isTypeOnly?: boolean
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
Schema.Imports = ({ isTypeOnly }: SchemaImportsProps): ReactNode => {
|
|
108
|
-
const { tree } = useSchema()
|
|
109
|
-
const { path: root } = useFile()
|
|
110
|
-
|
|
111
|
-
const refs = SchemaGenerator.deepSearch(tree, schemaKeywords.ref)
|
|
112
|
-
|
|
113
|
-
return (
|
|
114
|
-
<>
|
|
115
|
-
{refs
|
|
116
|
-
?.map((item, i) => {
|
|
117
|
-
if (!item.args.path) {
|
|
118
|
-
return undefined
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return <File.Import key={i} root={root} name={[item.args.name]} path={item.args.path} isTypeOnly={isTypeOnly} />
|
|
122
|
-
})
|
|
123
|
-
.filter(Boolean)}
|
|
124
|
-
</>
|
|
125
|
-
)
|
|
126
|
-
}
|
|
127
29
|
Schema.Context = SchemaContext
|
package/src/generator.tsx
CHANGED
|
@@ -6,6 +6,7 @@ import { App, createRoot } from '@kubb/react'
|
|
|
6
6
|
import type { KubbNode } from '@kubb/react/types'
|
|
7
7
|
import type { OperationGenerator } from './OperationGenerator.ts'
|
|
8
8
|
import type { SchemaGenerator, SchemaGeneratorOptions } from './SchemaGenerator.ts'
|
|
9
|
+
import type { Schema } from './SchemaMapper.ts'
|
|
9
10
|
import type { OperationsByMethod } from './types.ts'
|
|
10
11
|
|
|
11
12
|
type OperationsProps<TOptions extends PluginFactoryOptions> = {
|
|
@@ -23,16 +24,19 @@ type OperationProps<TOptions extends PluginFactoryOptions> = {
|
|
|
23
24
|
|
|
24
25
|
type SchemaProps<TOptions extends PluginFactoryOptions> = {
|
|
25
26
|
instance: Omit<SchemaGenerator<SchemaGeneratorOptions, TOptions>, 'build'>
|
|
26
|
-
name: string
|
|
27
|
-
schema: SchemaObject
|
|
28
27
|
options: TOptions['resolvedOptions']
|
|
28
|
+
schema: {
|
|
29
|
+
name: string
|
|
30
|
+
tree: Array<Schema>
|
|
31
|
+
value: SchemaObject
|
|
32
|
+
}
|
|
29
33
|
}
|
|
30
34
|
|
|
31
35
|
export type GeneratorOptions<TOptions extends PluginFactoryOptions> = {
|
|
32
36
|
name: string
|
|
33
|
-
operations?: (props: OperationsProps<TOptions>) => Promise<KubbFile.File[]>
|
|
34
|
-
operation?: (props: OperationProps<TOptions>) => Promise<KubbFile.File[]>
|
|
35
|
-
schema?: (props: SchemaProps<TOptions>) => Promise<KubbFile.File[]>
|
|
37
|
+
operations?: (this: GeneratorOptions<TOptions>, props: OperationsProps<TOptions>) => Promise<KubbFile.File[]>
|
|
38
|
+
operation?: (this: GeneratorOptions<TOptions>, props: OperationProps<TOptions>) => Promise<KubbFile.File[]>
|
|
39
|
+
schema?: (this: GeneratorOptions<TOptions>, props: SchemaProps<TOptions>) => Promise<KubbFile.File[]>
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
export type Generator<TOptions extends PluginFactoryOptions> = GeneratorOptions<TOptions>
|
|
@@ -43,13 +47,9 @@ export function createGenerator<TOptions extends PluginFactoryOptions>(parseOpti
|
|
|
43
47
|
|
|
44
48
|
export type ReactGeneratorOptions<TOptions extends PluginFactoryOptions> = {
|
|
45
49
|
name: string
|
|
46
|
-
Operations?: (props: OperationsProps<TOptions>) => KubbNode
|
|
47
|
-
Operation?: (props: OperationProps<TOptions>) => KubbNode
|
|
48
|
-
Schema?: (props: SchemaProps<TOptions>) => KubbNode
|
|
49
|
-
/**
|
|
50
|
-
* Combine all react nodes and only render ones(to string or render)
|
|
51
|
-
*/
|
|
52
|
-
render?: () => any
|
|
50
|
+
Operations?: (this: ReactGeneratorOptions<TOptions>, props: OperationsProps<TOptions>) => KubbNode
|
|
51
|
+
Operation?: (this: ReactGeneratorOptions<TOptions>, props: OperationProps<TOptions>) => KubbNode
|
|
52
|
+
Schema?: (this: ReactGeneratorOptions<TOptions>, props: SchemaProps<TOptions>) => KubbNode
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
export function createReactGenerator<TOptions extends PluginFactoryOptions>(parseOptions: ReactGeneratorOptions<TOptions>): Generator<TOptions> {
|
|
@@ -65,10 +65,12 @@ export function createReactGenerator<TOptions extends PluginFactoryOptions>(pars
|
|
|
65
65
|
logger: pluginManager.logger,
|
|
66
66
|
})
|
|
67
67
|
|
|
68
|
+
const Component = parseOptions.Operations.bind(this)
|
|
69
|
+
|
|
68
70
|
root.render(
|
|
69
71
|
<App pluginManager={pluginManager} plugin={plugin} mode={mode}>
|
|
70
72
|
<Oas oas={oas} operations={operations} generator={instance}>
|
|
71
|
-
<
|
|
73
|
+
<Component operations={operations} instance={instance} operationsByMethod={operationsByMethod} options={options} />
|
|
72
74
|
</Oas>
|
|
73
75
|
</App>,
|
|
74
76
|
)
|
|
@@ -85,11 +87,13 @@ export function createReactGenerator<TOptions extends PluginFactoryOptions>(pars
|
|
|
85
87
|
logger: pluginManager.logger,
|
|
86
88
|
})
|
|
87
89
|
|
|
90
|
+
const Component = parseOptions.Operation.bind(this)
|
|
91
|
+
|
|
88
92
|
root.render(
|
|
89
93
|
<App pluginManager={pluginManager} plugin={{ ...plugin, options }} mode={mode}>
|
|
90
94
|
<Oas oas={oas} operations={[operation]} generator={instance}>
|
|
91
95
|
<Oas.Operation operation={operation}>
|
|
92
|
-
<
|
|
96
|
+
<Component operation={operation} options={options} instance={instance} />
|
|
93
97
|
</Oas.Operation>
|
|
94
98
|
</Oas>
|
|
95
99
|
</App>,
|
|
@@ -97,7 +101,7 @@ export function createReactGenerator<TOptions extends PluginFactoryOptions>(pars
|
|
|
97
101
|
|
|
98
102
|
return root.files
|
|
99
103
|
},
|
|
100
|
-
async schema({ instance, schema,
|
|
104
|
+
async schema({ instance, schema, options }) {
|
|
101
105
|
if (!parseOptions.Schema) {
|
|
102
106
|
return []
|
|
103
107
|
}
|
|
@@ -107,13 +111,13 @@ export function createReactGenerator<TOptions extends PluginFactoryOptions>(pars
|
|
|
107
111
|
logger: pluginManager.logger,
|
|
108
112
|
})
|
|
109
113
|
|
|
110
|
-
const
|
|
114
|
+
const Component = parseOptions.Schema.bind(this)
|
|
111
115
|
|
|
112
116
|
root.render(
|
|
113
117
|
<App pluginManager={pluginManager} plugin={{ ...plugin, options }} mode={mode}>
|
|
114
118
|
<Oas oas={oas}>
|
|
115
|
-
<Oas.Schema name={name} value={schema} tree={tree}>
|
|
116
|
-
<
|
|
119
|
+
<Oas.Schema name={schema.name} value={schema.value} tree={schema.tree}>
|
|
120
|
+
<Component schema={schema} options={options} instance={instance} />
|
|
117
121
|
</Oas.Schema>
|
|
118
122
|
</Oas>
|
|
119
123
|
</App>,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { jsonGenerator } from './jsonGenerator.ts'
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { camelCase } from '@kubb/core/transformers'
|
|
2
|
+
import { createGenerator } from '../generator.tsx'
|
|
3
|
+
import type { PluginOas } from '../types.ts'
|
|
4
|
+
|
|
5
|
+
export const jsonGenerator = createGenerator<PluginOas>({
|
|
6
|
+
name: 'plugin-oas',
|
|
7
|
+
async schema({ schema, instance }) {
|
|
8
|
+
const { pluginManager, plugin } = instance.context
|
|
9
|
+
const file = pluginManager.getFile({
|
|
10
|
+
name: camelCase(schema.name),
|
|
11
|
+
extname: '.json',
|
|
12
|
+
mode: 'split',
|
|
13
|
+
pluginKey: plugin.key,
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
return [
|
|
17
|
+
{
|
|
18
|
+
...file,
|
|
19
|
+
sources: [
|
|
20
|
+
{
|
|
21
|
+
name: camelCase(schema.name),
|
|
22
|
+
isExportable: false,
|
|
23
|
+
isIndexable: false,
|
|
24
|
+
value: JSON.stringify(schema.value),
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
banner: plugin.options.output?.banner,
|
|
28
|
+
format: plugin.options.output?.footer,
|
|
29
|
+
},
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
})
|
package/src/hooks/index.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { useOas } from './useOas.ts'
|
|
2
2
|
export { useOperation } from './useOperation.ts'
|
|
3
3
|
export { useOperationManager } from './useOperationManager.ts'
|
|
4
|
+
export type { SchemaNames } from './useOperationManager.ts'
|
|
4
5
|
export { useOperations } from './useOperations.ts'
|
|
5
6
|
export { useSchema } from './useSchema.ts'
|
|
7
|
+
export { useSchemaManager } from './useSchemaManager.ts'
|