@kubb/plugin-client 5.0.0-beta.3 → 5.0.0-beta.30
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/README.md +24 -4
- package/dist/clients/axios.cjs +25 -3
- package/dist/clients/axios.cjs.map +1 -1
- package/dist/clients/axios.d.ts +9 -2
- package/dist/clients/axios.js +25 -3
- package/dist/clients/axios.js.map +1 -1
- package/dist/clients/fetch.cjs +20 -2
- package/dist/clients/fetch.cjs.map +1 -1
- package/dist/clients/fetch.d.ts +9 -2
- package/dist/clients/fetch.js +20 -2
- package/dist/clients/fetch.js.map +1 -1
- package/dist/index.cjs +524 -301
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +150 -84
- package/dist/index.js +525 -302
- package/dist/index.js.map +1 -1
- package/dist/templates/clients/axios.source.cjs +1 -1
- package/dist/templates/clients/axios.source.js +1 -1
- package/dist/templates/clients/fetch.source.cjs +1 -1
- package/dist/templates/clients/fetch.source.js +1 -1
- package/extension.yaml +1293 -0
- package/package.json +11 -17
- package/src/clients/axios.ts +41 -7
- package/src/clients/fetch.ts +30 -3
- package/src/components/ClassClient.tsx +17 -19
- package/src/components/Client.tsx +68 -51
- package/src/components/StaticClassClient.tsx +17 -19
- package/src/components/Url.tsx +7 -9
- package/src/components/WrapperClient.tsx +9 -5
- package/src/functionParams.ts +8 -8
- package/src/generators/classClientGenerator.tsx +40 -38
- package/src/generators/clientGenerator.tsx +32 -35
- package/src/generators/groupedClientGenerator.tsx +14 -8
- package/src/generators/operationsGenerator.tsx +12 -6
- package/src/generators/staticClassClientGenerator.tsx +34 -32
- package/src/plugin.ts +24 -11
- package/src/resolvers/resolverClient.ts +31 -8
- package/src/types.ts +90 -53
- package/src/utils.ts +30 -53
- package/templates/clients/axios.ts +0 -73
- package/templates/clients/fetch.ts +0 -96
- package/templates/config.ts +0 -43
|
@@ -1,17 +1,21 @@
|
|
|
1
|
-
import { camelCase } from '@internals/utils'
|
|
2
1
|
import { File } from '@kubb/renderer-jsx'
|
|
3
2
|
import type { KubbReactNode } from '@kubb/renderer-jsx/types'
|
|
4
3
|
|
|
4
|
+
type ClientController = {
|
|
5
|
+
className: string
|
|
6
|
+
propertyName: string
|
|
7
|
+
}
|
|
8
|
+
|
|
5
9
|
type Props = {
|
|
6
10
|
name: string
|
|
7
|
-
|
|
11
|
+
controllers: Array<ClientController>
|
|
8
12
|
isExportable?: boolean
|
|
9
13
|
isIndexable?: boolean
|
|
10
14
|
}
|
|
11
15
|
|
|
12
|
-
export function WrapperClient({ name,
|
|
13
|
-
const properties =
|
|
14
|
-
const assignments =
|
|
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')
|
|
15
19
|
|
|
16
20
|
const classCode = `export class ${name} {
|
|
17
21
|
${properties}
|
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() {
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
|
-
import {
|
|
2
|
+
import { resolveOperationTypeNames } from '@internals/shared'
|
|
3
|
+
import { camelCase } from '@internals/utils'
|
|
3
4
|
import type { ast } from '@kubb/core'
|
|
4
5
|
import { defineGenerator } from '@kubb/core'
|
|
5
6
|
import type { ResolverTs } from '@kubb/plugin-ts'
|
|
6
7
|
import { pluginTsName } from '@kubb/plugin-ts'
|
|
7
8
|
import type { ResolverZod } from '@kubb/plugin-zod'
|
|
8
9
|
import { pluginZodName } from '@kubb/plugin-zod'
|
|
9
|
-
import { File,
|
|
10
|
+
import { File, jsxRendererSync } from '@kubb/renderer-jsx'
|
|
10
11
|
import { ClassClient } from '../components/ClassClient'
|
|
11
12
|
import { WrapperClient } from '../components/WrapperClient'
|
|
12
13
|
import type { PluginClient } from '../types'
|
|
@@ -15,52 +16,50 @@ type OperationData = {
|
|
|
15
16
|
node: ast.OperationNode
|
|
16
17
|
name: string
|
|
17
18
|
tsResolver: ResolverTs
|
|
18
|
-
zodResolver: ResolverZod |
|
|
19
|
+
zodResolver: ResolverZod | null
|
|
19
20
|
typeFile: ast.FileNode
|
|
20
|
-
zodFile: ast.FileNode |
|
|
21
|
+
zodFile: ast.FileNode | null
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
type Controller = {
|
|
24
25
|
name: string
|
|
26
|
+
propertyName: string
|
|
25
27
|
file: ast.FileNode
|
|
26
28
|
operations: Array<OperationData>
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
function resolveTypeImportNames(node: ast.OperationNode, tsResolver: ResolverTs): Array<string> {
|
|
30
|
-
|
|
31
|
-
node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : undefined,
|
|
32
|
-
tsResolver.resolveResponseName(node),
|
|
33
|
-
...node.parameters.filter((p) => p.in === 'path').map((p) => tsResolver.resolvePathParamsName(node, p)),
|
|
34
|
-
...node.parameters.filter((p) => p.in === 'query').map((p) => tsResolver.resolveQueryParamsName(node, p)),
|
|
35
|
-
...node.parameters.filter((p) => p.in === 'header').map((p) => tsResolver.resolveHeaderParamsName(node, p)),
|
|
36
|
-
...node.responses.map((res) => tsResolver.resolveResponseStatusName(node, res.statusCode)),
|
|
37
|
-
]
|
|
38
|
-
return names.filter((n): n is string => Boolean(n))
|
|
32
|
+
return resolveOperationTypeNames(node, tsResolver, { order: 'body-response-first' })
|
|
39
33
|
}
|
|
40
34
|
|
|
41
35
|
function resolveZodImportNames(node: ast.OperationNode, zodResolver: ResolverZod): Array<string> {
|
|
42
|
-
const names: Array<string | undefined> = [
|
|
36
|
+
const names: Array<string | null | undefined> = [
|
|
43
37
|
zodResolver.resolveResponseName?.(node),
|
|
44
|
-
node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) :
|
|
38
|
+
node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null,
|
|
45
39
|
]
|
|
46
40
|
return names.filter((n): n is string => Boolean(n))
|
|
47
41
|
}
|
|
48
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
|
+
*/
|
|
49
48
|
export const classClientGenerator = defineGenerator<PluginClient>({
|
|
50
49
|
name: 'classClient',
|
|
51
|
-
renderer:
|
|
50
|
+
renderer: jsxRendererSync,
|
|
52
51
|
operations(nodes, ctx) {
|
|
53
|
-
const {
|
|
52
|
+
const { config, driver, resolver, root } = ctx
|
|
54
53
|
const { output, group, dataReturnType, paramsCasing, paramsType, pathParamsType, parser, importPath, sdk } = ctx.options
|
|
55
|
-
const baseURL = ctx.options.baseURL ??
|
|
54
|
+
const baseURL = ctx.options.baseURL ?? ctx.meta.baseURL
|
|
56
55
|
|
|
57
56
|
const pluginTs = driver.getPlugin(pluginTsName)
|
|
58
57
|
if (!pluginTs) return null
|
|
59
58
|
|
|
60
59
|
const tsResolver = driver.getResolver(pluginTsName)
|
|
61
60
|
const tsPluginOptions = pluginTs.options
|
|
62
|
-
const pluginZod = parser === 'zod' ? driver.getPlugin(pluginZodName) :
|
|
63
|
-
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) :
|
|
61
|
+
const pluginZod = parser === 'zod' ? driver.getPlugin(pluginZodName) : null
|
|
62
|
+
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null
|
|
64
63
|
|
|
65
64
|
function buildOperationData(node: ast.OperationNode): OperationData {
|
|
66
65
|
const typeFile = tsResolver.resolveFile(
|
|
@@ -71,9 +70,9 @@ export const classClientGenerator = defineGenerator<PluginClient>({
|
|
|
71
70
|
zodResolver && pluginZod?.options
|
|
72
71
|
? zodResolver.resolveFile(
|
|
73
72
|
{ name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
|
|
74
|
-
{ root, output: pluginZod.options?.output ?? output, group: pluginZod.options?.group },
|
|
73
|
+
{ root, output: pluginZod.options?.output ?? output, group: pluginZod.options?.group ?? undefined },
|
|
75
74
|
)
|
|
76
|
-
:
|
|
75
|
+
: null
|
|
77
76
|
|
|
78
77
|
return {
|
|
79
78
|
node: node,
|
|
@@ -87,29 +86,32 @@ export const classClientGenerator = defineGenerator<PluginClient>({
|
|
|
87
86
|
|
|
88
87
|
const controllers = nodes.reduce((acc, operationNode) => {
|
|
89
88
|
const tag = operationNode.tags[0]
|
|
90
|
-
const groupName = tag ? (group?.name?.({ group: camelCase(tag) }) ??
|
|
89
|
+
const groupName = tag ? (group?.name?.({ group: camelCase(tag) }) ?? resolver.resolveGroupName(tag)) : resolver.resolveGroupName('Client')
|
|
91
90
|
|
|
92
91
|
if (!tag && !group) {
|
|
93
|
-
const name = 'ApiClient'
|
|
94
|
-
const file = resolver.resolveFile({ name, extname: '.ts' }, { root, output, group })
|
|
92
|
+
const name = resolver.resolveClassName('ApiClient')
|
|
93
|
+
const file = resolver.resolveFile({ name, extname: '.ts' }, { root, output, group: group ?? undefined })
|
|
95
94
|
const operationData = buildOperationData(operationNode)
|
|
96
95
|
const previous = acc.find((item) => item.file.path === file.path)
|
|
97
96
|
|
|
98
97
|
if (previous) {
|
|
99
98
|
previous.operations.push(operationData)
|
|
100
99
|
} else {
|
|
101
|
-
acc.push({ name, file, operations: [operationData] })
|
|
100
|
+
acc.push({ name, propertyName: resolver.resolveClientPropertyName(name), file, operations: [operationData] })
|
|
102
101
|
}
|
|
103
|
-
|
|
102
|
+
return acc
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (tag) {
|
|
104
106
|
const name = groupName
|
|
105
|
-
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 })
|
|
106
108
|
const operationData = buildOperationData(operationNode)
|
|
107
109
|
const previous = acc.find((item) => item.file.path === file.path)
|
|
108
110
|
|
|
109
111
|
if (previous) {
|
|
110
112
|
previous.operations.push(operationData)
|
|
111
113
|
} else {
|
|
112
|
-
acc.push({ name, file, operations: [operationData] })
|
|
114
|
+
acc.push({ name, propertyName: resolver.resolveClientPropertyName(name), file, operations: [operationData] })
|
|
113
115
|
}
|
|
114
116
|
}
|
|
115
117
|
|
|
@@ -159,7 +161,7 @@ export const classClientGenerator = defineGenerator<PluginClient>({
|
|
|
159
161
|
const { typeImportsByFile, typeFilesByPath } = collectTypeImports(ops)
|
|
160
162
|
const { zodImportsByFile, zodFilesByPath } =
|
|
161
163
|
parser === 'zod' ? collectZodImports(ops) : { zodImportsByFile: new Map<string, Set<string>>(), zodFilesByPath: new Map<string, ast.FileNode>() }
|
|
162
|
-
const hasFormData = ops.some((op) => op.node.requestBody?.content?.
|
|
164
|
+
const hasFormData = ops.some((op) => op.node.requestBody?.content?.some((e) => e.contentType === 'multipart/form-data') ?? false)
|
|
163
165
|
|
|
164
166
|
return (
|
|
165
167
|
<File
|
|
@@ -167,18 +169,18 @@ export const classClientGenerator = defineGenerator<PluginClient>({
|
|
|
167
169
|
baseName={file.baseName}
|
|
168
170
|
path={file.path}
|
|
169
171
|
meta={file.meta}
|
|
170
|
-
banner={resolver.resolveBanner(
|
|
171
|
-
footer={resolver.resolveFooter(
|
|
172
|
+
banner={resolver.resolveBanner(ctx.meta, { output, config, file: { path: file.path, baseName: file.baseName } })}
|
|
173
|
+
footer={resolver.resolveFooter(ctx.meta, { output, config, file: { path: file.path, baseName: file.baseName } })}
|
|
172
174
|
>
|
|
173
175
|
{importPath ? (
|
|
174
176
|
<>
|
|
175
|
-
<File.Import name={'
|
|
177
|
+
<File.Import name={'client'} path={importPath} />
|
|
176
178
|
<File.Import name={['mergeConfig']} path={importPath} />
|
|
177
179
|
<File.Import name={['Client', 'RequestConfig', 'ResponseErrorConfig']} path={importPath} isTypeOnly />
|
|
178
180
|
</>
|
|
179
181
|
) : (
|
|
180
182
|
<>
|
|
181
|
-
<File.Import name={['
|
|
183
|
+
<File.Import name={['client']} root={file.path} path={path.resolve(root, '.kubb/client.ts')} />
|
|
182
184
|
<File.Import name={['mergeConfig']} root={file.path} path={path.resolve(root, '.kubb/client.ts')} />
|
|
183
185
|
<File.Import name={['Client', 'RequestConfig', 'ResponseErrorConfig']} root={file.path} path={path.resolve(root, '.kubb/client.ts')} isTypeOnly />
|
|
184
186
|
</>
|
|
@@ -218,7 +220,7 @@ export const classClientGenerator = defineGenerator<PluginClient>({
|
|
|
218
220
|
})
|
|
219
221
|
|
|
220
222
|
if (sdk) {
|
|
221
|
-
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 })
|
|
222
224
|
|
|
223
225
|
files.push(
|
|
224
226
|
<File
|
|
@@ -226,8 +228,8 @@ export const classClientGenerator = defineGenerator<PluginClient>({
|
|
|
226
228
|
baseName={sdkFile.baseName}
|
|
227
229
|
path={sdkFile.path}
|
|
228
230
|
meta={sdkFile.meta}
|
|
229
|
-
banner={resolver.resolveBanner(
|
|
230
|
-
footer={resolver.resolveFooter(
|
|
231
|
+
banner={resolver.resolveBanner(ctx.meta, { output, config, file: { path: sdkFile.path, baseName: sdkFile.baseName } })}
|
|
232
|
+
footer={resolver.resolveFooter(ctx.meta, { output, config, file: { path: sdkFile.path, baseName: sdkFile.baseName } })}
|
|
231
233
|
>
|
|
232
234
|
{importPath ? (
|
|
233
235
|
<File.Import name={['Client', 'RequestConfig']} path={importPath} isTypeOnly />
|
|
@@ -239,7 +241,7 @@ export const classClientGenerator = defineGenerator<PluginClient>({
|
|
|
239
241
|
<File.Import key={name} name={[name]} root={sdkFile.path} path={file.path} />
|
|
240
242
|
))}
|
|
241
243
|
|
|
242
|
-
<WrapperClient name={sdk.className}
|
|
244
|
+
<WrapperClient name={sdk.className} controllers={controllers.map(({ name, propertyName }) => ({ className: name, propertyName }))} />
|
|
243
245
|
</File>,
|
|
244
246
|
)
|
|
245
247
|
}
|
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
|
-
import {
|
|
2
|
+
import { resolveOperationTypeNames } from '@internals/shared'
|
|
3
|
+
import { defineGenerator } from '@kubb/core'
|
|
3
4
|
import { pluginTsName } from '@kubb/plugin-ts'
|
|
4
5
|
import { pluginZodName } from '@kubb/plugin-zod'
|
|
5
|
-
import { File,
|
|
6
|
+
import { File, jsxRendererSync } from '@kubb/renderer-jsx'
|
|
6
7
|
import { Client } from '../components/Client'
|
|
7
8
|
import { Url } from '../components/Url.tsx'
|
|
8
9
|
import type { PluginClient } from '../types'
|
|
9
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
|
+
*/
|
|
10
16
|
export const clientGenerator = defineGenerator<PluginClient>({
|
|
11
17
|
name: 'client',
|
|
12
|
-
renderer:
|
|
18
|
+
renderer: jsxRendererSync,
|
|
13
19
|
operation(node, ctx) {
|
|
14
|
-
const {
|
|
20
|
+
const { config, driver, resolver, root } = ctx
|
|
15
21
|
const { output, urlType, dataReturnType, paramsCasing, paramsType, pathParamsType, parser, importPath, group } = ctx.options
|
|
16
|
-
const baseURL = ctx.options.baseURL ??
|
|
22
|
+
const baseURL = ctx.options.baseURL ?? ctx.meta.baseURL
|
|
17
23
|
|
|
18
24
|
const pluginTs = driver.getPlugin(pluginTsName)
|
|
19
25
|
|
|
@@ -23,38 +29,31 @@ export const clientGenerator = defineGenerator<PluginClient>({
|
|
|
23
29
|
|
|
24
30
|
const tsResolver = driver.getResolver(pluginTsName)
|
|
25
31
|
|
|
26
|
-
const pluginZod = parser === 'zod' ? driver.getPlugin(pluginZodName) :
|
|
27
|
-
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) :
|
|
32
|
+
const pluginZod = parser === 'zod' ? driver.getPlugin(pluginZodName) : null
|
|
33
|
+
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null
|
|
28
34
|
|
|
29
|
-
const
|
|
30
|
-
const pathParams = casedParams.filter((p) => p.in === 'path')
|
|
31
|
-
const queryParams = casedParams.filter((p) => p.in === 'query')
|
|
32
|
-
const headerParams = casedParams.filter((p) => p.in === 'header')
|
|
33
|
-
|
|
34
|
-
const importedTypeNames = [
|
|
35
|
-
...pathParams.map((p) => tsResolver.resolvePathParamsName(node, p)),
|
|
36
|
-
...queryParams.map((p) => tsResolver.resolveQueryParamsName(node, p)),
|
|
37
|
-
...headerParams.map((p) => tsResolver.resolveHeaderParamsName(node, p)),
|
|
38
|
-
node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : undefined,
|
|
39
|
-
tsResolver.resolveResponseName(node),
|
|
40
|
-
...node.responses.map((res) => tsResolver.resolveResponseStatusName(node, res.statusCode)),
|
|
41
|
-
].filter(Boolean)
|
|
35
|
+
const importedTypeNames = resolveOperationTypeNames(node, tsResolver, { paramsCasing })
|
|
42
36
|
|
|
43
37
|
const importedZodNames =
|
|
44
38
|
zodResolver && parser === 'zod'
|
|
45
|
-
? [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(
|
|
40
|
+
(name): name is string => Boolean(name),
|
|
41
|
+
)
|
|
46
42
|
: []
|
|
47
43
|
|
|
48
44
|
const meta = {
|
|
49
45
|
name: resolver.resolveName(node.operationId),
|
|
50
|
-
urlName:
|
|
51
|
-
file: resolver.resolveFile(
|
|
46
|
+
urlName: resolver.resolveUrlName(node),
|
|
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
|
+
),
|
|
52
51
|
fileTs: tsResolver.resolveFile(
|
|
53
52
|
{ name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
|
|
54
53
|
{
|
|
55
54
|
root,
|
|
56
55
|
output: pluginTs.options?.output ?? output,
|
|
57
|
-
group: pluginTs.options?.group,
|
|
56
|
+
group: pluginTs.options?.group ?? undefined,
|
|
58
57
|
},
|
|
59
58
|
),
|
|
60
59
|
fileZod:
|
|
@@ -64,30 +63,30 @@ export const clientGenerator = defineGenerator<PluginClient>({
|
|
|
64
63
|
{
|
|
65
64
|
root,
|
|
66
65
|
output: pluginZod.options.output ?? output,
|
|
67
|
-
group: pluginZod.options?.group,
|
|
66
|
+
group: pluginZod.options?.group ?? undefined,
|
|
68
67
|
},
|
|
69
68
|
)
|
|
70
|
-
:
|
|
69
|
+
: null,
|
|
71
70
|
} as const
|
|
72
71
|
|
|
73
|
-
const
|
|
72
|
+
const hasFormData = node.requestBody?.content?.some((e) => e.contentType === 'multipart/form-data') ?? false
|
|
74
73
|
|
|
75
74
|
return (
|
|
76
75
|
<File
|
|
77
76
|
baseName={meta.file.baseName}
|
|
78
77
|
path={meta.file.path}
|
|
79
78
|
meta={meta.file.meta}
|
|
80
|
-
banner={resolver.resolveBanner(
|
|
81
|
-
footer={resolver.resolveFooter(
|
|
79
|
+
banner={resolver.resolveBanner(ctx.meta, { output, config, file: { path: meta.file.path, baseName: meta.file.baseName } })}
|
|
80
|
+
footer={resolver.resolveFooter(ctx.meta, { output, config, file: { path: meta.file.path, baseName: meta.file.baseName } })}
|
|
82
81
|
>
|
|
83
82
|
{importPath ? (
|
|
84
83
|
<>
|
|
85
|
-
<File.Import name={'
|
|
84
|
+
<File.Import name={'client'} path={importPath} />
|
|
86
85
|
<File.Import name={['Client', 'RequestConfig', 'ResponseErrorConfig']} path={importPath} isTypeOnly />
|
|
87
86
|
</>
|
|
88
87
|
) : (
|
|
89
88
|
<>
|
|
90
|
-
<File.Import name={['
|
|
89
|
+
<File.Import name={['client']} root={meta.file.path} path={path.resolve(root, '.kubb/client.ts')} />
|
|
91
90
|
<File.Import
|
|
92
91
|
name={['Client', 'RequestConfig', 'ResponseErrorConfig']}
|
|
93
92
|
root={meta.file.path}
|
|
@@ -97,11 +96,9 @@ export const clientGenerator = defineGenerator<PluginClient>({
|
|
|
97
96
|
</>
|
|
98
97
|
)}
|
|
99
98
|
|
|
100
|
-
{
|
|
101
|
-
<File.Import name={['buildFormData']} root={meta.file.path} path={path.resolve(root, '.kubb/config.ts')} />
|
|
102
|
-
)}
|
|
99
|
+
{hasFormData && <File.Import name={['buildFormData']} root={meta.file.path} path={path.resolve(root, '.kubb/config.ts')} />}
|
|
103
100
|
|
|
104
|
-
{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} />}
|
|
105
102
|
|
|
106
103
|
{meta.fileTs && importedTypeNames.length > 0 && (
|
|
107
104
|
<File.Import name={Array.from(new Set(importedTypeNames))} root={meta.file.path} path={meta.fileTs.path} isTypeOnly />
|
|
@@ -1,30 +1,36 @@
|
|
|
1
1
|
import { camelCase } from '@internals/utils'
|
|
2
2
|
import type { ast } from '@kubb/core'
|
|
3
3
|
import { defineGenerator } from '@kubb/core'
|
|
4
|
-
import { File, Function,
|
|
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
|
-
renderer:
|
|
15
|
+
renderer: jsxRendererSync,
|
|
10
16
|
operations(nodes, ctx) {
|
|
11
|
-
const { config, resolver,
|
|
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, file: { path: file.path, baseName: file.baseName, isAggregation: true } })}
|
|
65
|
+
footer={resolver.resolveFooter(ctx.meta, { output, config, file: { path: file.path, baseName: file.baseName, isAggregation: true } })}
|
|
60
66
|
>
|
|
61
67
|
{clients.map((client) => (
|
|
62
68
|
<File.Import key={client.name} name={[client.name]} root={file.path} path={client.file.path} />
|
|
@@ -1,25 +1,31 @@
|
|
|
1
1
|
import { defineGenerator } from '@kubb/core'
|
|
2
|
-
import { File,
|
|
2
|
+
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
|
-
renderer:
|
|
14
|
+
renderer: jsxRendererSync,
|
|
9
15
|
operations(nodes, ctx) {
|
|
10
|
-
const { config, resolver,
|
|
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, file: { path: file.path, baseName: file.baseName } })}
|
|
28
|
+
footer={resolver.resolveFooter(ctx.meta, { output, config, file: { path: file.path, baseName: file.baseName } })}
|
|
23
29
|
>
|
|
24
30
|
<Operations name={name} nodes={nodes} />
|
|
25
31
|
</File>
|