@kubb/swagger-ts 2.0.0-beta.1 → 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,30 +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'
5
+ import { keywordTypeNodes } from '@kubb/parser/factory'
4
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 { Oas, OasTypes, OpenAPIV3, OpenAPIV3_1, 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 = {
17
+ type Context = {
16
18
  oas: Oas
17
- usedEnumNames: Record<string, number>
18
-
19
- withJSDocs?: boolean
20
- resolveName: PluginContext['resolveName']
21
- enumType: 'enum' | 'asConst' | 'asPascalConst'
22
- dateType: 'string' | 'date'
23
- optionalType: 'questionToken' | 'undefined' | 'questionTokenAndUndefined'
19
+ pluginManager: PluginManager
24
20
  }
25
21
 
26
- export class TypeGenerator extends SchemaGenerator<Options, OasTypes.SchemaObject, ts.Node[]> {
22
+ export class TypeGenerator extends Generator<PluginOptions['resolvedOptions'], Context> {
27
23
  refs: Refs = {}
24
+ imports: ImportMeta[] = []
28
25
 
29
26
  extraNodes: ts.Node[] = []
30
27
 
@@ -33,19 +30,6 @@ export class TypeGenerator extends SchemaGenerator<Options, OasTypes.SchemaObjec
33
30
  // Keep track of already used type aliases
34
31
  #usedAliasNames: Record<string, number> = {}
35
32
 
36
- #caseOptions: CaseOptions = {
37
- delimiter: '',
38
- stripRegexp: /[^A-Z0-9$]/gi,
39
- }
40
-
41
- constructor(
42
- options: Options,
43
- ) {
44
- super(options)
45
-
46
- return this
47
- }
48
-
49
33
  build({
50
34
  schema,
51
35
  baseName,
@@ -58,7 +42,7 @@ export class TypeGenerator extends SchemaGenerator<Options, OasTypes.SchemaObjec
58
42
  keysToOmit?: string[]
59
43
  }): ts.Node[] {
60
44
  const nodes: ts.Node[] = []
61
- const type = this.#getTypeFromSchema(schema, baseName)
45
+ const type = this.getTypeFromSchema(schema, baseName)
62
46
 
63
47
  if (!type) {
64
48
  return this.extraNodes
@@ -66,7 +50,7 @@ export class TypeGenerator extends SchemaGenerator<Options, OasTypes.SchemaObjec
66
50
 
67
51
  const node = factory.createTypeAliasDeclaration({
68
52
  modifiers: [factory.modifiers.export],
69
- name: this.options.resolveName({ name: baseName }) || baseName,
53
+ name: this.context.pluginManager.resolveName({ name: baseName, pluginKey, type: 'type' }),
70
54
  type: keysToOmit?.length ? factory.createOmitDeclaration({ keys: keysToOmit, type, nonNullable: true }) : type,
71
55
  })
72
56
 
@@ -74,7 +58,7 @@ export class TypeGenerator extends SchemaGenerator<Options, OasTypes.SchemaObjec
74
58
  nodes.push(
75
59
  factory.appendJSDocToNode({
76
60
  node,
77
- comments: [`@description ${description}`],
61
+ comments: [`@description ${transformers.trim(description)}`],
78
62
  }),
79
63
  )
80
64
  } else {
@@ -97,7 +81,7 @@ export class TypeGenerator extends SchemaGenerator<Options, OasTypes.SchemaObjec
97
81
  * Delegates to getBaseTypeFromSchema internally and
98
82
  * optionally adds a union with null.
99
83
  */
100
- #getTypeFromSchema(schema?: OasTypes.SchemaObject, name?: string): ts.TypeNode | null {
84
+ getTypeFromSchema(schema?: OasTypes.SchemaObject, name?: string): ts.TypeNode | null {
101
85
  const type = this.#getBaseTypeFromSchema(schema, name)
102
86
 
103
87
  if (!type) {
@@ -114,7 +98,7 @@ export class TypeGenerator extends SchemaGenerator<Options, OasTypes.SchemaObjec
114
98
  /**
115
99
  * Recursively creates a type literal with the given props.
116
100
  */
117
- #getTypeFromProperties(baseSchema?: OasTypes.SchemaObject, baseName?: string) {
101
+ #getTypeFromProperties(baseSchema?: OasTypes.SchemaObject, baseName?: string): ts.TypeNode | null {
118
102
  const { optionalType } = this.options
119
103
  const properties = baseSchema?.properties || {}
120
104
  const required = baseSchema?.required
@@ -124,39 +108,38 @@ export class TypeGenerator extends SchemaGenerator<Options, OasTypes.SchemaObjec
124
108
  const schema = properties[name] as OasTypes.SchemaObject
125
109
 
126
110
  const isRequired = Array.isArray(required) ? required.includes(name) : !!required
127
- let type = this.#getTypeFromSchema(schema, this.options.resolveName({ name: `${baseName || ''} ${name}` }))
111
+ let type = this.getTypeFromSchema(schema, this.context.pluginManager.resolveName({ name: `${baseName || ''} ${name}`, pluginKey, type: 'type' }))
128
112
 
129
113
  if (!type) {
130
114
  return null
131
115
  }
132
116
 
133
- if (!isRequired && ['undefined', 'questionTokenAndUndefined'].includes(optionalType)) {
117
+ if (!isRequired && ['undefined', 'questionTokenAndUndefined'].includes(optionalType as string)) {
134
118
  type = factory.createUnionDeclaration({ nodes: [type, factory.keywordTypeNodes.undefined] })
135
119
  }
136
120
  const propertySignature = factory.createPropertySignature({
137
- questionToken: ['questionToken', 'questionTokenAndUndefined'].includes(optionalType) && !isRequired,
121
+ questionToken: ['questionToken', 'questionTokenAndUndefined'].includes(optionalType as string) && !isRequired,
138
122
  name,
139
123
  type: type as ts.TypeNode,
140
124
  readOnly: schema.readOnly,
141
125
  })
142
- if (this.options.withJSDocs) {
143
- return factory.appendJSDocToNode({
144
- node: propertySignature,
145
- comments: [
146
- schema.description ? `@description ${schema.description}` : undefined,
147
- schema.type ? `@type ${schema.type?.toString()}${isRequired ? '' : ' | undefined'} ${schema.format || ''}` : undefined,
148
- schema.example ? `@example ${schema.example as string}` : undefined,
149
- schema.deprecated ? `@deprecated` : undefined,
150
- schema.default !== undefined && typeof schema.default === 'string' ? `@default '${schema.default}'` : undefined,
151
- schema.default !== undefined && typeof schema.default !== 'string' ? `@default ${schema.default as string}` : undefined,
152
- ].filter(Boolean),
153
- })
154
- }
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
+ })
155
138
 
156
139
  return propertySignature
157
140
  })
158
141
  if (additionalProperties) {
159
- const type = additionalProperties === true ? factory.keywordTypeNodes.any : this.#getTypeFromSchema(additionalProperties as OasTypes.SchemaObject)
142
+ const type = additionalProperties === true ? factory.keywordTypeNodes.any : this.getTypeFromSchema(additionalProperties as OasTypes.SchemaObject)
160
143
 
161
144
  if (type) {
162
145
  members.push(factory.createIndexSignature(type))
@@ -177,18 +160,26 @@ export class TypeGenerator extends SchemaGenerator<Options, OasTypes.SchemaObjec
177
160
  }
178
161
 
179
162
  const originalName = getUniqueName($ref.replace(/.+\//, ''), this.#usedAliasNames)
180
- const propertyName = this.options.resolveName({ name: originalName }) || originalName
163
+ const propertyName = this.context.pluginManager.resolveName({ name: originalName, pluginKey, type: 'type' })
181
164
 
182
165
  ref = this.refs[$ref] = {
183
166
  propertyName,
184
167
  originalName,
185
168
  }
186
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
+
187
178
  return factory.createTypeReferenceNode(ref.propertyName, undefined)
188
179
  }
189
180
 
190
181
  #getParsedSchema(schema?: OasTypes.SchemaObject) {
191
- const parsedSchema = getSchemaFactory(this.options.oas)(schema)
182
+ const parsedSchema = getSchemaFactory(this.context.oas)(schema)
192
183
  return parsedSchema
193
184
  }
194
185
 
@@ -218,7 +209,7 @@ export class TypeGenerator extends SchemaGenerator<Options, OasTypes.SchemaObjec
218
209
  withParentheses: true,
219
210
  nodes: schema.oneOf
220
211
  .map((item) => {
221
- return item && this.#getBaseTypeFromSchema(item as OasTypes.SchemaObject)
212
+ return item && this.getTypeFromSchema(item as OasTypes.SchemaObject)
222
213
  })
223
214
  .filter((item) => {
224
215
  return item && item !== factory.keywordTypeNodes.any
@@ -227,7 +218,7 @@ export class TypeGenerator extends SchemaGenerator<Options, OasTypes.SchemaObjec
227
218
 
228
219
  if (schemaWithoutOneOf.properties) {
229
220
  return factory.createIntersectionDeclaration({
230
- nodes: [this.#getBaseTypeFromSchema(schemaWithoutOneOf, baseName), union].filter(Boolean),
221
+ nodes: [this.getTypeFromSchema(schemaWithoutOneOf, baseName), union].filter(Boolean),
231
222
  })
232
223
  }
233
224
 
@@ -241,7 +232,7 @@ export class TypeGenerator extends SchemaGenerator<Options, OasTypes.SchemaObjec
241
232
  withParentheses: true,
242
233
  nodes: schema.anyOf
243
234
  .map((item) => {
244
- return item && this.#getBaseTypeFromSchema(item as OasTypes.SchemaObject)
235
+ return item && this.getTypeFromSchema(item as OasTypes.SchemaObject)
245
236
  })
246
237
  .filter((item) => {
247
238
  return item && item !== factory.keywordTypeNodes.any
@@ -250,7 +241,7 @@ export class TypeGenerator extends SchemaGenerator<Options, OasTypes.SchemaObjec
250
241
 
251
242
  if (schemaWithoutAnyOf.properties) {
252
243
  return factory.createIntersectionDeclaration({
253
- nodes: [this.#getBaseTypeFromSchema(schemaWithoutAnyOf, baseName), union].filter(Boolean),
244
+ nodes: [this.getTypeFromSchema(schemaWithoutAnyOf, baseName), union].filter(Boolean),
254
245
  })
255
246
  }
256
247
 
@@ -264,7 +255,7 @@ export class TypeGenerator extends SchemaGenerator<Options, OasTypes.SchemaObjec
264
255
  withParentheses: true,
265
256
  nodes: schema.allOf
266
257
  .map((item) => {
267
- return item && this.#getBaseTypeFromSchema(item as OasTypes.SchemaObject)
258
+ return item && this.getTypeFromSchema(item as OasTypes.SchemaObject)
268
259
  })
269
260
  .filter((item) => {
270
261
  return item && item !== factory.keywordTypeNodes.any
@@ -273,7 +264,7 @@ export class TypeGenerator extends SchemaGenerator<Options, OasTypes.SchemaObjec
273
264
 
274
265
  if (schemaWithoutAllOf.properties) {
275
266
  return factory.createIntersectionDeclaration({
276
- nodes: [this.#getBaseTypeFromSchema(schemaWithoutAllOf, baseName), and].filter(Boolean),
267
+ nodes: [this.getTypeFromSchema(schemaWithoutAllOf, baseName), and].filter(Boolean),
277
268
  })
278
269
  }
279
270
 
@@ -296,13 +287,13 @@ export class TypeGenerator extends SchemaGenerator<Options, OasTypes.SchemaObjec
296
287
 
297
288
  this.extraNodes.push(
298
289
  ...factory.createEnumDeclaration({
299
- name: camelCase(enumName, this.#caseOptions),
300
- typeName: this.options.resolveName({ name: enumName }),
290
+ name: transformers.camelCase(enumName),
291
+ typeName: this.context.pluginManager.resolveName({ name: enumName, pluginKey, type: 'type' }),
301
292
  enums,
302
293
  type: this.options.enumType,
303
294
  }),
304
295
  )
305
- return factory.createTypeReferenceNode(this.options.resolveName({ name: enumName }), undefined)
296
+ return factory.createTypeReferenceNode(this.context.pluginManager.resolveName({ name: enumName, pluginKey, type: 'type' }), undefined)
306
297
  }
307
298
 
308
299
  if (schema.enum) {
@@ -315,7 +306,7 @@ export class TypeGenerator extends SchemaGenerator<Options, OasTypes.SchemaObjec
315
306
 
316
307
  if ('items' in schema) {
317
308
  // items -> array
318
- const node = this.#getTypeFromSchema(schema.items as OasTypes.SchemaObject, baseName)
309
+ const node = this.getTypeFromSchema(schema.items as OasTypes.SchemaObject, baseName)
319
310
  if (node) {
320
311
  return factory.createArrayTypeNode(node)
321
312
  }
@@ -332,7 +323,7 @@ export class TypeGenerator extends SchemaGenerator<Options, OasTypes.SchemaObjec
332
323
  return factory.createTupleDeclaration({
333
324
  nodes: prefixItems.map((item) => {
334
325
  // no baseType so we can fall back on an union when using enum
335
- return this.#getBaseTypeFromSchema(item, undefined)
326
+ return this.getTypeFromSchema(item, undefined)
336
327
  }) as Array<ts.TypeNode>,
337
328
  })
338
329
  }
@@ -342,14 +333,38 @@ export class TypeGenerator extends SchemaGenerator<Options, OasTypes.SchemaObjec
342
333
  return this.#getTypeFromProperties(schema, baseName)
343
334
  }
344
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
+
345
359
  if (schema.type) {
346
360
  if (Array.isArray(schema.type)) {
361
+ // TODO remove hardcoded first type, second nullable
347
362
  // OPENAPI v3.1.0: https://www.openapis.org/blog/2021/02/16/migrating-from-openapi-3-0-to-3-1-0
348
363
  const [type, nullable] = schema.type as Array<OpenAPIV3_1.NonArraySchemaObjectType>
349
364
 
350
365
  return factory.createUnionDeclaration({
351
366
  nodes: [
352
- this.#getBaseTypeFromSchema(
367
+ this.getTypeFromSchema(
353
368
  {
354
369
  ...schema,
355
370
  type,
@@ -375,11 +390,6 @@ export class TypeGenerator extends SchemaGenerator<Options, OasTypes.SchemaObjec
375
390
  return factory.createTypeReferenceNode('Blob', [])
376
391
  }
377
392
 
378
- // detect assertion "const" and define the type property as a Literal
379
- if (version === '3.1' && typeof schema['const'] === 'string') {
380
- return factory.createLiteralTypeNode(factory.createStringLiteral(schema['const']))
381
- }
382
-
383
393
  return factory.keywordTypeNodes.any
384
394
  }
385
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
@@ -0,0 +1,136 @@
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 Query({
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
+ Query.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.errors)
116
+
117
+ const { source, imports } = builder.build()
118
+
119
+ return (
120
+ <>
121
+ <File<FileMeta>
122
+ baseName={file.baseName}
123
+ path={file.path}
124
+ meta={file.meta}
125
+ >
126
+ {mode === 'directory' && imports.map((item, index) => {
127
+ return <File.Import key={index} root={file.path} {...item} />
128
+ })}
129
+ <File.Source>
130
+ {source}
131
+ {printCombinedSchema(factoryName, operation, schemas)}
132
+ </File.Source>
133
+ </File>
134
+ </>
135
+ )
136
+ }
@@ -0,0 +1,3 @@
1
+ export { Mutation } from './Mutation.tsx'
2
+ export { Oas } from './Oas.tsx'
3
+ export { Query } from './Query.tsx'