@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,82 +0,0 @@
1
- import { camelCase } from '@internals/utils'
2
- import type { ast } from '@kubb/core'
3
- import { defineGenerator } from '@kubb/core'
4
- import { File, Function, jsxRenderer } from '@kubb/renderer-jsx'
5
- import type { PluginClient } from '../types'
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
- */
13
- export const groupedClientGenerator = defineGenerator<PluginClient>({
14
- name: 'groupedClient',
15
- renderer: jsxRenderer,
16
- operations(nodes, ctx) {
17
- const { config, resolver, root } = ctx
18
- const { output, group } = ctx.options
19
-
20
- const controllers = nodes.reduce(
21
- (acc, operationNode) => {
22
- if (group?.type === 'tag') {
23
- const tag = operationNode.tags[0]
24
- const name = tag ? group?.name?.({ group: camelCase(tag) }) : null
25
-
26
- if (!tag || !name) {
27
- return acc
28
- }
29
-
30
- const file = resolver.resolveFile({ name, extname: '.ts', tag }, { root, output, group: group ?? undefined })
31
- const clientFile = resolver.resolveFile(
32
- { name: operationNode.operationId, extname: '.ts', tag: operationNode.tags[0] ?? 'default', path: operationNode.path },
33
- { root, output, group: group ?? undefined },
34
- )
35
-
36
- const client = {
37
- name: resolver.resolveName(operationNode.operationId),
38
- file: clientFile,
39
- }
40
-
41
- const previous = acc.find((item) => item.file.path === file.path)
42
-
43
- if (previous) {
44
- previous.clients.push(client)
45
- } else {
46
- acc.push({ name, file, clients: [client] })
47
- }
48
- }
49
-
50
- return acc
51
- },
52
- [] as Array<{ name: string; file: ast.FileNode; clients: Array<{ name: string; file: ast.FileNode }> }>,
53
- )
54
-
55
- return (
56
- <>
57
- {controllers.map(({ name, file, clients }) => {
58
- return (
59
- <File
60
- key={file.path}
61
- baseName={file.baseName}
62
- path={file.path}
63
- meta={file.meta}
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 } })}
66
- >
67
- {clients.map((client) => (
68
- <File.Import key={client.name} name={[client.name]} root={file.path} path={client.file.path} />
69
- ))}
70
-
71
- <File.Source name={name} isExportable isIndexable>
72
- <Function export name={name}>
73
- {`return { ${clients.map((client) => client.name).join(', ')} }`}
74
- </Function>
75
- </File.Source>
76
- </File>
77
- )
78
- })}
79
- </>
80
- )
81
- },
82
- })
@@ -1,47 +0,0 @@
1
- import { Url } from '@internals/utils'
2
- import { ast, defineGenerator } from '@kubb/core'
3
- import type { PluginClient } from '../types'
4
-
5
- /**
6
- * Generates an `operations.ts` file that re-exports every operation grouped
7
- * by HTTP method. Enabled when `pluginClient({ operations: true })`. Useful
8
- * for building meta-tooling on top of the generated client (route
9
- * registries, API explorers).
10
- */
11
- export const operationsGenerator = defineGenerator<PluginClient>({
12
- name: 'client',
13
- operations(nodes, ctx) {
14
- const { config, resolver, root } = ctx
15
- const { output, group } = ctx.options
16
-
17
- const name = 'operations'
18
- const file = resolver.resolveFile({ name, extname: '.ts' }, { root, output, group: group ?? undefined })
19
-
20
- const operationsObject: Record<string, { path: string; method: string }> = {}
21
- for (const node of nodes) {
22
- if (!ast.isHttpOperationNode(node)) continue
23
- operationsObject[node.operationId] = {
24
- path: Url.toPath(node.path),
25
- method: node.method.toLowerCase(),
26
- }
27
- }
28
-
29
- return [
30
- ast.createFile({
31
- baseName: file.baseName,
32
- path: file.path,
33
- meta: file.meta,
34
- banner: resolver.resolveBanner(ctx.meta, { output, config, file: { path: file.path, baseName: file.baseName } }),
35
- footer: resolver.resolveFooter(ctx.meta, { output, config, file: { path: file.path, baseName: file.baseName } }),
36
- sources: [
37
- ast.createSource({
38
- name,
39
- isExportable: true,
40
- isIndexable: true,
41
- nodes: [ast.createConst({ name, export: true, nodes: [ast.createText(JSON.stringify(operationsObject, undefined, 2))] })],
42
- }),
43
- ],
44
- }),
45
- ]
46
- },
47
- })
@@ -1,240 +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 { StaticClassClient } from '../components/StaticClassClient'
11
- import type { PluginClient } from '../types'
12
- import { isParserEnabled, resolveQueryParamsParser, resolveRequestParser, resolveResponseParser } from '../utils.ts'
13
-
14
- type OperationData = {
15
- node: ast.OperationNode
16
- name: string
17
- tsResolver: ResolverTs
18
- zodResolver: ResolverZod | null
19
- typeFile: ast.FileNode
20
- zodFile: ast.FileNode | null
21
- }
22
-
23
- type Controller = {
24
- name: string
25
- file: ast.FileNode
26
- operations: Array<OperationData>
27
- }
28
-
29
- function resolveTypeImportNames(node: ast.OperationNode, tsResolver: ResolverTs): Array<string> {
30
- return resolveOperationTypeNames(node, tsResolver, { order: 'body-response-first' })
31
- }
32
-
33
- function resolveZodImportNames(node: ast.OperationNode, zodResolver: ResolverZod, parser: PluginClient['resolvedOptions']['parser']): Array<string> {
34
- const { query: queryParams } = getOperationParameters(node)
35
- const names: Array<string | null | undefined> = [
36
- resolveResponseParser(parser) === 'zod' ? zodResolver.resolveResponseName?.(node) : null,
37
- resolveRequestParser(parser) === 'zod' && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null,
38
- resolveQueryParamsParser(parser) === 'zod' && queryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, queryParams[0]!) : null,
39
- ]
40
- return names.filter((n): n is string => Boolean(n))
41
- }
42
-
43
- /**
44
- * Built-in `operations` generator for `@kubb/plugin-client` when
45
- * `clientType: 'staticClass'`. Emits one class per tag, with a static method
46
- * per operation so callers can use `Pet.getPetById(...)` without
47
- * instantiating the class.
48
- */
49
- export const staticClassClientGenerator = defineGenerator<PluginClient>({
50
- name: 'staticClassClient',
51
- renderer: jsxRenderer,
52
- operations(nodes, ctx) {
53
- const { config, driver, resolver, root } = ctx
54
- const { output, group, dataReturnType, paramsCasing, paramsType, pathParamsType, parser, importPath } = ctx.options
55
- const baseURL = ctx.options.baseURL ?? ctx.meta.baseURL
56
-
57
- const pluginTs = driver.getPlugin(pluginTsName)
58
- if (!pluginTs) return null
59
-
60
- const tsResolver = driver.getResolver(pluginTsName)
61
- const tsPluginOptions = pluginTs.options
62
- const pluginZod = isParserEnabled(parser) ? driver.getPlugin(pluginZodName) : null
63
- const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null
64
-
65
- function buildOperationData(node: ast.OperationNode): OperationData {
66
- const typeFile = tsResolver.resolveFile(operationFileEntry(node, node.operationId), {
67
- root,
68
- output: tsPluginOptions?.output ?? output,
69
- group: tsPluginOptions?.group,
70
- })
71
- const zodFile =
72
- zodResolver && pluginZod?.options
73
- ? zodResolver.resolveFile(operationFileEntry(node, node.operationId), {
74
- root,
75
- output: pluginZod.options?.output ?? output,
76
- group: pluginZod.options?.group ?? undefined,
77
- })
78
- : null
79
-
80
- return {
81
- node: node,
82
- name: resolver.resolveName(node.operationId),
83
- tsResolver,
84
- zodResolver,
85
- typeFile,
86
- zodFile,
87
- }
88
- }
89
-
90
- const controllers = nodes.reduce((acc, operationNode) => {
91
- if (!ast.isHttpOperationNode(operationNode)) return acc
92
- const tag = operationNode.tags[0]
93
- const groupName = tag ? (group?.name?.({ group: camelCase(tag) }) ?? resolver.resolveGroupName(tag)) : resolver.resolveClassName('Client')
94
-
95
- if (!tag && !group) {
96
- const name = resolver.resolveClassName('ApiClient')
97
- const file = resolver.resolveFile({ name, extname: '.ts' }, { root, output, group: group ?? undefined })
98
- const operationData = buildOperationData(operationNode)
99
- const previous = acc.find((item) => item.file.path === file.path)
100
-
101
- if (previous) {
102
- previous.operations.push(operationData)
103
- } else {
104
- acc.push({ name, file, operations: [operationData] })
105
- }
106
- return acc
107
- }
108
-
109
- if (tag) {
110
- const name = groupName
111
- const file = resolver.resolveFile({ name, extname: '.ts', tag }, { root, output, group: group ?? undefined })
112
- const operationData = buildOperationData(operationNode)
113
- const previous = acc.find((item) => item.file.path === file.path)
114
-
115
- if (previous) {
116
- previous.operations.push(operationData)
117
- } else {
118
- acc.push({ name, file, operations: [operationData] })
119
- }
120
- }
121
-
122
- return acc
123
- }, [] as Array<Controller>)
124
-
125
- function collectTypeImports(ops: Array<OperationData>) {
126
- const typeImportsByFile = new Map<string, Set<string>>()
127
- const typeFilesByPath = new Map<string, ast.FileNode>()
128
-
129
- ops.forEach((op) => {
130
- const names = resolveTypeImportNames(op.node, tsResolver)
131
- if (!typeImportsByFile.has(op.typeFile.path)) {
132
- typeImportsByFile.set(op.typeFile.path, new Set())
133
- }
134
- const imports = typeImportsByFile.get(op.typeFile.path)!
135
- names.forEach((n) => {
136
- imports.add(n)
137
- })
138
- typeFilesByPath.set(op.typeFile.path, op.typeFile)
139
- })
140
-
141
- return { typeImportsByFile, typeFilesByPath }
142
- }
143
-
144
- function collectZodImports(ops: Array<OperationData>) {
145
- const zodImportsByFile = new Map<string, Set<string>>()
146
- const zodFilesByPath = new Map<string, ast.FileNode>()
147
-
148
- ops.forEach((op) => {
149
- if (!op.zodFile || !zodResolver) return
150
- const names = resolveZodImportNames(op.node, zodResolver, parser)
151
- if (!zodImportsByFile.has(op.zodFile.path)) {
152
- zodImportsByFile.set(op.zodFile.path, new Set())
153
- }
154
- const imports = zodImportsByFile.get(op.zodFile.path)!
155
- names.forEach((n) => {
156
- imports.add(n)
157
- })
158
- zodFilesByPath.set(op.zodFile.path, op.zodFile)
159
- })
160
-
161
- return { zodImportsByFile, zodFilesByPath }
162
- }
163
-
164
- return (
165
- <>
166
- {controllers.map(({ name, file, operations: ops }) => {
167
- const { typeImportsByFile, typeFilesByPath } = collectTypeImports(ops)
168
- const { zodImportsByFile, zodFilesByPath } = isParserEnabled(parser)
169
- ? collectZodImports(ops)
170
- : { zodImportsByFile: new Map<string, Set<string>>(), zodFilesByPath: new Map<string, ast.FileNode>() }
171
- const hasFormData = ops.some((op) => op.node.requestBody?.content?.some((e) => e.contentType === 'multipart/form-data') ?? false)
172
-
173
- return (
174
- <File
175
- key={file.path}
176
- baseName={file.baseName}
177
- path={file.path}
178
- meta={file.meta}
179
- banner={resolver.resolveBanner(ctx.meta, { output, config, file: { path: file.path, baseName: file.baseName } })}
180
- footer={resolver.resolveFooter(ctx.meta, { output, config, file: { path: file.path, baseName: file.baseName } })}
181
- >
182
- {importPath ? (
183
- <>
184
- <File.Import name={'client'} path={importPath} />
185
- <File.Import name={['mergeConfig']} path={importPath} />
186
- <File.Import name={['Client', 'RequestConfig', 'ResponseErrorConfig']} path={importPath} isTypeOnly />
187
- </>
188
- ) : (
189
- <>
190
- <File.Import name={['client']} root={file.path} path={path.resolve(root, '.kubb/client.ts')} />
191
- <File.Import name={['mergeConfig']} root={file.path} path={path.resolve(root, '.kubb/client.ts')} />
192
- <File.Import
193
- name={['Client', 'RequestConfig', 'ResponseErrorConfig']}
194
- root={file.path}
195
- path={path.resolve(root, '.kubb/client.ts')}
196
- isTypeOnly
197
- />
198
- </>
199
- )}
200
-
201
- {hasFormData && <File.Import name={['buildFormData']} root={file.path} path={path.resolve(root, '.kubb/config.ts')} />}
202
-
203
- {parser === 'zod' && zodResolver && ops.some((op) => op.node.requestBody?.content?.[0]?.schema != null) && (
204
- <File.Import name={['z']} path="zod" isTypeOnly />
205
- )}
206
-
207
- {Array.from(typeImportsByFile.entries()).map(([filePath, importSet]) => {
208
- const typeFile = typeFilesByPath.get(filePath)
209
- if (!typeFile) return null
210
- const importNames = Array.from(importSet).filter(Boolean)
211
- if (importNames.length === 0) return null
212
- return <File.Import key={filePath} name={importNames} root={file.path} path={typeFile.path} isTypeOnly />
213
- })}
214
-
215
- {isParserEnabled(parser) &&
216
- Array.from(zodImportsByFile.entries()).map(([filePath, importSet]) => {
217
- const zodFile = zodFilesByPath.get(filePath)
218
- if (!zodFile) return null
219
- const importNames = Array.from(importSet).filter(Boolean)
220
- if (importNames.length === 0) return null
221
- return <File.Import key={filePath} name={importNames} root={file.path} path={zodFile.path} />
222
- })}
223
-
224
- <StaticClassClient
225
- name={name}
226
- operations={ops}
227
- baseURL={baseURL}
228
- dataReturnType={dataReturnType}
229
- pathParamsType={pathParamsType}
230
- paramsCasing={paramsCasing}
231
- paramsType={paramsType}
232
- parser={parser}
233
- />
234
- </File>
235
- )
236
- })}
237
- </>
238
- )
239
- },
240
- })
@@ -1,46 +0,0 @@
1
- import { camelCase, ensureValidVarName, pascalCase, toFilePath } from '@internals/utils'
2
- import { defineResolver } from '@kubb/core'
3
- import type { PluginClient } from '../types.ts'
4
-
5
- /**
6
- * Default resolver used by `@kubb/plugin-client`. Decides the names and file
7
- * paths for every generated client function or class. Functions and files use
8
- * camelCase; classes and tag groups use PascalCase.
9
- *
10
- * @example Resolve client function and class names
11
- * ```ts
12
- * import { resolverClient } from '@kubb/plugin-client'
13
- *
14
- * resolverClient.default('list pets', 'function') // 'listPets'
15
- * resolverClient.resolveClassName('pet') // 'Pet'
16
- * resolverClient.resolveGroupName('pet') // 'PetClient'
17
- * resolverClient.resolveUrlName(operationNode) // 'getShowPetByIdUrl'
18
- * ```
19
- */
20
- export const resolverClient = defineResolver<PluginClient>(() => ({
21
- name: 'default',
22
- pluginName: 'plugin-client',
23
- default(name, type) {
24
- if (type === 'file') return toFilePath(name)
25
- return ensureValidVarName(camelCase(name))
26
- },
27
- resolveName(name) {
28
- return this.default(name, 'function')
29
- },
30
- resolvePathName(name, type) {
31
- return this.default(name, type)
32
- },
33
- resolveClassName(name) {
34
- return ensureValidVarName(pascalCase(name))
35
- },
36
- resolveGroupName(name) {
37
- return ensureValidVarName(pascalCase(`${name} Client`))
38
- },
39
- resolveClientPropertyName(name) {
40
- return ensureValidVarName(camelCase(name))
41
- },
42
- resolveUrlName(node) {
43
- const name = this.resolveName(node.operationId)
44
- return `get${name.charAt(0).toUpperCase()}${name.slice(1)}Url`
45
- },
46
- }))
@@ -1,4 +0,0 @@
1
- // @ts-expect-error - import attributes are handled at build time by importAttributeTextPlugin
2
- import content from '../../../templates/clients/axios.ts' with { type: 'text' }
3
-
4
- export const source = content as string
@@ -1,4 +0,0 @@
1
- // @ts-expect-error - import attributes are handled at build time by importAttributeTextPlugin
2
- import content from '../../../templates/clients/fetch.ts' with { type: 'text' }
3
-
4
- export const source = content as string
@@ -1,4 +0,0 @@
1
- // @ts-expect-error - import attributes are handled at build time by importAttributeTextPlugin
2
- import content from '../../templates/config.ts' with { type: 'text' }
3
-
4
- export const source = content as string