@kubb/plugin-client 5.0.0-beta.15 → 5.0.0-beta.25
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.
- package/dist/clients/fetch.cjs.map +1 -1
- package/dist/clients/fetch.d.ts +1 -1
- package/dist/clients/fetch.js.map +1 -1
- package/dist/index.cjs +165 -113
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +126 -66
- package/dist/index.js +165 -113
- package/dist/index.js.map +1 -1
- package/dist/templates/clients/fetch.source.cjs +1 -1
- package/dist/templates/clients/fetch.source.js +1 -1
- package/extension.yaml +774 -260
- package/package.json +6 -6
- package/src/clients/fetch.ts +1 -1
- package/src/components/ClassClient.tsx +5 -5
- package/src/components/Client.tsx +18 -18
- package/src/components/StaticClassClient.tsx +5 -5
- package/src/components/Url.tsx +2 -2
- package/src/functionParams.ts +8 -8
- package/src/generators/classClientGenerator.tsx +26 -18
- package/src/generators/clientGenerator.tsx +20 -12
- package/src/generators/groupedClientGenerator.tsx +12 -6
- package/src/generators/operationsGenerator.tsx +10 -4
- package/src/generators/staticClassClientGenerator.tsx +24 -15
- package/src/plugin.ts +24 -11
- package/src/resolvers/resolverClient.ts +10 -4
- package/src/types.ts +66 -53
- package/src/utils.ts +15 -15
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kubb/plugin-client",
|
|
3
|
-
"version": "5.0.0-beta.
|
|
3
|
+
"version": "5.0.0-beta.25",
|
|
4
4
|
"description": "Generate type-safe HTTP clients from your OpenAPI specification. Supports Axios, Fetch, and custom client adapters with full TypeScript inference and zero boilerplate.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"api-client",
|
|
@@ -87,10 +87,10 @@
|
|
|
87
87
|
"registry": "https://registry.npmjs.org/"
|
|
88
88
|
},
|
|
89
89
|
"dependencies": {
|
|
90
|
-
"@kubb/core": "5.0.0-beta.
|
|
91
|
-
"@kubb/renderer-jsx": "5.0.0-beta.
|
|
92
|
-
"@kubb/plugin-ts": "5.0.0-beta.
|
|
93
|
-
"@kubb/plugin-zod": "5.0.0-beta.
|
|
90
|
+
"@kubb/core": "5.0.0-beta.25",
|
|
91
|
+
"@kubb/renderer-jsx": "5.0.0-beta.25",
|
|
92
|
+
"@kubb/plugin-ts": "5.0.0-beta.25",
|
|
93
|
+
"@kubb/plugin-zod": "5.0.0-beta.25"
|
|
94
94
|
},
|
|
95
95
|
"devDependencies": {
|
|
96
96
|
"axios": "^1.16.1",
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
"@internals/utils": "0.0.0"
|
|
99
99
|
},
|
|
100
100
|
"peerDependencies": {
|
|
101
|
-
"@kubb/renderer-jsx": "5.0.0-beta.
|
|
101
|
+
"@kubb/renderer-jsx": "5.0.0-beta.25",
|
|
102
102
|
"axios": "^1.7.2"
|
|
103
103
|
},
|
|
104
104
|
"peerDependenciesMeta": {
|
package/src/clients/fetch.ts
CHANGED
|
@@ -14,7 +14,7 @@ export type RequestConfig<TData = unknown> = {
|
|
|
14
14
|
data?: TData | FormData
|
|
15
15
|
responseType?: 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream'
|
|
16
16
|
signal?: AbortSignal
|
|
17
|
-
headers?: [string, string]
|
|
17
|
+
headers?: Array<[string, string]> | Record<string, string>
|
|
18
18
|
credentials?: RequestCredentials
|
|
19
19
|
contentType?: string
|
|
20
20
|
}
|
|
@@ -14,7 +14,7 @@ type OperationData = {
|
|
|
14
14
|
node: ast.OperationNode
|
|
15
15
|
name: string
|
|
16
16
|
tsResolver: ResolverTs
|
|
17
|
-
zodResolver?: ResolverZod
|
|
17
|
+
zodResolver?: ResolverZod | null
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
type Props = {
|
|
@@ -22,7 +22,7 @@ type Props = {
|
|
|
22
22
|
isExportable?: boolean
|
|
23
23
|
isIndexable?: boolean
|
|
24
24
|
operations: Array<OperationData>
|
|
25
|
-
baseURL: string | undefined
|
|
25
|
+
baseURL: string | null | undefined
|
|
26
26
|
dataReturnType: PluginClient['resolvedOptions']['dataReturnType']
|
|
27
27
|
paramsCasing: PluginClient['resolvedOptions']['paramsCasing']
|
|
28
28
|
paramsType: PluginClient['resolvedOptions']['pathParamsType']
|
|
@@ -35,8 +35,8 @@ type GenerateMethodProps = {
|
|
|
35
35
|
node: ast.OperationNode
|
|
36
36
|
name: string
|
|
37
37
|
tsResolver: ResolverTs
|
|
38
|
-
zodResolver?: ResolverZod
|
|
39
|
-
baseURL: string | undefined
|
|
38
|
+
zodResolver?: ResolverZod | null
|
|
39
|
+
baseURL: string | null | undefined
|
|
40
40
|
dataReturnType: PluginClient['resolvedOptions']['dataReturnType']
|
|
41
41
|
parser: PluginClient['resolvedOptions']['parser'] | undefined
|
|
42
42
|
paramsType: PluginClient['resolvedOptions']['paramsType']
|
|
@@ -62,7 +62,7 @@ function generateMethod({
|
|
|
62
62
|
const { defaultContentType: contentType, isMultipleContentTypes, hasFormData } = getContentTypeInfo(node)
|
|
63
63
|
const isFormData = !isMultipleContentTypes && contentType === 'multipart/form-data'
|
|
64
64
|
const { header: headerParams } = getOperationParameters(node)
|
|
65
|
-
const headerParamsName = headerParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, headerParams[0]!) :
|
|
65
|
+
const headerParamsName = headerParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, headerParams[0]!) : null
|
|
66
66
|
const headers = isMultipleContentTypes ? (headerParamsName ? ['...headers'] : []) : buildHeaders(contentType, !!headerParamsName)
|
|
67
67
|
const generics = buildGenerics(node, tsResolver)
|
|
68
68
|
const paramsNode = buildClientParamsNode({ paramsType, paramsCasing, pathParamsType, node, tsResolver, isConfigurable: true })
|
|
@@ -18,7 +18,7 @@ type Props = {
|
|
|
18
18
|
isConfigurable?: boolean
|
|
19
19
|
returnType?: string
|
|
20
20
|
|
|
21
|
-
baseURL: string | undefined
|
|
21
|
+
baseURL: string | null | undefined
|
|
22
22
|
dataReturnType: PluginClient['resolvedOptions']['dataReturnType']
|
|
23
23
|
paramsCasing: PluginClient['resolvedOptions']['paramsCasing']
|
|
24
24
|
paramsType: PluginClient['resolvedOptions']['pathParamsType']
|
|
@@ -26,7 +26,7 @@ type Props = {
|
|
|
26
26
|
parser: PluginClient['resolvedOptions']['parser'] | undefined
|
|
27
27
|
node: ast.OperationNode
|
|
28
28
|
tsResolver: ResolverTs
|
|
29
|
-
zodResolver?: ResolverZod
|
|
29
|
+
zodResolver?: ResolverZod | null
|
|
30
30
|
children?: KubbReactNode
|
|
31
31
|
}
|
|
32
32
|
|
|
@@ -96,17 +96,17 @@ export function Client({
|
|
|
96
96
|
const { path: originalPathParams, query: originalQueryParams, header: originalHeaderParams } = getOperationParameters(node)
|
|
97
97
|
const { path: casedPathParams, query: casedQueryParams, header: casedHeaderParams } = getOperationParameters(node, { paramsCasing })
|
|
98
98
|
|
|
99
|
-
const pathParamsMapping = paramsCasing && !urlName ? buildParamsMapping(originalPathParams, casedPathParams) :
|
|
100
|
-
const queryParamsMapping = paramsCasing ? buildParamsMapping(originalQueryParams, casedQueryParams) :
|
|
101
|
-
const headerParamsMapping = paramsCasing ? buildParamsMapping(originalHeaderParams, casedHeaderParams) :
|
|
99
|
+
const pathParamsMapping = paramsCasing && !urlName ? buildParamsMapping(originalPathParams, casedPathParams) : null
|
|
100
|
+
const queryParamsMapping = paramsCasing ? buildParamsMapping(originalQueryParams, casedQueryParams) : null
|
|
101
|
+
const headerParamsMapping = paramsCasing ? buildParamsMapping(originalHeaderParams, casedHeaderParams) : null
|
|
102
102
|
|
|
103
|
-
const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) :
|
|
103
|
+
const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : null
|
|
104
104
|
const responseName = tsResolver.resolveResponseName(node)
|
|
105
|
-
const queryParamsName = originalQueryParams.length > 0 ? tsResolver.resolveQueryParamsName(node, originalQueryParams[0]!) :
|
|
106
|
-
const headerParamsName = originalHeaderParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, originalHeaderParams[0]!) :
|
|
105
|
+
const queryParamsName = originalQueryParams.length > 0 ? tsResolver.resolveQueryParamsName(node, originalQueryParams[0]!) : null
|
|
106
|
+
const headerParamsName = originalHeaderParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, originalHeaderParams[0]!) : null
|
|
107
107
|
|
|
108
|
-
const zodResponseName = zodResolver && parser === 'zod' ? zodResolver.resolveResponseName?.(node) :
|
|
109
|
-
const zodRequestName = zodResolver && parser === 'zod' && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) :
|
|
108
|
+
const zodResponseName = zodResolver && parser === 'zod' ? zodResolver.resolveResponseName?.(node) : null
|
|
109
|
+
const zodRequestName = zodResolver && parser === 'zod' && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null
|
|
110
110
|
|
|
111
111
|
const errorNames = node.responses
|
|
112
112
|
.filter((r) => {
|
|
@@ -116,8 +116,8 @@ export function Client({
|
|
|
116
116
|
.map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode))
|
|
117
117
|
|
|
118
118
|
const headers = [
|
|
119
|
-
!isMultipleContentTypes && contentType !== 'application/json' && contentType !== 'multipart/form-data' ? `'Content-Type': '${contentType}'` :
|
|
120
|
-
headerParamsName ? (headerParamsMapping ? '...mappedHeaders' : '...headers') :
|
|
119
|
+
!isMultipleContentTypes && contentType !== 'application/json' && contentType !== 'multipart/form-data' ? `'Content-Type': '${contentType}'` : null,
|
|
120
|
+
headerParamsName ? (headerParamsMapping ? '...mappedHeaders' : '...headers') : null,
|
|
121
121
|
].filter(Boolean)
|
|
122
122
|
|
|
123
123
|
const TError = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(' | ') : 'Error'}>`
|
|
@@ -158,8 +158,8 @@ export function Client({
|
|
|
158
158
|
? {
|
|
159
159
|
value: `\`${baseURL}\``,
|
|
160
160
|
}
|
|
161
|
-
:
|
|
162
|
-
params: queryParamsName ? (queryParamsMapping ? { value: 'mappedParams' } : {}) :
|
|
161
|
+
: null,
|
|
162
|
+
params: queryParamsName ? (queryParamsMapping ? { value: 'mappedParams' } : {}) : null,
|
|
163
163
|
data: requestName
|
|
164
164
|
? {
|
|
165
165
|
value:
|
|
@@ -169,18 +169,18 @@ export function Client({
|
|
|
169
169
|
? 'formData as FormData'
|
|
170
170
|
: 'requestData',
|
|
171
171
|
}
|
|
172
|
-
:
|
|
173
|
-
contentType: isConfigurable && isMultipleContentTypes ? {} :
|
|
172
|
+
: null,
|
|
173
|
+
contentType: isConfigurable && isMultipleContentTypes ? {} : null,
|
|
174
174
|
requestConfig: isConfigurable
|
|
175
175
|
? {
|
|
176
176
|
mode: 'inlineSpread',
|
|
177
177
|
}
|
|
178
|
-
:
|
|
178
|
+
: null,
|
|
179
179
|
headers: headers.length
|
|
180
180
|
? {
|
|
181
181
|
value: isConfigurable ? `{ ${headers.join(', ')}, ...requestConfig.headers }` : `{ ${headers.join(', ')} }`,
|
|
182
182
|
}
|
|
183
|
-
:
|
|
183
|
+
: null,
|
|
184
184
|
},
|
|
185
185
|
},
|
|
186
186
|
})
|
|
@@ -14,7 +14,7 @@ type OperationData = {
|
|
|
14
14
|
node: ast.OperationNode
|
|
15
15
|
name: string
|
|
16
16
|
tsResolver: ResolverTs
|
|
17
|
-
zodResolver?: ResolverZod
|
|
17
|
+
zodResolver?: ResolverZod | null
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
type Props = {
|
|
@@ -22,7 +22,7 @@ type Props = {
|
|
|
22
22
|
isExportable?: boolean
|
|
23
23
|
isIndexable?: boolean
|
|
24
24
|
operations: Array<OperationData>
|
|
25
|
-
baseURL: string | undefined
|
|
25
|
+
baseURL: string | null | undefined
|
|
26
26
|
dataReturnType: PluginClient['resolvedOptions']['dataReturnType']
|
|
27
27
|
paramsCasing: PluginClient['resolvedOptions']['paramsCasing']
|
|
28
28
|
paramsType: PluginClient['resolvedOptions']['pathParamsType']
|
|
@@ -35,8 +35,8 @@ type GenerateMethodProps = {
|
|
|
35
35
|
node: ast.OperationNode
|
|
36
36
|
name: string
|
|
37
37
|
tsResolver: ResolverTs
|
|
38
|
-
zodResolver?: ResolverZod
|
|
39
|
-
baseURL: string | undefined
|
|
38
|
+
zodResolver?: ResolverZod | null
|
|
39
|
+
baseURL: string | null | undefined
|
|
40
40
|
dataReturnType: PluginClient['resolvedOptions']['dataReturnType']
|
|
41
41
|
parser: PluginClient['resolvedOptions']['parser'] | undefined
|
|
42
42
|
paramsType: PluginClient['resolvedOptions']['paramsType']
|
|
@@ -62,7 +62,7 @@ function generateMethod({
|
|
|
62
62
|
const { defaultContentType: contentType, isMultipleContentTypes, hasFormData } = getContentTypeInfo(node)
|
|
63
63
|
const isFormData = !isMultipleContentTypes && contentType === 'multipart/form-data'
|
|
64
64
|
const { header: headerParams } = getOperationParameters(node)
|
|
65
|
-
const headerParamsName = headerParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, headerParams[0]!) :
|
|
65
|
+
const headerParamsName = headerParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, headerParams[0]!) : null
|
|
66
66
|
const headers = isMultipleContentTypes ? (headerParamsName ? ['...headers'] : []) : buildHeaders(contentType, !!headerParamsName)
|
|
67
67
|
const generics = buildGenerics(node, tsResolver)
|
|
68
68
|
const paramsNode = buildClientParamsNode({ paramsType, paramsCasing, pathParamsType, node, tsResolver, isConfigurable: true })
|
package/src/components/Url.tsx
CHANGED
|
@@ -12,7 +12,7 @@ type Props = {
|
|
|
12
12
|
isExportable?: boolean
|
|
13
13
|
isIndexable?: boolean
|
|
14
14
|
|
|
15
|
-
baseURL: string | undefined
|
|
15
|
+
baseURL: string | null | undefined
|
|
16
16
|
paramsCasing: PluginClient['resolvedOptions']['paramsCasing']
|
|
17
17
|
paramsType: PluginClient['resolvedOptions']['pathParamsType']
|
|
18
18
|
pathParamsType: PluginClient['resolvedOptions']['pathParamsType']
|
|
@@ -70,7 +70,7 @@ export function Url({
|
|
|
70
70
|
|
|
71
71
|
const { path: originalPathParams } = getOperationParameters(node)
|
|
72
72
|
const { path: casedPathParams } = getOperationParameters(node, { paramsCasing })
|
|
73
|
-
const pathParamsMapping = paramsCasing ? buildParamsMapping(originalPathParams, casedPathParams) :
|
|
73
|
+
const pathParamsMapping = paramsCasing ? buildParamsMapping(originalPathParams, casedPathParams) : null
|
|
74
74
|
|
|
75
75
|
return (
|
|
76
76
|
<File.Source name={name} isExportable={isExportable} isIndexable={isIndexable}>
|
package/src/functionParams.ts
CHANGED
|
@@ -11,7 +11,7 @@ type ParamLeaf = {
|
|
|
11
11
|
|
|
12
12
|
type ParamGroup = {
|
|
13
13
|
mode: 'object' | 'inlineSpread'
|
|
14
|
-
children: Record<string, ParamLeaf | undefined>
|
|
14
|
+
children: Record<string, ParamLeaf | null | undefined>
|
|
15
15
|
default?: string
|
|
16
16
|
}
|
|
17
17
|
|
|
@@ -25,14 +25,14 @@ function isGroup(spec: ParamSpec): spec is ParamGroup {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
function createType(type?: string) {
|
|
28
|
-
return type ? ast.createParamsType({ variant: 'reference', name: type }) :
|
|
28
|
+
return type ? ast.createParamsType({ variant: 'reference', name: type }) : null
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
function createDeclarationLeaf(name: string, spec: ParamLeaf): ast.FunctionParameterNode {
|
|
32
32
|
if (spec.default !== undefined) {
|
|
33
33
|
return ast.createFunctionParameter({
|
|
34
34
|
name,
|
|
35
|
-
type: createType(spec.type),
|
|
35
|
+
type: createType(spec.type) ?? undefined,
|
|
36
36
|
default: spec.default,
|
|
37
37
|
rest: spec.mode === 'inlineSpread',
|
|
38
38
|
})
|
|
@@ -40,7 +40,7 @@ function createDeclarationLeaf(name: string, spec: ParamLeaf): ast.FunctionParam
|
|
|
40
40
|
|
|
41
41
|
return ast.createFunctionParameter({
|
|
42
42
|
name,
|
|
43
|
-
type: createType(spec.type),
|
|
43
|
+
type: createType(spec.type) ?? undefined,
|
|
44
44
|
optional: !!spec.optional,
|
|
45
45
|
rest: spec.mode === 'inlineSpread',
|
|
46
46
|
})
|
|
@@ -52,7 +52,7 @@ function createDeclarationParam(name: string, spec: ParamSpec): ast.FunctionPara
|
|
|
52
52
|
inline: spec.mode === 'inlineSpread',
|
|
53
53
|
default: spec.default,
|
|
54
54
|
properties: Object.entries(spec.children)
|
|
55
|
-
.filter(([, child]) => child
|
|
55
|
+
.filter(([, child]) => child != null)
|
|
56
56
|
.map(([childName, child]) => createDeclarationLeaf(childName, child!)),
|
|
57
57
|
})
|
|
58
58
|
}
|
|
@@ -65,7 +65,7 @@ function createCallParam(name: string, spec: ParamSpec): ast.FunctionParameterNo
|
|
|
65
65
|
return ast.createParameterGroup({
|
|
66
66
|
inline: spec.mode === 'inlineSpread',
|
|
67
67
|
properties: Object.entries(spec.children)
|
|
68
|
-
.filter(([, child]) => child
|
|
68
|
+
.filter(([, child]) => child != null)
|
|
69
69
|
.map(([childName, child]) =>
|
|
70
70
|
ast.createFunctionParameter({
|
|
71
71
|
name:
|
|
@@ -92,8 +92,8 @@ function createCallParam(name: string, spec: ParamSpec): ast.FunctionParameterNo
|
|
|
92
92
|
* Creates function parameter builders for generating function signatures and calls.
|
|
93
93
|
* Returns utilities to output constructor signatures (`toConstructor()`) or call expressions (`toCall()`).
|
|
94
94
|
*/
|
|
95
|
-
export function createFunctionParams(params: Record<string, ParamSpec | undefined>) {
|
|
96
|
-
const entries = Object.entries(params).filter(([, spec]) => spec
|
|
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
97
|
|
|
98
98
|
return {
|
|
99
99
|
toConstructor() {
|
|
@@ -16,9 +16,9 @@ type OperationData = {
|
|
|
16
16
|
node: ast.OperationNode
|
|
17
17
|
name: string
|
|
18
18
|
tsResolver: ResolverTs
|
|
19
|
-
zodResolver: ResolverZod |
|
|
19
|
+
zodResolver: ResolverZod | null
|
|
20
20
|
typeFile: ast.FileNode
|
|
21
|
-
zodFile: ast.FileNode |
|
|
21
|
+
zodFile: ast.FileNode | null
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
type Controller = {
|
|
@@ -33,28 +33,33 @@ function resolveTypeImportNames(node: ast.OperationNode, tsResolver: ResolverTs)
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
function resolveZodImportNames(node: ast.OperationNode, zodResolver: ResolverZod): Array<string> {
|
|
36
|
-
const names: Array<string | undefined> = [
|
|
36
|
+
const names: Array<string | null | undefined> = [
|
|
37
37
|
zodResolver.resolveResponseName?.(node),
|
|
38
|
-
node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) :
|
|
38
|
+
node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null,
|
|
39
39
|
]
|
|
40
40
|
return names.filter((n): n is string => Boolean(n))
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Built-in `operations` generator for `@kubb/plugin-client` when
|
|
45
|
+
* `clientType: 'class'`. Emits one class per tag, with one instance method
|
|
46
|
+
* per operation and a shared constructor for request configuration.
|
|
47
|
+
*/
|
|
43
48
|
export const classClientGenerator = defineGenerator<PluginClient>({
|
|
44
49
|
name: 'classClient',
|
|
45
50
|
renderer: jsxRendererSync,
|
|
46
51
|
operations(nodes, ctx) {
|
|
47
|
-
const { config, driver, resolver, root
|
|
52
|
+
const { config, driver, resolver, root } = ctx
|
|
48
53
|
const { output, group, dataReturnType, paramsCasing, paramsType, pathParamsType, parser, importPath, sdk } = ctx.options
|
|
49
|
-
const baseURL = ctx.options.baseURL ??
|
|
54
|
+
const baseURL = ctx.options.baseURL ?? ctx.meta.baseURL
|
|
50
55
|
|
|
51
56
|
const pluginTs = driver.getPlugin(pluginTsName)
|
|
52
57
|
if (!pluginTs) return null
|
|
53
58
|
|
|
54
59
|
const tsResolver = driver.getResolver(pluginTsName)
|
|
55
60
|
const tsPluginOptions = pluginTs.options
|
|
56
|
-
const pluginZod = parser === 'zod' ? driver.getPlugin(pluginZodName) :
|
|
57
|
-
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) :
|
|
61
|
+
const pluginZod = parser === 'zod' ? driver.getPlugin(pluginZodName) : null
|
|
62
|
+
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null
|
|
58
63
|
|
|
59
64
|
function buildOperationData(node: ast.OperationNode): OperationData {
|
|
60
65
|
const typeFile = tsResolver.resolveFile(
|
|
@@ -65,9 +70,9 @@ export const classClientGenerator = defineGenerator<PluginClient>({
|
|
|
65
70
|
zodResolver && pluginZod?.options
|
|
66
71
|
? zodResolver.resolveFile(
|
|
67
72
|
{ name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
|
|
68
|
-
{ root, output: pluginZod.options?.output ?? output, group: pluginZod.options?.group },
|
|
73
|
+
{ root, output: pluginZod.options?.output ?? output, group: pluginZod.options?.group ?? undefined },
|
|
69
74
|
)
|
|
70
|
-
:
|
|
75
|
+
: null
|
|
71
76
|
|
|
72
77
|
return {
|
|
73
78
|
node: node,
|
|
@@ -85,7 +90,7 @@ export const classClientGenerator = defineGenerator<PluginClient>({
|
|
|
85
90
|
|
|
86
91
|
if (!tag && !group) {
|
|
87
92
|
const name = resolver.resolveClassName('ApiClient')
|
|
88
|
-
const file = resolver.resolveFile({ name, extname: '.ts' }, { root, output, group })
|
|
93
|
+
const file = resolver.resolveFile({ name, extname: '.ts' }, { root, output, group: group ?? undefined })
|
|
89
94
|
const operationData = buildOperationData(operationNode)
|
|
90
95
|
const previous = acc.find((item) => item.file.path === file.path)
|
|
91
96
|
|
|
@@ -94,9 +99,12 @@ export const classClientGenerator = defineGenerator<PluginClient>({
|
|
|
94
99
|
} else {
|
|
95
100
|
acc.push({ name, propertyName: resolver.resolveClientPropertyName(name), file, operations: [operationData] })
|
|
96
101
|
}
|
|
97
|
-
|
|
102
|
+
return acc
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (tag) {
|
|
98
106
|
const name = groupName
|
|
99
|
-
const file = resolver.resolveFile({ name, extname: '.ts', tag }, { root, output, group })
|
|
107
|
+
const file = resolver.resolveFile({ name, extname: '.ts', tag }, { root, output, group: group ?? undefined })
|
|
100
108
|
const operationData = buildOperationData(operationNode)
|
|
101
109
|
const previous = acc.find((item) => item.file.path === file.path)
|
|
102
110
|
|
|
@@ -161,8 +169,8 @@ export const classClientGenerator = defineGenerator<PluginClient>({
|
|
|
161
169
|
baseName={file.baseName}
|
|
162
170
|
path={file.path}
|
|
163
171
|
meta={file.meta}
|
|
164
|
-
banner={resolver.resolveBanner(
|
|
165
|
-
footer={resolver.resolveFooter(
|
|
172
|
+
banner={resolver.resolveBanner(ctx.meta, { output, config })}
|
|
173
|
+
footer={resolver.resolveFooter(ctx.meta, { output, config })}
|
|
166
174
|
>
|
|
167
175
|
{importPath ? (
|
|
168
176
|
<>
|
|
@@ -212,7 +220,7 @@ export const classClientGenerator = defineGenerator<PluginClient>({
|
|
|
212
220
|
})
|
|
213
221
|
|
|
214
222
|
if (sdk) {
|
|
215
|
-
const sdkFile = resolver.resolveFile({ name: sdk.className, extname: '.ts' }, { root, output, group })
|
|
223
|
+
const sdkFile = resolver.resolveFile({ name: sdk.className, extname: '.ts' }, { root, output, group: group ?? undefined })
|
|
216
224
|
|
|
217
225
|
files.push(
|
|
218
226
|
<File
|
|
@@ -220,8 +228,8 @@ export const classClientGenerator = defineGenerator<PluginClient>({
|
|
|
220
228
|
baseName={sdkFile.baseName}
|
|
221
229
|
path={sdkFile.path}
|
|
222
230
|
meta={sdkFile.meta}
|
|
223
|
-
banner={resolver.resolveBanner(
|
|
224
|
-
footer={resolver.resolveFooter(
|
|
231
|
+
banner={resolver.resolveBanner(ctx.meta, { output, config })}
|
|
232
|
+
footer={resolver.resolveFooter(ctx.meta, { output, config })}
|
|
225
233
|
>
|
|
226
234
|
{importPath ? (
|
|
227
235
|
<File.Import name={['Client', 'RequestConfig']} path={importPath} isTypeOnly />
|
|
@@ -8,13 +8,18 @@ import { Client } from '../components/Client'
|
|
|
8
8
|
import { Url } from '../components/Url.tsx'
|
|
9
9
|
import type { PluginClient } from '../types'
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Built-in operation generator for `@kubb/plugin-client`. Emits one async
|
|
13
|
+
* function per OpenAPI operation, plus the matching URL helper. Used when
|
|
14
|
+
* `clientType: 'function'` (the default).
|
|
15
|
+
*/
|
|
11
16
|
export const clientGenerator = defineGenerator<PluginClient>({
|
|
12
17
|
name: 'client',
|
|
13
18
|
renderer: jsxRendererSync,
|
|
14
19
|
operation(node, ctx) {
|
|
15
|
-
const { config, driver, resolver, root
|
|
20
|
+
const { config, driver, resolver, root } = ctx
|
|
16
21
|
const { output, urlType, dataReturnType, paramsCasing, paramsType, pathParamsType, parser, importPath, group } = ctx.options
|
|
17
|
-
const baseURL = ctx.options.baseURL ??
|
|
22
|
+
const baseURL = ctx.options.baseURL ?? ctx.meta.baseURL
|
|
18
23
|
|
|
19
24
|
const pluginTs = driver.getPlugin(pluginTsName)
|
|
20
25
|
|
|
@@ -24,14 +29,14 @@ export const clientGenerator = defineGenerator<PluginClient>({
|
|
|
24
29
|
|
|
25
30
|
const tsResolver = driver.getResolver(pluginTsName)
|
|
26
31
|
|
|
27
|
-
const pluginZod = parser === 'zod' ? driver.getPlugin(pluginZodName) :
|
|
28
|
-
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) :
|
|
32
|
+
const pluginZod = parser === 'zod' ? driver.getPlugin(pluginZodName) : null
|
|
33
|
+
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null
|
|
29
34
|
|
|
30
35
|
const importedTypeNames = resolveOperationTypeNames(node, tsResolver, { paramsCasing })
|
|
31
36
|
|
|
32
37
|
const importedZodNames =
|
|
33
38
|
zodResolver && parser === 'zod'
|
|
34
|
-
? [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) :
|
|
39
|
+
? [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null].filter(
|
|
35
40
|
(name): name is string => Boolean(name),
|
|
36
41
|
)
|
|
37
42
|
: []
|
|
@@ -39,13 +44,16 @@ export const clientGenerator = defineGenerator<PluginClient>({
|
|
|
39
44
|
const meta = {
|
|
40
45
|
name: resolver.resolveName(node.operationId),
|
|
41
46
|
urlName: resolver.resolveUrlName(node),
|
|
42
|
-
file: resolver.resolveFile(
|
|
47
|
+
file: resolver.resolveFile(
|
|
48
|
+
{ name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
|
|
49
|
+
{ root, output, group: group ?? undefined },
|
|
50
|
+
),
|
|
43
51
|
fileTs: tsResolver.resolveFile(
|
|
44
52
|
{ name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
|
|
45
53
|
{
|
|
46
54
|
root,
|
|
47
55
|
output: pluginTs.options?.output ?? output,
|
|
48
|
-
group: pluginTs.options?.group,
|
|
56
|
+
group: pluginTs.options?.group ?? undefined,
|
|
49
57
|
},
|
|
50
58
|
),
|
|
51
59
|
fileZod:
|
|
@@ -55,10 +63,10 @@ export const clientGenerator = defineGenerator<PluginClient>({
|
|
|
55
63
|
{
|
|
56
64
|
root,
|
|
57
65
|
output: pluginZod.options.output ?? output,
|
|
58
|
-
group: pluginZod.options?.group,
|
|
66
|
+
group: pluginZod.options?.group ?? undefined,
|
|
59
67
|
},
|
|
60
68
|
)
|
|
61
|
-
:
|
|
69
|
+
: null,
|
|
62
70
|
} as const
|
|
63
71
|
|
|
64
72
|
const hasFormData = node.requestBody?.content?.some((e) => e.contentType === 'multipart/form-data') ?? false
|
|
@@ -68,8 +76,8 @@ export const clientGenerator = defineGenerator<PluginClient>({
|
|
|
68
76
|
baseName={meta.file.baseName}
|
|
69
77
|
path={meta.file.path}
|
|
70
78
|
meta={meta.file.meta}
|
|
71
|
-
banner={resolver.resolveBanner(
|
|
72
|
-
footer={resolver.resolveFooter(
|
|
79
|
+
banner={resolver.resolveBanner(ctx.meta, { output, config })}
|
|
80
|
+
footer={resolver.resolveFooter(ctx.meta, { output, config })}
|
|
73
81
|
>
|
|
74
82
|
{importPath ? (
|
|
75
83
|
<>
|
|
@@ -90,7 +98,7 @@ export const clientGenerator = defineGenerator<PluginClient>({
|
|
|
90
98
|
|
|
91
99
|
{hasFormData && <File.Import name={['buildFormData']} root={meta.file.path} path={path.resolve(root, '.kubb/config.ts')} />}
|
|
92
100
|
|
|
93
|
-
{meta.fileZod && importedZodNames.length > 0 && <File.Import name={importedZodNames as string
|
|
101
|
+
{meta.fileZod && importedZodNames.length > 0 && <File.Import name={importedZodNames as Array<string>} root={meta.file.path} path={meta.fileZod.path} />}
|
|
94
102
|
|
|
95
103
|
{meta.fileTs && importedTypeNames.length > 0 && (
|
|
96
104
|
<File.Import name={Array.from(new Set(importedTypeNames))} root={meta.file.path} path={meta.fileTs.path} isTypeOnly />
|
|
@@ -4,27 +4,33 @@ import { defineGenerator } from '@kubb/core'
|
|
|
4
4
|
import { File, Function, jsxRendererSync } from '@kubb/renderer-jsx'
|
|
5
5
|
import type { PluginClient } from '../types'
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Emits one aggregate file per tag/group when `group` is configured. Each
|
|
9
|
+
* file re-exports every client function for that group, so callers can
|
|
10
|
+
* `import { petController } from './gen/clients'` instead of importing
|
|
11
|
+
* each operation individually.
|
|
12
|
+
*/
|
|
7
13
|
export const groupedClientGenerator = defineGenerator<PluginClient>({
|
|
8
14
|
name: 'groupedClient',
|
|
9
15
|
renderer: jsxRendererSync,
|
|
10
16
|
operations(nodes, ctx) {
|
|
11
|
-
const { config, resolver, root
|
|
17
|
+
const { config, resolver, root } = ctx
|
|
12
18
|
const { output, group } = ctx.options
|
|
13
19
|
|
|
14
20
|
const controllers = nodes.reduce(
|
|
15
21
|
(acc, operationNode) => {
|
|
16
22
|
if (group?.type === 'tag') {
|
|
17
23
|
const tag = operationNode.tags[0]
|
|
18
|
-
const name = tag ? group?.name?.({ group: camelCase(tag) }) :
|
|
24
|
+
const name = tag ? group?.name?.({ group: camelCase(tag) }) : null
|
|
19
25
|
|
|
20
26
|
if (!tag || !name) {
|
|
21
27
|
return acc
|
|
22
28
|
}
|
|
23
29
|
|
|
24
|
-
const file = resolver.resolveFile({ name, extname: '.ts', tag }, { root, output, group })
|
|
30
|
+
const file = resolver.resolveFile({ name, extname: '.ts', tag }, { root, output, group: group ?? undefined })
|
|
25
31
|
const clientFile = resolver.resolveFile(
|
|
26
32
|
{ name: operationNode.operationId, extname: '.ts', tag: operationNode.tags[0] ?? 'default', path: operationNode.path },
|
|
27
|
-
{ root, output, group },
|
|
33
|
+
{ root, output, group: group ?? undefined },
|
|
28
34
|
)
|
|
29
35
|
|
|
30
36
|
const client = {
|
|
@@ -55,8 +61,8 @@ export const groupedClientGenerator = defineGenerator<PluginClient>({
|
|
|
55
61
|
baseName={file.baseName}
|
|
56
62
|
path={file.path}
|
|
57
63
|
meta={file.meta}
|
|
58
|
-
banner={resolver.resolveBanner(
|
|
59
|
-
footer={resolver.resolveFooter(
|
|
64
|
+
banner={resolver.resolveBanner(ctx.meta, { output, config })}
|
|
65
|
+
footer={resolver.resolveFooter(ctx.meta, { output, config })}
|
|
60
66
|
>
|
|
61
67
|
{clients.map((client) => (
|
|
62
68
|
<File.Import key={client.name} name={[client.name]} root={file.path} path={client.file.path} />
|
|
@@ -3,23 +3,29 @@ import { File, jsxRendererSync } from '@kubb/renderer-jsx'
|
|
|
3
3
|
import { Operations } from '../components/Operations'
|
|
4
4
|
import type { PluginClient } from '../types'
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Generates an `operations.ts` file that re-exports every operation grouped
|
|
8
|
+
* by HTTP method. Enabled when `pluginClient({ operations: true })`. Useful
|
|
9
|
+
* for building meta-tooling on top of the generated client (route
|
|
10
|
+
* registries, API explorers).
|
|
11
|
+
*/
|
|
6
12
|
export const operationsGenerator = defineGenerator<PluginClient>({
|
|
7
13
|
name: 'client',
|
|
8
14
|
renderer: jsxRendererSync,
|
|
9
15
|
operations(nodes, ctx) {
|
|
10
|
-
const { config, resolver, root
|
|
16
|
+
const { config, resolver, root } = ctx
|
|
11
17
|
const { output, group } = ctx.options
|
|
12
18
|
|
|
13
19
|
const name = 'operations'
|
|
14
|
-
const file = resolver.resolveFile({ name, extname: '.ts' }, { root, output, group })
|
|
20
|
+
const file = resolver.resolveFile({ name, extname: '.ts' }, { root, output, group: group ?? undefined })
|
|
15
21
|
|
|
16
22
|
return (
|
|
17
23
|
<File
|
|
18
24
|
baseName={file.baseName}
|
|
19
25
|
path={file.path}
|
|
20
26
|
meta={file.meta}
|
|
21
|
-
banner={resolver.resolveBanner(
|
|
22
|
-
footer={resolver.resolveFooter(
|
|
27
|
+
banner={resolver.resolveBanner(ctx.meta, { output, config })}
|
|
28
|
+
footer={resolver.resolveFooter(ctx.meta, { output, config })}
|
|
23
29
|
>
|
|
24
30
|
<Operations name={name} nodes={nodes} />
|
|
25
31
|
</File>
|