@kubb/plugin-client 5.0.0-alpha.9 → 5.0.0-beta.15

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.
Files changed (62) hide show
  1. package/LICENSE +17 -10
  2. package/README.md +27 -7
  3. package/dist/clients/axios.cjs +10 -3
  4. package/dist/clients/axios.cjs.map +1 -1
  5. package/dist/clients/axios.d.ts +5 -4
  6. package/dist/clients/axios.js +9 -2
  7. package/dist/clients/axios.js.map +1 -1
  8. package/dist/clients/fetch.cjs +5 -2
  9. package/dist/clients/fetch.cjs.map +1 -1
  10. package/dist/clients/fetch.d.ts +3 -2
  11. package/dist/clients/fetch.js +5 -2
  12. package/dist/clients/fetch.js.map +1 -1
  13. package/dist/index.cjs +1830 -97
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.ts +330 -4
  16. package/dist/index.js +1816 -95
  17. package/dist/index.js.map +1 -1
  18. package/dist/templates/clients/axios.source.cjs +1 -1
  19. package/dist/templates/clients/axios.source.js +1 -1
  20. package/dist/templates/clients/fetch.source.cjs +1 -1
  21. package/dist/templates/clients/fetch.source.js +1 -1
  22. package/extension.yaml +779 -0
  23. package/package.json +62 -85
  24. package/src/clients/axios.ts +19 -4
  25. package/src/clients/fetch.ts +10 -2
  26. package/src/components/ClassClient.tsx +46 -145
  27. package/src/components/Client.tsx +109 -139
  28. package/src/components/Operations.tsx +10 -10
  29. package/src/components/StaticClassClient.tsx +46 -142
  30. package/src/components/Url.tsx +38 -50
  31. package/src/components/WrapperClient.tsx +11 -7
  32. package/src/functionParams.ts +118 -0
  33. package/src/generators/classClientGenerator.tsx +143 -172
  34. package/src/generators/clientGenerator.tsx +83 -81
  35. package/src/generators/groupedClientGenerator.tsx +50 -52
  36. package/src/generators/operationsGenerator.tsx +11 -18
  37. package/src/generators/staticClassClientGenerator.tsx +172 -184
  38. package/src/index.ts +9 -2
  39. package/src/plugin.ts +115 -145
  40. package/src/resolvers/resolverClient.ts +38 -0
  41. package/src/types.ts +128 -44
  42. package/src/utils.ts +156 -0
  43. package/dist/StaticClassClient-By-aMAe4.cjs +0 -677
  44. package/dist/StaticClassClient-By-aMAe4.cjs.map +0 -1
  45. package/dist/StaticClassClient-CCn9g9eF.js +0 -636
  46. package/dist/StaticClassClient-CCn9g9eF.js.map +0 -1
  47. package/dist/components.cjs +0 -7
  48. package/dist/components.d.ts +0 -216
  49. package/dist/components.js +0 -2
  50. package/dist/generators-BYUJaeZP.js +0 -723
  51. package/dist/generators-BYUJaeZP.js.map +0 -1
  52. package/dist/generators-DTxD9FDY.cjs +0 -753
  53. package/dist/generators-DTxD9FDY.cjs.map +0 -1
  54. package/dist/generators.cjs +0 -7
  55. package/dist/generators.d.ts +0 -517
  56. package/dist/generators.js +0 -2
  57. package/dist/types-DBQdg-BV.d.ts +0 -169
  58. package/src/components/index.ts +0 -5
  59. package/src/generators/index.ts +0 -5
  60. package/templates/clients/axios.ts +0 -70
  61. package/templates/clients/fetch.ts +0 -93
  62. package/templates/config.ts +0 -43
@@ -1,133 +1,129 @@
1
1
  import path from 'node:path'
2
- import { camelCase, pascalCase } from '@internals/utils'
3
- import { usePluginDriver } from '@kubb/core/hooks'
4
- import type { KubbFile } from '@kubb/fabric-core/types'
5
- import type { Operation } from '@kubb/oas'
6
- import type { OperationSchemas } from '@kubb/plugin-oas'
7
- import { createReactGenerator } from '@kubb/plugin-oas/generators'
8
- import { useOas, useOperationManager } from '@kubb/plugin-oas/hooks'
9
- import { getBanner, getFooter } from '@kubb/plugin-oas/utils'
2
+ import { resolveOperationTypeNames } from '@internals/shared'
3
+ import { camelCase } from '@internals/utils'
4
+ import type { ast } from '@kubb/core'
5
+ import { defineGenerator } from '@kubb/core'
6
+ import type { ResolverTs } from '@kubb/plugin-ts'
10
7
  import { pluginTsName } from '@kubb/plugin-ts'
8
+ import type { ResolverZod } from '@kubb/plugin-zod'
11
9
  import { pluginZodName } from '@kubb/plugin-zod'
12
- import { File } from '@kubb/react-fabric'
10
+ import { File, jsxRendererSync } from '@kubb/renderer-jsx'
13
11
  import { ClassClient } from '../components/ClassClient'
14
12
  import { WrapperClient } from '../components/WrapperClient'
15
13
  import type { PluginClient } from '../types'
16
14
 
17
15
  type OperationData = {
18
- operation: Operation
16
+ node: ast.OperationNode
19
17
  name: string
20
- typeSchemas: OperationSchemas
21
- zodSchemas: OperationSchemas | undefined
22
- typeFile: KubbFile.File
23
- zodFile: KubbFile.File
18
+ tsResolver: ResolverTs
19
+ zodResolver: ResolverZod | undefined
20
+ typeFile: ast.FileNode
21
+ zodFile: ast.FileNode | undefined
24
22
  }
25
23
 
26
24
  type Controller = {
27
25
  name: string
28
- file: KubbFile.File
26
+ propertyName: string
27
+ file: ast.FileNode
29
28
  operations: Array<OperationData>
30
29
  }
31
30
 
32
- export const classClientGenerator = createReactGenerator<PluginClient>({
33
- name: 'classClient',
34
- Operations({ operations, generator, plugin, config }) {
35
- const { options, name: pluginName } = plugin
36
- const driver = usePluginDriver()
37
-
38
- const oas = useOas()
39
- const { getName, getFile, getGroup, getSchemas } = useOperationManager(generator)
31
+ function resolveTypeImportNames(node: ast.OperationNode, tsResolver: ResolverTs): Array<string> {
32
+ return resolveOperationTypeNames(node, tsResolver, { order: 'body-response-first' })
33
+ }
40
34
 
41
- function buildOperationData(operation: Operation): OperationData {
42
- const type = {
43
- file: getFile(operation, { pluginName: pluginTsName }),
44
- schemas: getSchemas(operation, { pluginName: pluginTsName, type: 'type' }),
45
- }
35
+ function resolveZodImportNames(node: ast.OperationNode, zodResolver: ResolverZod): Array<string> {
36
+ const names: Array<string | undefined> = [
37
+ zodResolver.resolveResponseName?.(node),
38
+ node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : undefined,
39
+ ]
40
+ return names.filter((n): n is string => Boolean(n))
41
+ }
46
42
 
47
- const zod = {
48
- file: getFile(operation, { pluginName: pluginZodName }),
49
- schemas: getSchemas(operation, { pluginName: pluginZodName, type: 'function' }),
50
- }
43
+ export const classClientGenerator = defineGenerator<PluginClient>({
44
+ name: 'classClient',
45
+ renderer: jsxRendererSync,
46
+ operations(nodes, ctx) {
47
+ const { config, driver, resolver, root, inputNode } = ctx
48
+ const { output, group, dataReturnType, paramsCasing, paramsType, pathParamsType, parser, importPath, sdk } = ctx.options
49
+ const baseURL = ctx.options.baseURL ?? inputNode.meta?.baseURL
50
+
51
+ const pluginTs = driver.getPlugin(pluginTsName)
52
+ if (!pluginTs) return null
53
+
54
+ const tsResolver = driver.getResolver(pluginTsName)
55
+ const tsPluginOptions = pluginTs.options
56
+ const pluginZod = parser === 'zod' ? driver.getPlugin(pluginZodName) : undefined
57
+ const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : undefined
58
+
59
+ function buildOperationData(node: ast.OperationNode): OperationData {
60
+ const typeFile = tsResolver.resolveFile(
61
+ { name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
62
+ { root, output: tsPluginOptions?.output ?? output, group: tsPluginOptions?.group },
63
+ )
64
+ const zodFile =
65
+ zodResolver && pluginZod?.options
66
+ ? zodResolver.resolveFile(
67
+ { name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
68
+ { root, output: pluginZod.options?.output ?? output, group: pluginZod.options?.group },
69
+ )
70
+ : undefined
51
71
 
52
72
  return {
53
- operation,
54
- name: getName(operation, { type: 'function' }),
55
- typeSchemas: type.schemas,
56
- zodSchemas: zod.schemas,
57
- typeFile: type.file,
58
- zodFile: zod.file,
73
+ node: node,
74
+ name: resolver.resolveName(node.operationId),
75
+ tsResolver,
76
+ zodResolver,
77
+ typeFile,
78
+ zodFile,
59
79
  }
60
80
  }
61
81
 
62
- // Group operations by tag
63
- const controllers = operations.reduce(
64
- (acc, operation) => {
65
- const group = getGroup(operation)
66
- const groupName = group?.tag ? (options.group?.name?.({ group: camelCase(group.tag) }) ?? pascalCase(group.tag)) : 'Client'
67
-
68
- if (!group?.tag && !options.group) {
69
- // If no grouping, put all operations in a single class
70
- const name = 'ApiClient'
71
- const file = driver.getFile({
72
- name,
73
- extname: '.ts',
74
- pluginName,
75
- })
76
-
77
- const operationData = buildOperationData(operation)
78
- const previousFile = acc.find((item) => item.file.path === file.path)
82
+ const controllers = nodes.reduce((acc, operationNode) => {
83
+ const tag = operationNode.tags[0]
84
+ const groupName = tag ? (group?.name?.({ group: camelCase(tag) }) ?? resolver.resolveGroupName(tag)) : resolver.resolveGroupName('Client')
79
85
 
80
- if (previousFile) {
81
- previousFile.operations.push(operationData)
82
- } else {
83
- acc.push({ name, file, operations: [operationData] })
84
- }
85
- } else if (group?.tag) {
86
- // Group by tag
87
- const name = groupName
88
- const file = driver.getFile({
89
- name,
90
- extname: '.ts',
91
- pluginName,
92
- options: { group },
93
- })
86
+ if (!tag && !group) {
87
+ const name = resolver.resolveClassName('ApiClient')
88
+ const file = resolver.resolveFile({ name, extname: '.ts' }, { root, output, group })
89
+ const operationData = buildOperationData(operationNode)
90
+ const previous = acc.find((item) => item.file.path === file.path)
94
91
 
95
- const operationData = buildOperationData(operation)
96
- const previousFile = acc.find((item) => item.file.path === file.path)
97
-
98
- if (previousFile) {
99
- previousFile.operations.push(operationData)
100
- } else {
101
- acc.push({ name, file, operations: [operationData] })
102
- }
92
+ if (previous) {
93
+ previous.operations.push(operationData)
94
+ } else {
95
+ acc.push({ name, propertyName: resolver.resolveClientPropertyName(name), file, operations: [operationData] })
96
+ }
97
+ } else if (tag) {
98
+ const name = groupName
99
+ const file = resolver.resolveFile({ name, extname: '.ts', tag }, { root, output, group })
100
+ const operationData = buildOperationData(operationNode)
101
+ const previous = acc.find((item) => item.file.path === file.path)
102
+
103
+ if (previous) {
104
+ previous.operations.push(operationData)
105
+ } else {
106
+ acc.push({ name, propertyName: resolver.resolveClientPropertyName(name), file, operations: [operationData] })
103
107
  }
108
+ }
104
109
 
105
- return acc
106
- },
107
- [] as Array<Controller>,
108
- )
110
+ return acc
111
+ }, [] as Array<Controller>)
109
112
 
110
113
  function collectTypeImports(ops: Array<OperationData>) {
111
114
  const typeImportsByFile = new Map<string, Set<string>>()
112
- const typeFilesByPath = new Map<string, KubbFile.File>()
115
+ const typeFilesByPath = new Map<string, ast.FileNode>()
113
116
 
114
117
  ops.forEach((op) => {
115
- const { typeSchemas, typeFile } = op
116
-
117
- if (!typeImportsByFile.has(typeFile.path)) {
118
- typeImportsByFile.set(typeFile.path, new Set())
118
+ const names = resolveTypeImportNames(op.node, tsResolver)
119
+ if (!typeImportsByFile.has(op.typeFile.path)) {
120
+ typeImportsByFile.set(op.typeFile.path, new Set())
119
121
  }
120
- const typeImports = typeImportsByFile.get(typeFile.path)!
121
-
122
- if (typeSchemas.request?.name) typeImports.add(typeSchemas.request.name)
123
- if (typeSchemas.response?.name) typeImports.add(typeSchemas.response.name)
124
- if (typeSchemas.pathParams?.name) typeImports.add(typeSchemas.pathParams.name)
125
- if (typeSchemas.queryParams?.name) typeImports.add(typeSchemas.queryParams.name)
126
- if (typeSchemas.headerParams?.name) typeImports.add(typeSchemas.headerParams.name)
127
- typeSchemas.statusCodes?.forEach((item) => {
128
- if (item?.name) typeImports.add(item.name)
122
+ const imports = typeImportsByFile.get(op.typeFile.path)!
123
+ names.forEach((n) => {
124
+ imports.add(n)
129
125
  })
130
- typeFilesByPath.set(typeFile.path, typeFile)
126
+ typeFilesByPath.set(op.typeFile.path, op.typeFile)
131
127
  })
132
128
 
133
129
  return { typeImportsByFile, typeFilesByPath }
@@ -135,19 +131,19 @@ export const classClientGenerator = createReactGenerator<PluginClient>({
135
131
 
136
132
  function collectZodImports(ops: Array<OperationData>) {
137
133
  const zodImportsByFile = new Map<string, Set<string>>()
138
- const zodFilesByPath = new Map<string, KubbFile.File>()
134
+ const zodFilesByPath = new Map<string, ast.FileNode>()
139
135
 
140
136
  ops.forEach((op) => {
141
- const { zodSchemas, zodFile } = op
142
-
143
- if (!zodImportsByFile.has(zodFile.path)) {
144
- zodImportsByFile.set(zodFile.path, new Set())
137
+ if (!op.zodFile || !zodResolver) return
138
+ const names = resolveZodImportNames(op.node, zodResolver)
139
+ if (!zodImportsByFile.has(op.zodFile.path)) {
140
+ zodImportsByFile.set(op.zodFile.path, new Set())
145
141
  }
146
- const zodImports = zodImportsByFile.get(zodFile.path)!
147
-
148
- if (zodSchemas?.response?.name) zodImports.add(zodSchemas.response.name)
149
- if (zodSchemas?.request?.name) zodImports.add(zodSchemas.request.name)
150
- zodFilesByPath.set(zodFile.path, zodFile)
142
+ const imports = zodImportsByFile.get(op.zodFile.path)!
143
+ names.forEach((n) => {
144
+ imports.add(n)
145
+ })
146
+ zodFilesByPath.set(op.zodFile.path, op.zodFile)
151
147
  })
152
148
 
153
149
  return { zodImportsByFile, zodFilesByPath }
@@ -156,10 +152,8 @@ export const classClientGenerator = createReactGenerator<PluginClient>({
156
152
  const files = controllers.map(({ name, file, operations: ops }) => {
157
153
  const { typeImportsByFile, typeFilesByPath } = collectTypeImports(ops)
158
154
  const { zodImportsByFile, zodFilesByPath } =
159
- options.parser === 'zod'
160
- ? collectZodImports(ops)
161
- : { zodImportsByFile: new Map<string, Set<string>>(), zodFilesByPath: new Map<string, KubbFile.File>() }
162
- const hasFormData = ops.some((op) => op.operation.getContentType() === 'multipart/form-data')
155
+ parser === 'zod' ? collectZodImports(ops) : { zodImportsByFile: new Map<string, Set<string>>(), zodFilesByPath: new Map<string, ast.FileNode>() }
156
+ const hasFormData = ops.some((op) => op.node.requestBody?.content?.some((e) => e.contentType === 'multipart/form-data') ?? false)
163
157
 
164
158
  return (
165
159
  <File
@@ -167,106 +161,83 @@ export const classClientGenerator = createReactGenerator<PluginClient>({
167
161
  baseName={file.baseName}
168
162
  path={file.path}
169
163
  meta={file.meta}
170
- banner={getBanner({ oas, output: options.output, config: driver.config })}
171
- footer={getFooter({ oas, output: options.output })}
164
+ banner={resolver.resolveBanner(inputNode, { output, config })}
165
+ footer={resolver.resolveFooter(inputNode, { output, config })}
172
166
  >
173
- {options.importPath ? (
167
+ {importPath ? (
174
168
  <>
175
- <File.Import name={'fetch'} path={options.importPath} />
176
- <File.Import name={['mergeConfig']} path={options.importPath} />
177
- <File.Import name={['Client', 'RequestConfig', 'ResponseErrorConfig']} path={options.importPath} isTypeOnly />
169
+ <File.Import name={'fetch'} path={importPath} />
170
+ <File.Import name={['mergeConfig']} path={importPath} />
171
+ <File.Import name={['Client', 'RequestConfig', 'ResponseErrorConfig']} path={importPath} isTypeOnly />
178
172
  </>
179
173
  ) : (
180
174
  <>
181
- <File.Import name={['fetch']} root={file.path} path={path.resolve(config.root, config.output.path, '.kubb/fetch.ts')} />
182
- <File.Import name={['mergeConfig']} root={file.path} path={path.resolve(config.root, config.output.path, '.kubb/fetch.ts')} />
183
- <File.Import
184
- name={['Client', 'RequestConfig', 'ResponseErrorConfig']}
185
- root={file.path}
186
- path={path.resolve(config.root, config.output.path, '.kubb/fetch.ts')}
187
- isTypeOnly
188
- />
175
+ <File.Import name={['fetch']} root={file.path} path={path.resolve(root, '.kubb/client.ts')} />
176
+ <File.Import name={['mergeConfig']} root={file.path} path={path.resolve(root, '.kubb/client.ts')} />
177
+ <File.Import name={['Client', 'RequestConfig', 'ResponseErrorConfig']} root={file.path} path={path.resolve(root, '.kubb/client.ts')} isTypeOnly />
189
178
  </>
190
179
  )}
191
180
 
192
- {hasFormData && <File.Import name={['buildFormData']} root={file.path} path={path.resolve(config.root, config.output.path, '.kubb/config.ts')} />}
181
+ {hasFormData && <File.Import name={['buildFormData']} root={file.path} path={path.resolve(root, '.kubb/config.ts')} />}
193
182
 
194
- {Array.from(typeImportsByFile.entries()).map(([filePath, imports]) => {
183
+ {Array.from(typeImportsByFile.entries()).map(([filePath, importSet]) => {
195
184
  const typeFile = typeFilesByPath.get(filePath)
196
- if (!typeFile) {
197
- return null
198
- }
199
- const importNames = Array.from(imports).filter(Boolean)
200
- if (importNames.length === 0) {
201
- return null
202
- }
185
+ if (!typeFile) return null
186
+ const importNames = Array.from(importSet).filter(Boolean)
187
+ if (importNames.length === 0) return null
203
188
  return <File.Import key={filePath} name={importNames} root={file.path} path={typeFile.path} isTypeOnly />
204
189
  })}
205
190
 
206
- {options.parser === 'zod' &&
207
- Array.from(zodImportsByFile.entries()).map(([filePath, imports]) => {
191
+ {parser === 'zod' &&
192
+ Array.from(zodImportsByFile.entries()).map(([filePath, importSet]) => {
208
193
  const zodFile = zodFilesByPath.get(filePath)
209
- if (!zodFile) {
210
- return null
211
- }
212
- const importNames = Array.from(imports).filter(Boolean)
213
- if (importNames.length === 0) {
214
- return null
215
- }
216
-
194
+ if (!zodFile) return null
195
+ const importNames = Array.from(importSet).filter(Boolean)
196
+ if (importNames.length === 0) return null
217
197
  return <File.Import key={filePath} name={importNames} root={file.path} path={zodFile.path} />
218
198
  })}
219
199
 
220
200
  <ClassClient
221
201
  name={name}
222
202
  operations={ops}
223
- baseURL={options.baseURL}
224
- dataReturnType={options.dataReturnType}
225
- pathParamsType={options.pathParamsType}
226
- paramsCasing={options.paramsCasing}
227
- paramsType={options.paramsType}
228
- parser={options.parser}
203
+ baseURL={baseURL}
204
+ dataReturnType={dataReturnType}
205
+ pathParamsType={pathParamsType}
206
+ paramsCasing={paramsCasing}
207
+ paramsType={paramsType}
208
+ parser={parser}
229
209
  />
230
210
  </File>
231
211
  )
232
212
  })
233
213
 
234
- if (options.wrapper) {
235
- const wrapperFile = driver.getFile({
236
- name: options.wrapper.className,
237
- extname: '.ts',
238
- pluginName,
239
- })
214
+ if (sdk) {
215
+ const sdkFile = resolver.resolveFile({ name: sdk.className, extname: '.ts' }, { root, output, group })
240
216
 
241
217
  files.push(
242
218
  <File
243
- key={wrapperFile.path}
244
- baseName={wrapperFile.baseName}
245
- path={wrapperFile.path}
246
- meta={wrapperFile.meta}
247
- banner={getBanner({ oas, output: options.output, config: driver.config })}
248
- footer={getFooter({ oas, output: options.output })}
219
+ key={sdkFile.path}
220
+ baseName={sdkFile.baseName}
221
+ path={sdkFile.path}
222
+ meta={sdkFile.meta}
223
+ banner={resolver.resolveBanner(inputNode, { output, config })}
224
+ footer={resolver.resolveFooter(inputNode, { output, config })}
249
225
  >
250
- {options.importPath ? (
251
- <File.Import name={['Client', 'RequestConfig']} path={options.importPath} isTypeOnly />
226
+ {importPath ? (
227
+ <File.Import name={['Client', 'RequestConfig']} path={importPath} isTypeOnly />
252
228
  ) : (
253
- <File.Import
254
- name={['Client', 'RequestConfig']}
255
- root={wrapperFile.path}
256
- path={path.resolve(config.root, config.output.path, '.kubb/fetch.ts')}
257
- isTypeOnly
258
- />
229
+ <File.Import name={['Client', 'RequestConfig']} root={sdkFile.path} path={path.resolve(root, '.kubb/client.ts')} isTypeOnly />
259
230
  )}
260
231
 
261
232
  {controllers.map(({ name, file }) => (
262
- <File.Import key={name} name={[name]} root={wrapperFile.path} path={file.path} />
233
+ <File.Import key={name} name={[name]} root={sdkFile.path} path={file.path} />
263
234
  ))}
264
235
 
265
- <WrapperClient name={options.wrapper.className} classNames={controllers.map(({ name }) => name)} />
236
+ <WrapperClient name={sdk.className} controllers={controllers.map(({ name, propertyName }) => ({ className: name, propertyName }))} />
266
237
  </File>,
267
238
  )
268
239
  }
269
240
 
270
- return files
241
+ return <>{files}</>
271
242
  },
272
243
  })
@@ -1,123 +1,125 @@
1
1
  import path from 'node:path'
2
- import { usePluginDriver } from '@kubb/core/hooks'
3
- import { createReactGenerator } from '@kubb/plugin-oas/generators'
4
- import { useOas, useOperationManager } from '@kubb/plugin-oas/hooks'
5
- import { getBanner, getFooter } from '@kubb/plugin-oas/utils'
2
+ import { resolveOperationTypeNames } from '@internals/shared'
3
+ import { defineGenerator } from '@kubb/core'
6
4
  import { pluginTsName } from '@kubb/plugin-ts'
7
5
  import { pluginZodName } from '@kubb/plugin-zod'
8
- import { File } from '@kubb/react-fabric'
6
+ import { File, jsxRendererSync } from '@kubb/renderer-jsx'
9
7
  import { Client } from '../components/Client'
10
8
  import { Url } from '../components/Url.tsx'
11
9
  import type { PluginClient } from '../types'
12
10
 
13
- export const clientGenerator = createReactGenerator<PluginClient>({
11
+ export const clientGenerator = defineGenerator<PluginClient>({
14
12
  name: 'client',
15
- Operation({ config, plugin, operation, generator }) {
16
- const driver = usePluginDriver()
17
- const {
18
- options,
19
- options: { output, urlType },
20
- } = plugin
13
+ renderer: jsxRendererSync,
14
+ operation(node, ctx) {
15
+ const { config, driver, resolver, root, inputNode } = ctx
16
+ const { output, urlType, dataReturnType, paramsCasing, paramsType, pathParamsType, parser, importPath, group } = ctx.options
17
+ const baseURL = ctx.options.baseURL ?? inputNode.meta?.baseURL
21
18
 
22
- const oas = useOas()
23
- const { getSchemas, getName, getFile } = useOperationManager(generator)
19
+ const pluginTs = driver.getPlugin(pluginTsName)
24
20
 
25
- const client = {
26
- name: getName(operation, { type: 'function' }),
27
- file: getFile(operation),
21
+ if (!pluginTs) {
22
+ return null
28
23
  }
29
24
 
30
- const url = {
31
- name: getName(operation, { type: 'function', suffix: 'url', prefix: 'get' }),
32
- file: getFile(operation),
33
- }
25
+ const tsResolver = driver.getResolver(pluginTsName)
34
26
 
35
- const type = {
36
- file: getFile(operation, { pluginName: pluginTsName }),
37
- schemas: getSchemas(operation, { pluginName: pluginTsName, type: 'type' }),
38
- }
27
+ const pluginZod = parser === 'zod' ? driver.getPlugin(pluginZodName) : undefined
28
+ const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : undefined
39
29
 
40
- const zod = {
41
- file: getFile(operation, { pluginName: pluginZodName }),
42
- schemas: getSchemas(operation, { pluginName: pluginZodName, type: 'function' }),
43
- }
30
+ const importedTypeNames = resolveOperationTypeNames(node, tsResolver, { paramsCasing })
31
+
32
+ const importedZodNames =
33
+ zodResolver && parser === 'zod'
34
+ ? [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : undefined].filter(
35
+ (name): name is string => Boolean(name),
36
+ )
37
+ : []
44
38
 
45
- const isFormData = operation.getContentType() === 'multipart/form-data'
39
+ const meta = {
40
+ name: resolver.resolveName(node.operationId),
41
+ urlName: resolver.resolveUrlName(node),
42
+ file: resolver.resolveFile({ name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path }, { root, output, group }),
43
+ fileTs: tsResolver.resolveFile(
44
+ { name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
45
+ {
46
+ root,
47
+ output: pluginTs.options?.output ?? output,
48
+ group: pluginTs.options?.group,
49
+ },
50
+ ),
51
+ fileZod:
52
+ zodResolver && pluginZod?.options
53
+ ? zodResolver.resolveFile(
54
+ { name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
55
+ {
56
+ root,
57
+ output: pluginZod.options.output ?? output,
58
+ group: pluginZod.options?.group,
59
+ },
60
+ )
61
+ : undefined,
62
+ } as const
63
+
64
+ const hasFormData = node.requestBody?.content?.some((e) => e.contentType === 'multipart/form-data') ?? false
46
65
 
47
66
  return (
48
67
  <File
49
- baseName={client.file.baseName}
50
- path={client.file.path}
51
- meta={client.file.meta}
52
- banner={getBanner({ oas, output, config: driver.config })}
53
- footer={getFooter({ oas, output })}
68
+ baseName={meta.file.baseName}
69
+ path={meta.file.path}
70
+ meta={meta.file.meta}
71
+ banner={resolver.resolveBanner(inputNode, { output, config })}
72
+ footer={resolver.resolveFooter(inputNode, { output, config })}
54
73
  >
55
- {options.importPath ? (
74
+ {importPath ? (
56
75
  <>
57
- <File.Import name={'fetch'} path={options.importPath} />
58
- <File.Import name={['Client', 'RequestConfig', 'ResponseErrorConfig']} path={options.importPath} isTypeOnly />
76
+ <File.Import name={'fetch'} path={importPath} />
77
+ <File.Import name={['Client', 'RequestConfig', 'ResponseErrorConfig']} path={importPath} isTypeOnly />
59
78
  </>
60
79
  ) : (
61
80
  <>
62
- <File.Import name={['fetch']} root={client.file.path} path={path.resolve(config.root, config.output.path, '.kubb/fetch.ts')} />
81
+ <File.Import name={['fetch']} root={meta.file.path} path={path.resolve(root, '.kubb/client.ts')} />
63
82
  <File.Import
64
83
  name={['Client', 'RequestConfig', 'ResponseErrorConfig']}
65
- root={client.file.path}
66
- path={path.resolve(config.root, config.output.path, '.kubb/fetch.ts')}
84
+ root={meta.file.path}
85
+ path={path.resolve(root, '.kubb/client.ts')}
67
86
  isTypeOnly
68
87
  />
69
88
  </>
70
89
  )}
71
90
 
72
- {isFormData && type.schemas.request?.name && (
73
- <File.Import name={['buildFormData']} root={client.file.path} path={path.resolve(config.root, config.output.path, '.kubb/config.ts')} />
74
- )}
91
+ {hasFormData && <File.Import name={['buildFormData']} root={meta.file.path} path={path.resolve(root, '.kubb/config.ts')} />}
75
92
 
76
- {options.parser === 'zod' && (
77
- <File.Import
78
- name={[zod.schemas.response.name, zod.schemas.request?.name].filter((x): x is string => Boolean(x))}
79
- root={client.file.path}
80
- path={zod.file.path}
81
- />
93
+ {meta.fileZod && importedZodNames.length > 0 && <File.Import name={importedZodNames as string[]} root={meta.file.path} path={meta.fileZod.path} />}
94
+
95
+ {meta.fileTs && importedTypeNames.length > 0 && (
96
+ <File.Import name={Array.from(new Set(importedTypeNames))} root={meta.file.path} path={meta.fileTs.path} isTypeOnly />
82
97
  )}
83
- <File.Import
84
- name={[
85
- type.schemas.request?.name,
86
- type.schemas.response.name,
87
- type.schemas.pathParams?.name,
88
- type.schemas.queryParams?.name,
89
- type.schemas.headerParams?.name,
90
- ...(type.schemas.statusCodes?.map((item) => item.name) || []),
91
- ].filter((x): x is string => Boolean(x))}
92
- root={client.file.path}
93
- path={type.file.path}
94
- isTypeOnly
95
- />
96
98
 
97
99
  <Url
98
- name={url.name}
99
- baseURL={options.baseURL}
100
- pathParamsType={options.pathParamsType}
101
- paramsCasing={options.paramsCasing}
102
- paramsType={options.paramsType}
103
- typeSchemas={type.schemas}
104
- operation={operation}
100
+ name={meta.urlName}
101
+ baseURL={baseURL}
102
+ pathParamsType={pathParamsType}
103
+ paramsCasing={paramsCasing}
104
+ paramsType={paramsType}
105
+ node={node}
106
+ tsResolver={tsResolver}
105
107
  isIndexable={urlType === 'export'}
106
108
  isExportable={urlType === 'export'}
107
109
  />
108
110
 
109
111
  <Client
110
- name={client.name}
111
- urlName={url.name}
112
- baseURL={options.baseURL}
113
- dataReturnType={options.dataReturnType}
114
- pathParamsType={options.pathParamsType}
115
- paramsCasing={options.paramsCasing}
116
- paramsType={options.paramsType}
117
- typeSchemas={type.schemas}
118
- operation={operation}
119
- parser={options.parser}
120
- zodSchemas={zod.schemas}
112
+ name={meta.name}
113
+ urlName={meta.urlName}
114
+ baseURL={baseURL}
115
+ dataReturnType={dataReturnType}
116
+ pathParamsType={pathParamsType}
117
+ paramsCasing={paramsCasing}
118
+ paramsType={paramsType}
119
+ node={node}
120
+ tsResolver={tsResolver}
121
+ zodResolver={zodResolver}
122
+ parser={parser}
121
123
  />
122
124
  </File>
123
125
  )