@kubb/plugin-client 5.0.0-alpha.8 → 5.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.
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 +1818 -97
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.ts +330 -4
  16. package/dist/index.js +1804 -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 +776 -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 -488
  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,16 +1,16 @@
1
+ import { buildOperationComments, buildParamsMapping, buildRequestConfigType, getContentTypeInfo, getOperationParameters } from '@internals/shared'
1
2
  import { isValidVarName, URLPath } from '@internals/utils'
2
- import { getDefaultValue, isOptional, type Operation } from '@kubb/oas'
3
- import type { OperationSchemas } from '@kubb/plugin-oas'
4
- import { getComments, getParamsMapping, getPathParams } from '@kubb/plugin-oas/utils'
5
- import { File, Function, FunctionParams } from '@kubb/react-fabric'
6
- import type { FabricReactNode } from '@kubb/react-fabric/types'
3
+ import { ast } from '@kubb/core'
4
+ import type { ResolverTs } from '@kubb/plugin-ts'
5
+ import { functionPrinter } from '@kubb/plugin-ts'
6
+ import type { ResolverZod } from '@kubb/plugin-zod'
7
+ import { File, Function } from '@kubb/renderer-jsx'
8
+ import type { KubbReactNode } from '@kubb/renderer-jsx/types'
9
+ import { createFunctionParams } from '../functionParams.ts'
7
10
  import type { PluginClient } from '../types.ts'
8
- import { Url } from './Url.tsx'
11
+ import { buildUrlParamsNode } from './Url.tsx'
9
12
 
10
13
  type Props = {
11
- /**
12
- * Name of the function
13
- */
14
14
  name: string
15
15
  urlName?: string
16
16
  isExportable?: boolean
@@ -24,106 +24,50 @@ type Props = {
24
24
  paramsType: PluginClient['resolvedOptions']['pathParamsType']
25
25
  pathParamsType: PluginClient['resolvedOptions']['pathParamsType']
26
26
  parser: PluginClient['resolvedOptions']['parser'] | undefined
27
- typeSchemas: OperationSchemas
28
- zodSchemas: OperationSchemas | undefined
29
- operation: Operation
30
- children?: FabricReactNode
27
+ node: ast.OperationNode
28
+ tsResolver: ResolverTs
29
+ zodResolver?: ResolverZod
30
+ children?: KubbReactNode
31
31
  }
32
32
 
33
33
  type GetParamsProps = {
34
34
  paramsCasing: PluginClient['resolvedOptions']['paramsCasing']
35
35
  paramsType: PluginClient['resolvedOptions']['paramsType']
36
36
  pathParamsType: PluginClient['resolvedOptions']['pathParamsType']
37
- typeSchemas: OperationSchemas
37
+ node: ast.OperationNode
38
+ tsResolver: ResolverTs
38
39
  isConfigurable: boolean
39
40
  }
40
41
 
41
- function getParams({ paramsType, paramsCasing, pathParamsType, typeSchemas, isConfigurable }: GetParamsProps) {
42
- if (paramsType === 'object') {
43
- const pathParams = getPathParams(typeSchemas.pathParams, {
44
- typed: true,
45
- casing: paramsCasing,
46
- })
47
-
48
- const children = {
49
- ...pathParams,
50
- data: typeSchemas.request?.name
51
- ? {
52
- type: typeSchemas.request?.name,
53
- optional: isOptional(typeSchemas.request?.schema),
54
- }
55
- : undefined,
56
- params: typeSchemas.queryParams?.name
57
- ? {
58
- type: typeSchemas.queryParams?.name,
59
- optional: isOptional(typeSchemas.queryParams?.schema),
60
- }
61
- : undefined,
62
- headers: typeSchemas.headerParams?.name
63
- ? {
64
- type: typeSchemas.headerParams?.name,
65
- optional: isOptional(typeSchemas.headerParams?.schema),
66
- }
67
- : undefined,
68
- }
69
-
70
- // Check if all children are optional or undefined
71
- const allChildrenAreOptional = Object.values(children).every((child) => !child || child.optional)
72
-
73
- return FunctionParams.factory({
74
- data: {
75
- mode: 'object',
76
- children,
77
- default: allChildrenAreOptional ? '{}' : undefined,
78
- },
79
- config: isConfigurable
80
- ? {
81
- type: typeSchemas.request?.name
82
- ? `Partial<RequestConfig<${typeSchemas.request?.name}>> & { client?: Client }`
83
- : 'Partial<RequestConfig> & { client?: Client }',
84
- default: '{}',
85
- }
86
- : undefined,
87
- })
88
- }
42
+ const declarationPrinter = functionPrinter({ mode: 'declaration' })
89
43
 
90
- return FunctionParams.factory({
91
- pathParams: typeSchemas.pathParams?.name
92
- ? {
93
- mode: pathParamsType === 'object' ? 'object' : 'inlineSpread',
94
- children: getPathParams(typeSchemas.pathParams, {
95
- typed: true,
96
- casing: paramsCasing,
97
- }),
98
- default: getDefaultValue(typeSchemas.pathParams?.schema),
99
- }
100
- : undefined,
101
- data: typeSchemas.request?.name
102
- ? {
103
- type: typeSchemas.request?.name,
104
- optional: isOptional(typeSchemas.request?.schema),
105
- }
106
- : undefined,
107
- params: typeSchemas.queryParams?.name
108
- ? {
109
- type: typeSchemas.queryParams?.name,
110
- optional: isOptional(typeSchemas.queryParams?.schema),
111
- }
112
- : undefined,
113
- headers: typeSchemas.headerParams?.name
114
- ? {
115
- type: typeSchemas.headerParams?.name,
116
- optional: isOptional(typeSchemas.headerParams?.schema),
117
- }
118
- : undefined,
119
- config: isConfigurable
120
- ? {
121
- type: typeSchemas.request?.name
122
- ? `Partial<RequestConfig<${typeSchemas.request?.name}>> & { client?: Client }`
123
- : 'Partial<RequestConfig> & { client?: Client }',
124
- default: '{}',
125
- }
126
- : undefined,
44
+ export function buildClientParamsNode({
45
+ paramsType,
46
+ paramsCasing,
47
+ pathParamsType,
48
+ node,
49
+ tsResolver,
50
+ isConfigurable,
51
+ }: GetParamsProps): ast.FunctionParametersNode {
52
+ return ast.createOperationParams(node, {
53
+ paramsType,
54
+ pathParamsType: paramsType === 'object' ? 'object' : pathParamsType === 'object' ? 'object' : 'inline',
55
+ paramsCasing,
56
+ resolver: tsResolver,
57
+ extraParams: [
58
+ ...(isConfigurable
59
+ ? [
60
+ ast.createFunctionParameter({
61
+ name: 'config',
62
+ type: ast.createParamsType({
63
+ variant: 'reference',
64
+ name: buildRequestConfigType(node, tsResolver),
65
+ }),
66
+ default: '{}',
67
+ }),
68
+ ]
69
+ : []),
70
+ ],
127
71
  })
128
72
  }
129
73
 
@@ -132,60 +76,82 @@ export function Client({
132
76
  isExportable = true,
133
77
  isIndexable = true,
134
78
  returnType,
135
- typeSchemas,
136
79
  baseURL,
137
80
  dataReturnType,
138
81
  parser,
139
- zodSchemas,
140
82
  paramsType,
141
83
  paramsCasing,
142
84
  pathParamsType,
143
- operation,
85
+ node,
86
+ tsResolver,
87
+ zodResolver,
144
88
  urlName,
145
89
  children,
146
90
  isConfigurable = true,
147
- }: Props): FabricReactNode {
148
- const path = new URLPath(operation.path)
149
- const contentType = operation.getContentType()
150
- const isFormData = contentType === 'multipart/form-data'
91
+ }: Props): KubbReactNode {
92
+ const path = new URLPath(node.path)
93
+ const { defaultContentType: contentType, isMultipleContentTypes, hasFormData } = getContentTypeInfo(node)
94
+ const isFormData = !isMultipleContentTypes && contentType === 'multipart/form-data'
95
+
96
+ const { path: originalPathParams, query: originalQueryParams, header: originalHeaderParams } = getOperationParameters(node)
97
+ const { path: casedPathParams, query: casedQueryParams, header: casedHeaderParams } = getOperationParameters(node, { paramsCasing })
98
+
99
+ const pathParamsMapping = paramsCasing && !urlName ? buildParamsMapping(originalPathParams, casedPathParams) : undefined
100
+ const queryParamsMapping = paramsCasing ? buildParamsMapping(originalQueryParams, casedQueryParams) : undefined
101
+ const headerParamsMapping = paramsCasing ? buildParamsMapping(originalHeaderParams, casedHeaderParams) : undefined
151
102
 
152
- // Generate parameter mappings when paramsCasing is used
153
- // Apply to pathParams, queryParams and headerParams
154
- const pathParamsMapping = paramsCasing ? getParamsMapping(typeSchemas.pathParams, { casing: paramsCasing }) : undefined
155
- const queryParamsMapping = paramsCasing ? getParamsMapping(typeSchemas.queryParams, { casing: paramsCasing }) : undefined
156
- const headerParamsMapping = paramsCasing ? getParamsMapping(typeSchemas.headerParams, { casing: paramsCasing }) : undefined
103
+ const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : undefined
104
+ const responseName = tsResolver.resolveResponseName(node)
105
+ const queryParamsName = originalQueryParams.length > 0 ? tsResolver.resolveQueryParamsName(node, originalQueryParams[0]!) : undefined
106
+ const headerParamsName = originalHeaderParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, originalHeaderParams[0]!) : undefined
107
+
108
+ const zodResponseName = zodResolver && parser === 'zod' ? zodResolver.resolveResponseName?.(node) : undefined
109
+ const zodRequestName = zodResolver && parser === 'zod' && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : undefined
110
+
111
+ const errorNames = node.responses
112
+ .filter((r) => {
113
+ const code = Number.parseInt(r.statusCode, 10)
114
+ return code >= 400
115
+ })
116
+ .map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode))
157
117
 
158
118
  const headers = [
159
- contentType !== 'application/json' && contentType !== 'multipart/form-data' ? `'Content-Type': '${contentType}'` : undefined,
160
- typeSchemas.headerParams?.name ? (headerParamsMapping ? '...mappedHeaders' : '...headers') : undefined,
119
+ !isMultipleContentTypes && contentType !== 'application/json' && contentType !== 'multipart/form-data' ? `'Content-Type': '${contentType}'` : undefined,
120
+ headerParamsName ? (headerParamsMapping ? '...mappedHeaders' : '...headers') : undefined,
161
121
  ].filter(Boolean)
162
122
 
163
- const TError = `ResponseErrorConfig<${typeSchemas.errors?.map((item) => item.name).join(' | ') || 'Error'}>`
123
+ const TError = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(' | ') : 'Error'}>`
164
124
 
165
- const generics = [typeSchemas.response.name, TError, typeSchemas.request?.name || 'unknown'].filter(Boolean)
166
- const params = getParams({
125
+ const generics = [responseName, TError, requestName || 'unknown'].filter(Boolean)
126
+ const paramsNode = buildClientParamsNode({
167
127
  paramsType,
168
128
  paramsCasing,
169
129
  pathParamsType,
170
- typeSchemas,
130
+ node,
131
+ tsResolver,
171
132
  isConfigurable,
172
133
  })
173
- const urlParams = Url.getParams({
134
+ const paramsSignature = declarationPrinter.print(paramsNode) ?? ''
135
+
136
+ const urlParamsNode = buildUrlParamsNode({
174
137
  paramsType,
175
138
  paramsCasing,
176
139
  pathParamsType,
177
- typeSchemas,
140
+ node,
141
+ tsResolver,
178
142
  })
143
+ const callPrinter = functionPrinter({ mode: 'call' })
144
+ const urlParamsCall = callPrinter.print(urlParamsNode) ?? ''
179
145
 
180
- const clientParams = FunctionParams.factory({
146
+ const clientParams = createFunctionParams({
181
147
  config: {
182
148
  mode: 'object',
183
149
  children: {
184
150
  method: {
185
- value: JSON.stringify(operation.method.toUpperCase()),
151
+ value: JSON.stringify(node.method.toUpperCase()),
186
152
  },
187
153
  url: {
188
- value: urlName ? `${urlName}(${urlParams.toCall()}).url.toString()` : path.template,
154
+ value: urlName ? `${urlName}(${urlParamsCall}).url.toString()` : path.template,
189
155
  },
190
156
  baseURL:
191
157
  baseURL && !urlName
@@ -193,12 +159,18 @@ export function Client({
193
159
  value: `\`${baseURL}\``,
194
160
  }
195
161
  : undefined,
196
- params: typeSchemas.queryParams?.name ? (queryParamsMapping ? { value: 'mappedParams' } : {}) : undefined,
197
- data: typeSchemas.request?.name
162
+ params: queryParamsName ? (queryParamsMapping ? { value: 'mappedParams' } : {}) : undefined,
163
+ data: requestName
198
164
  ? {
199
- value: isFormData ? 'formData as FormData' : 'requestData',
165
+ value:
166
+ isMultipleContentTypes && hasFormData
167
+ ? "contentType === 'multipart/form-data' ? formData as FormData : requestData"
168
+ : isFormData
169
+ ? 'formData as FormData'
170
+ : 'requestData',
200
171
  }
201
172
  : undefined,
173
+ contentType: isConfigurable && isMultipleContentTypes ? {} : undefined,
202
174
  requestConfig: isConfigurable
203
175
  ? {
204
176
  mode: 'inlineSpread',
@@ -217,8 +189,8 @@ export function Client({
217
189
  children
218
190
  ) : (
219
191
  <>
220
- {dataReturnType === 'full' && parser === 'zod' && zodSchemas && `return {...res, data: ${zodSchemas.response.name}.parse(res.data)}`}
221
- {dataReturnType === 'data' && parser === 'zod' && zodSchemas && `return ${zodSchemas.response.name}.parse(res.data)`}
192
+ {dataReturnType === 'full' && parser === 'zod' && zodResponseName && `return {...res, data: ${zodResponseName}.parse(res.data)}`}
193
+ {dataReturnType === 'data' && parser === 'zod' && zodResponseName && `return ${zodResponseName}.parse(res.data)`}
222
194
  {dataReturnType === 'full' && parser === 'client' && 'return res'}
223
195
  {dataReturnType === 'data' && parser === 'client' && 'return res.data'}
224
196
  </>
@@ -233,18 +205,20 @@ export function Client({
233
205
  name={name}
234
206
  async
235
207
  export={isExportable}
236
- params={params.toConstructor()}
208
+ params={paramsSignature}
237
209
  JSDoc={{
238
- comments: getComments(operation),
210
+ comments: buildOperationComments(node, { link: 'urlPath', linkPosition: 'beforeDeprecated', splitLines: true }),
239
211
  }}
240
212
  returnType={returnType}
241
213
  >
242
- {isConfigurable ? 'const { client: request = fetch, ...requestConfig } = config' : ''}
214
+ {isConfigurable
215
+ ? `const { client: request = fetch, ${isMultipleContentTypes ? `contentType = ${JSON.stringify(contentType)}, ` : ''}...requestConfig } = config`
216
+ : ''}
243
217
  <br />
244
218
  <br />
245
219
  {pathParamsMapping &&
246
220
  Object.entries(pathParamsMapping)
247
- .filter(([originalName, camelCaseName]) => originalName !== camelCaseName && isValidVarName(originalName))
221
+ .filter(([originalName, camelCaseName]) => isValidVarName(originalName) && originalName !== camelCaseName)
248
222
  .map(([originalName, camelCaseName]) => `const ${originalName} = ${camelCaseName}`)
249
223
  .join('\n')}
250
224
  {pathParamsMapping && (
@@ -253,7 +227,7 @@ export function Client({
253
227
  <br />
254
228
  </>
255
229
  )}
256
- {queryParamsMapping && typeSchemas.queryParams?.name && (
230
+ {queryParamsMapping && queryParamsName && (
257
231
  <>
258
232
  {`const mappedParams = params ? { ${Object.entries(queryParamsMapping)
259
233
  .map(([originalName, camelCaseName]) => `"${originalName}": params.${camelCaseName}`)
@@ -262,7 +236,7 @@ export function Client({
262
236
  <br />
263
237
  </>
264
238
  )}
265
- {headerParamsMapping && typeSchemas.headerParams?.name && (
239
+ {headerParamsMapping && headerParamsName && (
266
240
  <>
267
241
  {`const mappedHeaders = headers ? { ${Object.entries(headerParamsMapping)
268
242
  .map(([originalName, camelCaseName]) => `"${originalName}": headers.${camelCaseName}`)
@@ -271,11 +245,9 @@ export function Client({
271
245
  <br />
272
246
  </>
273
247
  )}
274
- {parser === 'zod' && zodSchemas?.request?.name
275
- ? `const requestData = ${zodSchemas.request.name}.parse(data)`
276
- : typeSchemas?.request?.name && 'const requestData = data'}
248
+ {parser === 'zod' && zodRequestName ? `const requestData = ${zodRequestName}.parse(data)` : requestName && 'const requestData = data'}
277
249
  <br />
278
- {isFormData && typeSchemas?.request?.name && 'const formData = buildFormData(requestData)'}
250
+ {(isFormData || (isMultipleContentTypes && hasFormData)) && requestName && 'const formData = buildFormData(requestData)'}
279
251
  <br />
280
252
  {isConfigurable
281
253
  ? `const res = await request<${generics.join(', ')}>(${clientParams.toCall()})`
@@ -287,5 +259,3 @@ export function Client({
287
259
  </>
288
260
  )
289
261
  }
290
-
291
- Client.getParams = getParams
@@ -1,20 +1,20 @@
1
1
  import { URLPath } from '@internals/utils'
2
- import type { HttpMethod, Operation } from '@kubb/oas'
3
- import { Const, File } from '@kubb/react-fabric'
4
- import type { FabricReactNode } from '@kubb/react-fabric/types'
2
+ import type { ast } from '@kubb/core'
3
+ import { Const, File } from '@kubb/renderer-jsx'
4
+ import type { KubbReactNode } from '@kubb/renderer-jsx/types'
5
5
 
6
6
  type OperationsProps = {
7
7
  name: string
8
- operations: Array<Operation>
8
+ nodes: Array<ast.OperationNode>
9
9
  }
10
10
 
11
- export function Operations({ name, operations }: OperationsProps): FabricReactNode {
12
- const operationsObject: Record<string, { path: string; method: HttpMethod }> = {}
11
+ export function Operations({ name, nodes }: OperationsProps): KubbReactNode {
12
+ const operationsObject: Record<string, { path: string; method: string }> = {}
13
13
 
14
- operations.forEach((operation) => {
15
- operationsObject[operation.getOperationId()] = {
16
- path: new URLPath(operation.path).URL,
17
- method: operation.method,
14
+ nodes.forEach((node) => {
15
+ operationsObject[node.operationId] = {
16
+ path: new URLPath(node.path).URL,
17
+ method: node.method.toLowerCase(),
18
18
  }
19
19
  })
20
20
 
@@ -1,36 +1,41 @@
1
+ import { buildOperationComments, getContentTypeInfo, getOperationParameters } from '@internals/shared'
1
2
  import { buildJSDoc, URLPath } from '@internals/utils'
2
- import type { Operation } from '@kubb/oas'
3
- import type { OperationSchemas } from '@kubb/plugin-oas'
4
- import { getComments } from '@kubb/plugin-oas/utils'
5
- import { File, FunctionParams } from '@kubb/react-fabric'
6
- import type { FabricReactNode } from '@kubb/react-fabric/types'
3
+ import type { ast } from '@kubb/core'
4
+ import type { ResolverTs } from '@kubb/plugin-ts'
5
+ import { functionPrinter } from '@kubb/plugin-ts'
6
+ import type { ResolverZod } from '@kubb/plugin-zod'
7
+ import { File } from '@kubb/renderer-jsx'
8
+ import type { KubbReactNode } from '@kubb/renderer-jsx/types'
7
9
  import type { PluginClient } from '../types.ts'
8
- import { Client } from './Client.tsx'
10
+ import { buildClassClientParams, buildFormDataLine, buildGenerics, buildHeaders, buildRequestDataLine, buildReturnStatement } from '../utils.ts'
11
+ import { buildClientParamsNode } from './Client.tsx'
12
+
13
+ type OperationData = {
14
+ node: ast.OperationNode
15
+ name: string
16
+ tsResolver: ResolverTs
17
+ zodResolver?: ResolverZod
18
+ }
9
19
 
10
20
  type Props = {
11
21
  name: string
12
22
  isExportable?: boolean
13
23
  isIndexable?: boolean
14
- operations: Array<{
15
- operation: Operation
16
- name: string
17
- typeSchemas: OperationSchemas
18
- zodSchemas: OperationSchemas | undefined
19
- }>
24
+ operations: Array<OperationData>
20
25
  baseURL: string | undefined
21
26
  dataReturnType: PluginClient['resolvedOptions']['dataReturnType']
22
27
  paramsCasing: PluginClient['resolvedOptions']['paramsCasing']
23
28
  paramsType: PluginClient['resolvedOptions']['pathParamsType']
24
29
  pathParamsType: PluginClient['resolvedOptions']['pathParamsType']
25
30
  parser: PluginClient['resolvedOptions']['parser'] | undefined
26
- children?: FabricReactNode
31
+ children?: KubbReactNode
27
32
  }
28
33
 
29
34
  type GenerateMethodProps = {
30
- operation: Operation
35
+ node: ast.OperationNode
31
36
  name: string
32
- typeSchemas: OperationSchemas
33
- zodSchemas: OperationSchemas | undefined
37
+ tsResolver: ResolverTs
38
+ zodResolver?: ResolverZod
34
39
  baseURL: string | undefined
35
40
  dataReturnType: PluginClient['resolvedOptions']['dataReturnType']
36
41
  parser: PluginClient['resolvedOptions']['parser'] | undefined
@@ -39,115 +44,13 @@ type GenerateMethodProps = {
39
44
  pathParamsType: PluginClient['resolvedOptions']['pathParamsType']
40
45
  }
41
46
 
42
- function buildHeaders(contentType: string, hasHeaderParams: boolean): Array<string> {
43
- return [
44
- contentType !== 'application/json' && contentType !== 'multipart/form-data' ? `'Content-Type': '${contentType}'` : undefined,
45
- hasHeaderParams ? '...headers' : undefined,
46
- ].filter(Boolean) as Array<string>
47
- }
48
-
49
- function buildGenerics(typeSchemas: OperationSchemas): Array<string> {
50
- const TError = `ResponseErrorConfig<${typeSchemas.errors?.map((item) => item.name).join(' | ') || 'Error'}>`
51
- return [typeSchemas.response.name, TError, typeSchemas.request?.name || 'unknown'].filter(Boolean)
52
- }
53
-
54
- function buildClientParams({
55
- operation,
56
- path,
57
- baseURL,
58
- typeSchemas,
59
- isFormData,
60
- headers,
61
- }: {
62
- operation: Operation
63
- path: URLPath
64
- baseURL: string | undefined
65
- typeSchemas: OperationSchemas
66
- isFormData: boolean
67
- headers: Array<string>
68
- }) {
69
- return FunctionParams.factory({
70
- config: {
71
- mode: 'object',
72
- children: {
73
- requestConfig: {
74
- mode: 'inlineSpread',
75
- },
76
- method: {
77
- value: JSON.stringify(operation.method.toUpperCase()),
78
- },
79
- url: {
80
- value: path.template,
81
- },
82
- baseURL: baseURL
83
- ? {
84
- value: JSON.stringify(baseURL),
85
- }
86
- : undefined,
87
- params: typeSchemas.queryParams?.name ? {} : undefined,
88
- data: typeSchemas.request?.name
89
- ? {
90
- value: isFormData ? 'formData as FormData' : 'requestData',
91
- }
92
- : undefined,
93
- headers: headers.length
94
- ? {
95
- value: `{ ${headers.join(', ')}, ...requestConfig.headers }`,
96
- }
97
- : undefined,
98
- },
99
- },
100
- })
101
- }
102
-
103
- function buildRequestDataLine({
104
- parser,
105
- zodSchemas,
106
- typeSchemas,
107
- }: {
108
- parser: PluginClient['resolvedOptions']['parser'] | undefined
109
- zodSchemas: OperationSchemas | undefined
110
- typeSchemas: OperationSchemas
111
- }): string {
112
- if (parser === 'zod' && zodSchemas?.request?.name) {
113
- return `const requestData = ${zodSchemas.request.name}.parse(data)`
114
- }
115
- if (typeSchemas?.request?.name) {
116
- return 'const requestData = data'
117
- }
118
- return ''
119
- }
120
-
121
- function buildFormDataLine(isFormData: boolean, hasRequest: boolean): string {
122
- return isFormData && hasRequest ? 'const formData = buildFormData(requestData)' : ''
123
- }
124
-
125
- function buildReturnStatement({
126
- dataReturnType,
127
- parser,
128
- zodSchemas,
129
- }: {
130
- dataReturnType: PluginClient['resolvedOptions']['dataReturnType']
131
- parser: PluginClient['resolvedOptions']['parser'] | undefined
132
- zodSchemas: OperationSchemas | undefined
133
- }): string {
134
- if (dataReturnType === 'full' && parser === 'zod' && zodSchemas) {
135
- return `return {...res, data: ${zodSchemas.response.name}.parse(res.data)}`
136
- }
137
- if (dataReturnType === 'data' && parser === 'zod' && zodSchemas) {
138
- return `return ${zodSchemas.response.name}.parse(res.data)`
139
- }
140
- if (dataReturnType === 'full' && parser === 'client') {
141
- return 'return res'
142
- }
143
- return 'return res.data'
144
- }
47
+ const declarationPrinter = functionPrinter({ mode: 'declaration' })
145
48
 
146
49
  function generateMethod({
147
- operation,
50
+ node,
148
51
  name,
149
- typeSchemas,
150
- zodSchemas,
52
+ tsResolver,
53
+ zodResolver,
151
54
  baseURL,
152
55
  dataReturnType,
153
56
  parser,
@@ -155,21 +58,24 @@ function generateMethod({
155
58
  paramsCasing,
156
59
  pathParamsType,
157
60
  }: GenerateMethodProps): string {
158
- const path = new URLPath(operation.path, { casing: paramsCasing })
159
- const contentType = operation.getContentType()
160
- const isFormData = contentType === 'multipart/form-data'
161
- const headers = buildHeaders(contentType, !!typeSchemas.headerParams?.name)
162
- const generics = buildGenerics(typeSchemas)
163
- const params = Client.getParams({ paramsType, paramsCasing, pathParamsType, typeSchemas, isConfigurable: true })
164
- const clientParams = buildClientParams({ operation, path, baseURL, typeSchemas, isFormData, headers })
165
- const jsdoc = buildJSDoc(getComments(operation))
61
+ const path = new URLPath(node.path, { casing: paramsCasing })
62
+ const { defaultContentType: contentType, isMultipleContentTypes, hasFormData } = getContentTypeInfo(node)
63
+ const isFormData = !isMultipleContentTypes && contentType === 'multipart/form-data'
64
+ const { header: headerParams } = getOperationParameters(node)
65
+ const headerParamsName = headerParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, headerParams[0]!) : undefined
66
+ const headers = isMultipleContentTypes ? (headerParamsName ? ['...headers'] : []) : buildHeaders(contentType, !!headerParamsName)
67
+ const generics = buildGenerics(node, tsResolver)
68
+ const paramsNode = buildClientParamsNode({ paramsType, paramsCasing, pathParamsType, node, tsResolver, isConfigurable: true })
69
+ const paramsSignature = declarationPrinter.print(paramsNode) ?? ''
70
+ const clientParams = buildClassClientParams({ node, path, baseURL, tsResolver, isFormData, isMultipleContentTypes, hasFormData, headers })
71
+ const jsdoc = buildJSDoc(buildOperationComments(node, { link: 'urlPath', linkPosition: 'beforeDeprecated', splitLines: true }))
166
72
 
167
- const requestDataLine = buildRequestDataLine({ parser, zodSchemas, typeSchemas })
168
- const formDataLine = buildFormDataLine(isFormData, !!typeSchemas?.request?.name)
169
- const returnStatement = buildReturnStatement({ dataReturnType, parser, zodSchemas })
73
+ const requestDataLine = buildRequestDataLine({ parser, node, zodResolver })
74
+ const formDataLine = buildFormDataLine(isFormData || (isMultipleContentTypes && hasFormData), !!node.requestBody?.content?.[0]?.schema)
75
+ const returnStatement = buildReturnStatement({ dataReturnType, parser, node, zodResolver })
170
76
 
171
77
  const methodBody = [
172
- 'const { client: request = fetch, ...requestConfig } = mergeConfig(this.#config, config)',
78
+ `const { client: request = fetch, ${isMultipleContentTypes ? `contentType = ${JSON.stringify(contentType)}, ` : ''}...requestConfig } = mergeConfig(this.#config, config)`,
173
79
  '',
174
80
  requestDataLine,
175
81
  formDataLine,
@@ -180,8 +86,7 @@ function generateMethod({
180
86
  .map((line) => ` ${line}`)
181
87
  .join('\n')
182
88
 
183
- // Indent static method by 2 spaces, body by 4 spaces (matching snapshot)
184
- return `${jsdoc} static async ${name}(${params.toConstructor()}) {\n${methodBody}\n }`
89
+ return `${jsdoc} static async ${name}(${paramsSignature}) {\n${methodBody}\n }`
185
90
  }
186
91
 
187
92
  export function StaticClassClient({
@@ -196,13 +101,13 @@ export function StaticClassClient({
196
101
  paramsCasing,
197
102
  pathParamsType,
198
103
  children,
199
- }: Props): FabricReactNode {
200
- const methods = operations.map(({ operation, name: methodName, typeSchemas, zodSchemas }) =>
104
+ }: Props): KubbReactNode {
105
+ const methods = operations.map(({ node, name: methodName, tsResolver, zodResolver }) =>
201
106
  generateMethod({
202
- operation,
107
+ node,
203
108
  name: methodName,
204
- typeSchemas,
205
- zodSchemas,
109
+ tsResolver,
110
+ zodResolver,
206
111
  baseURL,
207
112
  dataReturnType,
208
113
  parser,
@@ -221,4 +126,3 @@ export function StaticClassClient({
221
126
  </File.Source>
222
127
  )
223
128
  }
224
- StaticClassClient.getParams = Client.getParams