@kubb/plugin-client 5.0.0-beta.56 → 5.5.2

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 (55) hide show
  1. package/README.md +8 -61
  2. package/dist/index.cjs +754 -1745
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.ts +170 -349
  5. package/dist/index.js +733 -1733
  6. package/dist/index.js.map +1 -1
  7. package/package.json +10 -63
  8. package/src/generators/clientGenerator.tsx +6 -132
  9. package/src/index.ts +1 -9
  10. package/src/plugin.ts +32 -122
  11. package/src/types.ts +27 -249
  12. package/dist/chunk-Bx3C2hgW.cjs +0 -38
  13. package/dist/clients/axios.cjs +0 -68
  14. package/dist/clients/axios.cjs.map +0 -1
  15. package/dist/clients/axios.d.ts +0 -49
  16. package/dist/clients/axios.js +0 -58
  17. package/dist/clients/axios.js.map +0 -1
  18. package/dist/clients/fetch.cjs +0 -128
  19. package/dist/clients/fetch.cjs.map +0 -1
  20. package/dist/clients/fetch.d.ts +0 -50
  21. package/dist/clients/fetch.js +0 -120
  22. package/dist/clients/fetch.js.map +0 -1
  23. package/dist/templates/clients/axios.source.cjs +0 -7
  24. package/dist/templates/clients/axios.source.cjs.map +0 -1
  25. package/dist/templates/clients/axios.source.d.ts +0 -7
  26. package/dist/templates/clients/axios.source.js +0 -6
  27. package/dist/templates/clients/axios.source.js.map +0 -1
  28. package/dist/templates/clients/fetch.source.cjs +0 -7
  29. package/dist/templates/clients/fetch.source.cjs.map +0 -1
  30. package/dist/templates/clients/fetch.source.d.ts +0 -7
  31. package/dist/templates/clients/fetch.source.js +0 -6
  32. package/dist/templates/clients/fetch.source.js.map +0 -1
  33. package/dist/templates/config.source.cjs +0 -7
  34. package/dist/templates/config.source.cjs.map +0 -1
  35. package/dist/templates/config.source.d.ts +0 -7
  36. package/dist/templates/config.source.js +0 -6
  37. package/dist/templates/config.source.js.map +0 -1
  38. package/src/clients/axios.ts +0 -113
  39. package/src/clients/fetch.ts +0 -201
  40. package/src/components/ClassClient.tsx +0 -161
  41. package/src/components/Client.tsx +0 -281
  42. package/src/components/StaticClassClient.tsx +0 -153
  43. package/src/components/Url.tsx +0 -90
  44. package/src/components/WrapperClient.tsx +0 -33
  45. package/src/functionParams.ts +0 -118
  46. package/src/generators/classClientGenerator.tsx +0 -261
  47. package/src/generators/groupedClientGenerator.tsx +0 -82
  48. package/src/generators/operationsGenerator.ts +0 -47
  49. package/src/generators/staticClassClientGenerator.tsx +0 -240
  50. package/src/resolvers/resolverClient.ts +0 -46
  51. package/src/templates/clients/axios.source.ts +0 -4
  52. package/src/templates/clients/fetch.source.ts +0 -4
  53. package/src/templates/config.source.ts +0 -4
  54. package/src/utils.ts +0 -259
  55. /package/dist/{chunk-C0LytTxp.js → rolldown-runtime-C0LytTxp.js} +0 -0
@@ -1,201 +0,0 @@
1
- /**
2
- * RequestCredentials
3
- */
4
- export type RequestCredentials = 'omit' | 'same-origin' | 'include'
5
-
6
- /**
7
- * Header values may be objects (e.g. JSON-encoded filter headers like `X-Filter`).
8
- * Non-string values are JSON-serialized before the request is sent.
9
- */
10
- export type HeaderValue = string | number | boolean | null | undefined | object
11
- export type HeadersInit = Array<[string, HeaderValue]> | Record<string, HeaderValue>
12
-
13
- /**
14
- * Subset of FetchRequestConfig
15
- */
16
- export type RequestConfig<TData = unknown> = {
17
- baseURL?: string
18
- url?: string
19
- method?: 'GET' | 'PUT' | 'PATCH' | 'POST' | 'DELETE' | 'OPTIONS' | 'HEAD'
20
- params?: unknown
21
- data?: TData | FormData
22
- responseType?: 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream'
23
- signal?: AbortSignal
24
- headers?: HeadersInit
25
- credentials?: RequestCredentials
26
- contentType?: string
27
- }
28
-
29
- /**
30
- * Subset of FetchResponse
31
- */
32
- export type ResponseConfig<TData = unknown> = {
33
- data: TData
34
- status: number
35
- statusText: string
36
- headers: Headers
37
- }
38
-
39
- let _config: Partial<RequestConfig> = {}
40
-
41
- export const getConfig = () => _config
42
-
43
- export const setConfig = (config: Partial<RequestConfig>) => {
44
- _config = config
45
- return getConfig()
46
- }
47
-
48
- export const mergeConfig = <T extends RequestConfig>(...configs: Array<Partial<T>>): Partial<T> => {
49
- return configs.reduce<Partial<T>>((merged, config) => {
50
- return {
51
- ...merged,
52
- ...config,
53
- headers: {
54
- ...(Array.isArray(merged.headers) ? Object.fromEntries(merged.headers) : merged.headers),
55
- ...(Array.isArray(config.headers) ? Object.fromEntries(config.headers) : config.headers),
56
- },
57
- }
58
- }, {})
59
- }
60
-
61
- /**
62
- * Serializes header values into the string form `fetch` expects.
63
- * Objects (including arrays) are JSON-stringified so spec-defined object
64
- * headers like `X-Filter` are sent in their canonical JSON-string form.
65
- */
66
- function serializeHeaders(headers: HeadersInit | undefined): Record<string, string> {
67
- if (!headers) return {}
68
- const entries = Array.isArray(headers) ? headers : Object.entries(headers)
69
- const result: Record<string, string> = {}
70
- for (const [key, value] of entries) {
71
- if (value === undefined || value === null) continue
72
- result[key] = typeof value === 'string' ? value : typeof value === 'object' ? JSON.stringify(value) : String(value)
73
- }
74
- return result
75
- }
76
-
77
- /**
78
- * Serializes the request body into the form `fetch` expects.
79
- * `FormData`, `URLSearchParams`, `Blob`, `ArrayBuffer` and string bodies are passed through
80
- * untouched. Plain objects are encoded as `URLSearchParams` for `application/x-www-form-urlencoded`
81
- * and JSON-serialized otherwise.
82
- */
83
- function serializeBody(data: unknown, contentType?: string): BodyInit | undefined {
84
- if (data === undefined || data === null) return undefined
85
- if (data instanceof FormData || data instanceof URLSearchParams || data instanceof Blob || data instanceof ArrayBuffer || ArrayBuffer.isView(data)) {
86
- return data as BodyInit
87
- }
88
- if (typeof data === 'string') return data
89
- if (contentType?.includes('application/x-www-form-urlencoded')) {
90
- return new URLSearchParams(data as Record<string, string>)
91
- }
92
- return JSON.stringify(data)
93
- }
94
-
95
- /**
96
- * Picks a `responseType` from a `Content-Type` header, or `undefined` when it is not recognized.
97
- */
98
- function detectResponseType(contentType: string | null): RequestConfig['responseType'] {
99
- if (!contentType) return undefined
100
- if (contentType.includes('application/json') || contentType.includes('text/json')) return 'json'
101
- if (contentType.includes('text/')) return 'text'
102
- if (contentType.includes('image/') || contentType.includes('application/octet-stream')) return 'blob'
103
- return undefined
104
- }
105
-
106
- /**
107
- * Parses a `fetch` response body.
108
- *
109
- * - Empty responses (204/205/304 or no body) resolve to `{}`.
110
- * - An explicit `responseType` (or one detected from the `Content-Type` header) forces the matching
111
- * `Response` method.
112
- * - As a last resort the body is read as text and `JSON.parse`d, falling back to the raw text.
113
- */
114
- async function parseResponse<TData>(response: Response, responseType?: RequestConfig['responseType']): Promise<TData> {
115
- if ([204, 205, 304].includes(response.status) || !response.body) {
116
- return {} as TData
117
- }
118
-
119
- switch (responseType ?? detectResponseType(response.headers.get('Content-Type'))) {
120
- case 'text':
121
- case 'document':
122
- return (await response.text()) as TData
123
- case 'blob':
124
- return (await response.blob()) as TData
125
- case 'arraybuffer':
126
- return (await response.arrayBuffer()) as TData
127
- case 'stream':
128
- return response.body as TData
129
- case 'json':
130
- return (await response.json()) as TData
131
- }
132
-
133
- // Explicit but unrecognized responseType keeps the JSON default; otherwise read text and parse it.
134
- if (responseType) {
135
- return (await response.json()) as TData
136
- }
137
- const text = await response.text()
138
- if (!text) {
139
- return {} as TData
140
- }
141
- try {
142
- return JSON.parse(text) as TData
143
- } catch {
144
- return text as TData
145
- }
146
- }
147
-
148
- export type ResponseErrorConfig<TError = unknown> = TError
149
-
150
- export type Client = <TResponseData, _TError = unknown, TRequestData = unknown>(
151
- config: RequestConfig<TRequestData>,
152
- request?: unknown,
153
- ) => Promise<ResponseConfig<TResponseData>>
154
-
155
- export const client = async <TResponseData, _TError = unknown, RequestData = unknown>(
156
- paramsConfig: RequestConfig<RequestData>,
157
- _request?: unknown,
158
- ): Promise<ResponseConfig<TResponseData>> => {
159
- const normalizedParams = new URLSearchParams()
160
-
161
- const config = mergeConfig(getConfig(), paramsConfig)
162
-
163
- Object.entries(config.params || {}).forEach(([key, value]) => {
164
- if (value !== undefined) {
165
- normalizedParams.append(key, value === null ? 'null' : value.toString())
166
- }
167
- })
168
-
169
- let targetUrl = [config.baseURL, config.url].filter(Boolean).join('')
170
-
171
- if (config.params) {
172
- targetUrl += `?${normalizedParams}`
173
- }
174
-
175
- const headers: Record<string, string> = {
176
- ...(config.contentType && config.contentType !== 'multipart/form-data' ? { 'Content-Type': config.contentType } : {}),
177
- ...serializeHeaders(config.headers),
178
- }
179
-
180
- const response = await globalThis.fetch(targetUrl, {
181
- credentials: config.credentials || 'same-origin',
182
- method: config.method?.toUpperCase(),
183
- body: serializeBody(config.data, headers['Content-Type'] ?? headers['content-type']),
184
- signal: config.signal,
185
- headers,
186
- })
187
-
188
- const data = await parseResponse<TResponseData>(response, config.responseType)
189
-
190
- return {
191
- data,
192
- status: response.status,
193
- statusText: response.statusText,
194
- headers: response.headers as Headers,
195
- }
196
- }
197
-
198
- client.getConfig = getConfig
199
- client.setConfig = setConfig
200
-
201
- export default client
@@ -1,161 +0,0 @@
1
- import { buildOperationComments, getContentTypeInfo, getOperationParameters } from '@internals/shared'
2
- import { Url } from '@internals/utils'
3
- import { buildJSDoc, stringify } from '@kubb/ast/utils'
4
- import { ast } from '@kubb/core'
5
- import type { ResolverTs } from '@kubb/plugin-ts'
6
- import { functionPrinter } from '@kubb/plugin-ts'
7
- import type { ResolverZod } from '@kubb/plugin-zod'
8
- import { File } from '@kubb/renderer-jsx'
9
- import type { KubbReactNode } from '@kubb/renderer-jsx/types'
10
- import type { PluginClient } from '../types.ts'
11
- import {
12
- buildClassClientParams,
13
- buildFormDataLine,
14
- buildGenerics,
15
- buildHeaders,
16
- buildQueryParamsLine,
17
- buildRequestDataLine,
18
- buildReturnStatement,
19
- resolveQueryParamsParser,
20
- } from '../utils.ts'
21
- import { buildClientParamsNode } from './Client.tsx'
22
-
23
- type OperationData = {
24
- node: ast.OperationNode
25
- name: string
26
- tsResolver: ResolverTs
27
- zodResolver?: ResolverZod | null
28
- }
29
-
30
- type Props = {
31
- name: string
32
- isExportable?: boolean
33
- isIndexable?: boolean
34
- operations: Array<OperationData>
35
- baseURL: string | null | undefined
36
- dataReturnType: PluginClient['resolvedOptions']['dataReturnType']
37
- paramsCasing: PluginClient['resolvedOptions']['paramsCasing']
38
- paramsType: PluginClient['resolvedOptions']['pathParamsType']
39
- pathParamsType: PluginClient['resolvedOptions']['pathParamsType']
40
- parser: PluginClient['resolvedOptions']['parser'] | undefined
41
- children?: KubbReactNode
42
- }
43
-
44
- type GenerateMethodProps = {
45
- node: ast.OperationNode
46
- name: string
47
- tsResolver: ResolverTs
48
- zodResolver?: ResolverZod | null
49
- baseURL: string | null | undefined
50
- dataReturnType: PluginClient['resolvedOptions']['dataReturnType']
51
- parser: PluginClient['resolvedOptions']['parser'] | undefined
52
- paramsType: PluginClient['resolvedOptions']['paramsType']
53
- paramsCasing: PluginClient['resolvedOptions']['paramsCasing']
54
- pathParamsType: PluginClient['resolvedOptions']['pathParamsType']
55
- }
56
-
57
- const declarationPrinter = functionPrinter({ mode: 'declaration' })
58
-
59
- function generateMethod({
60
- node,
61
- name,
62
- tsResolver,
63
- zodResolver,
64
- baseURL,
65
- dataReturnType,
66
- parser,
67
- paramsType,
68
- paramsCasing,
69
- pathParamsType,
70
- }: GenerateMethodProps): string {
71
- if (!ast.isHttpOperationNode(node)) return ''
72
- const { defaultContentType: contentType, isMultipleContentTypes, hasFormData } = getContentTypeInfo(node)
73
- const isFormData = !isMultipleContentTypes && contentType === 'multipart/form-data'
74
- const { header: headerParams } = getOperationParameters(node)
75
- const headerParamsName = headerParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, headerParams[0]!) : null
76
- const headers = isMultipleContentTypes ? (headerParamsName ? ['...headers'] : []) : buildHeaders(contentType, !!headerParamsName)
77
- const generics = buildGenerics(node, tsResolver, { dataReturnType, zodResolver, parser })
78
- const paramsNode = buildClientParamsNode({ paramsType, paramsCasing, pathParamsType, node, tsResolver, isConfigurable: true })
79
- const paramsSignature = declarationPrinter.print(paramsNode) ?? ''
80
- const { query: queryParams } = getOperationParameters(node)
81
- const zodQueryParamsName =
82
- zodResolver && resolveQueryParamsParser(parser) === 'zod' && queryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, queryParams[0]!) : null
83
- const clientParams = buildClassClientParams({
84
- node,
85
- url: Url.toTemplateString(node.path, { casing: paramsCasing }),
86
- baseURL,
87
- tsResolver,
88
- isFormData,
89
- isMultipleContentTypes,
90
- hasFormData,
91
- headers,
92
- zodQueryParamsName,
93
- })
94
- const jsdoc = buildJSDoc(buildOperationComments(node, { link: 'urlPath', linkPosition: 'beforeDeprecated', splitLines: true }))
95
-
96
- const requestDataLine = buildRequestDataLine({ parser, node, zodResolver })
97
- const queryParamsLine = buildQueryParamsLine({ parser, node, zodResolver })
98
- const formDataLine = buildFormDataLine(isFormData || (isMultipleContentTypes && hasFormData), !!node.requestBody?.content?.[0]?.schema)
99
- const returnStatement = buildReturnStatement({ dataReturnType, parser, node, zodResolver, tsResolver })
100
-
101
- const methodBody = [
102
- `const { client: request = client, ${isMultipleContentTypes ? `contentType = ${stringify(contentType)}, ` : ''}...requestConfig } = mergeConfig(this.#config, config)`,
103
- '',
104
- requestDataLine,
105
- queryParamsLine,
106
- formDataLine,
107
- `const res = await request<${generics.join(', ')}>(${clientParams.toCall()})`,
108
- returnStatement,
109
- ]
110
- .filter(Boolean)
111
- .map((line) => ` ${line}`)
112
- .join('\n')
113
-
114
- return `${jsdoc}async ${name}(${paramsSignature}) {\n${methodBody}\n }`
115
- }
116
-
117
- export function ClassClient({
118
- name,
119
- isExportable = true,
120
- isIndexable = true,
121
- operations,
122
- baseURL,
123
- dataReturnType,
124
- parser,
125
- paramsType,
126
- paramsCasing,
127
- pathParamsType,
128
- children,
129
- }: Props): KubbReactNode {
130
- const methods = operations.map(({ node, name: methodName, tsResolver, zodResolver }) =>
131
- generateMethod({
132
- node,
133
- name: methodName,
134
- tsResolver,
135
- zodResolver,
136
- baseURL,
137
- dataReturnType,
138
- parser,
139
- paramsType,
140
- paramsCasing,
141
- pathParamsType,
142
- }),
143
- )
144
-
145
- const classCode = `export class ${name} {
146
- #config: Partial<RequestConfig> & { client?: Client }
147
-
148
- constructor(config: Partial<RequestConfig> & { client?: Client } = {}) {
149
- this.#config = config
150
- }
151
-
152
- ${methods.join('\n\n')}
153
- }`
154
-
155
- return (
156
- <File.Source name={name} isExportable={isExportable} isIndexable={isIndexable}>
157
- {classCode}
158
- {children}
159
- </File.Source>
160
- )
161
- }
@@ -1,281 +0,0 @@
1
- import {
2
- buildOperationComments,
3
- buildParamsMapping,
4
- buildRequestConfigType,
5
- getContentTypeInfo,
6
- getOperationParameters,
7
- getResponseType,
8
- resolveSuccessNames,
9
- } from '@internals/shared'
10
- import { isValidVarName, Url } from '@internals/utils'
11
- import { stringify } from '@kubb/ast/utils'
12
- import { ast } from '@kubb/core'
13
- import type { ResolverTs } from '@kubb/plugin-ts'
14
- import { functionPrinter } from '@kubb/plugin-ts'
15
- import type { ResolverZod } from '@kubb/plugin-zod'
16
- import { File, Function } from '@kubb/renderer-jsx'
17
- import type { KubbReactNode } from '@kubb/renderer-jsx/types'
18
- import { createFunctionParams } from '../functionParams.ts'
19
- import type { PluginClient } from '../types.ts'
20
- import { buildStatusUnionType, resolveQueryParamsParser, resolveRequestParser, resolveResponseParser } from '../utils.ts'
21
- import { buildUrlParamsNode } from './Url.tsx'
22
-
23
- type Props = {
24
- name: string
25
- urlName?: string
26
- isExportable?: boolean
27
- isIndexable?: boolean
28
- isConfigurable?: boolean
29
- returnType?: string
30
-
31
- baseURL: string | null | undefined
32
- dataReturnType: PluginClient['resolvedOptions']['dataReturnType']
33
- paramsCasing: PluginClient['resolvedOptions']['paramsCasing']
34
- paramsType: PluginClient['resolvedOptions']['pathParamsType']
35
- pathParamsType: PluginClient['resolvedOptions']['pathParamsType']
36
- parser: PluginClient['resolvedOptions']['parser'] | undefined
37
- node: ast.OperationNode
38
- tsResolver: ResolverTs
39
- zodResolver?: ResolverZod | null
40
- children?: KubbReactNode
41
- }
42
-
43
- type GetParamsProps = {
44
- paramsCasing: PluginClient['resolvedOptions']['paramsCasing']
45
- paramsType: PluginClient['resolvedOptions']['paramsType']
46
- pathParamsType: PluginClient['resolvedOptions']['pathParamsType']
47
- node: ast.OperationNode
48
- tsResolver: ResolverTs
49
- isConfigurable: boolean
50
- }
51
-
52
- const declarationPrinter = functionPrinter({ mode: 'declaration' })
53
-
54
- export function buildClientParamsNode({
55
- paramsType,
56
- paramsCasing,
57
- pathParamsType,
58
- node,
59
- tsResolver,
60
- isConfigurable,
61
- }: GetParamsProps): ast.FunctionParametersNode {
62
- return ast.createOperationParams(node, {
63
- paramsType,
64
- pathParamsType: paramsType === 'object' ? 'object' : pathParamsType === 'object' ? 'object' : 'inline',
65
- paramsCasing,
66
- resolver: tsResolver,
67
- extraParams: [
68
- ...(isConfigurable
69
- ? [
70
- ast.createFunctionParameter({
71
- name: 'config',
72
- type: ast.createParamsType({
73
- variant: 'reference',
74
- name: buildRequestConfigType(node, tsResolver),
75
- }),
76
- default: '{}',
77
- }),
78
- ]
79
- : []),
80
- ],
81
- })
82
- }
83
-
84
- export function Client({
85
- name,
86
- isExportable = true,
87
- isIndexable = true,
88
- returnType,
89
- baseURL,
90
- dataReturnType,
91
- parser,
92
- paramsType,
93
- paramsCasing,
94
- pathParamsType,
95
- node,
96
- tsResolver,
97
- zodResolver,
98
- urlName,
99
- children,
100
- isConfigurable = true,
101
- }: Props): KubbReactNode {
102
- if (!ast.isHttpOperationNode(node)) return null
103
- const { defaultContentType: contentType, isMultipleContentTypes, hasFormData } = getContentTypeInfo(node)
104
- const isFormData = !isMultipleContentTypes && contentType === 'multipart/form-data'
105
- const responseType = getResponseType(node)
106
-
107
- const { path: originalPathParams, query: originalQueryParams, header: originalHeaderParams } = getOperationParameters(node)
108
- const { path: casedPathParams, query: casedQueryParams, header: casedHeaderParams } = getOperationParameters(node, { paramsCasing })
109
-
110
- const pathParamsMapping = paramsCasing && !urlName ? buildParamsMapping(originalPathParams, casedPathParams) : null
111
- const queryParamsMapping = paramsCasing ? buildParamsMapping(originalQueryParams, casedQueryParams) : null
112
- const headerParamsMapping = paramsCasing ? buildParamsMapping(originalHeaderParams, casedHeaderParams) : null
113
-
114
- const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : null
115
- const successNames = resolveSuccessNames(node, tsResolver)
116
- const responseName = successNames.length > 0 ? successNames.join(' | ') : tsResolver.resolveResponseName(node)
117
- const queryParamsName = originalQueryParams.length > 0 ? tsResolver.resolveQueryParamsName(node, originalQueryParams[0]!) : null
118
- const headerParamsName = originalHeaderParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, originalHeaderParams[0]!) : null
119
-
120
- const requestParser = resolveRequestParser(parser)
121
- const responseParser = resolveResponseParser(parser)
122
- const zodResponseName = zodResolver && responseParser === 'zod' ? zodResolver.resolveResponseName?.(node) : null
123
- const zodRequestName = zodResolver && requestParser === 'zod' && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null
124
- const queryParamsParser = resolveQueryParamsParser(parser)
125
- const zodQueryParamsName =
126
- zodResolver && queryParamsParser === 'zod' && originalQueryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, originalQueryParams[0]!) : null
127
-
128
- const errorNames = node.responses
129
- .filter((r) => {
130
- const code = Number.parseInt(r.statusCode, 10)
131
- return code >= 400
132
- })
133
- .map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode))
134
-
135
- const headers = [
136
- !isMultipleContentTypes && contentType !== 'application/json' && contentType !== 'multipart/form-data' ? `'Content-Type': '${contentType}'` : null,
137
- headerParamsName ? (headerParamsMapping ? '...mappedHeaders' : '...headers') : null,
138
- ].filter(Boolean)
139
-
140
- const TError = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(' | ') : 'Error'}>`
141
-
142
- const allStatusNames = node.responses.map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode))
143
- const genericsResponseName = dataReturnType === 'full' ? (allStatusNames.length > 0 ? allStatusNames.join(' | ') : responseName) : responseName
144
- // z.output<> reflects the post-transform type (e.g. date coercion turns Date → string), avoiding a compile error on the generated call
145
- const requestGenericType = parser === 'zod' && zodRequestName ? `z.output<typeof ${zodRequestName}>` : requestName || 'unknown'
146
- const generics = [genericsResponseName, TError, requestGenericType].filter(Boolean)
147
- const paramsNode = buildClientParamsNode({
148
- paramsType,
149
- paramsCasing,
150
- pathParamsType,
151
- node,
152
- tsResolver,
153
- isConfigurable,
154
- })
155
- const paramsSignature = declarationPrinter.print(paramsNode) ?? ''
156
-
157
- const urlParamsNode = buildUrlParamsNode({
158
- paramsType,
159
- paramsCasing,
160
- pathParamsType,
161
- node,
162
- tsResolver,
163
- })
164
- const callPrinter = functionPrinter({ mode: 'call' })
165
- const urlParamsCall = callPrinter.print(urlParamsNode) ?? ''
166
-
167
- const clientParams = createFunctionParams({
168
- config: {
169
- mode: 'object',
170
- children: {
171
- method: {
172
- value: stringify(node.method.toUpperCase()),
173
- },
174
- url: {
175
- value: urlName ? `${urlName}(${urlParamsCall}).url.toString()` : Url.toTemplateString(node.path),
176
- },
177
- baseURL:
178
- baseURL && !urlName
179
- ? {
180
- value: `\`${baseURL}\``,
181
- }
182
- : null,
183
- params: queryParamsName ? (zodQueryParamsName ? { value: 'requestParams' } : queryParamsMapping ? { value: 'mappedParams' } : {}) : null,
184
- data: requestName
185
- ? {
186
- value:
187
- isMultipleContentTypes && hasFormData
188
- ? "contentType === 'multipart/form-data' ? formData as FormData : requestData"
189
- : isFormData
190
- ? 'formData as FormData'
191
- : 'requestData',
192
- }
193
- : null,
194
- contentType: isConfigurable && isMultipleContentTypes ? {} : null,
195
- responseType: responseType ? { value: stringify(responseType) } : null,
196
- requestConfig: isConfigurable
197
- ? {
198
- mode: 'inlineSpread',
199
- }
200
- : null,
201
- headers: headers.length
202
- ? {
203
- value: isConfigurable ? `{ ${headers.join(', ')}, ...requestConfig.headers }` : `{ ${headers.join(', ')} }`,
204
- }
205
- : null,
206
- },
207
- },
208
- })
209
-
210
- const statusUnionType = dataReturnType === 'full' ? buildStatusUnionType(node, tsResolver) : null
211
-
212
- const childrenElement = children ? (
213
- children
214
- ) : (
215
- <>
216
- {dataReturnType === 'full' &&
217
- responseParser === 'zod' &&
218
- zodResponseName &&
219
- statusUnionType &&
220
- `return {...res, data: ${zodResponseName}.parse(res.data)} as ${statusUnionType}`}
221
- {dataReturnType === 'full' && responseParser !== 'zod' && statusUnionType && `return res as ${statusUnionType}`}
222
- {dataReturnType === 'data' && responseParser === 'zod' && zodResponseName && `return ${zodResponseName}.parse(res.data)`}
223
- {dataReturnType === 'data' && responseParser !== 'zod' && 'return res.data'}
224
- </>
225
- )
226
-
227
- return (
228
- <>
229
- <br />
230
-
231
- <File.Source name={name} isExportable={isExportable} isIndexable={isIndexable}>
232
- <Function
233
- name={name}
234
- async
235
- export={isExportable}
236
- params={paramsSignature}
237
- JSDoc={{
238
- comments: buildOperationComments(node, { link: 'urlPath', linkPosition: 'beforeDeprecated', splitLines: true }),
239
- }}
240
- returnType={returnType}
241
- >
242
- {isConfigurable
243
- ? `const { client: request = client, ${isMultipleContentTypes ? `contentType = ${stringify(contentType)}, ` : ''}...requestConfig } = config`
244
- : ''}
245
- <br />
246
- {pathParamsMapping &&
247
- Object.entries(pathParamsMapping)
248
- .filter(([originalName, camelCaseName]) => isValidVarName(originalName) && originalName !== camelCaseName)
249
- .map(([originalName, camelCaseName]) => `const ${originalName} = ${camelCaseName}`)
250
- .join('\n')}
251
- {pathParamsMapping && <br />}
252
- {queryParamsMapping && queryParamsName && (
253
- <>
254
- {`const mappedParams = params ? { ${Object.entries(queryParamsMapping)
255
- .map(([originalName, camelCaseName]) => `"${originalName}": params.${camelCaseName}`)
256
- .join(', ')} } : undefined`}
257
- <br />
258
- </>
259
- )}
260
- {headerParamsMapping && headerParamsName && (
261
- <>
262
- {`const mappedHeaders = headers ? { ${Object.entries(headerParamsMapping)
263
- .map(([originalName, camelCaseName]) => `"${originalName}": headers.${camelCaseName}`)
264
- .join(', ')} } : undefined`}
265
- <br />
266
- </>
267
- )}
268
- {requestParser === 'zod' && zodRequestName ? `const requestData = ${zodRequestName}.parse(data)` : requestName && 'const requestData = data'}
269
- {zodQueryParamsName && `const requestParams = ${zodQueryParamsName}.parse(params)`}
270
- {(isFormData || (isMultipleContentTypes && hasFormData)) && requestName && 'const formData = buildFormData(requestData)'}
271
- <br />
272
- {isConfigurable
273
- ? `const res = await request<${generics.join(', ')}>(${clientParams.toCall()})`
274
- : `const res = await client<${generics.join(', ')}>(${clientParams.toCall()})`}
275
- <br />
276
- {childrenElement}
277
- </Function>
278
- </File.Source>
279
- </>
280
- )
281
- }