@kubb/swagger-ts 2.0.0-alpha.9 → 2.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.
@@ -1,28 +1,27 @@
1
- import { SchemaGenerator } from '@kubb/core'
1
+ import { Generator } from '@kubb/core'
2
+ import transformers from '@kubb/core/transformers'
2
3
  import { getUniqueName } from '@kubb/core/utils'
3
4
  import * as factory from '@kubb/parser/factory'
4
- import { isReference } from '@kubb/swagger/utils'
5
+ import { keywordTypeNodes } from '@kubb/parser/factory'
6
+ import { getSchemaFactory, isReference } from '@kubb/swagger/utils'
5
7
 
6
- import { camelCase } from 'change-case'
8
+ import { pluginKey } from './plugin.ts'
7
9
 
8
- import type { PluginContext } from '@kubb/core'
10
+ import type { PluginManager } from '@kubb/core'
9
11
  import type { ts } from '@kubb/parser'
10
- import type { OpenAPIV3, Refs } from '@kubb/swagger'
11
- import type { Options as CaseOptions } from 'change-case'
12
+ import type { ImportMeta, Oas, OasTypes, OpenAPIV3, OpenAPIV3_1, Refs } from '@kubb/swagger'
13
+ import type { PluginOptions } from './types.ts'
12
14
 
13
15
  // based on https://github.com/cellular/oazapfts/blob/7ba226ebb15374e8483cc53e7532f1663179a22c/src/codegen/generate.ts#L398
14
16
 
15
- type Options = {
16
- usedEnumNames: Record<string, number>
17
-
18
- withJSDocs?: boolean
19
- resolveName: PluginContext['resolveName']
20
- enumType: 'enum' | 'asConst' | 'asPascalConst'
21
- dateType: 'string' | 'date'
22
- optionalType: 'questionToken' | 'undefined' | 'questionTokenAndUndefined'
17
+ type Context = {
18
+ oas: Oas
19
+ pluginManager: PluginManager
23
20
  }
24
- export class TypeGenerator extends SchemaGenerator<Options, OpenAPIV3.SchemaObject, ts.Node[]> {
21
+
22
+ export class TypeGenerator extends Generator<PluginOptions['resolvedOptions'], Context> {
25
23
  refs: Refs = {}
24
+ imports: ImportMeta[] = []
26
25
 
27
26
  extraNodes: ts.Node[] = []
28
27
 
@@ -31,39 +30,19 @@ export class TypeGenerator extends SchemaGenerator<Options, OpenAPIV3.SchemaObje
31
30
  // Keep track of already used type aliases
32
31
  #usedAliasNames: Record<string, number> = {}
33
32
 
34
- #caseOptions: CaseOptions = {
35
- delimiter: '',
36
- stripRegexp: /[^A-Z0-9$]/gi,
37
- }
38
-
39
- constructor(
40
- options: Options = {
41
- usedEnumNames: {},
42
- withJSDocs: true,
43
- resolveName: ({ name }) => name,
44
- enumType: 'asConst',
45
- dateType: 'string',
46
- optionalType: 'questionToken',
47
- },
48
- ) {
49
- super(options)
50
-
51
- return this
52
- }
53
-
54
33
  build({
55
34
  schema,
56
35
  baseName,
57
36
  description,
58
37
  keysToOmit,
59
38
  }: {
60
- schema: OpenAPIV3.SchemaObject
39
+ schema: OasTypes.SchemaObject
61
40
  baseName: string
62
41
  description?: string
63
42
  keysToOmit?: string[]
64
43
  }): ts.Node[] {
65
44
  const nodes: ts.Node[] = []
66
- const type = this.#getTypeFromSchema(schema, baseName)
45
+ const type = this.getTypeFromSchema(schema, baseName)
67
46
 
68
47
  if (!type) {
69
48
  return this.extraNodes
@@ -71,7 +50,7 @@ export class TypeGenerator extends SchemaGenerator<Options, OpenAPIV3.SchemaObje
71
50
 
72
51
  const node = factory.createTypeAliasDeclaration({
73
52
  modifiers: [factory.modifiers.export],
74
- name: this.options.resolveName({ name: baseName }) || baseName,
53
+ name: this.context.pluginManager.resolveName({ name: baseName, pluginKey, type: 'type' }),
75
54
  type: keysToOmit?.length ? factory.createOmitDeclaration({ keys: keysToOmit, type, nonNullable: true }) : type,
76
55
  })
77
56
 
@@ -79,7 +58,7 @@ export class TypeGenerator extends SchemaGenerator<Options, OpenAPIV3.SchemaObje
79
58
  nodes.push(
80
59
  factory.appendJSDocToNode({
81
60
  node,
82
- comments: [`@description ${description}`],
61
+ comments: [`@description ${transformers.trim(description)}`],
83
62
  }),
84
63
  )
85
64
  } else {
@@ -102,7 +81,7 @@ export class TypeGenerator extends SchemaGenerator<Options, OpenAPIV3.SchemaObje
102
81
  * Delegates to getBaseTypeFromSchema internally and
103
82
  * optionally adds a union with null.
104
83
  */
105
- #getTypeFromSchema(schema?: OpenAPIV3.SchemaObject, name?: string): ts.TypeNode | null {
84
+ getTypeFromSchema(schema?: OasTypes.SchemaObject, name?: string): ts.TypeNode | null {
106
85
  const type = this.#getBaseTypeFromSchema(schema, name)
107
86
 
108
87
  if (!type) {
@@ -119,49 +98,48 @@ export class TypeGenerator extends SchemaGenerator<Options, OpenAPIV3.SchemaObje
119
98
  /**
120
99
  * Recursively creates a type literal with the given props.
121
100
  */
122
- #getTypeFromProperties(baseSchema?: OpenAPIV3.SchemaObject, baseName?: string) {
101
+ #getTypeFromProperties(baseSchema?: OasTypes.SchemaObject, baseName?: string): ts.TypeNode | null {
123
102
  const { optionalType } = this.options
124
103
  const properties = baseSchema?.properties || {}
125
104
  const required = baseSchema?.required
126
105
  const additionalProperties = baseSchema?.additionalProperties
127
106
 
128
107
  const members: Array<ts.TypeElement | null> = Object.keys(properties).map((name) => {
129
- const schema = properties[name] as OpenAPIV3.SchemaObject
108
+ const schema = properties[name] as OasTypes.SchemaObject
130
109
 
131
- const isRequired = required && required.includes(name)
132
- let type = this.#getTypeFromSchema(schema, this.options.resolveName({ name: `${baseName || ''} ${name}` }))
110
+ const isRequired = Array.isArray(required) ? required.includes(name) : !!required
111
+ let type = this.getTypeFromSchema(schema, this.context.pluginManager.resolveName({ name: `${baseName || ''} ${name}`, pluginKey, type: 'type' }))
133
112
 
134
113
  if (!type) {
135
114
  return null
136
115
  }
137
116
 
138
- if (!isRequired && ['undefined', 'questionTokenAndUndefined'].includes(optionalType)) {
117
+ if (!isRequired && ['undefined', 'questionTokenAndUndefined'].includes(optionalType as string)) {
139
118
  type = factory.createUnionDeclaration({ nodes: [type, factory.keywordTypeNodes.undefined] })
140
119
  }
141
120
  const propertySignature = factory.createPropertySignature({
142
- questionToken: ['questionToken', 'questionTokenAndUndefined'].includes(optionalType) && !isRequired,
121
+ questionToken: ['questionToken', 'questionTokenAndUndefined'].includes(optionalType as string) && !isRequired,
143
122
  name,
144
123
  type: type as ts.TypeNode,
145
124
  readOnly: schema.readOnly,
146
125
  })
147
- if (this.options.withJSDocs) {
148
- return factory.appendJSDocToNode({
149
- node: propertySignature,
150
- comments: [
151
- schema.description ? `@description ${schema.description}` : undefined,
152
- schema.type ? `@type ${schema.type}${isRequired ? '' : ' | undefined'} ${schema.format || ''}` : undefined,
153
- schema.example ? `@example ${schema.example as string}` : undefined,
154
- schema.deprecated ? `@deprecated` : undefined,
155
- schema.default !== undefined && typeof schema.default === 'string' ? `@default '${schema.default}'` : undefined,
156
- schema.default !== undefined && typeof schema.default !== 'string' ? `@default ${schema.default as string}` : undefined,
157
- ].filter(Boolean),
158
- })
159
- }
126
+
127
+ return factory.appendJSDocToNode({
128
+ node: propertySignature,
129
+ comments: [
130
+ schema.description ? `@description ${schema.description}` : undefined,
131
+ schema.type ? `@type ${schema.type?.toString()}${isRequired ? '' : ' | undefined'} ${schema.format || ''}` : undefined,
132
+ schema.example ? `@example ${schema.example as string}` : undefined,
133
+ schema.deprecated ? `@deprecated` : undefined,
134
+ schema.default !== undefined && typeof schema.default === 'string' ? `@default '${schema.default}'` : undefined,
135
+ schema.default !== undefined && typeof schema.default !== 'string' ? `@default ${schema.default as string}` : undefined,
136
+ ].filter(Boolean),
137
+ })
160
138
 
161
139
  return propertySignature
162
140
  })
163
141
  if (additionalProperties) {
164
- const type = additionalProperties === true ? factory.keywordTypeNodes.any : this.#getTypeFromSchema(additionalProperties as OpenAPIV3.SchemaObject)
142
+ const type = additionalProperties === true ? factory.keywordTypeNodes.any : this.getTypeFromSchema(additionalProperties as OasTypes.SchemaObject)
165
143
 
166
144
  if (type) {
167
145
  members.push(factory.createIndexSignature(type))
@@ -182,21 +160,39 @@ export class TypeGenerator extends SchemaGenerator<Options, OpenAPIV3.SchemaObje
182
160
  }
183
161
 
184
162
  const originalName = getUniqueName($ref.replace(/.+\//, ''), this.#usedAliasNames)
185
- const propertyName = this.options.resolveName({ name: originalName }) || originalName
163
+ const propertyName = this.context.pluginManager.resolveName({ name: originalName, pluginKey, type: 'type' })
186
164
 
187
165
  ref = this.refs[$ref] = {
188
166
  propertyName,
189
167
  originalName,
190
168
  }
191
169
 
170
+ const path = this.context.pluginManager.resolvePath({ baseName: propertyName, pluginKey })
171
+
172
+ this.imports.push({
173
+ ref,
174
+ path: path || '',
175
+ isTypeOnly: true,
176
+ })
177
+
192
178
  return factory.createTypeReferenceNode(ref.propertyName, undefined)
193
179
  }
194
180
 
181
+ #getParsedSchema(schema?: OasTypes.SchemaObject) {
182
+ const parsedSchema = getSchemaFactory(this.context.oas)(schema)
183
+ return parsedSchema
184
+ }
185
+
195
186
  /**
196
187
  * This is the very core of the OpenAPI to TS conversion - it takes a
197
188
  * schema and returns the appropriate type.
198
189
  */
199
- #getBaseTypeFromSchema(schema: OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject | undefined, baseName?: string): ts.TypeNode | null {
190
+ #getBaseTypeFromSchema(
191
+ _schema: OasTypes.SchemaObject | undefined,
192
+ baseName?: string,
193
+ ): ts.TypeNode | null {
194
+ const { schema, version } = this.#getParsedSchema(_schema)
195
+
200
196
  if (!schema) {
201
197
  return factory.keywordTypeNodes.any
202
198
  }
@@ -207,13 +203,13 @@ export class TypeGenerator extends SchemaGenerator<Options, OpenAPIV3.SchemaObje
207
203
 
208
204
  if (schema.oneOf) {
209
205
  // union
210
- const schemaWithoutOneOf = { ...schema, oneOf: undefined }
206
+ const schemaWithoutOneOf = { ...schema, oneOf: undefined } as OasTypes.SchemaObject
211
207
 
212
208
  const union = factory.createUnionDeclaration({
213
209
  withParentheses: true,
214
210
  nodes: schema.oneOf
215
- .map((item: OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject) => {
216
- return this.#getBaseTypeFromSchema(item)
211
+ .map((item) => {
212
+ return item && this.getTypeFromSchema(item as OasTypes.SchemaObject)
217
213
  })
218
214
  .filter((item) => {
219
215
  return item && item !== factory.keywordTypeNodes.any
@@ -222,7 +218,7 @@ export class TypeGenerator extends SchemaGenerator<Options, OpenAPIV3.SchemaObje
222
218
 
223
219
  if (schemaWithoutOneOf.properties) {
224
220
  return factory.createIntersectionDeclaration({
225
- nodes: [this.#getBaseTypeFromSchema(schemaWithoutOneOf, baseName), union].filter(Boolean),
221
+ nodes: [this.getTypeFromSchema(schemaWithoutOneOf, baseName), union].filter(Boolean),
226
222
  })
227
223
  }
228
224
 
@@ -230,13 +226,13 @@ export class TypeGenerator extends SchemaGenerator<Options, OpenAPIV3.SchemaObje
230
226
  }
231
227
 
232
228
  if (schema.anyOf) {
233
- const schemaWithoutAnyOf = { ...schema, anyOf: undefined }
229
+ const schemaWithoutAnyOf = { ...schema, anyOf: undefined } as OasTypes.SchemaObject
234
230
 
235
231
  const union = factory.createUnionDeclaration({
236
232
  withParentheses: true,
237
233
  nodes: schema.anyOf
238
- .map((item: OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject) => {
239
- return this.#getBaseTypeFromSchema(item)
234
+ .map((item) => {
235
+ return item && this.getTypeFromSchema(item as OasTypes.SchemaObject)
240
236
  })
241
237
  .filter((item) => {
242
238
  return item && item !== factory.keywordTypeNodes.any
@@ -245,7 +241,7 @@ export class TypeGenerator extends SchemaGenerator<Options, OpenAPIV3.SchemaObje
245
241
 
246
242
  if (schemaWithoutAnyOf.properties) {
247
243
  return factory.createIntersectionDeclaration({
248
- nodes: [this.#getBaseTypeFromSchema(schemaWithoutAnyOf, baseName), union].filter(Boolean),
244
+ nodes: [this.getTypeFromSchema(schemaWithoutAnyOf, baseName), union].filter(Boolean),
249
245
  })
250
246
  }
251
247
 
@@ -253,13 +249,13 @@ export class TypeGenerator extends SchemaGenerator<Options, OpenAPIV3.SchemaObje
253
249
  }
254
250
  if (schema.allOf) {
255
251
  // intersection/add
256
- const schemaWithoutAllOf = { ...schema, allOf: undefined }
252
+ const schemaWithoutAllOf = { ...schema, allOf: undefined } as OasTypes.SchemaObject
257
253
 
258
254
  const and = factory.createIntersectionDeclaration({
259
255
  withParentheses: true,
260
256
  nodes: schema.allOf
261
- .map((item: OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject) => {
262
- return this.#getBaseTypeFromSchema(item)
257
+ .map((item) => {
258
+ return item && this.getTypeFromSchema(item as OasTypes.SchemaObject)
263
259
  })
264
260
  .filter((item) => {
265
261
  return item && item !== factory.keywordTypeNodes.any
@@ -268,7 +264,7 @@ export class TypeGenerator extends SchemaGenerator<Options, OpenAPIV3.SchemaObje
268
264
 
269
265
  if (schemaWithoutAllOf.properties) {
270
266
  return factory.createIntersectionDeclaration({
271
- nodes: [this.#getBaseTypeFromSchema(schemaWithoutAllOf, baseName), and].filter(Boolean),
267
+ nodes: [this.getTypeFromSchema(schemaWithoutAllOf, baseName), and].filter(Boolean),
272
268
  })
273
269
  }
274
270
 
@@ -285,24 +281,24 @@ export class TypeGenerator extends SchemaGenerator<Options, OpenAPIV3.SchemaObje
285
281
 
286
282
  if ('x-enumNames' in schema) {
287
283
  enums = [...new Set(schema['x-enumNames'] as string[])].map((key: string, index) => {
288
- return [key, schema.enum?.[index]]
284
+ return [key, schema.enum?.[index] as string]
289
285
  })
290
286
  }
291
287
 
292
288
  this.extraNodes.push(
293
289
  ...factory.createEnumDeclaration({
294
- name: camelCase(enumName, this.#caseOptions),
295
- typeName: this.options.resolveName({ name: enumName }),
290
+ name: transformers.camelCase(enumName),
291
+ typeName: this.context.pluginManager.resolveName({ name: enumName, pluginKey, type: 'type' }),
296
292
  enums,
297
293
  type: this.options.enumType,
298
294
  }),
299
295
  )
300
- return factory.createTypeReferenceNode(this.options.resolveName({ name: enumName }), undefined)
296
+ return factory.createTypeReferenceNode(this.context.pluginManager.resolveName({ name: enumName, pluginKey, type: 'type' }), undefined)
301
297
  }
302
298
 
303
299
  if (schema.enum) {
304
300
  return factory.createUnionDeclaration({
305
- nodes: schema.enum.map((name: string) => {
301
+ nodes: schema.enum.map((name) => {
306
302
  return factory.createLiteralTypeNode(typeof name === 'number' ? factory.createNumericLiteral(name) : factory.createStringLiteral(`${name}`))
307
303
  }) as unknown as Array<ts.TypeNode>,
308
304
  })
@@ -310,22 +306,24 @@ export class TypeGenerator extends SchemaGenerator<Options, OpenAPIV3.SchemaObje
310
306
 
311
307
  if ('items' in schema) {
312
308
  // items -> array
313
- const node = this.#getTypeFromSchema(schema.items as OpenAPIV3.SchemaObject, baseName)
309
+ const node = this.getTypeFromSchema(schema.items as OasTypes.SchemaObject, baseName)
314
310
  if (node) {
315
311
  return factory.createArrayTypeNode(node)
316
312
  }
317
313
  }
314
+
318
315
  /**
319
316
  * OpenAPI 3.1
320
317
  * @link https://json-schema.org/understanding-json-schema/reference/array.html#tuple-validation
321
318
  */
319
+
322
320
  if ('prefixItems' in schema) {
323
- const prefixItems = schema.prefixItems as OpenAPIV3.SchemaObject[]
321
+ const prefixItems = schema.prefixItems as OasTypes.SchemaObject[]
324
322
 
325
323
  return factory.createTupleDeclaration({
326
324
  nodes: prefixItems.map((item) => {
327
325
  // no baseType so we can fall back on an union when using enum
328
- return this.#getBaseTypeFromSchema(item, undefined)
326
+ return this.getTypeFromSchema(item, undefined)
329
327
  }) as Array<ts.TypeNode>,
330
328
  })
331
329
  }
@@ -335,14 +333,38 @@ export class TypeGenerator extends SchemaGenerator<Options, OpenAPIV3.SchemaObje
335
333
  return this.#getTypeFromProperties(schema, baseName)
336
334
  }
337
335
 
336
+ /**
337
+ * validate "const" property as defined in JSON-Schema-Validation
338
+ *
339
+ * https://json-schema.org/draft/2020-12/json-schema-validation#name-const
340
+ *
341
+ * > 6.1.3. const
342
+ * > The value of this keyword MAY be of any type, including null.
343
+ * > Use of this keyword is functionally equivalent to an "enum" (Section 6.1.2) with a single value.
344
+ * > An instance validates successfully against this keyword if its value is equal to the value of the keyword.
345
+ */
346
+ if (version === '3.1' && 'const' in schema) {
347
+ // const keyword takes precendence over the actual type.
348
+ if (schema['const']) {
349
+ if (typeof schema['const'] === 'string') {
350
+ return factory.createLiteralTypeNode(factory.createStringLiteral(schema['const']))
351
+ } else if (typeof schema['const'] === 'number') {
352
+ return factory.createLiteralTypeNode(factory.createNumericLiteral(schema['const']))
353
+ }
354
+ } else {
355
+ return keywordTypeNodes.null
356
+ }
357
+ }
358
+
338
359
  if (schema.type) {
339
360
  if (Array.isArray(schema.type)) {
361
+ // TODO remove hardcoded first type, second nullable
340
362
  // 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<OpenAPIV3.NonArraySchemaObjectType>
363
+ const [type, nullable] = schema.type as Array<OpenAPIV3_1.NonArraySchemaObjectType>
342
364
 
343
365
  return factory.createUnionDeclaration({
344
366
  nodes: [
345
- this.#getBaseTypeFromSchema(
367
+ this.getTypeFromSchema(
346
368
  {
347
369
  ...schema,
348
370
  type,
@@ -368,11 +390,6 @@ export class TypeGenerator extends SchemaGenerator<Options, OpenAPIV3.SchemaObje
368
390
  return factory.createTypeReferenceNode('Blob', [])
369
391
  }
370
392
 
371
- // detect assertion "const" and define the type property as a Literal
372
- if ('const' in schema && schema['const'] !== undefined && typeof schema['const'] === 'string') {
373
- return factory.createLiteralTypeNode(factory.createStringLiteral(schema['const']))
374
- }
375
-
376
393
  return factory.keywordTypeNodes.any
377
394
  }
378
395
  }
@@ -0,0 +1,137 @@
1
+ import transformers from '@kubb/core/transformers'
2
+ import { print } from '@kubb/parser'
3
+ import * as factory from '@kubb/parser/factory'
4
+ import { File, usePlugin, usePluginManager } from '@kubb/react'
5
+ import { useOas, useOperation, useOperationFile, useOperationName, useSchemas } from '@kubb/swagger/hooks'
6
+
7
+ import { TypeBuilder } from '../TypeBuilder.ts'
8
+
9
+ import type { KubbFile } from '@kubb/core'
10
+ import type { ts } from '@kubb/parser'
11
+ import type { Operation, OperationSchemas } from '@kubb/swagger'
12
+ import type { ReactNode } from 'react'
13
+ import type { FileMeta, PluginOptions } from '../types.ts'
14
+
15
+ type Props = {
16
+ builder: TypeBuilder
17
+ }
18
+
19
+ function printCombinedSchema(name: string, operation: Operation, schemas: OperationSchemas): string {
20
+ const properties: Record<string, ts.TypeNode> = {
21
+ 'response': factory.createTypeReferenceNode(
22
+ factory.createIdentifier(schemas.response.name),
23
+ undefined,
24
+ ),
25
+ }
26
+
27
+ if (schemas.request) {
28
+ properties['request'] = factory.createTypeReferenceNode(
29
+ factory.createIdentifier(schemas.request.name),
30
+ undefined,
31
+ )
32
+ }
33
+
34
+ if (schemas.pathParams) {
35
+ properties['pathParams'] = factory.createTypeReferenceNode(
36
+ factory.createIdentifier(schemas.pathParams.name),
37
+ undefined,
38
+ )
39
+ }
40
+
41
+ if (schemas.queryParams) {
42
+ properties['queryParams'] = factory.createTypeReferenceNode(
43
+ factory.createIdentifier(schemas.queryParams.name),
44
+ undefined,
45
+ )
46
+ }
47
+
48
+ if (schemas.headerParams) {
49
+ properties['headerParams'] = factory.createTypeReferenceNode(
50
+ factory.createIdentifier(schemas.headerParams.name),
51
+ undefined,
52
+ )
53
+ }
54
+
55
+ if (schemas.errors) {
56
+ properties['errors'] = factory.createUnionDeclaration({
57
+ nodes: schemas.errors.map(error => {
58
+ return factory.createTypeReferenceNode(
59
+ factory.createIdentifier(error.name),
60
+ undefined,
61
+ )
62
+ }),
63
+ })!
64
+ }
65
+
66
+ const namespaceNode = factory.createNamespaceDeclaration({
67
+ name: operation.method === 'get' ? `${name}Query` : `${name}Mutation`,
68
+ statements: Object.keys(properties).map(key => {
69
+ const type = properties[key]
70
+ if (!type) {
71
+ return undefined
72
+ }
73
+ return factory.createTypeAliasDeclaration({
74
+ modifiers: [factory.modifiers.export],
75
+ name: transformers.pascalCase(key),
76
+ type,
77
+ })
78
+ }).filter(Boolean),
79
+ })
80
+
81
+ return print(namespaceNode)
82
+ }
83
+
84
+ export function Mutation({
85
+ builder,
86
+ }: Props): ReactNode {
87
+ const { source } = builder.build()
88
+
89
+ return (
90
+ <>
91
+ {source}
92
+ </>
93
+ )
94
+ }
95
+
96
+ type FileProps = {
97
+ mode: KubbFile.Mode
98
+ }
99
+
100
+ Mutation.File = function({ mode }: FileProps): ReactNode {
101
+ const { options } = usePlugin<PluginOptions>()
102
+
103
+ const schemas = useSchemas()
104
+ const pluginManager = usePluginManager()
105
+ const oas = useOas()
106
+ const file = useOperationFile()
107
+ const factoryName = useOperationName({ type: 'type' })
108
+ const operation = useOperation()
109
+
110
+ const builder = new TypeBuilder(options, { oas, pluginManager })
111
+ .add(schemas.pathParams)
112
+ .add(schemas.queryParams)
113
+ .add(schemas.headerParams)
114
+ .add(schemas.response)
115
+ .add(schemas.request)
116
+ .add(schemas.errors)
117
+
118
+ const { source, imports } = builder.build()
119
+
120
+ return (
121
+ <>
122
+ <File<FileMeta>
123
+ baseName={file.baseName}
124
+ path={file.path}
125
+ meta={file.meta}
126
+ >
127
+ {mode === 'directory' && imports.map((item, index) => {
128
+ return <File.Import key={index} root={file.path} {...item} />
129
+ })}
130
+ <File.Source>
131
+ {source}
132
+ {printCombinedSchema(factoryName, operation, schemas)}
133
+ </File.Source>
134
+ </File>
135
+ </>
136
+ )
137
+ }
@@ -0,0 +1,84 @@
1
+ import { File, Type, usePlugin } from '@kubb/react'
2
+ import { useFile } from '@kubb/react'
3
+ import { useOas } from '@kubb/swagger/hooks'
4
+
5
+ import type { OasTypes } from '@kubb/swagger'
6
+ import type { ReactNode } from 'react'
7
+ import type { FileMeta, PluginOptions } from '../types.ts'
8
+
9
+ type TemplateProps = {
10
+ /**
11
+ * Name of the function
12
+ */
13
+ name: string
14
+ typeName: string
15
+ api: OasTypes.OASDocument
16
+ }
17
+
18
+ function Template({
19
+ name,
20
+ typeName,
21
+ api,
22
+ }: TemplateProps): ReactNode {
23
+ return (
24
+ <>
25
+ {`export const ${name} = ${JSON.stringify(api, undefined, 2)} as const`}
26
+ <br />
27
+ <Type name={typeName} export>
28
+ {`Infer<typeof ${name}>`}
29
+ </Type>
30
+ </>
31
+ )
32
+ }
33
+
34
+ const defaultTemplates = { default: Template } as const
35
+
36
+ type Props = {
37
+ name: string
38
+ typeName: string
39
+ /**
40
+ * This will make it possible to override the default behaviour.
41
+ */
42
+ Template?: React.ComponentType<React.ComponentProps<typeof Template>>
43
+ }
44
+
45
+ export function Oas({
46
+ name,
47
+ typeName,
48
+ Template = defaultTemplates.default,
49
+ }: Props): ReactNode {
50
+ const oas = useOas()
51
+
52
+ return <Template name={name} typeName={typeName} api={oas.api} />
53
+ }
54
+
55
+ type FileProps = {
56
+ name: string
57
+ typeName: string
58
+ /**
59
+ * This will make it possible to override the default behaviour.
60
+ */
61
+ templates?: typeof defaultTemplates
62
+ }
63
+
64
+ Oas.File = function({ name, typeName, templates = defaultTemplates }: FileProps): ReactNode {
65
+ const { key: pluginKey } = usePlugin<PluginOptions>()
66
+ const file = useFile({ name, pluginKey })
67
+
68
+ const Template = templates.default
69
+
70
+ return (
71
+ <File<FileMeta>
72
+ baseName={file.baseName}
73
+ path={file.path}
74
+ meta={file.meta}
75
+ >
76
+ <File.Import name={['Infer']} path="@kubb/swagger-ts/oas" isTypeOnly />
77
+ <File.Source>
78
+ <Oas Template={Template} name={name} typeName={typeName} />
79
+ </File.Source>
80
+ </File>
81
+ )
82
+ }
83
+
84
+ Oas.templates = defaultTemplates