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

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.
@@ -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
- })
package/src/index.ts DELETED
@@ -1,10 +0,0 @@
1
- export { Client } from './components/Client.tsx'
2
- export { classClientGenerator } from './generators/classClientGenerator.tsx'
3
- export { clientGenerator } from './generators/clientGenerator.tsx'
4
- export { groupedClientGenerator } from './generators/groupedClientGenerator.tsx'
5
- export { operationsGenerator } from './generators/operationsGenerator.ts'
6
- export { staticClassClientGenerator } from './generators/staticClassClientGenerator.tsx'
7
- export { default, pluginClient, pluginClientName } from './plugin.ts'
8
- export { resolverClient } from './resolvers/resolverClient.ts'
9
- export { isParserEnabled, resolveQueryParamsParser, resolveRequestParser, resolveResponseParser } from './utils.ts'
10
- export type { ClientImportPath, PluginClient, ResolverClient } from './types.ts'
package/src/plugin.ts DELETED
@@ -1,161 +0,0 @@
1
- import path from 'node:path'
2
- import { createGroupConfig } from '@internals/shared'
3
-
4
- import { ast, definePlugin } from '@kubb/core'
5
- import { pluginTsName } from '@kubb/plugin-ts'
6
- import { pluginZodName } from '@kubb/plugin-zod'
7
- import { classClientGenerator } from './generators/classClientGenerator.tsx'
8
- import { clientGenerator } from './generators/clientGenerator.tsx'
9
- import { groupedClientGenerator } from './generators/groupedClientGenerator.tsx'
10
- import { operationsGenerator } from './generators/operationsGenerator.ts'
11
- import { staticClassClientGenerator } from './generators/staticClassClientGenerator.tsx'
12
- import { resolverClient } from './resolvers/resolverClient.ts'
13
- import { source as axiosClientSource } from './templates/clients/axios.source.ts'
14
- import { source as fetchClientSource } from './templates/clients/fetch.source.ts'
15
- import { source as configSource } from './templates/config.source.ts'
16
- import type { PluginClient } from './types.ts'
17
- import { isParserEnabled } from './utils.ts'
18
-
19
- /**
20
- * Canonical plugin name for `@kubb/plugin-client`. Used for driver lookups and
21
- * cross-plugin dependency references.
22
- */
23
- export const pluginClientName = 'plugin-client' satisfies PluginClient['name']
24
-
25
- /**
26
- * Generates one HTTP client function per OpenAPI operation. Each function has
27
- * typed path params, query params, body, and response, so callers use the API
28
- * like any other typed function. Ships with `axios` and `fetch` runtimes; bring
29
- * your own by setting `importPath`.
30
- *
31
- * @example
32
- * ```ts
33
- * import { defineConfig } from 'kubb'
34
- * import { pluginTs } from '@kubb/plugin-ts'
35
- * import { pluginClient } from '@kubb/plugin-client'
36
- *
37
- * export default defineConfig({
38
- * input: { path: './petStore.yaml' },
39
- * output: { path: './src/gen' },
40
- * plugins: [
41
- * pluginTs(),
42
- * pluginClient({
43
- * output: { path: './clients' },
44
- * client: 'fetch',
45
- * }),
46
- * ],
47
- * })
48
- * ```
49
- */
50
- export const pluginClient = definePlugin<PluginClient>((options) => {
51
- const {
52
- output = { path: 'clients', barrel: { type: 'named' } },
53
- group,
54
- exclude = [],
55
- include,
56
- override = [],
57
- urlType = false,
58
- dataReturnType = 'data',
59
- paramsType = 'inline',
60
- pathParamsType = paramsType === 'object' ? 'object' : options.pathParamsType || 'inline',
61
- operations = false,
62
- paramsCasing,
63
- clientType = options.sdk ? 'class' : 'function',
64
- parser = false,
65
- client = 'axios',
66
- importPath,
67
- bundle = false,
68
- sdk,
69
- baseURL,
70
- resolver: userResolver,
71
- transformer: userTransformer,
72
- } = options
73
-
74
- const resolvedImportPath = importPath ?? (!bundle ? `@kubb/plugin-client/clients/${client}` : undefined)
75
-
76
- const selectedGenerators =
77
- options.generators ??
78
- [
79
- clientType === 'staticClass' ? staticClassClientGenerator : clientType === 'class' ? classClientGenerator : clientGenerator,
80
- group && clientType === 'function' ? groupedClientGenerator : null,
81
- operations ? operationsGenerator : null,
82
- ].filter((x): x is NonNullable<typeof x> => Boolean(x))
83
-
84
- const groupConfig = createGroupConfig(group)
85
-
86
- return {
87
- name: pluginClientName,
88
- options,
89
- dependencies: [pluginTsName, isParserEnabled(parser) ? pluginZodName : null].filter((dependency): dependency is string => Boolean(dependency)),
90
- hooks: {
91
- 'kubb:plugin:setup'(ctx) {
92
- const resolver = userResolver ? { ...resolverClient, ...userResolver } : resolverClient
93
-
94
- ctx.setOptions({
95
- client,
96
- clientType,
97
- bundle,
98
- output,
99
- exclude,
100
- include,
101
- override,
102
- group: groupConfig,
103
- parser,
104
- dataReturnType,
105
- importPath: resolvedImportPath,
106
- baseURL,
107
- paramsType,
108
- paramsCasing,
109
- pathParamsType,
110
- urlType,
111
- sdk,
112
- resolver,
113
- })
114
- ctx.setResolver(resolver)
115
- if (userTransformer) {
116
- ctx.setTransformer(userTransformer)
117
- }
118
- for (const gen of selectedGenerators) {
119
- ctx.addGenerator(gen)
120
- }
121
-
122
- const root = path.resolve(ctx.config.root, ctx.config.output.path)
123
-
124
- const isRelativePath = resolvedImportPath?.startsWith('.')
125
-
126
- if (!isRelativePath) {
127
- const isInlineSource = bundle && !resolvedImportPath
128
-
129
- ctx.injectFile({
130
- baseName: 'client.ts',
131
- path: path.resolve(root, '.kubb/client.ts'),
132
- sources: [
133
- ast.createSource({
134
- name: 'client',
135
- nodes: isInlineSource ? [ast.createText(client === 'fetch' ? fetchClientSource : axiosClientSource)] : [],
136
- isExportable: true,
137
- isIndexable: true,
138
- }),
139
- ],
140
- exports: !isInlineSource && resolvedImportPath ? [ast.createExport({ path: resolvedImportPath })] : [],
141
- })
142
- }
143
-
144
- ctx.injectFile({
145
- baseName: 'config.ts',
146
- path: path.resolve(root, '.kubb/config.ts'),
147
- sources: [
148
- ast.createSource({
149
- name: 'config',
150
- nodes: [ast.createText(configSource)],
151
- isExportable: false,
152
- isIndexable: false,
153
- }),
154
- ],
155
- })
156
- },
157
- },
158
- }
159
- })
160
-
161
- export default pluginClient
@@ -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