@kubb/plugin-client 5.0.0-alpha.9 → 5.0.0-beta.4

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 (61) hide show
  1. package/LICENSE +17 -10
  2. package/README.md +4 -4
  3. package/dist/clients/axios.cjs +2 -2
  4. package/dist/clients/axios.cjs.map +1 -1
  5. package/dist/clients/axios.d.ts +4 -4
  6. package/dist/clients/axios.js +1 -1
  7. package/dist/clients/axios.js.map +1 -1
  8. package/dist/clients/fetch.cjs +1 -1
  9. package/dist/clients/fetch.cjs.map +1 -1
  10. package/dist/clients/fetch.d.ts +2 -2
  11. package/dist/clients/fetch.js +1 -1
  12. package/dist/clients/fetch.js.map +1 -1
  13. package/dist/index.cjs +1739 -97
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.ts +324 -4
  16. package/dist/index.js +1725 -95
  17. package/dist/index.js.map +1 -1
  18. package/dist/templates/clients/axios.source.cjs +1 -1
  19. package/dist/templates/clients/axios.source.js +1 -1
  20. package/dist/templates/clients/fetch.source.cjs +1 -1
  21. package/dist/templates/clients/fetch.source.js +1 -1
  22. package/extension.yaml +776 -0
  23. package/package.json +67 -84
  24. package/src/clients/axios.ts +5 -1
  25. package/src/clients/fetch.ts +5 -1
  26. package/src/components/ClassClient.tsx +45 -142
  27. package/src/components/Client.tsx +90 -129
  28. package/src/components/Operations.tsx +10 -10
  29. package/src/components/StaticClassClient.tsx +44 -138
  30. package/src/components/Url.tsx +38 -48
  31. package/src/components/WrapperClient.tsx +3 -3
  32. package/src/functionParams.ts +118 -0
  33. package/src/generators/classClientGenerator.tsx +148 -171
  34. package/src/generators/clientGenerator.tsx +95 -82
  35. package/src/generators/groupedClientGenerator.tsx +50 -52
  36. package/src/generators/operationsGenerator.tsx +11 -18
  37. package/src/generators/staticClassClientGenerator.tsx +178 -183
  38. package/src/index.ts +9 -2
  39. package/src/plugin.ts +115 -145
  40. package/src/resolvers/resolverClient.ts +22 -0
  41. package/src/types.ts +104 -44
  42. package/src/utils.ts +180 -0
  43. package/templates/clients/axios.ts +5 -2
  44. package/templates/clients/fetch.ts +5 -2
  45. package/dist/StaticClassClient-By-aMAe4.cjs +0 -677
  46. package/dist/StaticClassClient-By-aMAe4.cjs.map +0 -1
  47. package/dist/StaticClassClient-CCn9g9eF.js +0 -636
  48. package/dist/StaticClassClient-CCn9g9eF.js.map +0 -1
  49. package/dist/components.cjs +0 -7
  50. package/dist/components.d.ts +0 -216
  51. package/dist/components.js +0 -2
  52. package/dist/generators-BYUJaeZP.js +0 -723
  53. package/dist/generators-BYUJaeZP.js.map +0 -1
  54. package/dist/generators-DTxD9FDY.cjs +0 -753
  55. package/dist/generators-DTxD9FDY.cjs.map +0 -1
  56. package/dist/generators.cjs +0 -7
  57. package/dist/generators.d.ts +0 -517
  58. package/dist/generators.js +0 -2
  59. package/dist/types-DBQdg-BV.d.ts +0 -169
  60. package/src/components/index.ts +0 -5
  61. package/src/generators/index.ts +0 -5
package/src/plugin.ts CHANGED
@@ -1,189 +1,159 @@
1
1
  import path from 'node:path'
2
2
  import { camelCase } from '@internals/utils'
3
- import { createPlugin, type Group, getBarrelFiles, getMode } from '@kubb/core'
4
- import { OperationGenerator, pluginOasName } from '@kubb/plugin-oas'
3
+
4
+ import { ast, definePlugin, type Group } from '@kubb/core'
5
+ import { pluginTsName } from '@kubb/plugin-ts'
5
6
  import { pluginZodName } from '@kubb/plugin-zod'
6
- import { classClientGenerator, operationsGenerator } from './generators'
7
+ import { classClientGenerator } from './generators/classClientGenerator.tsx'
7
8
  import { clientGenerator } from './generators/clientGenerator.tsx'
8
9
  import { groupedClientGenerator } from './generators/groupedClientGenerator.tsx'
10
+ import { operationsGenerator } from './generators/operationsGenerator.tsx'
9
11
  import { staticClassClientGenerator } from './generators/staticClassClientGenerator.tsx'
12
+ import { resolverClient } from './resolvers/resolverClient.ts'
10
13
  import { source as axiosClientSource } from './templates/clients/axios.source.ts'
11
14
  import { source as fetchClientSource } from './templates/clients/fetch.source.ts'
12
15
  import { source as configSource } from './templates/config.source.ts'
13
16
  import type { PluginClient } from './types.ts'
14
17
 
18
+ /**
19
+ * Canonical plugin name for `@kubb/plugin-client`, used in driver lookups and warnings.
20
+ */
15
21
  export const pluginClientName = 'plugin-client' satisfies PluginClient['name']
16
22
 
17
- export const pluginClient = createPlugin<PluginClient>((options) => {
23
+ /**
24
+ * Generates type-safe HTTP client functions or classes from an OpenAPI specification.
25
+ * Creates client APIs by walking operations and delegating to generators.
26
+ * Writes barrel files based on the configured `barrelType`.
27
+ *
28
+ * @example Client generator
29
+ * ```ts
30
+ * import pluginClient from '@kubb/plugin-client'
31
+ * export default defineConfig({
32
+ * plugins: [pluginClient({ output: { path: 'clients' } })]
33
+ * })
34
+ * ```
35
+ */
36
+ export const pluginClient = definePlugin<PluginClient>((options) => {
18
37
  const {
19
38
  output = { path: 'clients', barrelType: 'named' },
20
39
  group,
21
- urlType = false,
22
40
  exclude = [],
23
41
  include,
24
42
  override = [],
25
- transformers = {},
43
+ urlType = false,
26
44
  dataReturnType = 'data',
27
45
  paramsType = 'inline',
28
46
  pathParamsType = paramsType === 'object' ? 'object' : options.pathParamsType || 'inline',
29
47
  operations = false,
30
- baseURL,
31
48
  paramsCasing,
32
- clientType = 'function',
49
+ clientType = options.sdk ? 'class' : 'function',
33
50
  parser = 'client',
34
51
  client = 'axios',
35
52
  importPath,
36
- contentType,
37
53
  bundle = false,
38
- wrapper,
54
+ sdk,
55
+ baseURL,
56
+ resolver: userResolver,
57
+ transformer: userTransformer,
39
58
  } = options
40
59
 
41
60
  const resolvedImportPath = importPath ?? (!bundle ? `@kubb/plugin-client/clients/${client}` : undefined)
42
61
 
43
- const defaultGenerators = [
44
- clientType === 'staticClass' ? staticClassClientGenerator : clientType === 'class' ? classClientGenerator : clientGenerator,
45
- group && clientType === 'function' ? groupedClientGenerator : undefined,
46
- operations ? operationsGenerator : undefined,
47
- ].filter((x): x is NonNullable<typeof x> => Boolean(x))
48
-
49
- const generators = options.generators ?? defaultGenerators
50
-
51
- return {
52
- name: pluginClientName,
53
- options: {
54
- client,
55
- clientType,
56
- bundle,
57
- output,
58
- group,
59
- parser,
60
- dataReturnType,
61
- importPath: resolvedImportPath,
62
- paramsType,
63
- paramsCasing,
64
- pathParamsType,
65
- baseURL,
66
- urlType,
67
- wrapper,
68
- },
69
- pre: [pluginOasName, parser === 'zod' ? pluginZodName : undefined].filter(Boolean),
70
- resolvePath(baseName, pathMode, options) {
71
- const root = path.resolve(this.config.root, this.config.output.path)
72
- const mode = pathMode ?? getMode(path.resolve(root, output.path))
73
-
74
- if (mode === 'single') {
75
- /**
76
- * when output is a file then we will always append to the same file(output file), see fileManager.addOrAppend
77
- * Other plugins then need to call addOrAppend instead of just add from the fileManager class
78
- */
79
- return path.resolve(root, output.path)
80
- }
81
-
82
- if (group && (options?.group?.path || options?.group?.tag)) {
83
- const groupName: Group['name'] = group?.name
62
+ const selectedGenerators =
63
+ options.generators ??
64
+ [
65
+ clientType === 'staticClass' ? staticClassClientGenerator : clientType === 'class' ? classClientGenerator : clientGenerator,
66
+ group && clientType === 'function' ? groupedClientGenerator : undefined,
67
+ operations ? operationsGenerator : undefined,
68
+ ].filter((x): x is NonNullable<typeof x> => Boolean(x))
69
+
70
+ const groupConfig = group
71
+ ? ({
72
+ ...group,
73
+ name: group.name
84
74
  ? group.name
85
- : (ctx) => {
86
- if (group?.type === 'path') {
75
+ : (ctx: { group: string }) => {
76
+ if (group.type === 'path') {
87
77
  return `${ctx.group.split('/')[1]}`
88
78
  }
89
79
  return `${camelCase(ctx.group)}Controller`
90
- }
91
-
92
- return path.resolve(
93
- root,
94
- output.path,
95
- groupName({
96
- group: group.type === 'path' ? options.group.path! : options.group.tag!,
97
- }),
98
- baseName,
99
- )
100
- }
101
-
102
- return path.resolve(root, output.path, baseName)
103
- },
104
- resolveName(name, type) {
105
- const resolvedName = camelCase(name, { isFile: type === 'file' })
106
-
107
- if (type) {
108
- return transformers?.name?.(resolvedName, type) || resolvedName
109
- }
110
-
111
- return resolvedName
112
- },
113
- async install() {
114
- const root = path.resolve(this.config.root, this.config.output.path)
115
- const mode = getMode(path.resolve(root, output.path))
116
- const oas = await this.getOas()
117
- const baseURL = await this.getBaseURL()
118
-
119
- // pre add bundled fetch
120
- if (bundle && !this.plugin.options.importPath) {
121
- await this.addFile({
122
- baseName: 'fetch.ts',
123
- path: path.resolve(root, '.kubb/fetch.ts'),
124
- sources: [
125
- {
126
- name: 'fetch',
127
- value: this.plugin.options.client === 'fetch' ? fetchClientSource : axiosClientSource,
128
- isExportable: true,
129
- isIndexable: true,
130
80
  },
131
- ],
132
- imports: [],
133
- exports: [],
134
- })
135
- }
81
+ } satisfies Group)
82
+ : undefined
136
83
 
137
- await this.addFile({
138
- baseName: 'config.ts',
139
- path: path.resolve(root, '.kubb/config.ts'),
140
- sources: [
141
- {
142
- name: 'config',
143
- value: configSource,
144
- isExportable: false,
145
- isIndexable: false,
146
- },
147
- ],
148
- imports: [],
149
- exports: [],
150
- })
151
-
152
- const operationGenerator = new OperationGenerator(
153
- baseURL
154
- ? {
155
- ...this.plugin.options,
156
- baseURL,
157
- }
158
- : this.plugin.options,
159
- {
160
- fabric: this.fabric,
161
- oas,
162
- driver: this.driver,
163
- events: this.events,
164
- plugin: this.plugin,
165
- contentType,
84
+ return {
85
+ name: pluginClientName,
86
+ options,
87
+ dependencies: [pluginTsName, parser === 'zod' ? pluginZodName : undefined].filter(Boolean),
88
+ hooks: {
89
+ 'kubb:plugin:setup'(ctx) {
90
+ const resolver = userResolver ? { ...resolverClient, ...userResolver } : resolverClient
91
+
92
+ ctx.setOptions({
93
+ client,
94
+ clientType,
95
+ bundle,
96
+ output,
166
97
  exclude,
167
98
  include,
168
99
  override,
169
- mode,
170
- },
171
- )
172
-
173
- const files = await operationGenerator.build(...generators)
174
-
175
- await this.upsertFile(...files)
176
-
177
- const barrelFiles = await getBarrelFiles(this.fabric.files, {
178
- type: output.barrelType ?? 'named',
179
- root,
180
- output,
181
- meta: {
182
- pluginName: this.plugin.name,
183
- },
184
- })
185
-
186
- await this.upsertFile(...barrelFiles)
100
+ group: groupConfig,
101
+ parser,
102
+ dataReturnType,
103
+ importPath: resolvedImportPath,
104
+ baseURL,
105
+ paramsType,
106
+ paramsCasing,
107
+ pathParamsType,
108
+ urlType,
109
+ sdk,
110
+ resolver,
111
+ })
112
+ ctx.setResolver(resolver)
113
+ if (userTransformer) {
114
+ ctx.setTransformer(userTransformer)
115
+ }
116
+ for (const gen of selectedGenerators) {
117
+ ctx.addGenerator(gen)
118
+ }
119
+
120
+ const root = path.resolve(ctx.config.root, ctx.config.output.path)
121
+
122
+ const isRelativePath = resolvedImportPath?.startsWith('.')
123
+
124
+ if (!isRelativePath) {
125
+ const isInlineSource = bundle && !resolvedImportPath
126
+
127
+ ctx.injectFile({
128
+ baseName: 'client.ts',
129
+ path: path.resolve(root, '.kubb/client.ts'),
130
+ sources: [
131
+ ast.createSource({
132
+ name: 'client',
133
+ nodes: isInlineSource ? [ast.createText(client === 'fetch' ? fetchClientSource : axiosClientSource)] : [],
134
+ isExportable: true,
135
+ isIndexable: true,
136
+ }),
137
+ ],
138
+ exports: !isInlineSource && resolvedImportPath ? [ast.createExport({ path: resolvedImportPath })] : [],
139
+ })
140
+ }
141
+
142
+ ctx.injectFile({
143
+ baseName: 'config.ts',
144
+ path: path.resolve(root, '.kubb/config.ts'),
145
+ sources: [
146
+ ast.createSource({
147
+ name: 'config',
148
+ nodes: [ast.createText(configSource)],
149
+ isExportable: false,
150
+ isIndexable: false,
151
+ }),
152
+ ],
153
+ })
154
+ },
187
155
  },
188
156
  }
189
157
  })
158
+
159
+ export default pluginClient
@@ -0,0 +1,22 @@
1
+ import { camelCase } from '@internals/utils'
2
+ import { defineResolver } from '@kubb/core'
3
+ import type { PluginClient } from '../types.ts'
4
+
5
+ /**
6
+ * Naming convention resolver for client plugin.
7
+ *
8
+ * Provides default naming helpers using camelCase for functions and file paths.
9
+ *
10
+ * @example
11
+ * `resolverClient.default('list pets', 'function') // → 'listPets'`
12
+ */
13
+ export const resolverClient = defineResolver<PluginClient>((ctx) => ({
14
+ name: 'default',
15
+ pluginName: 'plugin-client',
16
+ default(name, type) {
17
+ return camelCase(name, { isFile: type === 'file' })
18
+ },
19
+ resolveName(name) {
20
+ return ctx.default(name, 'function')
21
+ },
22
+ }))
package/src/types.ts CHANGED
@@ -1,8 +1,17 @@
1
- import type { Group, Output, PluginFactoryOptions, ResolveNameParams } from '@kubb/core'
1
+ import type { ast, Exclude, Generator, Group, Include, Output, Override, PluginFactoryOptions, Resolver } from '@kubb/core'
2
2
 
3
- import type { contentType, Oas } from '@kubb/oas'
4
- import type { Exclude, Include, Override, ResolvePathOptions } from '@kubb/plugin-oas'
5
- import type { Generator } from '@kubb/plugin-oas/generators'
3
+ /**
4
+ * The concrete resolver type for `@kubb/plugin-client`.
5
+ * Extends the base `Resolver` with a `resolveName` helper for client function names.
6
+ */
7
+ export type ResolverClient = Resolver & {
8
+ /**
9
+ * Resolves the function name for a given raw operation name.
10
+ * @example Resolving operation names
11
+ * `resolver.resolveName('show pet by id') // -> 'showPetById'`
12
+ */
13
+ resolveName(this: ResolverClient, name: string): string
14
+ }
6
15
 
7
16
  /**
8
17
  * Use either a preset `client` type OR a custom `importPath`, not both.
@@ -36,17 +45,52 @@ export type ClientImportPath =
36
45
  bundle?: never
37
46
  }
38
47
 
48
+ /**
49
+ * Discriminated union that ties `pathParamsType` to the `paramsType` values where it is meaningful.
50
+ *
51
+ * - `paramsType: 'object'` — all parameters (including path params) are merged into a single
52
+ * destructured object. `pathParamsType` is never reached in this code path and has no effect.
53
+ * - `paramsType?: 'inline'` (or omitted) — each parameter group is a separate function argument.
54
+ * `pathParamsType` controls whether the path-param group itself is destructured (`'object'`)
55
+ * or spread as individual arguments (`'inline'`).
56
+ */
57
+ type ParamsTypeOptions =
58
+ | {
59
+ /**
60
+ * All parameters — path, query, headers, and body — are merged into a single
61
+ * destructured object argument.
62
+ * - 'object' returns the params and pathParams as an object.
63
+ * @default 'inline'
64
+ */
65
+ paramsType: 'object'
66
+ /**
67
+ * `pathParamsType` has no effect when `paramsType` is `'object'`.
68
+ * Path params are already inside the single destructured object.
69
+ */
70
+ pathParamsType?: never
71
+ }
72
+ | {
73
+ /**
74
+ * Each parameter group is emitted as a separate function argument.
75
+ * - 'inline' returns the params as comma separated params.
76
+ * @default 'inline'
77
+ */
78
+ paramsType?: 'inline'
79
+ /**
80
+ * Controls how path parameters are arranged within the inline argument list.
81
+ * - 'object' groups path params into a destructured object: `{ petId }: PathParams`.
82
+ * - 'inline' emits each path param as its own argument: `petId: string`.
83
+ * @default 'inline'
84
+ */
85
+ pathParamsType?: 'object' | 'inline'
86
+ }
87
+
39
88
  export type Options = {
40
89
  /**
41
- * Specify the export location for the files and define the behavior of the output
90
+ * Specify the export location for the files and define the behavior of the output.
42
91
  * @default { path: 'clients', barrelType: 'named' }
43
92
  */
44
- output?: Output<Oas>
45
- /**
46
- * Define which contentType should be used.
47
- * By default, the first JSON valid mediaType is used
48
- */
49
- contentType?: contentType
93
+ output?: Output
50
94
  /**
51
95
  * Group the clients based on the provided name.
52
96
  */
@@ -88,25 +132,11 @@ export type Options = {
88
132
  */
89
133
  dataReturnType?: 'data' | 'full'
90
134
  /**
91
- * How to style your params, by default no casing is applied
135
+ * How to style your params, by default no casing is applied.
92
136
  * - 'camelcase' uses camelCase for pathParams, queryParams and headerParams names
93
137
  * @note response types (data/body) are not affected by this option
94
138
  */
95
139
  paramsCasing?: 'camelcase'
96
- /**
97
- * How to pass your params.
98
- * - 'object' returns the params and pathParams as an object.
99
- * - 'inline' returns the params as comma separated params.
100
- * @default 'inline'
101
- */
102
- paramsType?: 'object' | 'inline'
103
- /**
104
- * How to pass your pathParams.
105
- * - 'object' returns the pathParams as an object.
106
- * - 'inline' returns the pathParams as comma separated params.
107
- * @default 'inline'
108
- */
109
- pathParamsType?: 'object' | 'inline'
110
140
  /**
111
141
  * Which parser can be used before returning the data.
112
142
  * - 'client' returns the data as-is from the client.
@@ -130,41 +160,71 @@ export type Options = {
130
160
  */
131
161
  bundle?: boolean
132
162
  /**
133
- * Generate a wrapper class that composes all tag-based client classes into a single entry point.
134
- */
135
- wrapper?: {
163
+ * Generate an SDK facade class that composes all tag-based client classes into a single entry point.
164
+ * Setting this option automatically enables `clientType: 'class'`.
165
+ * @example
166
+ * ```ts
167
+ * pluginClient({
168
+ * sdk: { className: 'PetStoreSDK' },
169
+ * })
170
+ * // Generates a class with a shared constructor config and one property per tag:
171
+ * // class PetStoreSDK {
172
+ * // readonly petController: petController
173
+ * // readonly storeController: storeController
174
+ * // constructor(config = {}) { ... }
175
+ * // }
176
+ * ```
177
+ */
178
+ sdk?: {
136
179
  /**
137
- * Name of the wrapper class.
180
+ * Name of the generated SDK facade class.
138
181
  */
139
182
  className: string
140
183
  }
141
- transformers?: {
142
- /**
143
- * Customize the names based on the type that is provided by the plugin.
144
- */
145
- name?: (name: ResolveNameParams['name'], type?: ResolveNameParams['type']) => string
146
- }
147
184
  /**
148
- * Define some generators next to the client generators
185
+ * Override individual resolver methods. Any method you omit falls back to the
186
+ * preset resolver's implementation. Use `this.default(...)` to call it.
187
+ */
188
+ resolver?: Partial<ResolverClient> & ThisType<ResolverClient>
189
+ /**
190
+ * Single AST visitor applied to each node before printing.
191
+ * Return `null` or `undefined` from a method to leave the node unchanged.
192
+ */
193
+ transformer?: ast.Visitor
194
+ /**
195
+ * Define some generators next to the client generators.
149
196
  */
150
197
  generators?: Array<Generator<PluginClient>>
151
- } & ClientImportPath
198
+ } & ClientImportPath &
199
+ ParamsTypeOptions
152
200
 
153
201
  type ResolvedOptions = {
154
- output: Output<Oas>
155
- group?: Options['group']
156
- baseURL: string | undefined
202
+ output: Output
203
+ exclude: Array<Exclude>
204
+ include: Array<Include> | undefined
205
+ override: Array<Override<ResolvedOptions>>
206
+ group: Group | undefined
157
207
  client: Options['client']
158
208
  clientType: NonNullable<Options['clientType']>
159
209
  bundle: NonNullable<Options['bundle']>
160
210
  parser: NonNullable<Options['parser']>
161
211
  urlType: NonNullable<Options['urlType']>
162
212
  importPath: Options['importPath']
213
+ baseURL: Options['baseURL']
163
214
  dataReturnType: NonNullable<Options['dataReturnType']>
164
- pathParamsType: NonNullable<Options['pathParamsType']>
215
+ pathParamsType: NonNullable<NonNullable<Options['pathParamsType']>>
165
216
  paramsType: NonNullable<Options['paramsType']>
166
217
  paramsCasing: Options['paramsCasing']
167
- wrapper: Options['wrapper']
218
+ sdk: Options['sdk']
219
+ resolver: ResolverClient
168
220
  }
169
221
 
170
- export type PluginClient = PluginFactoryOptions<'plugin-client', Options, ResolvedOptions, never, ResolvePathOptions>
222
+ export type PluginClient = PluginFactoryOptions<'plugin-client', Options, ResolvedOptions, ResolverClient>
223
+
224
+ declare global {
225
+ namespace Kubb {
226
+ interface PluginRegistry {
227
+ 'plugin-client': PluginClient
228
+ }
229
+ }
230
+ }