@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,153 +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} static async ${name}(${paramsSignature}) {\n${methodBody}\n }`
115
- }
116
-
117
- export function StaticClassClient({
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} {\n static #config: Partial<RequestConfig> & { client?: Client } = {}\n\n${methods.join('\n\n')}\n}`
146
-
147
- return (
148
- <File.Source name={name} isExportable={isExportable} isIndexable={isIndexable}>
149
- {classCode}
150
- {children}
151
- </File.Source>
152
- )
153
- }
@@ -1,90 +0,0 @@
1
- import { buildParamsMapping, getOperationParameters } from '@internals/shared'
2
- import { isValidVarName, Url as UrlHelper } from '@internals/utils'
3
- import { ast } from '@kubb/core'
4
- import type { ResolverTs } from '@kubb/plugin-ts'
5
- import { functionPrinter } from '@kubb/plugin-ts'
6
- import { Const, File, Function } from '@kubb/renderer-jsx'
7
- import type { KubbReactNode } from '@kubb/renderer-jsx/types'
8
- import type { PluginClient } from '../types.ts'
9
-
10
- type Props = {
11
- name: string
12
- isExportable?: boolean
13
- isIndexable?: boolean
14
-
15
- baseURL: string | null | undefined
16
- paramsCasing: PluginClient['resolvedOptions']['paramsCasing']
17
- paramsType: PluginClient['resolvedOptions']['pathParamsType']
18
- pathParamsType: PluginClient['resolvedOptions']['pathParamsType']
19
- node: ast.OperationNode
20
- tsResolver: ResolverTs
21
- }
22
-
23
- type GetParamsProps = {
24
- paramsCasing: PluginClient['resolvedOptions']['paramsCasing']
25
- paramsType: PluginClient['resolvedOptions']['paramsType']
26
- pathParamsType: PluginClient['resolvedOptions']['pathParamsType']
27
- node: ast.OperationNode
28
- tsResolver: ResolverTs
29
- }
30
-
31
- const declarationPrinter = functionPrinter({ mode: 'declaration' })
32
-
33
- export function buildUrlParamsNode({ paramsType, paramsCasing, pathParamsType, node, tsResolver }: GetParamsProps): ast.FunctionParametersNode {
34
- // Build a URL-only node with only path params (no body, query, header)
35
- const urlNode: ast.OperationNode = {
36
- ...node,
37
- parameters: node.parameters.filter((p) => p.in === 'path'),
38
- requestBody: undefined,
39
- }
40
-
41
- return ast.createOperationParams(urlNode, {
42
- paramsType: paramsType === 'object' ? 'object' : 'inline',
43
- pathParamsType: paramsType === 'object' ? 'object' : pathParamsType === 'object' ? 'object' : 'inline',
44
- paramsCasing,
45
- resolver: tsResolver,
46
- })
47
- }
48
-
49
- export function Url({
50
- name,
51
- isExportable = true,
52
- isIndexable = true,
53
- baseURL,
54
- paramsType,
55
- paramsCasing,
56
- pathParamsType,
57
- node,
58
- tsResolver,
59
- }: Props): KubbReactNode {
60
- if (!ast.isHttpOperationNode(node)) return null
61
-
62
- const paramsNode = buildUrlParamsNode({
63
- paramsType,
64
- paramsCasing,
65
- pathParamsType,
66
- node,
67
- tsResolver,
68
- })
69
- const paramsSignature = declarationPrinter.print(paramsNode) ?? ''
70
-
71
- const { path: originalPathParams } = getOperationParameters(node)
72
- const { path: casedPathParams } = getOperationParameters(node, { paramsCasing })
73
- const pathParamsMapping = paramsCasing ? buildParamsMapping(originalPathParams, casedPathParams) : null
74
-
75
- return (
76
- <File.Source name={name} isExportable={isExportable} isIndexable={isIndexable}>
77
- <Function name={name} export={isExportable} params={paramsSignature}>
78
- {pathParamsMapping &&
79
- Object.entries(pathParamsMapping)
80
- .filter(([originalName, camelCaseName]) => isValidVarName(originalName) && originalName !== camelCaseName)
81
- .map(([originalName, camelCaseName]) => `const ${originalName} = ${camelCaseName}`)
82
- .join('\n')}
83
- {pathParamsMapping && Object.keys(pathParamsMapping).length > 0 && <br />}
84
- <Const name={'res'}>{`{ method: '${node.method.toUpperCase()}', url: ${UrlHelper.toTemplateString(node.path, { prefix: baseURL })} as const }`}</Const>
85
- <br />
86
- return res
87
- </Function>
88
- </File.Source>
89
- )
90
- }
@@ -1,33 +0,0 @@
1
- import { File } from '@kubb/renderer-jsx'
2
- import type { KubbReactNode } from '@kubb/renderer-jsx/types'
3
-
4
- type ClientController = {
5
- className: string
6
- propertyName: string
7
- }
8
-
9
- type Props = {
10
- name: string
11
- controllers: Array<ClientController>
12
- isExportable?: boolean
13
- isIndexable?: boolean
14
- }
15
-
16
- export function WrapperClient({ name, controllers, isExportable = true, isIndexable = true }: Props): KubbReactNode {
17
- const properties = controllers.map(({ className, propertyName }) => ` readonly ${propertyName}: ${className}`).join('\n')
18
- const assignments = controllers.map(({ className, propertyName }) => ` this.${propertyName} = new ${className}(config)`).join('\n')
19
-
20
- const classCode = `export class ${name} {
21
- ${properties}
22
-
23
- constructor(config: Partial<RequestConfig> & { client?: Client } = {}) {
24
- ${assignments}
25
- }
26
- }`
27
-
28
- return (
29
- <File.Source name={name} isExportable={isExportable} isIndexable={isIndexable}>
30
- {classCode}
31
- </File.Source>
32
- )
33
- }
@@ -1,118 +0,0 @@
1
- import { ast } from '@kubb/core'
2
- import { functionPrinter } from '@kubb/plugin-ts'
3
-
4
- type ParamLeaf = {
5
- type?: string
6
- optional?: boolean
7
- default?: string
8
- value?: string
9
- mode?: 'inlineSpread'
10
- }
11
-
12
- type ParamGroup = {
13
- mode: 'object' | 'inlineSpread'
14
- children: Record<string, ParamLeaf | null | undefined>
15
- default?: string
16
- }
17
-
18
- type ParamSpec = ParamLeaf | ParamGroup
19
-
20
- const declarationPrinter = functionPrinter({ mode: 'declaration' })
21
- const callPrinter = functionPrinter({ mode: 'call' })
22
-
23
- function isGroup(spec: ParamSpec): spec is ParamGroup {
24
- return 'children' in spec
25
- }
26
-
27
- function createType(type?: string) {
28
- return type ? ast.createParamsType({ variant: 'reference', name: type }) : null
29
- }
30
-
31
- function createDeclarationLeaf(name: string, spec: ParamLeaf): ast.FunctionParameterNode {
32
- if (spec.default !== undefined) {
33
- return ast.createFunctionParameter({
34
- name,
35
- type: createType(spec.type) ?? undefined,
36
- default: spec.default,
37
- rest: spec.mode === 'inlineSpread',
38
- })
39
- }
40
-
41
- return ast.createFunctionParameter({
42
- name,
43
- type: createType(spec.type) ?? undefined,
44
- optional: !!spec.optional,
45
- rest: spec.mode === 'inlineSpread',
46
- })
47
- }
48
-
49
- function createDeclarationParam(name: string, spec: ParamSpec): ast.FunctionParameterNode | ast.ParameterGroupNode {
50
- if (isGroup(spec)) {
51
- return ast.createParameterGroup({
52
- inline: spec.mode === 'inlineSpread',
53
- default: spec.default,
54
- properties: Object.entries(spec.children)
55
- .filter(([, child]) => child != null)
56
- .map(([childName, child]) => createDeclarationLeaf(childName, child!)),
57
- })
58
- }
59
-
60
- return createDeclarationLeaf(name, spec)
61
- }
62
-
63
- function createCallParam(name: string, spec: ParamSpec): ast.FunctionParameterNode | ast.ParameterGroupNode {
64
- if (isGroup(spec)) {
65
- return ast.createParameterGroup({
66
- inline: spec.mode === 'inlineSpread',
67
- properties: Object.entries(spec.children)
68
- .filter(([, child]) => child != null)
69
- .map(([childName, child]) =>
70
- ast.createFunctionParameter({
71
- name:
72
- child?.mode === 'inlineSpread'
73
- ? spec.mode === 'inlineSpread'
74
- ? (child.value ?? childName)
75
- : `...${child.value ?? childName}`
76
- : child?.value
77
- ? `${childName}: ${child.value}`
78
- : childName,
79
- rest: spec.mode === 'inlineSpread' && child?.mode === 'inlineSpread',
80
- }),
81
- ),
82
- })
83
- }
84
-
85
- return ast.createFunctionParameter({
86
- name: spec.value ?? name,
87
- rest: spec.mode === 'inlineSpread',
88
- })
89
- }
90
-
91
- /**
92
- * Creates function parameter builders for generating function signatures and calls.
93
- * Returns utilities to output constructor signatures (`toConstructor()`) or call expressions (`toCall()`).
94
- */
95
- export function createFunctionParams(params: Record<string, ParamSpec | null | undefined>) {
96
- const entries = Object.entries(params).filter(([, spec]) => spec != null) as Array<[string, ParamSpec]>
97
-
98
- return {
99
- toConstructor() {
100
- return (
101
- declarationPrinter.print(
102
- ast.createFunctionParameters({
103
- params: entries.map(([name, spec]) => createDeclarationParam(name, spec)),
104
- }),
105
- ) ?? ''
106
- )
107
- },
108
- toCall() {
109
- return (
110
- callPrinter.print(
111
- ast.createFunctionParameters({
112
- params: entries.map(([name, spec]) => createCallParam(name, spec)),
113
- }),
114
- ) ?? ''
115
- )
116
- },
117
- }
118
- }
@@ -1,261 +0,0 @@
1
- import path from 'node:path'
2
- import { getOperationParameters, operationFileEntry, resolveOperationTypeNames } from '@internals/shared'
3
- import { camelCase } from '@internals/utils'
4
- import { ast, defineGenerator } from '@kubb/core'
5
- import type { ResolverTs } from '@kubb/plugin-ts'
6
- import { pluginTsName } from '@kubb/plugin-ts'
7
- import type { ResolverZod } from '@kubb/plugin-zod'
8
- import { pluginZodName } from '@kubb/plugin-zod'
9
- import { File, jsxRenderer } from '@kubb/renderer-jsx'
10
- import { ClassClient } from '../components/ClassClient'
11
- import { WrapperClient } from '../components/WrapperClient'
12
- import type { PluginClient } from '../types'
13
- import { isParserEnabled, resolveQueryParamsParser, resolveRequestParser, resolveResponseParser } from '../utils.ts'
14
-
15
- type OperationData = {
16
- node: ast.OperationNode
17
- name: string
18
- tsResolver: ResolverTs
19
- zodResolver: ResolverZod | null
20
- typeFile: ast.FileNode
21
- zodFile: ast.FileNode | null
22
- }
23
-
24
- type Controller = {
25
- name: string
26
- propertyName: string
27
- file: ast.FileNode
28
- operations: Array<OperationData>
29
- }
30
-
31
- function resolveTypeImportNames(node: ast.OperationNode, tsResolver: ResolverTs): Array<string> {
32
- return resolveOperationTypeNames(node, tsResolver, { order: 'body-response-first' })
33
- }
34
-
35
- function resolveZodImportNames(node: ast.OperationNode, zodResolver: ResolverZod, parser: PluginClient['resolvedOptions']['parser']): Array<string> {
36
- const { query: queryParams } = getOperationParameters(node)
37
- const names: Array<string | null | undefined> = [
38
- resolveResponseParser(parser) === 'zod' ? zodResolver.resolveResponseName?.(node) : null,
39
- resolveRequestParser(parser) === 'zod' && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null,
40
- resolveQueryParamsParser(parser) === 'zod' && queryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, queryParams[0]!) : null,
41
- ]
42
- return names.filter((n): n is string => Boolean(n))
43
- }
44
-
45
- /**
46
- * Built-in `operations` generator for `@kubb/plugin-client` when
47
- * `clientType: 'class'`. Emits one class per tag, with one instance method
48
- * per operation and a shared constructor for request configuration.
49
- */
50
- export const classClientGenerator = defineGenerator<PluginClient>({
51
- name: 'classClient',
52
- renderer: jsxRenderer,
53
- operations(nodes, ctx) {
54
- const { config, driver, resolver, root } = ctx
55
- const { output, group, dataReturnType, paramsCasing, paramsType, pathParamsType, parser, importPath, sdk } = ctx.options
56
- const baseURL = ctx.options.baseURL ?? ctx.meta.baseURL
57
-
58
- const pluginTs = driver.getPlugin(pluginTsName)
59
- if (!pluginTs) return null
60
-
61
- const tsResolver = driver.getResolver(pluginTsName)
62
- const tsPluginOptions = pluginTs.options
63
- const pluginZod = isParserEnabled(parser) ? driver.getPlugin(pluginZodName) : null
64
- const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null
65
-
66
- function buildOperationData(node: ast.OperationNode): OperationData {
67
- const typeFile = tsResolver.resolveFile(operationFileEntry(node, node.operationId), {
68
- root,
69
- output: tsPluginOptions?.output ?? output,
70
- group: tsPluginOptions?.group,
71
- })
72
- const zodFile =
73
- zodResolver && pluginZod?.options
74
- ? zodResolver.resolveFile(operationFileEntry(node, node.operationId), {
75
- root,
76
- output: pluginZod.options?.output ?? output,
77
- group: pluginZod.options?.group ?? undefined,
78
- })
79
- : null
80
-
81
- return {
82
- node: node,
83
- name: resolver.resolveName(node.operationId),
84
- tsResolver,
85
- zodResolver,
86
- typeFile,
87
- zodFile,
88
- }
89
- }
90
-
91
- const controllers = nodes.reduce((acc, operationNode) => {
92
- if (!ast.isHttpOperationNode(operationNode)) return acc
93
- const tag = operationNode.tags[0]
94
- const groupName = tag ? (group?.name?.({ group: camelCase(tag) }) ?? resolver.resolveGroupName(tag)) : resolver.resolveClassName('Client')
95
-
96
- if (!tag && !group) {
97
- const name = resolver.resolveClassName('ApiClient')
98
- const file = resolver.resolveFile({ name, extname: '.ts' }, { root, output, group: group ?? undefined })
99
- const operationData = buildOperationData(operationNode)
100
- const previous = acc.find((item) => item.file.path === file.path)
101
-
102
- if (previous) {
103
- previous.operations.push(operationData)
104
- } else {
105
- acc.push({ name, propertyName: resolver.resolveClientPropertyName(name), file, operations: [operationData] })
106
- }
107
- return acc
108
- }
109
-
110
- if (tag) {
111
- const name = groupName
112
- const file = resolver.resolveFile({ name, extname: '.ts', tag }, { root, output, group: group ?? undefined })
113
- const operationData = buildOperationData(operationNode)
114
- const previous = acc.find((item) => item.file.path === file.path)
115
-
116
- if (previous) {
117
- previous.operations.push(operationData)
118
- } else {
119
- acc.push({ name, propertyName: resolver.resolveClientPropertyName(name), file, operations: [operationData] })
120
- }
121
- }
122
-
123
- return acc
124
- }, [] as Array<Controller>)
125
-
126
- function collectTypeImports(ops: Array<OperationData>) {
127
- const typeImportsByFile = new Map<string, Set<string>>()
128
- const typeFilesByPath = new Map<string, ast.FileNode>()
129
-
130
- ops.forEach((op) => {
131
- const names = resolveTypeImportNames(op.node, tsResolver)
132
- if (!typeImportsByFile.has(op.typeFile.path)) {
133
- typeImportsByFile.set(op.typeFile.path, new Set())
134
- }
135
- const imports = typeImportsByFile.get(op.typeFile.path)!
136
- names.forEach((n) => {
137
- imports.add(n)
138
- })
139
- typeFilesByPath.set(op.typeFile.path, op.typeFile)
140
- })
141
-
142
- return { typeImportsByFile, typeFilesByPath }
143
- }
144
-
145
- function collectZodImports(ops: Array<OperationData>) {
146
- const zodImportsByFile = new Map<string, Set<string>>()
147
- const zodFilesByPath = new Map<string, ast.FileNode>()
148
-
149
- ops.forEach((op) => {
150
- if (!op.zodFile || !zodResolver) return
151
- const names = resolveZodImportNames(op.node, zodResolver, parser)
152
- if (!zodImportsByFile.has(op.zodFile.path)) {
153
- zodImportsByFile.set(op.zodFile.path, new Set())
154
- }
155
- const imports = zodImportsByFile.get(op.zodFile.path)!
156
- names.forEach((n) => {
157
- imports.add(n)
158
- })
159
- zodFilesByPath.set(op.zodFile.path, op.zodFile)
160
- })
161
-
162
- return { zodImportsByFile, zodFilesByPath }
163
- }
164
-
165
- const files = controllers.map(({ name, file, operations: ops }) => {
166
- const { typeImportsByFile, typeFilesByPath } = collectTypeImports(ops)
167
- const { zodImportsByFile, zodFilesByPath } = isParserEnabled(parser)
168
- ? collectZodImports(ops)
169
- : { zodImportsByFile: new Map<string, Set<string>>(), zodFilesByPath: new Map<string, ast.FileNode>() }
170
- const hasFormData = ops.some((op) => op.node.requestBody?.content?.some((e) => e.contentType === 'multipart/form-data') ?? false)
171
-
172
- return (
173
- <File
174
- key={file.path}
175
- baseName={file.baseName}
176
- path={file.path}
177
- meta={file.meta}
178
- banner={resolver.resolveBanner(ctx.meta, { output, config, file: { path: file.path, baseName: file.baseName } })}
179
- footer={resolver.resolveFooter(ctx.meta, { output, config, file: { path: file.path, baseName: file.baseName } })}
180
- >
181
- {importPath ? (
182
- <>
183
- <File.Import name={'client'} path={importPath} />
184
- <File.Import name={['mergeConfig']} path={importPath} />
185
- <File.Import name={['Client', 'RequestConfig', 'ResponseErrorConfig']} path={importPath} isTypeOnly />
186
- </>
187
- ) : (
188
- <>
189
- <File.Import name={['client']} root={file.path} path={path.resolve(root, '.kubb/client.ts')} />
190
- <File.Import name={['mergeConfig']} root={file.path} path={path.resolve(root, '.kubb/client.ts')} />
191
- <File.Import name={['Client', 'RequestConfig', 'ResponseErrorConfig']} root={file.path} path={path.resolve(root, '.kubb/client.ts')} isTypeOnly />
192
- </>
193
- )}
194
-
195
- {hasFormData && <File.Import name={['buildFormData']} root={file.path} path={path.resolve(root, '.kubb/config.ts')} />}
196
-
197
- {parser === 'zod' && zodResolver && ops.some((op) => op.node.requestBody?.content?.[0]?.schema != null) && (
198
- <File.Import name={['z']} path="zod" isTypeOnly />
199
- )}
200
-
201
- {Array.from(typeImportsByFile.entries()).map(([filePath, importSet]) => {
202
- const typeFile = typeFilesByPath.get(filePath)
203
- if (!typeFile) return null
204
- const importNames = Array.from(importSet).filter(Boolean)
205
- if (importNames.length === 0) return null
206
- return <File.Import key={filePath} name={importNames} root={file.path} path={typeFile.path} isTypeOnly />
207
- })}
208
-
209
- {isParserEnabled(parser) &&
210
- Array.from(zodImportsByFile.entries()).map(([filePath, importSet]) => {
211
- const zodFile = zodFilesByPath.get(filePath)
212
- if (!zodFile) return null
213
- const importNames = Array.from(importSet).filter(Boolean)
214
- if (importNames.length === 0) return null
215
- return <File.Import key={filePath} name={importNames} root={file.path} path={zodFile.path} />
216
- })}
217
-
218
- <ClassClient
219
- name={name}
220
- operations={ops}
221
- baseURL={baseURL}
222
- dataReturnType={dataReturnType}
223
- pathParamsType={pathParamsType}
224
- paramsCasing={paramsCasing}
225
- paramsType={paramsType}
226
- parser={parser}
227
- />
228
- </File>
229
- )
230
- })
231
-
232
- if (sdk) {
233
- const sdkFile = resolver.resolveFile({ name: sdk.className, extname: '.ts' }, { root, output, group: group ?? undefined })
234
-
235
- files.push(
236
- <File
237
- key={sdkFile.path}
238
- baseName={sdkFile.baseName}
239
- path={sdkFile.path}
240
- meta={sdkFile.meta}
241
- banner={resolver.resolveBanner(ctx.meta, { output, config, file: { path: sdkFile.path, baseName: sdkFile.baseName } })}
242
- footer={resolver.resolveFooter(ctx.meta, { output, config, file: { path: sdkFile.path, baseName: sdkFile.baseName } })}
243
- >
244
- {importPath ? (
245
- <File.Import name={['Client', 'RequestConfig']} path={importPath} isTypeOnly />
246
- ) : (
247
- <File.Import name={['Client', 'RequestConfig']} root={sdkFile.path} path={path.resolve(root, '.kubb/client.ts')} isTypeOnly />
248
- )}
249
-
250
- {controllers.map(({ name, file }) => (
251
- <File.Import key={name} name={[name]} root={sdkFile.path} path={file.path} />
252
- ))}
253
-
254
- <WrapperClient name={sdk.className} controllers={controllers.map(({ name, propertyName }) => ({ className: name, propertyName }))} />
255
- </File>,
256
- )
257
- }
258
-
259
- return <>{files}</>
260
- },
261
- })