@kubb/plugin-client 5.0.0-beta.4 → 5.0.0-beta.56
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 +39 -24
- package/dist/clients/axios.cjs +26 -4
- package/dist/clients/axios.cjs.map +1 -1
- package/dist/clients/axios.d.ts +11 -5
- package/dist/clients/axios.js +26 -4
- package/dist/clients/axios.js.map +1 -1
- package/dist/clients/fetch.cjs +77 -9
- package/dist/clients/fetch.cjs.map +1 -1
- package/dist/clients/fetch.d.ts +10 -3
- package/dist/clients/fetch.js +77 -9
- package/dist/clients/fetch.js.map +1 -1
- package/dist/index.cjs +836 -514
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +206 -105
- package/dist/index.js +833 -515
- package/dist/index.js.map +1 -1
- package/dist/templates/clients/axios.source.cjs +1 -1
- package/dist/templates/clients/axios.source.cjs.map +1 -1
- package/dist/templates/clients/axios.source.d.ts +1 -1
- package/dist/templates/clients/axios.source.js +1 -1
- package/dist/templates/clients/axios.source.js.map +1 -1
- package/dist/templates/clients/fetch.source.cjs +1 -1
- package/dist/templates/clients/fetch.source.cjs.map +1 -1
- package/dist/templates/clients/fetch.source.d.ts +1 -1
- package/dist/templates/clients/fetch.source.js +1 -1
- package/dist/templates/clients/fetch.source.js.map +1 -1
- package/dist/templates/config.source.cjs.map +1 -1
- package/dist/templates/config.source.d.ts +1 -1
- package/dist/templates/config.source.js.map +1 -1
- package/package.json +14 -26
- package/src/clients/axios.ts +41 -7
- package/src/clients/fetch.ts +106 -6
- package/src/components/ClassClient.tsx +47 -24
- package/src/components/Client.tsx +100 -71
- package/src/components/StaticClassClient.tsx +47 -24
- package/src/components/Url.tsx +10 -12
- package/src/components/WrapperClient.tsx +9 -5
- package/src/functionParams.ts +8 -8
- package/src/generators/classClientGenerator.tsx +63 -51
- package/src/generators/clientGenerator.tsx +45 -48
- package/src/generators/groupedClientGenerator.tsx +12 -6
- package/src/generators/operationsGenerator.ts +47 -0
- package/src/generators/staticClassClientGenerator.tsx +57 -45
- package/src/index.ts +2 -1
- package/src/plugin.ts +30 -28
- package/src/resolvers/resolverClient.ts +32 -8
- package/src/types.ts +111 -65
- package/src/utils.ts +142 -63
- package/extension.yaml +0 -776
- package/src/components/Operations.tsx +0 -28
- package/src/generators/operationsGenerator.tsx +0 -28
- package/templates/clients/axios.ts +0 -73
- package/templates/clients/fetch.ts +0 -96
- package/templates/config.ts +0 -43
- /package/dist/{chunk-ByKO4r7w.cjs → chunk-Bx3C2hgW.cjs} +0 -0
- /package/dist/{chunk--u3MIqq1.js → chunk-C0LytTxp.js} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { t as __name } from "./chunk
|
|
1
|
+
import { t as __name } from "./chunk-C0LytTxp.js";
|
|
2
2
|
import { source } from "./templates/clients/axios.source.js";
|
|
3
3
|
import { source as source$1 } from "./templates/clients/fetch.source.js";
|
|
4
4
|
import { source as source$2 } from "./templates/config.source.js";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { ast, defineGenerator, definePlugin, defineResolver } from "@kubb/core";
|
|
7
|
+
import { buildJSDoc, stringify } from "@kubb/ast/utils";
|
|
7
8
|
import { functionPrinter, pluginTsName } from "@kubb/plugin-ts";
|
|
8
9
|
import { Const, File, Function, jsxRenderer } from "@kubb/renderer-jsx";
|
|
9
10
|
import { Fragment, jsx, jsxs } from "@kubb/renderer-jsx/jsx-runtime";
|
|
@@ -19,68 +20,57 @@ import { pluginZodName } from "@kubb/plugin-zod";
|
|
|
19
20
|
function toCamelOrPascal(text, pascal) {
|
|
20
21
|
return text.trim().replace(/([a-z\d])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/(\d)([a-z])/g, "$1 $2").split(/[\s\-_./\\:]+/).filter(Boolean).map((word, i) => {
|
|
21
22
|
if (word.length > 1 && word === word.toUpperCase()) return word;
|
|
22
|
-
|
|
23
|
-
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
23
|
+
return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
|
|
24
24
|
}).join("").replace(/[^a-zA-Z0-9]/g, "");
|
|
25
25
|
}
|
|
26
26
|
/**
|
|
27
|
-
* Splits `text` on `.` and applies `transformPart` to each segment.
|
|
28
|
-
* The last segment receives `isLast = true`, all earlier segments receive `false`.
|
|
29
|
-
* Segments are joined with `/` to form a file path.
|
|
30
|
-
*
|
|
31
|
-
* Only splits on dots followed by a letter so that version numbers
|
|
32
|
-
* embedded in operationIds (e.g. `v2025.0`) are kept intact.
|
|
33
|
-
*/
|
|
34
|
-
function applyToFileParts(text, transformPart) {
|
|
35
|
-
const parts = text.split(/\.(?=[a-zA-Z])/);
|
|
36
|
-
return parts.map((part, i) => transformPart(part, i === parts.length - 1)).join("/");
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
27
|
* Converts `text` to camelCase.
|
|
40
|
-
* When `isFile` is `true`, dot-separated segments are each cased independently and joined with `/`.
|
|
41
28
|
*
|
|
42
|
-
* @example
|
|
43
|
-
* camelCase('hello-world')
|
|
44
|
-
*
|
|
29
|
+
* @example Word boundaries
|
|
30
|
+
* `camelCase('hello-world') // 'helloWorld'`
|
|
31
|
+
*
|
|
32
|
+
* @example With a prefix
|
|
33
|
+
* `camelCase('tag', { prefix: 'create' }) // 'createTag'`
|
|
45
34
|
*/
|
|
46
|
-
function camelCase(text, {
|
|
47
|
-
if (isFile) return applyToFileParts(text, (part, isLast) => camelCase(part, isLast ? {
|
|
48
|
-
prefix,
|
|
49
|
-
suffix
|
|
50
|
-
} : {}));
|
|
35
|
+
function camelCase(text, { prefix = "", suffix = "" } = {}) {
|
|
51
36
|
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
|
|
52
37
|
}
|
|
53
38
|
/**
|
|
54
39
|
* Converts `text` to PascalCase.
|
|
55
|
-
* When `isFile` is `true`, the last dot-separated segment is PascalCased and earlier segments are camelCased.
|
|
56
40
|
*
|
|
57
|
-
* @example
|
|
58
|
-
* pascalCase('hello-world')
|
|
59
|
-
*
|
|
41
|
+
* @example Word boundaries
|
|
42
|
+
* `pascalCase('hello-world') // 'HelloWorld'`
|
|
43
|
+
*
|
|
44
|
+
* @example With a suffix
|
|
45
|
+
* `pascalCase('tag', { suffix: 'schema' }) // 'TagSchema'`
|
|
60
46
|
*/
|
|
61
|
-
function pascalCase(text, {
|
|
62
|
-
if (isFile) return applyToFileParts(text, (part, isLast) => isLast ? pascalCase(part, {
|
|
63
|
-
prefix,
|
|
64
|
-
suffix
|
|
65
|
-
}) : camelCase(part));
|
|
47
|
+
function pascalCase(text, { prefix = "", suffix = "" } = {}) {
|
|
66
48
|
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
|
|
67
49
|
}
|
|
68
50
|
//#endregion
|
|
69
|
-
//#region ../../internals/utils/src/
|
|
51
|
+
//#region ../../internals/utils/src/fs.ts
|
|
70
52
|
/**
|
|
71
|
-
* Builds a
|
|
72
|
-
*
|
|
53
|
+
* Builds a nested file path from a dotted name. Splits on dots that precede a letter
|
|
54
|
+
* (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
|
|
55
|
+
* every earlier segment, applies `caseLast` to the final segment, and joins with `/`.
|
|
73
56
|
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
57
|
+
* Empty segments are dropped before joining. They arise when the name starts with a dot
|
|
58
|
+
* followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to
|
|
59
|
+
* an empty string). Without this a leading `/` would form, which `path.resolve` reads as an
|
|
60
|
+
* absolute path, letting generated files escape the configured output directory.
|
|
61
|
+
*
|
|
62
|
+
* @example Nested path from a dotted name
|
|
63
|
+
* `toFilePath('pet.petId') // 'pet/petId'`
|
|
64
|
+
*
|
|
65
|
+
* @example PascalCase the final segment
|
|
66
|
+
* `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`
|
|
67
|
+
*
|
|
68
|
+
* @example Suffix applied to the final segment only
|
|
69
|
+
* `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
|
|
79
70
|
*/
|
|
80
|
-
function
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
return `/**\n${comments.map((c) => `${indent}${c}`).join("\n")}\n */${suffix}`;
|
|
71
|
+
function toFilePath(name, caseLast = camelCase) {
|
|
72
|
+
const parts = name.split(/\.(?=[a-zA-Z])/);
|
|
73
|
+
return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
|
|
84
74
|
}
|
|
85
75
|
//#endregion
|
|
86
76
|
//#region ../../internals/utils/src/reserved.ts
|
|
@@ -185,100 +175,104 @@ function isValidVarName(name) {
|
|
|
185
175
|
if (!name || reservedWords.has(name)) return false;
|
|
186
176
|
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
|
|
187
177
|
}
|
|
188
|
-
//#endregion
|
|
189
|
-
//#region ../../internals/utils/src/urlPath.ts
|
|
190
178
|
/**
|
|
191
|
-
*
|
|
179
|
+
* Returns `name` when it's a syntactically valid JavaScript variable name,
|
|
180
|
+
* otherwise prefixes it with `_` so the result is a valid identifier.
|
|
181
|
+
*
|
|
182
|
+
* Useful for sanitizing OpenAPI schema names or operation IDs that start with
|
|
183
|
+
* a digit (e.g. `409`, `504AccountCancel`) before using them as exported
|
|
184
|
+
* variable, type, or function names.
|
|
192
185
|
*
|
|
193
186
|
* @example
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
*
|
|
187
|
+
* ```ts
|
|
188
|
+
* ensureValidVarName('409') // '_409'
|
|
189
|
+
* ensureValidVarName('504AccountCancel') // '_504AccountCancel'
|
|
190
|
+
* ensureValidVarName('Pet') // 'Pet'
|
|
191
|
+
* ensureValidVarName('class') // '_class'
|
|
192
|
+
* ```
|
|
197
193
|
*/
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
194
|
+
function ensureValidVarName(name) {
|
|
195
|
+
if (!name || isValidVarName(name)) return name;
|
|
196
|
+
return `_${name}`;
|
|
197
|
+
}
|
|
198
|
+
//#endregion
|
|
199
|
+
//#region ../../internals/utils/src/url.ts
|
|
200
|
+
function transformParam(raw, casing) {
|
|
201
|
+
const param = isValidVarName(raw) ? raw : camelCase(raw);
|
|
202
|
+
return casing === "camelcase" ? camelCase(param) : param;
|
|
203
|
+
}
|
|
204
|
+
function toParamsObject(path, { replacer, casing } = {}) {
|
|
205
|
+
const params = {};
|
|
206
|
+
for (const match of path.matchAll(/\{([^}]+)\}/g)) {
|
|
207
|
+
const param = transformParam(match[1], casing);
|
|
208
|
+
const key = replacer ? replacer(param) : param;
|
|
209
|
+
params[key] = key;
|
|
207
210
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
return this.toURLPath();
|
|
211
|
+
return Object.keys(params).length > 0 ? params : null;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Helpers for OpenAPI/Swagger paths, plus a thin wrapper over the native `URL`.
|
|
215
|
+
*/
|
|
216
|
+
var Url$1 = class Url$1 {
|
|
217
|
+
static {
|
|
218
|
+
__name(this, "Url");
|
|
217
219
|
}
|
|
218
|
-
/**
|
|
220
|
+
/**
|
|
221
|
+
* Reports whether `url` is a parseable absolute URL. Delegates to the native `URL.canParse`.
|
|
219
222
|
*
|
|
220
223
|
* @example
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
* new URLPath('/pet/{petId}').isURL // false
|
|
224
|
-
* ```
|
|
224
|
+
* Url.canParse('https://petstore.swagger.io/v2') // true
|
|
225
|
+
* Url.canParse('/pet/{petId}') // false
|
|
225
226
|
*/
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
return !!new URL(this.path).href;
|
|
229
|
-
} catch {
|
|
230
|
-
return false;
|
|
231
|
-
}
|
|
227
|
+
static canParse(url, base) {
|
|
228
|
+
return URL.canParse(url, base);
|
|
232
229
|
}
|
|
233
230
|
/**
|
|
234
|
-
* Converts
|
|
231
|
+
* Converts an OpenAPI/Swagger path to Express-style colon syntax.
|
|
235
232
|
*
|
|
236
233
|
* @example
|
|
237
|
-
*
|
|
238
|
-
* new URLPath('/account/monetary-accountID').template // '`/account/${monetaryAccountId}`'
|
|
234
|
+
* Url.toPath('/pet/{petId}') // '/pet/:petId'
|
|
239
235
|
*/
|
|
240
|
-
|
|
241
|
-
return
|
|
236
|
+
static toPath(path) {
|
|
237
|
+
return path.replace(/\{([^}]+)\}/g, ":$1");
|
|
242
238
|
}
|
|
243
|
-
/**
|
|
239
|
+
/**
|
|
240
|
+
* Converts an OpenAPI/Swagger path to a TypeScript template literal string.
|
|
241
|
+
* `prefix` is prepended inside the literal, `replacer` transforms each parameter name,
|
|
242
|
+
* and `casing` controls parameter identifier casing.
|
|
244
243
|
*
|
|
245
244
|
* @example
|
|
246
|
-
*
|
|
247
|
-
* new URLPath('/pet/{petId}').object
|
|
248
|
-
* // { url: '/pet/:petId', params: { petId: 'petId' } }
|
|
249
|
-
* ```
|
|
250
|
-
*/
|
|
251
|
-
get object() {
|
|
252
|
-
return this.toObject();
|
|
253
|
-
}
|
|
254
|
-
/** Returns a map of path parameter names, or `undefined` when the path has no parameters.
|
|
245
|
+
* Url.toTemplateString('/pet/{petId}') // '`/pet/${petId}`'
|
|
255
246
|
*
|
|
256
247
|
* @example
|
|
257
|
-
*
|
|
258
|
-
* new URLPath('/pet/{petId}').params // { petId: 'petId' }
|
|
259
|
-
* new URLPath('/pet').params // undefined
|
|
260
|
-
* ```
|
|
248
|
+
* Url.toTemplateString('/pet/{petId}', { prefix: 'https://api' }) // '`https://api/pet/${petId}`'
|
|
261
249
|
*/
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
250
|
+
static toTemplateString(path, { prefix, replacer, casing } = {}) {
|
|
251
|
+
const result = path.split(/\{([^}]+)\}/).map((part, i) => {
|
|
252
|
+
if (i % 2 === 0) return part;
|
|
253
|
+
const param = transformParam(part, casing);
|
|
254
|
+
return `\${${replacer ? replacer(param) : param}}`;
|
|
255
|
+
}).join("");
|
|
256
|
+
return `\`${prefix ?? ""}${result}\``;
|
|
268
257
|
}
|
|
269
258
|
/**
|
|
270
|
-
*
|
|
259
|
+
* Returns the path and its extracted params as a structured `URLObject`, or as a stringified
|
|
260
|
+
* expression when `stringify` is set.
|
|
261
|
+
*
|
|
262
|
+
* @example
|
|
263
|
+
* Url.toObject('/pet/{petId}')
|
|
264
|
+
* // { url: '/pet/:petId', params: { petId: 'petId' } }
|
|
271
265
|
*/
|
|
272
|
-
|
|
273
|
-
for (const match of this.path.matchAll(/\{([^}]+)\}/g)) {
|
|
274
|
-
const raw = match[1];
|
|
275
|
-
fn(raw, this.#transformParam(raw));
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
toObject({ type = "path", replacer, stringify } = {}) {
|
|
266
|
+
static toObject(path, { type = "path", replacer, stringify, casing } = {}) {
|
|
279
267
|
const object = {
|
|
280
|
-
url: type === "path" ?
|
|
281
|
-
|
|
268
|
+
url: type === "path" ? Url$1.toPath(path) : Url$1.toTemplateString(path, {
|
|
269
|
+
replacer,
|
|
270
|
+
casing
|
|
271
|
+
}),
|
|
272
|
+
params: toParamsObject(path, {
|
|
273
|
+
replacer,
|
|
274
|
+
casing
|
|
275
|
+
})
|
|
282
276
|
};
|
|
283
277
|
if (stringify) {
|
|
284
278
|
if (type === "template") return JSON.stringify(object).replaceAll("'", "").replaceAll(`"`, "");
|
|
@@ -287,51 +281,211 @@ var URLPath = class {
|
|
|
287
281
|
}
|
|
288
282
|
return object;
|
|
289
283
|
}
|
|
290
|
-
/**
|
|
291
|
-
* Converts the OpenAPI path to a TypeScript template literal string.
|
|
292
|
-
* An optional `replacer` can transform each extracted parameter name before interpolation.
|
|
293
|
-
*
|
|
294
|
-
* @example
|
|
295
|
-
* new URLPath('/pet/{petId}').toTemplateString() // '`/pet/${petId}`'
|
|
296
|
-
*/
|
|
297
|
-
toTemplateString({ prefix = "", replacer } = {}) {
|
|
298
|
-
return `\`${prefix}${this.path.split(/\{([^}]+)\}/).map((part, i) => {
|
|
299
|
-
if (i % 2 === 0) return part;
|
|
300
|
-
const param = this.#transformParam(part);
|
|
301
|
-
return `\${${replacer ? replacer(param) : param}}`;
|
|
302
|
-
}).join("")}\``;
|
|
303
|
-
}
|
|
304
|
-
/**
|
|
305
|
-
* Extracts all `{param}` segments from the path and returns them as a key-value map.
|
|
306
|
-
* An optional `replacer` transforms each parameter name in both key and value positions.
|
|
307
|
-
* Returns `undefined` when no path parameters are found.
|
|
308
|
-
*
|
|
309
|
-
* @example
|
|
310
|
-
* ```ts
|
|
311
|
-
* new URLPath('/pet/{petId}/tag/{tagId}').getParams()
|
|
312
|
-
* // { petId: 'petId', tagId: 'tagId' }
|
|
313
|
-
* ```
|
|
314
|
-
*/
|
|
315
|
-
getParams(replacer) {
|
|
316
|
-
const params = {};
|
|
317
|
-
this.#eachParam((_raw, param) => {
|
|
318
|
-
const key = replacer ? replacer(param) : param;
|
|
319
|
-
params[key] = key;
|
|
320
|
-
});
|
|
321
|
-
return Object.keys(params).length > 0 ? params : void 0;
|
|
322
|
-
}
|
|
323
|
-
/** Converts the OpenAPI path to Express-style colon syntax.
|
|
324
|
-
*
|
|
325
|
-
* @example
|
|
326
|
-
* ```ts
|
|
327
|
-
* new URLPath('/pet/{petId}').toURLPath() // '/pet/:petId'
|
|
328
|
-
* ```
|
|
329
|
-
*/
|
|
330
|
-
toURLPath() {
|
|
331
|
-
return this.path.replace(/\{([^}]+)\}/g, ":$1");
|
|
332
|
-
}
|
|
333
284
|
};
|
|
334
285
|
//#endregion
|
|
286
|
+
//#region ../../internals/shared/src/operation.ts
|
|
287
|
+
/**
|
|
288
|
+
* Builds the `ResolverFileParams` every operation generator passes to
|
|
289
|
+
* `resolver.resolveFile`: a file named `name`, tagged by the operation's first
|
|
290
|
+
* tag (or `'default'`), at the operation's path. Centralizes the entry object
|
|
291
|
+
* that was repeated at dozens of call sites across the client and query plugins.
|
|
292
|
+
*
|
|
293
|
+
* @example
|
|
294
|
+
* ```ts
|
|
295
|
+
* resolver.resolveFile(operationFileEntry(node, node.operationId), { root, output, group })
|
|
296
|
+
* ```
|
|
297
|
+
*/
|
|
298
|
+
function operationFileEntry(node, name, extname = ".ts") {
|
|
299
|
+
return {
|
|
300
|
+
name,
|
|
301
|
+
extname,
|
|
302
|
+
tag: node.tags[0] ?? "default",
|
|
303
|
+
path: node.path
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
function getOperationLink(node, link) {
|
|
307
|
+
if (!link) return null;
|
|
308
|
+
if (typeof link === "function") return link(node) ?? null;
|
|
309
|
+
if (link === "urlPath") return node.path ? `{@link ${Url$1.toPath(node.path)}}` : null;
|
|
310
|
+
return node.path ? `{@link ${node.path.replaceAll("{", ":").replaceAll("}", "")}}` : null;
|
|
311
|
+
}
|
|
312
|
+
function getContentTypeInfo(node) {
|
|
313
|
+
const contentTypes = node.requestBody?.content?.map((e) => e.contentType) ?? [];
|
|
314
|
+
const isMultipleContentTypes = contentTypes.length > 1;
|
|
315
|
+
return {
|
|
316
|
+
contentTypes,
|
|
317
|
+
isMultipleContentTypes,
|
|
318
|
+
contentTypeUnion: isMultipleContentTypes ? contentTypes.map((ct) => JSON.stringify(ct)).join(" | ") : "",
|
|
319
|
+
defaultContentType: contentTypes[0] ?? "application/json",
|
|
320
|
+
hasFormData: contentTypes.some((ct) => ct === "multipart/form-data")
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Derives the default `responseType` for an operation from its primary success response.
|
|
325
|
+
*
|
|
326
|
+
* Returns a value only when that response declares a single non-JSON content type — a binary type
|
|
327
|
+
* (`application/octet-stream`, `application/pdf`, `image/*`, `audio/*`, `video/*`) maps to `'blob'`
|
|
328
|
+
* and other `text/*` maps to `'text'`. Otherwise `undefined`, leaving the runtime client's
|
|
329
|
+
* `Content-Type` auto-detection in charge.
|
|
330
|
+
*/
|
|
331
|
+
function getResponseType(node) {
|
|
332
|
+
const contentTypes = getPrimarySuccessResponse(node)?.content?.map((entry) => entry.contentType) ?? [];
|
|
333
|
+
if (contentTypes.length !== 1) return void 0;
|
|
334
|
+
const baseType = contentTypes[0].split(";")[0].trim().toLowerCase();
|
|
335
|
+
if (baseType === "application/json" || baseType.endsWith("+json") || baseType === "text/json") return void 0;
|
|
336
|
+
if (baseType.startsWith("text/")) return "text";
|
|
337
|
+
if (baseType === "application/octet-stream" || baseType === "application/pdf" || /^(image|audio|video)\//.test(baseType)) return "blob";
|
|
338
|
+
}
|
|
339
|
+
function buildRequestConfigType(node, resolver) {
|
|
340
|
+
const requestName = node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : null;
|
|
341
|
+
const { isMultipleContentTypes, contentTypeUnion } = getContentTypeInfo(node);
|
|
342
|
+
return `${requestName ? `Partial<RequestConfig<${requestName}>>` : "Partial<RequestConfig>"} & { ${["client?: Client", isMultipleContentTypes ? `contentType?: ${contentTypeUnion}` : null].filter(Boolean).join("; ")} }`;
|
|
343
|
+
}
|
|
344
|
+
function buildOperationComments(node, options = {}) {
|
|
345
|
+
const { link = "pathTemplate", linkPosition = "afterDeprecated", splitLines = false } = options;
|
|
346
|
+
const linkComment = getOperationLink(node, link);
|
|
347
|
+
const filteredComments = (linkPosition === "beforeDeprecated" ? [
|
|
348
|
+
node.description && `@description ${node.description}`,
|
|
349
|
+
node.summary && `@summary ${node.summary}`,
|
|
350
|
+
linkComment,
|
|
351
|
+
node.deprecated && "@deprecated"
|
|
352
|
+
] : [
|
|
353
|
+
node.description && `@description ${node.description}`,
|
|
354
|
+
node.summary && `@summary ${node.summary}`,
|
|
355
|
+
node.deprecated && "@deprecated",
|
|
356
|
+
linkComment
|
|
357
|
+
]).filter((comment) => Boolean(comment));
|
|
358
|
+
if (!splitLines) return filteredComments;
|
|
359
|
+
return filteredComments.flatMap((text) => text.split(/\r?\n/).map((line) => line.trim())).filter((comment) => Boolean(comment));
|
|
360
|
+
}
|
|
361
|
+
function getOperationParameters(node, options = {}) {
|
|
362
|
+
const params = ast.caseParams(node.parameters, options.paramsCasing);
|
|
363
|
+
return {
|
|
364
|
+
path: params.filter((param) => param.in === "path"),
|
|
365
|
+
query: params.filter((param) => param.in === "query"),
|
|
366
|
+
header: params.filter((param) => param.in === "header"),
|
|
367
|
+
cookie: params.filter((param) => param.in === "cookie")
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
function getStatusCodeNumber(statusCode) {
|
|
371
|
+
const code = Number(statusCode);
|
|
372
|
+
return Number.isNaN(code) ? null : code;
|
|
373
|
+
}
|
|
374
|
+
function isSuccessStatusCode(statusCode) {
|
|
375
|
+
const code = getStatusCodeNumber(statusCode);
|
|
376
|
+
return code !== null && code >= 200 && code < 300;
|
|
377
|
+
}
|
|
378
|
+
function isErrorStatusCode(statusCode) {
|
|
379
|
+
const code = getStatusCodeNumber(statusCode);
|
|
380
|
+
return code !== null && code >= 400;
|
|
381
|
+
}
|
|
382
|
+
function getSuccessResponses(responses) {
|
|
383
|
+
return responses.filter((response) => isSuccessStatusCode(response.statusCode));
|
|
384
|
+
}
|
|
385
|
+
function getOperationSuccessResponses(node) {
|
|
386
|
+
return getSuccessResponses(node.responses);
|
|
387
|
+
}
|
|
388
|
+
function getPrimarySuccessResponse(node) {
|
|
389
|
+
return getOperationSuccessResponses(node)[0] ?? null;
|
|
390
|
+
}
|
|
391
|
+
function resolveErrorNames(node, resolver) {
|
|
392
|
+
return node.responses.filter((response) => isErrorStatusCode(response.statusCode)).map((response) => resolver.resolveResponseStatusName(node, response.statusCode));
|
|
393
|
+
}
|
|
394
|
+
function resolveSuccessNames(node, resolver) {
|
|
395
|
+
return node.responses.filter((response) => isSuccessStatusCode(response.statusCode)).map((response) => resolver.resolveResponseStatusName(node, response.statusCode));
|
|
396
|
+
}
|
|
397
|
+
function resolveStatusCodeNames(node, resolver) {
|
|
398
|
+
return node.responses.map((response) => resolver.resolveResponseStatusName(node, response.statusCode));
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Builds the discriminated union type string for `dataReturnType: 'full'` return shapes.
|
|
402
|
+
* Each member is `{ status: N; data: StatusNType; statusText: string }`.
|
|
403
|
+
*/
|
|
404
|
+
function buildStatusUnionType(node, resolver) {
|
|
405
|
+
const members = node.responses.map((r) => {
|
|
406
|
+
const typeName = resolver.resolveResponseStatusName(node, r.statusCode);
|
|
407
|
+
const statusCode = Number.parseInt(r.statusCode, 10);
|
|
408
|
+
return `{ status: ${Number.isNaN(statusCode) ? "number" : String(statusCode)}; data: ${typeName}; statusText: string }`;
|
|
409
|
+
});
|
|
410
|
+
if (members.length === 1) return members[0];
|
|
411
|
+
return `(${members.join(" | ")})`;
|
|
412
|
+
}
|
|
413
|
+
const typeNamesByResolver = /* @__PURE__ */ new WeakMap();
|
|
414
|
+
function resolveOperationTypeNames(node, resolver, options = {}) {
|
|
415
|
+
const cacheKey = `${node.operationId}\0${options.paramsCasing ?? ""}\0${options.order ?? ""}\0${options.responseStatusNames ?? ""}\0${(options.exclude ?? []).join(",")}`;
|
|
416
|
+
let byResolver = typeNamesByResolver.get(resolver);
|
|
417
|
+
if (byResolver) {
|
|
418
|
+
const cached = byResolver.get(cacheKey);
|
|
419
|
+
if (cached) return cached;
|
|
420
|
+
} else {
|
|
421
|
+
byResolver = /* @__PURE__ */ new Map();
|
|
422
|
+
typeNamesByResolver.set(resolver, byResolver);
|
|
423
|
+
}
|
|
424
|
+
const { path, query, header } = getOperationParameters(node, { paramsCasing: options.paramsCasing });
|
|
425
|
+
const responseStatusNames = options.responseStatusNames === "error" ? resolveErrorNames(node, resolver) : options.responseStatusNames === false ? [] : resolveStatusCodeNames(node, resolver);
|
|
426
|
+
const exclude = new Set(options.exclude ?? []);
|
|
427
|
+
const paramNames = [
|
|
428
|
+
...path.map((param) => resolver.resolvePathParamsName(node, param)),
|
|
429
|
+
...query.map((param) => resolver.resolveQueryParamsName(node, param)),
|
|
430
|
+
...header.map((param) => resolver.resolveHeaderParamsName(node, param))
|
|
431
|
+
];
|
|
432
|
+
const bodyAndResponseNames = [node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : null, resolver.resolveResponseName(node)];
|
|
433
|
+
const result = (options.order === "body-response-first" ? [
|
|
434
|
+
...bodyAndResponseNames,
|
|
435
|
+
...paramNames,
|
|
436
|
+
...responseStatusNames
|
|
437
|
+
] : [
|
|
438
|
+
...paramNames,
|
|
439
|
+
...bodyAndResponseNames,
|
|
440
|
+
...responseStatusNames
|
|
441
|
+
]).filter((name) => Boolean(name) && !exclude.has(name));
|
|
442
|
+
byResolver.set(cacheKey, result);
|
|
443
|
+
return result;
|
|
444
|
+
}
|
|
445
|
+
//#endregion
|
|
446
|
+
//#region ../../internals/shared/src/group.ts
|
|
447
|
+
/**
|
|
448
|
+
* Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
|
|
449
|
+
* shared default naming so every plugin groups output consistently:
|
|
450
|
+
*
|
|
451
|
+
* - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).
|
|
452
|
+
* - other groups use the camelCased group (`pet store` → `petStore`).
|
|
453
|
+
*
|
|
454
|
+
* A user-provided `group.name` always wins over the default namer, so callers stay in
|
|
455
|
+
* control of their output folders. Returns `null` when grouping is disabled, matching the
|
|
456
|
+
* per-plugin convention.
|
|
457
|
+
*
|
|
458
|
+
* @param group - The user-supplied group option, or `undefined` to disable grouping.
|
|
459
|
+
*
|
|
460
|
+
* @example
|
|
461
|
+
* ```ts
|
|
462
|
+
* createGroupConfig(group) // shared across every plugin
|
|
463
|
+
* ```
|
|
464
|
+
*/
|
|
465
|
+
function createGroupConfig(group) {
|
|
466
|
+
if (!group) return null;
|
|
467
|
+
const defaultName = (ctx) => {
|
|
468
|
+
if (group.type === "path") return `${ctx.group.split("/")[1]}`;
|
|
469
|
+
return camelCase(ctx.group);
|
|
470
|
+
};
|
|
471
|
+
return {
|
|
472
|
+
...group,
|
|
473
|
+
name: group.name ? group.name : defaultName
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
//#endregion
|
|
477
|
+
//#region ../../internals/shared/src/params.ts
|
|
478
|
+
function buildParamsMapping(originalParams, mappedParams) {
|
|
479
|
+
const mapping = {};
|
|
480
|
+
let hasChanged = false;
|
|
481
|
+
originalParams.forEach((param, i) => {
|
|
482
|
+
const mappedName = mappedParams[i]?.name ?? param.name;
|
|
483
|
+
mapping[param.name] = mappedName;
|
|
484
|
+
if (param.name !== mappedName) hasChanged = true;
|
|
485
|
+
});
|
|
486
|
+
return hasChanged ? mapping : null;
|
|
487
|
+
}
|
|
488
|
+
//#endregion
|
|
335
489
|
//#region src/functionParams.ts
|
|
336
490
|
const declarationPrinter$4 = functionPrinter({ mode: "declaration" });
|
|
337
491
|
const callPrinter = functionPrinter({ mode: "call" });
|
|
@@ -342,18 +496,18 @@ function createType(type) {
|
|
|
342
496
|
return type ? ast.createParamsType({
|
|
343
497
|
variant: "reference",
|
|
344
498
|
name: type
|
|
345
|
-
}) :
|
|
499
|
+
}) : null;
|
|
346
500
|
}
|
|
347
501
|
function createDeclarationLeaf(name, spec) {
|
|
348
502
|
if (spec.default !== void 0) return ast.createFunctionParameter({
|
|
349
503
|
name,
|
|
350
|
-
type: createType(spec.type),
|
|
504
|
+
type: createType(spec.type) ?? void 0,
|
|
351
505
|
default: spec.default,
|
|
352
506
|
rest: spec.mode === "inlineSpread"
|
|
353
507
|
});
|
|
354
508
|
return ast.createFunctionParameter({
|
|
355
509
|
name,
|
|
356
|
-
type: createType(spec.type),
|
|
510
|
+
type: createType(spec.type) ?? void 0,
|
|
357
511
|
optional: !!spec.optional,
|
|
358
512
|
rest: spec.mode === "inlineSpread"
|
|
359
513
|
});
|
|
@@ -362,14 +516,14 @@ function createDeclarationParam(name, spec) {
|
|
|
362
516
|
if (isGroup(spec)) return ast.createParameterGroup({
|
|
363
517
|
inline: spec.mode === "inlineSpread",
|
|
364
518
|
default: spec.default,
|
|
365
|
-
properties: Object.entries(spec.children).filter(([, child]) => child
|
|
519
|
+
properties: Object.entries(spec.children).filter(([, child]) => child != null).map(([childName, child]) => createDeclarationLeaf(childName, child))
|
|
366
520
|
});
|
|
367
521
|
return createDeclarationLeaf(name, spec);
|
|
368
522
|
}
|
|
369
523
|
function createCallParam(name, spec) {
|
|
370
524
|
if (isGroup(spec)) return ast.createParameterGroup({
|
|
371
525
|
inline: spec.mode === "inlineSpread",
|
|
372
|
-
properties: Object.entries(spec.children).filter(([, child]) => child
|
|
526
|
+
properties: Object.entries(spec.children).filter(([, child]) => child != null).map(([childName, child]) => ast.createFunctionParameter({
|
|
373
527
|
name: child?.mode === "inlineSpread" ? spec.mode === "inlineSpread" ? child.value ?? childName : `...${child.value ?? childName}` : child?.value ? `${childName}: ${child.value}` : childName,
|
|
374
528
|
rest: spec.mode === "inlineSpread" && child?.mode === "inlineSpread"
|
|
375
529
|
}))
|
|
@@ -384,7 +538,7 @@ function createCallParam(name, spec) {
|
|
|
384
538
|
* Returns utilities to output constructor signatures (`toConstructor()`) or call expressions (`toCall()`).
|
|
385
539
|
*/
|
|
386
540
|
function createFunctionParams(params) {
|
|
387
|
-
const entries = Object.entries(params).filter(([, spec]) => spec
|
|
541
|
+
const entries = Object.entries(params).filter(([, spec]) => spec != null);
|
|
388
542
|
return {
|
|
389
543
|
toConstructor() {
|
|
390
544
|
return declarationPrinter$4.print(ast.createFunctionParameters({ params: entries.map(([name, spec]) => createDeclarationParam(name, spec)) })) ?? "";
|
|
@@ -397,83 +551,118 @@ function createFunctionParams(params) {
|
|
|
397
551
|
//#endregion
|
|
398
552
|
//#region src/utils.ts
|
|
399
553
|
/**
|
|
400
|
-
*
|
|
401
|
-
* Includes description, summary, link, and deprecation information.
|
|
554
|
+
* Returns `true` when any direction of the parser uses Zod (used for dependency checks).
|
|
402
555
|
*/
|
|
403
|
-
function
|
|
404
|
-
return
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
node.path && `{@link ${new URLPath(node.path).URL}}`,
|
|
408
|
-
node.deprecated && "@deprecated"
|
|
409
|
-
].filter((x) => Boolean(x)).flatMap((text) => text.split(/\r?\n/).map((line) => line.trim())).filter((x) => Boolean(x));
|
|
556
|
+
function isParserEnabled(parser) {
|
|
557
|
+
if (!parser) return false;
|
|
558
|
+
if (parser === "zod") return true;
|
|
559
|
+
return !!(parser.request || parser.response);
|
|
410
560
|
}
|
|
411
561
|
/**
|
|
412
|
-
*
|
|
413
|
-
*
|
|
562
|
+
* Returns `'zod'` when request body parsing is enabled, `null` otherwise.
|
|
563
|
+
* The string shorthand `'zod'` also enables request body parsing (existing behavior).
|
|
414
564
|
*/
|
|
415
|
-
function
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
565
|
+
function resolveRequestParser(parser) {
|
|
566
|
+
if (!parser) return null;
|
|
567
|
+
if (parser === "zod") return "zod";
|
|
568
|
+
return parser.request ?? null;
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Returns `'zod'` when query-parameters parsing is enabled, `null` otherwise.
|
|
572
|
+
* Only the object form `{ request: 'zod' }` enables query-params parsing.
|
|
573
|
+
* The string shorthand `'zod'` does not, preserving its existing behavior.
|
|
574
|
+
*/
|
|
575
|
+
function resolveQueryParamsParser(parser) {
|
|
576
|
+
if (!parser || parser === "zod") return null;
|
|
577
|
+
return parser.request ?? null;
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Returns `'zod'` when response-direction parsing is enabled, `null` otherwise.
|
|
581
|
+
* `parser: 'zod'` (string shorthand) maps to response parsing and returns `'zod'`.
|
|
582
|
+
*/
|
|
583
|
+
function resolveResponseParser(parser) {
|
|
584
|
+
if (!parser) return null;
|
|
585
|
+
if (parser === "zod") return "zod";
|
|
586
|
+
return parser.response ?? null;
|
|
424
587
|
}
|
|
425
588
|
/**
|
|
426
589
|
* Builds HTTP headers array for a client request.
|
|
427
590
|
* Includes Content-Type (if not default) and spreads header parameters if present.
|
|
428
591
|
*/
|
|
429
592
|
function buildHeaders(contentType, hasHeaderParams) {
|
|
430
|
-
return [contentType !== "application/json" && contentType !== "multipart/form-data" ? `'Content-Type': '${contentType}'` :
|
|
593
|
+
return [contentType !== "application/json" && contentType !== "multipart/form-data" ? `'Content-Type': '${contentType}'` : null, hasHeaderParams ? "...headers" : null].filter(Boolean);
|
|
431
594
|
}
|
|
432
595
|
/**
|
|
433
|
-
*
|
|
434
|
-
*
|
|
596
|
+
* Returns the generic type arguments — response, error, and request body — for a generated
|
|
597
|
+
* client call.
|
|
598
|
+
*
|
|
599
|
+
* When `dataReturnType` is `'full'`, the response generic widens to a union of all documented
|
|
600
|
+
* status types. When `parser` is `'zod'` and a request body schema exists, the request type
|
|
601
|
+
* uses `z.output<typeof schema>` to reflect post-transform types (e.g. date coercion).
|
|
435
602
|
*/
|
|
436
|
-
function buildGenerics(node, tsResolver) {
|
|
437
|
-
const
|
|
438
|
-
const
|
|
603
|
+
function buildGenerics(node, tsResolver, options = {}) {
|
|
604
|
+
const allStatusNames = node.responses.map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode));
|
|
605
|
+
const successNames = resolveSuccessNames(node, tsResolver);
|
|
606
|
+
const responseName = options.dataReturnType === "full" ? allStatusNames.length > 0 ? allStatusNames.join(" | ") : tsResolver.resolveResponseName(node) : successNames.length > 0 ? successNames.join(" | ") : tsResolver.resolveResponseName(node);
|
|
607
|
+
const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : null;
|
|
439
608
|
const errorNames = node.responses.filter((r) => Number.parseInt(r.statusCode, 10) >= 400).map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode));
|
|
609
|
+
const TError = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`;
|
|
610
|
+
const zodRequestName = options.parser === "zod" && options.zodResolver && node.requestBody?.content?.[0]?.schema ? options.zodResolver.resolveDataName?.(node) ?? null : null;
|
|
440
611
|
return [
|
|
441
612
|
responseName,
|
|
442
|
-
|
|
443
|
-
requestName || "unknown"
|
|
613
|
+
TError,
|
|
614
|
+
zodRequestName ? `z.output<typeof ${zodRequestName}>` : requestName || "unknown"
|
|
444
615
|
].filter(Boolean);
|
|
445
616
|
}
|
|
446
617
|
/**
|
|
447
618
|
* Builds the parameters object for a class-based client method.
|
|
448
619
|
* Includes URL, method, base URL, headers, and request/response data.
|
|
449
620
|
*/
|
|
450
|
-
function buildClassClientParams({ node,
|
|
451
|
-
const
|
|
452
|
-
const
|
|
621
|
+
function buildClassClientParams({ node, url, baseURL, tsResolver, isFormData, isMultipleContentTypes, hasFormData, headers, zodQueryParamsName }) {
|
|
622
|
+
const { query: queryParams } = getOperationParameters(node);
|
|
623
|
+
const queryParamsName = queryParams.length > 0 ? tsResolver.resolveQueryParamsName(node, queryParams[0]) : null;
|
|
624
|
+
const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : null;
|
|
625
|
+
const responseType = getResponseType(node);
|
|
453
626
|
return createFunctionParams({ config: {
|
|
454
627
|
mode: "object",
|
|
455
628
|
children: {
|
|
456
629
|
requestConfig: { mode: "inlineSpread" },
|
|
457
|
-
method: { value: JSON.stringify(node.method.toUpperCase()) },
|
|
458
|
-
url: { value:
|
|
459
|
-
baseURL: baseURL ? { value: JSON.stringify(baseURL) } :
|
|
460
|
-
params: queryParamsName ? {} :
|
|
461
|
-
data: requestName ? { value: isFormData ? "formData as FormData" : "requestData" } :
|
|
462
|
-
|
|
630
|
+
method: { value: JSON.stringify(ast.isHttpOperationNode(node) ? node.method.toUpperCase() : "") },
|
|
631
|
+
url: { value: url },
|
|
632
|
+
baseURL: baseURL ? { value: JSON.stringify(baseURL) } : null,
|
|
633
|
+
params: queryParamsName ? zodQueryParamsName ? { value: "requestParams" } : {} : null,
|
|
634
|
+
data: requestName ? { value: isMultipleContentTypes && hasFormData ? "contentType === 'multipart/form-data' ? formData as FormData : requestData" : isFormData ? "formData as FormData" : "requestData" } : null,
|
|
635
|
+
contentType: isMultipleContentTypes ? {} : null,
|
|
636
|
+
responseType: responseType ? { value: JSON.stringify(responseType) } : null,
|
|
637
|
+
headers: headers.length ? { value: `{ ${headers.join(", ")}, ...requestConfig.headers }` } : null
|
|
463
638
|
}
|
|
464
639
|
} });
|
|
465
640
|
}
|
|
466
641
|
/**
|
|
467
642
|
* Builds the request data parsing line for client methods.
|
|
468
|
-
* Applies Zod validation
|
|
643
|
+
* Applies Zod validation when `parser.request === 'zod'`, otherwise assigns data directly.
|
|
469
644
|
*/
|
|
470
645
|
function buildRequestDataLine({ parser, node, zodResolver }) {
|
|
471
|
-
const
|
|
472
|
-
|
|
646
|
+
const requestParser = resolveRequestParser(parser);
|
|
647
|
+
const zodRequestName = zodResolver && requestParser === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null;
|
|
648
|
+
if (requestParser === "zod" && zodRequestName) return `const requestData = ${zodRequestName}.parse(data)`;
|
|
473
649
|
if (node.requestBody?.content?.[0]?.schema) return "const requestData = data";
|
|
474
650
|
return "";
|
|
475
651
|
}
|
|
476
652
|
/**
|
|
653
|
+
* Builds the query parameters parsing line for client methods.
|
|
654
|
+
* Returns an empty string when no query params exist or query-params parsing is not enabled.
|
|
655
|
+
* Only the object form `parser: { request: 'zod' }` triggers this. `parser: 'zod'` does not.
|
|
656
|
+
*/
|
|
657
|
+
function buildQueryParamsLine({ parser, node, zodResolver }) {
|
|
658
|
+
if (resolveQueryParamsParser(parser) !== "zod" || !zodResolver) return "";
|
|
659
|
+
const { query: queryParams } = getOperationParameters(node);
|
|
660
|
+
if (queryParams.length === 0) return "";
|
|
661
|
+
const zodQueryParamsName = zodResolver.resolveQueryParamsName?.(node, queryParams[0]);
|
|
662
|
+
if (!zodQueryParamsName) return "";
|
|
663
|
+
return `const requestParams = ${zodQueryParamsName}.parse(params)`;
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
477
666
|
* Builds the form data conversion line for file upload requests.
|
|
478
667
|
* Returns empty string if not applicable.
|
|
479
668
|
*/
|
|
@@ -482,19 +671,24 @@ function buildFormDataLine(isFormData, hasRequest) {
|
|
|
482
671
|
}
|
|
483
672
|
/**
|
|
484
673
|
* Builds the return statement for a client method.
|
|
485
|
-
*
|
|
674
|
+
* When `dataReturnType` is `'full'`, casts the response to the status-discriminated union type.
|
|
675
|
+
* When `parser.response` is `'zod'`, pipes the response body through the Zod schema before returning.
|
|
486
676
|
*/
|
|
487
|
-
function buildReturnStatement({ dataReturnType, parser, node, zodResolver }) {
|
|
488
|
-
const
|
|
489
|
-
|
|
490
|
-
if (dataReturnType === "
|
|
491
|
-
|
|
677
|
+
function buildReturnStatement({ dataReturnType, parser, node, zodResolver, tsResolver }) {
|
|
678
|
+
const responseParser = resolveResponseParser(parser);
|
|
679
|
+
const zodResponseName = zodResolver && responseParser === "zod" ? zodResolver.resolveResponseName?.(node) : null;
|
|
680
|
+
if (dataReturnType === "full" && tsResolver) {
|
|
681
|
+
const unionType = buildStatusUnionType(node, tsResolver);
|
|
682
|
+
if (responseParser === "zod" && zodResponseName) return `return {...res, data: ${zodResponseName}.parse(res.data)} as ${unionType}`;
|
|
683
|
+
return `return res as ${unionType}`;
|
|
684
|
+
}
|
|
685
|
+
if (dataReturnType === "data" && responseParser === "zod" && zodResponseName) return `return ${zodResponseName}.parse(res.data)`;
|
|
492
686
|
return "return res.data";
|
|
493
687
|
}
|
|
494
688
|
//#endregion
|
|
495
689
|
//#region src/components/Url.tsx
|
|
496
690
|
const declarationPrinter$3 = functionPrinter({ mode: "declaration" });
|
|
497
|
-
function
|
|
691
|
+
function buildUrlParamsNode({ paramsType, paramsCasing, pathParamsType, node, tsResolver }) {
|
|
498
692
|
const urlNode = {
|
|
499
693
|
...node,
|
|
500
694
|
parameters: node.parameters.filter((p) => p.in === "path"),
|
|
@@ -507,10 +701,9 @@ function getParams$1({ paramsType, paramsCasing, pathParamsType, node, tsResolve
|
|
|
507
701
|
resolver: tsResolver
|
|
508
702
|
});
|
|
509
703
|
}
|
|
510
|
-
__name(getParams$1, "getParams");
|
|
511
704
|
function Url({ name, isExportable = true, isIndexable = true, baseURL, paramsType, paramsCasing, pathParamsType, node, tsResolver }) {
|
|
512
|
-
|
|
513
|
-
const paramsNode =
|
|
705
|
+
if (!ast.isHttpOperationNode(node)) return null;
|
|
706
|
+
const paramsNode = buildUrlParamsNode({
|
|
514
707
|
paramsType,
|
|
515
708
|
paramsCasing,
|
|
516
709
|
pathParamsType,
|
|
@@ -518,9 +711,9 @@ function Url({ name, isExportable = true, isIndexable = true, baseURL, paramsTyp
|
|
|
518
711
|
tsResolver
|
|
519
712
|
});
|
|
520
713
|
const paramsSignature = declarationPrinter$3.print(paramsNode) ?? "";
|
|
521
|
-
const originalPathParams = node
|
|
522
|
-
const casedPathParams =
|
|
523
|
-
const pathParamsMapping = paramsCasing ? buildParamsMapping(originalPathParams, casedPathParams) :
|
|
714
|
+
const { path: originalPathParams } = getOperationParameters(node);
|
|
715
|
+
const { path: casedPathParams } = getOperationParameters(node, { paramsCasing });
|
|
716
|
+
const pathParamsMapping = paramsCasing ? buildParamsMapping(originalPathParams, casedPathParams) : null;
|
|
524
717
|
return /* @__PURE__ */ jsx(File.Source, {
|
|
525
718
|
name,
|
|
526
719
|
isExportable,
|
|
@@ -534,7 +727,7 @@ function Url({ name, isExportable = true, isIndexable = true, baseURL, paramsTyp
|
|
|
534
727
|
pathParamsMapping && Object.keys(pathParamsMapping).length > 0 && /* @__PURE__ */ jsx("br", {}),
|
|
535
728
|
/* @__PURE__ */ jsx(Const, {
|
|
536
729
|
name: "res",
|
|
537
|
-
children: `{ method: '${node.method.toUpperCase()}', url: ${
|
|
730
|
+
children: `{ method: '${node.method.toUpperCase()}', url: ${Url$1.toTemplateString(node.path, { prefix: baseURL })} as const }`
|
|
538
731
|
}),
|
|
539
732
|
/* @__PURE__ */ jsx("br", {}),
|
|
540
733
|
"return res"
|
|
@@ -542,56 +735,58 @@ function Url({ name, isExportable = true, isIndexable = true, baseURL, paramsTyp
|
|
|
542
735
|
})
|
|
543
736
|
});
|
|
544
737
|
}
|
|
545
|
-
Url.getParams = getParams$1;
|
|
546
738
|
//#endregion
|
|
547
739
|
//#region src/components/Client.tsx
|
|
548
740
|
const declarationPrinter$2 = functionPrinter({ mode: "declaration" });
|
|
549
|
-
function
|
|
550
|
-
const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : void 0;
|
|
741
|
+
function buildClientParamsNode({ paramsType, paramsCasing, pathParamsType, node, tsResolver, isConfigurable }) {
|
|
551
742
|
return ast.createOperationParams(node, {
|
|
552
743
|
paramsType,
|
|
553
744
|
pathParamsType: paramsType === "object" ? "object" : pathParamsType === "object" ? "object" : "inline",
|
|
554
745
|
paramsCasing,
|
|
555
746
|
resolver: tsResolver,
|
|
556
|
-
extraParams: isConfigurable ? [ast.createFunctionParameter({
|
|
747
|
+
extraParams: [...isConfigurable ? [ast.createFunctionParameter({
|
|
557
748
|
name: "config",
|
|
558
749
|
type: ast.createParamsType({
|
|
559
750
|
variant: "reference",
|
|
560
|
-
name:
|
|
751
|
+
name: buildRequestConfigType(node, tsResolver)
|
|
561
752
|
}),
|
|
562
753
|
default: "{}"
|
|
563
|
-
})] : []
|
|
754
|
+
})] : []]
|
|
564
755
|
});
|
|
565
756
|
}
|
|
566
757
|
function Client({ name, isExportable = true, isIndexable = true, returnType, baseURL, dataReturnType, parser, paramsType, paramsCasing, pathParamsType, node, tsResolver, zodResolver, urlName, children, isConfigurable = true }) {
|
|
567
|
-
|
|
568
|
-
const contentType = node
|
|
569
|
-
const isFormData = contentType === "multipart/form-data";
|
|
570
|
-
const
|
|
571
|
-
const
|
|
572
|
-
const
|
|
573
|
-
const
|
|
574
|
-
const
|
|
575
|
-
const
|
|
576
|
-
const
|
|
577
|
-
const
|
|
578
|
-
const
|
|
579
|
-
const
|
|
580
|
-
const
|
|
581
|
-
const
|
|
582
|
-
const
|
|
583
|
-
const zodResponseName = zodResolver &&
|
|
584
|
-
const zodRequestName = zodResolver &&
|
|
758
|
+
if (!ast.isHttpOperationNode(node)) return null;
|
|
759
|
+
const { defaultContentType: contentType, isMultipleContentTypes, hasFormData } = getContentTypeInfo(node);
|
|
760
|
+
const isFormData = !isMultipleContentTypes && contentType === "multipart/form-data";
|
|
761
|
+
const responseType = getResponseType(node);
|
|
762
|
+
const { path: originalPathParams, query: originalQueryParams, header: originalHeaderParams } = getOperationParameters(node);
|
|
763
|
+
const { path: casedPathParams, query: casedQueryParams, header: casedHeaderParams } = getOperationParameters(node, { paramsCasing });
|
|
764
|
+
const pathParamsMapping = paramsCasing && !urlName ? buildParamsMapping(originalPathParams, casedPathParams) : null;
|
|
765
|
+
const queryParamsMapping = paramsCasing ? buildParamsMapping(originalQueryParams, casedQueryParams) : null;
|
|
766
|
+
const headerParamsMapping = paramsCasing ? buildParamsMapping(originalHeaderParams, casedHeaderParams) : null;
|
|
767
|
+
const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : null;
|
|
768
|
+
const successNames = resolveSuccessNames(node, tsResolver);
|
|
769
|
+
const responseName = successNames.length > 0 ? successNames.join(" | ") : tsResolver.resolveResponseName(node);
|
|
770
|
+
const queryParamsName = originalQueryParams.length > 0 ? tsResolver.resolveQueryParamsName(node, originalQueryParams[0]) : null;
|
|
771
|
+
const headerParamsName = originalHeaderParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, originalHeaderParams[0]) : null;
|
|
772
|
+
const requestParser = resolveRequestParser(parser);
|
|
773
|
+
const responseParser = resolveResponseParser(parser);
|
|
774
|
+
const zodResponseName = zodResolver && responseParser === "zod" ? zodResolver.resolveResponseName?.(node) : null;
|
|
775
|
+
const zodRequestName = zodResolver && requestParser === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null;
|
|
776
|
+
const queryParamsParser = resolveQueryParamsParser(parser);
|
|
777
|
+
const zodQueryParamsName = zodResolver && queryParamsParser === "zod" && originalQueryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, originalQueryParams[0]) : null;
|
|
585
778
|
const errorNames = node.responses.filter((r) => {
|
|
586
779
|
return Number.parseInt(r.statusCode, 10) >= 400;
|
|
587
780
|
}).map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode));
|
|
588
|
-
const headers = [contentType !== "application/json" && contentType !== "multipart/form-data" ? `'Content-Type': '${contentType}'` :
|
|
781
|
+
const headers = [!isMultipleContentTypes && contentType !== "application/json" && contentType !== "multipart/form-data" ? `'Content-Type': '${contentType}'` : null, headerParamsName ? headerParamsMapping ? "...mappedHeaders" : "...headers" : null].filter(Boolean);
|
|
782
|
+
const TError = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`;
|
|
783
|
+
const allStatusNames = node.responses.map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode));
|
|
589
784
|
const generics = [
|
|
590
|
-
responseName,
|
|
591
|
-
|
|
592
|
-
requestName || "unknown"
|
|
785
|
+
dataReturnType === "full" ? allStatusNames.length > 0 ? allStatusNames.join(" | ") : responseName : responseName,
|
|
786
|
+
TError,
|
|
787
|
+
parser === "zod" && zodRequestName ? `z.output<typeof ${zodRequestName}>` : requestName || "unknown"
|
|
593
788
|
].filter(Boolean);
|
|
594
|
-
const paramsNode =
|
|
789
|
+
const paramsNode = buildClientParamsNode({
|
|
595
790
|
paramsType,
|
|
596
791
|
paramsCasing,
|
|
597
792
|
pathParamsType,
|
|
@@ -600,7 +795,7 @@ function Client({ name, isExportable = true, isIndexable = true, returnType, bas
|
|
|
600
795
|
isConfigurable
|
|
601
796
|
});
|
|
602
797
|
const paramsSignature = declarationPrinter$2.print(paramsNode) ?? "";
|
|
603
|
-
const urlParamsNode =
|
|
798
|
+
const urlParamsNode = buildUrlParamsNode({
|
|
604
799
|
paramsType,
|
|
605
800
|
paramsCasing,
|
|
606
801
|
pathParamsType,
|
|
@@ -611,20 +806,23 @@ function Client({ name, isExportable = true, isIndexable = true, returnType, bas
|
|
|
611
806
|
const clientParams = createFunctionParams({ config: {
|
|
612
807
|
mode: "object",
|
|
613
808
|
children: {
|
|
614
|
-
method: { value:
|
|
615
|
-
url: { value: urlName ? `${urlName}(${urlParamsCall}).url.toString()` : path
|
|
616
|
-
baseURL: baseURL && !urlName ? { value: `\`${baseURL}\`` } :
|
|
617
|
-
params: queryParamsName ? queryParamsMapping ? { value: "mappedParams" } : {} :
|
|
618
|
-
data: requestName ? { value: isFormData ? "formData as FormData" : "requestData" } :
|
|
619
|
-
|
|
620
|
-
|
|
809
|
+
method: { value: stringify(node.method.toUpperCase()) },
|
|
810
|
+
url: { value: urlName ? `${urlName}(${urlParamsCall}).url.toString()` : Url$1.toTemplateString(node.path) },
|
|
811
|
+
baseURL: baseURL && !urlName ? { value: `\`${baseURL}\`` } : null,
|
|
812
|
+
params: queryParamsName ? zodQueryParamsName ? { value: "requestParams" } : queryParamsMapping ? { value: "mappedParams" } : {} : null,
|
|
813
|
+
data: requestName ? { value: isMultipleContentTypes && hasFormData ? "contentType === 'multipart/form-data' ? formData as FormData : requestData" : isFormData ? "formData as FormData" : "requestData" } : null,
|
|
814
|
+
contentType: isConfigurable && isMultipleContentTypes ? {} : null,
|
|
815
|
+
responseType: responseType ? { value: stringify(responseType) } : null,
|
|
816
|
+
requestConfig: isConfigurable ? { mode: "inlineSpread" } : null,
|
|
817
|
+
headers: headers.length ? { value: isConfigurable ? `{ ${headers.join(", ")}, ...requestConfig.headers }` : `{ ${headers.join(", ")} }` } : null
|
|
621
818
|
}
|
|
622
819
|
} });
|
|
820
|
+
const statusUnionType = dataReturnType === "full" ? buildStatusUnionType(node, tsResolver) : null;
|
|
623
821
|
const childrenElement = children ? children : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
624
|
-
dataReturnType === "full" &&
|
|
625
|
-
dataReturnType === "
|
|
626
|
-
dataReturnType === "
|
|
627
|
-
dataReturnType === "data" &&
|
|
822
|
+
dataReturnType === "full" && responseParser === "zod" && zodResponseName && statusUnionType && `return {...res, data: ${zodResponseName}.parse(res.data)} as ${statusUnionType}`,
|
|
823
|
+
dataReturnType === "full" && responseParser !== "zod" && statusUnionType && `return res as ${statusUnionType}`,
|
|
824
|
+
dataReturnType === "data" && responseParser === "zod" && zodResponseName && `return ${zodResponseName}.parse(res.data)`,
|
|
825
|
+
dataReturnType === "data" && responseParser !== "zod" && "return res.data"
|
|
628
826
|
] });
|
|
629
827
|
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("br", {}), /* @__PURE__ */ jsx(File.Source, {
|
|
630
828
|
name,
|
|
@@ -635,46 +833,46 @@ function Client({ name, isExportable = true, isIndexable = true, returnType, bas
|
|
|
635
833
|
async: true,
|
|
636
834
|
export: isExportable,
|
|
637
835
|
params: paramsSignature,
|
|
638
|
-
JSDoc: { comments:
|
|
836
|
+
JSDoc: { comments: buildOperationComments(node, {
|
|
837
|
+
link: "urlPath",
|
|
838
|
+
linkPosition: "beforeDeprecated",
|
|
839
|
+
splitLines: true
|
|
840
|
+
}) },
|
|
639
841
|
returnType,
|
|
640
842
|
children: [
|
|
641
|
-
isConfigurable ?
|
|
642
|
-
/* @__PURE__ */ jsx("br", {}),
|
|
843
|
+
isConfigurable ? `const { client: request = client, ${isMultipleContentTypes ? `contentType = ${stringify(contentType)}, ` : ""}...requestConfig } = config` : "",
|
|
643
844
|
/* @__PURE__ */ jsx("br", {}),
|
|
644
845
|
pathParamsMapping && Object.entries(pathParamsMapping).filter(([originalName, camelCaseName]) => isValidVarName(originalName) && originalName !== camelCaseName).map(([originalName, camelCaseName]) => `const ${originalName} = ${camelCaseName}`).join("\n"),
|
|
645
|
-
pathParamsMapping && /* @__PURE__ */
|
|
646
|
-
queryParamsMapping && queryParamsName && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
headerParamsMapping && headerParamsName && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
652
|
-
`const mappedHeaders = headers ? { ${Object.entries(headerParamsMapping).map(([originalName, camelCaseName]) => `"${originalName}": headers.${camelCaseName}`).join(", ")} } : undefined`,
|
|
653
|
-
/* @__PURE__ */ jsx("br", {}),
|
|
654
|
-
/* @__PURE__ */ jsx("br", {})
|
|
655
|
-
] }),
|
|
656
|
-
parser === "zod" && zodRequestName ? `const requestData = ${zodRequestName}.parse(data)` : requestName && "const requestData = data",
|
|
657
|
-
/* @__PURE__ */ jsx("br", {}),
|
|
658
|
-
isFormData && requestName && "const formData = buildFormData(requestData)",
|
|
846
|
+
pathParamsMapping && /* @__PURE__ */ jsx("br", {}),
|
|
847
|
+
queryParamsMapping && queryParamsName && /* @__PURE__ */ jsxs(Fragment, { children: [`const mappedParams = params ? { ${Object.entries(queryParamsMapping).map(([originalName, camelCaseName]) => `"${originalName}": params.${camelCaseName}`).join(", ")} } : undefined`, /* @__PURE__ */ jsx("br", {})] }),
|
|
848
|
+
headerParamsMapping && headerParamsName && /* @__PURE__ */ jsxs(Fragment, { children: [`const mappedHeaders = headers ? { ${Object.entries(headerParamsMapping).map(([originalName, camelCaseName]) => `"${originalName}": headers.${camelCaseName}`).join(", ")} } : undefined`, /* @__PURE__ */ jsx("br", {})] }),
|
|
849
|
+
requestParser === "zod" && zodRequestName ? `const requestData = ${zodRequestName}.parse(data)` : requestName && "const requestData = data",
|
|
850
|
+
zodQueryParamsName && `const requestParams = ${zodQueryParamsName}.parse(params)`,
|
|
851
|
+
(isFormData || isMultipleContentTypes && hasFormData) && requestName && "const formData = buildFormData(requestData)",
|
|
659
852
|
/* @__PURE__ */ jsx("br", {}),
|
|
660
|
-
isConfigurable ? `const res = await request<${generics.join(", ")}>(${clientParams.toCall()})` : `const res = await
|
|
853
|
+
isConfigurable ? `const res = await request<${generics.join(", ")}>(${clientParams.toCall()})` : `const res = await client<${generics.join(", ")}>(${clientParams.toCall()})`,
|
|
661
854
|
/* @__PURE__ */ jsx("br", {}),
|
|
662
855
|
childrenElement
|
|
663
856
|
]
|
|
664
857
|
})
|
|
665
858
|
})] });
|
|
666
859
|
}
|
|
667
|
-
Client.getParams = getParams;
|
|
668
860
|
//#endregion
|
|
669
861
|
//#region src/components/ClassClient.tsx
|
|
670
862
|
const declarationPrinter$1 = functionPrinter({ mode: "declaration" });
|
|
671
863
|
function generateMethod$1({ node, name, tsResolver, zodResolver, baseURL, dataReturnType, parser, paramsType, paramsCasing, pathParamsType }) {
|
|
672
|
-
|
|
673
|
-
const contentType = node
|
|
674
|
-
const isFormData = contentType === "multipart/form-data";
|
|
675
|
-
const
|
|
676
|
-
const
|
|
677
|
-
const
|
|
864
|
+
if (!ast.isHttpOperationNode(node)) return "";
|
|
865
|
+
const { defaultContentType: contentType, isMultipleContentTypes, hasFormData } = getContentTypeInfo(node);
|
|
866
|
+
const isFormData = !isMultipleContentTypes && contentType === "multipart/form-data";
|
|
867
|
+
const { header: headerParams } = getOperationParameters(node);
|
|
868
|
+
const headerParamsName = headerParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, headerParams[0]) : null;
|
|
869
|
+
const headers = isMultipleContentTypes ? headerParamsName ? ["...headers"] : [] : buildHeaders(contentType, !!headerParamsName);
|
|
870
|
+
const generics = buildGenerics(node, tsResolver, {
|
|
871
|
+
dataReturnType,
|
|
872
|
+
zodResolver,
|
|
873
|
+
parser
|
|
874
|
+
});
|
|
875
|
+
const paramsNode = buildClientParamsNode({
|
|
678
876
|
paramsType,
|
|
679
877
|
paramsCasing,
|
|
680
878
|
pathParamsType,
|
|
@@ -683,31 +881,47 @@ function generateMethod$1({ node, name, tsResolver, zodResolver, baseURL, dataRe
|
|
|
683
881
|
isConfigurable: true
|
|
684
882
|
});
|
|
685
883
|
const paramsSignature = declarationPrinter$1.print(paramsNode) ?? "";
|
|
884
|
+
const { query: queryParams } = getOperationParameters(node);
|
|
885
|
+
const zodQueryParamsName = zodResolver && resolveQueryParamsParser(parser) === "zod" && queryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, queryParams[0]) : null;
|
|
686
886
|
const clientParams = buildClassClientParams({
|
|
687
887
|
node,
|
|
688
|
-
path,
|
|
888
|
+
url: Url$1.toTemplateString(node.path, { casing: paramsCasing }),
|
|
689
889
|
baseURL,
|
|
690
890
|
tsResolver,
|
|
691
891
|
isFormData,
|
|
692
|
-
|
|
892
|
+
isMultipleContentTypes,
|
|
893
|
+
hasFormData,
|
|
894
|
+
headers,
|
|
895
|
+
zodQueryParamsName
|
|
693
896
|
});
|
|
694
|
-
const jsdoc = buildJSDoc(
|
|
897
|
+
const jsdoc = buildJSDoc(buildOperationComments(node, {
|
|
898
|
+
link: "urlPath",
|
|
899
|
+
linkPosition: "beforeDeprecated",
|
|
900
|
+
splitLines: true
|
|
901
|
+
}));
|
|
695
902
|
const requestDataLine = buildRequestDataLine({
|
|
696
903
|
parser,
|
|
697
904
|
node,
|
|
698
905
|
zodResolver
|
|
699
906
|
});
|
|
700
|
-
const
|
|
907
|
+
const queryParamsLine = buildQueryParamsLine({
|
|
908
|
+
parser,
|
|
909
|
+
node,
|
|
910
|
+
zodResolver
|
|
911
|
+
});
|
|
912
|
+
const formDataLine = buildFormDataLine(isFormData || isMultipleContentTypes && hasFormData, !!node.requestBody?.content?.[0]?.schema);
|
|
701
913
|
const returnStatement = buildReturnStatement({
|
|
702
914
|
dataReturnType,
|
|
703
915
|
parser,
|
|
704
916
|
node,
|
|
705
|
-
zodResolver
|
|
917
|
+
zodResolver,
|
|
918
|
+
tsResolver
|
|
706
919
|
});
|
|
707
920
|
return `${jsdoc}async ${name}(${paramsSignature}) {\n${[
|
|
708
|
-
|
|
921
|
+
`const { client: request = client, ${isMultipleContentTypes ? `contentType = ${stringify(contentType)}, ` : ""}...requestConfig } = mergeConfig(this.#config, config)`,
|
|
709
922
|
"",
|
|
710
923
|
requestDataLine,
|
|
924
|
+
queryParamsLine,
|
|
711
925
|
formDataLine,
|
|
712
926
|
`const res = await request<${generics.join(", ")}>(${clientParams.toCall()})`,
|
|
713
927
|
returnStatement
|
|
@@ -742,15 +956,14 @@ ${operations.map(({ node, name: methodName, tsResolver, zodResolver }) => genera
|
|
|
742
956
|
children: [classCode, children]
|
|
743
957
|
});
|
|
744
958
|
}
|
|
745
|
-
ClassClient.getParams = Client.getParams;
|
|
746
959
|
//#endregion
|
|
747
960
|
//#region src/components/WrapperClient.tsx
|
|
748
|
-
function WrapperClient({ name,
|
|
961
|
+
function WrapperClient({ name, controllers, isExportable = true, isIndexable = true }) {
|
|
749
962
|
const classCode = `export class ${name} {
|
|
750
|
-
${
|
|
963
|
+
${controllers.map(({ className, propertyName }) => ` readonly ${propertyName}: ${className}`).join("\n")}
|
|
751
964
|
|
|
752
965
|
constructor(config: Partial<RequestConfig> & { client?: Client } = {}) {
|
|
753
|
-
${
|
|
966
|
+
${controllers.map(({ className, propertyName }) => ` this.${propertyName} = new ${className}(config)`).join("\n")}
|
|
754
967
|
}
|
|
755
968
|
}`;
|
|
756
969
|
return /* @__PURE__ */ jsx(File.Source, {
|
|
@@ -763,54 +976,47 @@ ${classNames.map((className) => ` this.${camelCase(className)} = new ${classN
|
|
|
763
976
|
//#endregion
|
|
764
977
|
//#region src/generators/classClientGenerator.tsx
|
|
765
978
|
function resolveTypeImportNames$1(node, tsResolver) {
|
|
766
|
-
return
|
|
767
|
-
node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : void 0,
|
|
768
|
-
tsResolver.resolveResponseName(node),
|
|
769
|
-
...node.parameters.filter((p) => p.in === "path").map((p) => tsResolver.resolvePathParamsName(node, p)),
|
|
770
|
-
...node.parameters.filter((p) => p.in === "query").map((p) => tsResolver.resolveQueryParamsName(node, p)),
|
|
771
|
-
...node.parameters.filter((p) => p.in === "header").map((p) => tsResolver.resolveHeaderParamsName(node, p)),
|
|
772
|
-
...node.responses.map((res) => tsResolver.resolveResponseStatusName(node, res.statusCode))
|
|
773
|
-
].filter((n) => Boolean(n));
|
|
979
|
+
return resolveOperationTypeNames(node, tsResolver, { order: "body-response-first" });
|
|
774
980
|
}
|
|
775
981
|
__name(resolveTypeImportNames$1, "resolveTypeImportNames");
|
|
776
|
-
function resolveZodImportNames$1(node, zodResolver) {
|
|
777
|
-
|
|
982
|
+
function resolveZodImportNames$1(node, zodResolver, parser) {
|
|
983
|
+
const { query: queryParams } = getOperationParameters(node);
|
|
984
|
+
return [
|
|
985
|
+
resolveResponseParser(parser) === "zod" ? zodResolver.resolveResponseName?.(node) : null,
|
|
986
|
+
resolveRequestParser(parser) === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null,
|
|
987
|
+
resolveQueryParamsParser(parser) === "zod" && queryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, queryParams[0]) : null
|
|
988
|
+
].filter((n) => Boolean(n));
|
|
778
989
|
}
|
|
779
990
|
__name(resolveZodImportNames$1, "resolveZodImportNames");
|
|
991
|
+
/**
|
|
992
|
+
* Built-in `operations` generator for `@kubb/plugin-client` when
|
|
993
|
+
* `clientType: 'class'`. Emits one class per tag, with one instance method
|
|
994
|
+
* per operation and a shared constructor for request configuration.
|
|
995
|
+
*/
|
|
780
996
|
const classClientGenerator = defineGenerator({
|
|
781
997
|
name: "classClient",
|
|
782
998
|
renderer: jsxRenderer,
|
|
783
999
|
operations(nodes, ctx) {
|
|
784
|
-
const {
|
|
1000
|
+
const { config, driver, resolver, root } = ctx;
|
|
785
1001
|
const { output, group, dataReturnType, paramsCasing, paramsType, pathParamsType, parser, importPath, sdk } = ctx.options;
|
|
786
|
-
const baseURL = ctx.options.baseURL ??
|
|
1002
|
+
const baseURL = ctx.options.baseURL ?? ctx.meta.baseURL;
|
|
787
1003
|
const pluginTs = driver.getPlugin(pluginTsName);
|
|
788
1004
|
if (!pluginTs) return null;
|
|
789
1005
|
const tsResolver = driver.getResolver(pluginTsName);
|
|
790
1006
|
const tsPluginOptions = pluginTs.options;
|
|
791
|
-
const pluginZod = parser
|
|
792
|
-
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) :
|
|
1007
|
+
const pluginZod = isParserEnabled(parser) ? driver.getPlugin(pluginZodName) : null;
|
|
1008
|
+
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null;
|
|
793
1009
|
function buildOperationData(node) {
|
|
794
|
-
const typeFile = tsResolver.resolveFile({
|
|
795
|
-
name: node.operationId,
|
|
796
|
-
extname: ".ts",
|
|
797
|
-
tag: node.tags[0] ?? "default",
|
|
798
|
-
path: node.path
|
|
799
|
-
}, {
|
|
1010
|
+
const typeFile = tsResolver.resolveFile(operationFileEntry(node, node.operationId), {
|
|
800
1011
|
root,
|
|
801
1012
|
output: tsPluginOptions?.output ?? output,
|
|
802
1013
|
group: tsPluginOptions?.group
|
|
803
1014
|
});
|
|
804
|
-
const zodFile = zodResolver && pluginZod?.options ? zodResolver.resolveFile({
|
|
805
|
-
name: node.operationId,
|
|
806
|
-
extname: ".ts",
|
|
807
|
-
tag: node.tags[0] ?? "default",
|
|
808
|
-
path: node.path
|
|
809
|
-
}, {
|
|
1015
|
+
const zodFile = zodResolver && pluginZod?.options ? zodResolver.resolveFile(operationFileEntry(node, node.operationId), {
|
|
810
1016
|
root,
|
|
811
1017
|
output: pluginZod.options?.output ?? output,
|
|
812
|
-
group: pluginZod.options?.group
|
|
813
|
-
}) :
|
|
1018
|
+
group: pluginZod.options?.group ?? void 0
|
|
1019
|
+
}) : null;
|
|
814
1020
|
return {
|
|
815
1021
|
node,
|
|
816
1022
|
name: resolver.resolveName(node.operationId),
|
|
@@ -821,27 +1027,31 @@ const classClientGenerator = defineGenerator({
|
|
|
821
1027
|
};
|
|
822
1028
|
}
|
|
823
1029
|
const controllers = nodes.reduce((acc, operationNode) => {
|
|
1030
|
+
if (!ast.isHttpOperationNode(operationNode)) return acc;
|
|
824
1031
|
const tag = operationNode.tags[0];
|
|
825
|
-
const groupName = tag ? group?.name?.({ group: camelCase(tag) }) ??
|
|
1032
|
+
const groupName = tag ? group?.name?.({ group: camelCase(tag) }) ?? resolver.resolveGroupName(tag) : resolver.resolveClassName("Client");
|
|
826
1033
|
if (!tag && !group) {
|
|
827
|
-
const name = "ApiClient";
|
|
1034
|
+
const name = resolver.resolveClassName("ApiClient");
|
|
828
1035
|
const file = resolver.resolveFile({
|
|
829
1036
|
name,
|
|
830
1037
|
extname: ".ts"
|
|
831
1038
|
}, {
|
|
832
1039
|
root,
|
|
833
1040
|
output,
|
|
834
|
-
group
|
|
1041
|
+
group: group ?? void 0
|
|
835
1042
|
});
|
|
836
1043
|
const operationData = buildOperationData(operationNode);
|
|
837
1044
|
const previous = acc.find((item) => item.file.path === file.path);
|
|
838
1045
|
if (previous) previous.operations.push(operationData);
|
|
839
1046
|
else acc.push({
|
|
840
1047
|
name,
|
|
1048
|
+
propertyName: resolver.resolveClientPropertyName(name),
|
|
841
1049
|
file,
|
|
842
1050
|
operations: [operationData]
|
|
843
1051
|
});
|
|
844
|
-
|
|
1052
|
+
return acc;
|
|
1053
|
+
}
|
|
1054
|
+
if (tag) {
|
|
845
1055
|
const name = groupName;
|
|
846
1056
|
const file = resolver.resolveFile({
|
|
847
1057
|
name,
|
|
@@ -850,13 +1060,14 @@ const classClientGenerator = defineGenerator({
|
|
|
850
1060
|
}, {
|
|
851
1061
|
root,
|
|
852
1062
|
output,
|
|
853
|
-
group
|
|
1063
|
+
group: group ?? void 0
|
|
854
1064
|
});
|
|
855
1065
|
const operationData = buildOperationData(operationNode);
|
|
856
1066
|
const previous = acc.find((item) => item.file.path === file.path);
|
|
857
1067
|
if (previous) previous.operations.push(operationData);
|
|
858
1068
|
else acc.push({
|
|
859
1069
|
name,
|
|
1070
|
+
propertyName: resolver.resolveClientPropertyName(name),
|
|
860
1071
|
file,
|
|
861
1072
|
operations: [operationData]
|
|
862
1073
|
});
|
|
@@ -885,7 +1096,7 @@ const classClientGenerator = defineGenerator({
|
|
|
885
1096
|
const zodFilesByPath = /* @__PURE__ */ new Map();
|
|
886
1097
|
ops.forEach((op) => {
|
|
887
1098
|
if (!op.zodFile || !zodResolver) return;
|
|
888
|
-
const names = resolveZodImportNames$1(op.node, zodResolver);
|
|
1099
|
+
const names = resolveZodImportNames$1(op.node, zodResolver, parser);
|
|
889
1100
|
if (!zodImportsByFile.has(op.zodFile.path)) zodImportsByFile.set(op.zodFile.path, /* @__PURE__ */ new Set());
|
|
890
1101
|
const imports = zodImportsByFile.get(op.zodFile.path);
|
|
891
1102
|
names.forEach((n) => {
|
|
@@ -900,27 +1111,35 @@ const classClientGenerator = defineGenerator({
|
|
|
900
1111
|
}
|
|
901
1112
|
const files = controllers.map(({ name, file, operations: ops }) => {
|
|
902
1113
|
const { typeImportsByFile, typeFilesByPath } = collectTypeImports(ops);
|
|
903
|
-
const { zodImportsByFile, zodFilesByPath } = parser
|
|
1114
|
+
const { zodImportsByFile, zodFilesByPath } = isParserEnabled(parser) ? collectZodImports(ops) : {
|
|
904
1115
|
zodImportsByFile: /* @__PURE__ */ new Map(),
|
|
905
1116
|
zodFilesByPath: /* @__PURE__ */ new Map()
|
|
906
1117
|
};
|
|
907
|
-
const hasFormData = ops.some((op) => op.node.requestBody?.content?.
|
|
1118
|
+
const hasFormData = ops.some((op) => op.node.requestBody?.content?.some((e) => e.contentType === "multipart/form-data") ?? false);
|
|
908
1119
|
return /* @__PURE__ */ jsxs(File, {
|
|
909
1120
|
baseName: file.baseName,
|
|
910
1121
|
path: file.path,
|
|
911
1122
|
meta: file.meta,
|
|
912
|
-
banner: resolver.resolveBanner(
|
|
1123
|
+
banner: resolver.resolveBanner(ctx.meta, {
|
|
913
1124
|
output,
|
|
914
|
-
config
|
|
1125
|
+
config,
|
|
1126
|
+
file: {
|
|
1127
|
+
path: file.path,
|
|
1128
|
+
baseName: file.baseName
|
|
1129
|
+
}
|
|
915
1130
|
}),
|
|
916
|
-
footer: resolver.resolveFooter(
|
|
1131
|
+
footer: resolver.resolveFooter(ctx.meta, {
|
|
917
1132
|
output,
|
|
918
|
-
config
|
|
1133
|
+
config,
|
|
1134
|
+
file: {
|
|
1135
|
+
path: file.path,
|
|
1136
|
+
baseName: file.baseName
|
|
1137
|
+
}
|
|
919
1138
|
}),
|
|
920
1139
|
children: [
|
|
921
1140
|
importPath ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
922
1141
|
/* @__PURE__ */ jsx(File.Import, {
|
|
923
|
-
name: "
|
|
1142
|
+
name: "client",
|
|
924
1143
|
path: importPath
|
|
925
1144
|
}),
|
|
926
1145
|
/* @__PURE__ */ jsx(File.Import, {
|
|
@@ -938,7 +1157,7 @@ const classClientGenerator = defineGenerator({
|
|
|
938
1157
|
})
|
|
939
1158
|
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
940
1159
|
/* @__PURE__ */ jsx(File.Import, {
|
|
941
|
-
name: ["
|
|
1160
|
+
name: ["client"],
|
|
942
1161
|
root: file.path,
|
|
943
1162
|
path: path.resolve(root, ".kubb/client.ts")
|
|
944
1163
|
}),
|
|
@@ -963,6 +1182,11 @@ const classClientGenerator = defineGenerator({
|
|
|
963
1182
|
root: file.path,
|
|
964
1183
|
path: path.resolve(root, ".kubb/config.ts")
|
|
965
1184
|
}),
|
|
1185
|
+
parser === "zod" && zodResolver && ops.some((op) => op.node.requestBody?.content?.[0]?.schema != null) && /* @__PURE__ */ jsx(File.Import, {
|
|
1186
|
+
name: ["z"],
|
|
1187
|
+
path: "zod",
|
|
1188
|
+
isTypeOnly: true
|
|
1189
|
+
}),
|
|
966
1190
|
Array.from(typeImportsByFile.entries()).map(([filePath, importSet]) => {
|
|
967
1191
|
const typeFile = typeFilesByPath.get(filePath);
|
|
968
1192
|
if (!typeFile) return null;
|
|
@@ -975,7 +1199,7 @@ const classClientGenerator = defineGenerator({
|
|
|
975
1199
|
isTypeOnly: true
|
|
976
1200
|
}, filePath);
|
|
977
1201
|
}),
|
|
978
|
-
parser
|
|
1202
|
+
isParserEnabled(parser) && Array.from(zodImportsByFile.entries()).map(([filePath, importSet]) => {
|
|
979
1203
|
const zodFile = zodFilesByPath.get(filePath);
|
|
980
1204
|
if (!zodFile) return null;
|
|
981
1205
|
const importNames = Array.from(importSet).filter(Boolean);
|
|
@@ -1006,19 +1230,27 @@ const classClientGenerator = defineGenerator({
|
|
|
1006
1230
|
}, {
|
|
1007
1231
|
root,
|
|
1008
1232
|
output,
|
|
1009
|
-
group
|
|
1233
|
+
group: group ?? void 0
|
|
1010
1234
|
});
|
|
1011
1235
|
files.push(/* @__PURE__ */ jsxs(File, {
|
|
1012
1236
|
baseName: sdkFile.baseName,
|
|
1013
1237
|
path: sdkFile.path,
|
|
1014
1238
|
meta: sdkFile.meta,
|
|
1015
|
-
banner: resolver.resolveBanner(
|
|
1239
|
+
banner: resolver.resolveBanner(ctx.meta, {
|
|
1016
1240
|
output,
|
|
1017
|
-
config
|
|
1241
|
+
config,
|
|
1242
|
+
file: {
|
|
1243
|
+
path: sdkFile.path,
|
|
1244
|
+
baseName: sdkFile.baseName
|
|
1245
|
+
}
|
|
1018
1246
|
}),
|
|
1019
|
-
footer: resolver.resolveFooter(
|
|
1247
|
+
footer: resolver.resolveFooter(ctx.meta, {
|
|
1020
1248
|
output,
|
|
1021
|
-
config
|
|
1249
|
+
config,
|
|
1250
|
+
file: {
|
|
1251
|
+
path: sdkFile.path,
|
|
1252
|
+
baseName: sdkFile.baseName
|
|
1253
|
+
}
|
|
1022
1254
|
}),
|
|
1023
1255
|
children: [
|
|
1024
1256
|
importPath ? /* @__PURE__ */ jsx(File.Import, {
|
|
@@ -1038,7 +1270,10 @@ const classClientGenerator = defineGenerator({
|
|
|
1038
1270
|
}, name)),
|
|
1039
1271
|
/* @__PURE__ */ jsx(WrapperClient, {
|
|
1040
1272
|
name: sdk.className,
|
|
1041
|
-
|
|
1273
|
+
controllers: controllers.map(({ name, propertyName }) => ({
|
|
1274
|
+
className: name,
|
|
1275
|
+
propertyName
|
|
1276
|
+
}))
|
|
1042
1277
|
})
|
|
1043
1278
|
]
|
|
1044
1279
|
}, sdkFile.path));
|
|
@@ -1048,81 +1283,75 @@ const classClientGenerator = defineGenerator({
|
|
|
1048
1283
|
});
|
|
1049
1284
|
//#endregion
|
|
1050
1285
|
//#region src/generators/clientGenerator.tsx
|
|
1286
|
+
/**
|
|
1287
|
+
* Built-in operation generator for `@kubb/plugin-client`. Emits one async
|
|
1288
|
+
* function per OpenAPI operation, plus the matching URL helper. Used when
|
|
1289
|
+
* `clientType: 'function'` (the default).
|
|
1290
|
+
*/
|
|
1051
1291
|
const clientGenerator = defineGenerator({
|
|
1052
1292
|
name: "client",
|
|
1053
1293
|
renderer: jsxRenderer,
|
|
1054
1294
|
operation(node, ctx) {
|
|
1055
|
-
|
|
1295
|
+
if (!ast.isHttpOperationNode(node)) return null;
|
|
1296
|
+
const { config, driver, resolver, root } = ctx;
|
|
1056
1297
|
const { output, urlType, dataReturnType, paramsCasing, paramsType, pathParamsType, parser, importPath, group } = ctx.options;
|
|
1057
|
-
const baseURL = ctx.options.baseURL ??
|
|
1298
|
+
const baseURL = ctx.options.baseURL ?? ctx.meta.baseURL;
|
|
1058
1299
|
const pluginTs = driver.getPlugin(pluginTsName);
|
|
1059
1300
|
if (!pluginTs) return null;
|
|
1060
1301
|
const tsResolver = driver.getResolver(pluginTsName);
|
|
1061
|
-
const pluginZod = parser
|
|
1062
|
-
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) :
|
|
1063
|
-
const
|
|
1064
|
-
const
|
|
1065
|
-
const
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : void 0,
|
|
1072
|
-
tsResolver.resolveResponseName(node),
|
|
1073
|
-
...node.responses.map((res) => tsResolver.resolveResponseStatusName(node, res.statusCode))
|
|
1074
|
-
].filter(Boolean);
|
|
1075
|
-
const importedZodNames = zodResolver && parser === "zod" ? [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : void 0].filter(Boolean) : [];
|
|
1302
|
+
const pluginZod = isParserEnabled(parser) ? driver.getPlugin(pluginZodName) : null;
|
|
1303
|
+
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null;
|
|
1304
|
+
const importedTypeNames = resolveOperationTypeNames(node, tsResolver, { paramsCasing });
|
|
1305
|
+
const { query: queryParams } = getOperationParameters(node);
|
|
1306
|
+
const importedZodNames = zodResolver ? [
|
|
1307
|
+
resolveResponseParser(parser) === "zod" ? zodResolver.resolveResponseName?.(node) : null,
|
|
1308
|
+
resolveRequestParser(parser) === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null,
|
|
1309
|
+
resolveQueryParamsParser(parser) === "zod" && queryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, queryParams[0]) : null
|
|
1310
|
+
].filter((name) => Boolean(name)) : [];
|
|
1311
|
+
const zodRequestName = zodResolver && parser === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) ?? null : null;
|
|
1076
1312
|
const meta = {
|
|
1077
1313
|
name: resolver.resolveName(node.operationId),
|
|
1078
|
-
urlName:
|
|
1079
|
-
file: resolver.resolveFile({
|
|
1080
|
-
name: node.operationId,
|
|
1081
|
-
extname: ".ts",
|
|
1082
|
-
tag: node.tags[0] ?? "default",
|
|
1083
|
-
path: node.path
|
|
1084
|
-
}, {
|
|
1314
|
+
urlName: resolver.resolveUrlName(node),
|
|
1315
|
+
file: resolver.resolveFile(operationFileEntry(node, node.operationId), {
|
|
1085
1316
|
root,
|
|
1086
1317
|
output,
|
|
1087
|
-
group
|
|
1318
|
+
group: group ?? void 0
|
|
1088
1319
|
}),
|
|
1089
|
-
fileTs: tsResolver.resolveFile({
|
|
1090
|
-
name: node.operationId,
|
|
1091
|
-
extname: ".ts",
|
|
1092
|
-
tag: node.tags[0] ?? "default",
|
|
1093
|
-
path: node.path
|
|
1094
|
-
}, {
|
|
1320
|
+
fileTs: tsResolver.resolveFile(operationFileEntry(node, node.operationId), {
|
|
1095
1321
|
root,
|
|
1096
1322
|
output: pluginTs.options?.output ?? output,
|
|
1097
|
-
group: pluginTs.options?.group
|
|
1323
|
+
group: pluginTs.options?.group ?? void 0
|
|
1098
1324
|
}),
|
|
1099
|
-
fileZod: zodResolver && pluginZod?.options ? zodResolver.resolveFile({
|
|
1100
|
-
name: node.operationId,
|
|
1101
|
-
extname: ".ts",
|
|
1102
|
-
tag: node.tags[0] ?? "default",
|
|
1103
|
-
path: node.path
|
|
1104
|
-
}, {
|
|
1325
|
+
fileZod: zodResolver && pluginZod?.options ? zodResolver.resolveFile(operationFileEntry(node, node.operationId), {
|
|
1105
1326
|
root,
|
|
1106
1327
|
output: pluginZod.options.output ?? output,
|
|
1107
|
-
group: pluginZod.options?.group
|
|
1108
|
-
}) :
|
|
1328
|
+
group: pluginZod.options?.group ?? void 0
|
|
1329
|
+
}) : null
|
|
1109
1330
|
};
|
|
1110
|
-
const
|
|
1331
|
+
const hasFormData = node.requestBody?.content?.some((e) => e.contentType === "multipart/form-data") ?? false;
|
|
1111
1332
|
return /* @__PURE__ */ jsxs(File, {
|
|
1112
1333
|
baseName: meta.file.baseName,
|
|
1113
1334
|
path: meta.file.path,
|
|
1114
1335
|
meta: meta.file.meta,
|
|
1115
|
-
banner: resolver.resolveBanner(
|
|
1336
|
+
banner: resolver.resolveBanner(ctx.meta, {
|
|
1116
1337
|
output,
|
|
1117
|
-
config
|
|
1338
|
+
config,
|
|
1339
|
+
file: {
|
|
1340
|
+
path: meta.file.path,
|
|
1341
|
+
baseName: meta.file.baseName
|
|
1342
|
+
}
|
|
1118
1343
|
}),
|
|
1119
|
-
footer: resolver.resolveFooter(
|
|
1344
|
+
footer: resolver.resolveFooter(ctx.meta, {
|
|
1120
1345
|
output,
|
|
1121
|
-
config
|
|
1346
|
+
config,
|
|
1347
|
+
file: {
|
|
1348
|
+
path: meta.file.path,
|
|
1349
|
+
baseName: meta.file.baseName
|
|
1350
|
+
}
|
|
1122
1351
|
}),
|
|
1123
1352
|
children: [
|
|
1124
1353
|
importPath ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(File.Import, {
|
|
1125
|
-
name: "
|
|
1354
|
+
name: "client",
|
|
1126
1355
|
path: importPath
|
|
1127
1356
|
}), /* @__PURE__ */ jsx(File.Import, {
|
|
1128
1357
|
name: [
|
|
@@ -1133,7 +1362,7 @@ const clientGenerator = defineGenerator({
|
|
|
1133
1362
|
path: importPath,
|
|
1134
1363
|
isTypeOnly: true
|
|
1135
1364
|
})] }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(File.Import, {
|
|
1136
|
-
name: ["
|
|
1365
|
+
name: ["client"],
|
|
1137
1366
|
root: meta.file.path,
|
|
1138
1367
|
path: path.resolve(root, ".kubb/client.ts")
|
|
1139
1368
|
}), /* @__PURE__ */ jsx(File.Import, {
|
|
@@ -1146,11 +1375,16 @@ const clientGenerator = defineGenerator({
|
|
|
1146
1375
|
path: path.resolve(root, ".kubb/client.ts"),
|
|
1147
1376
|
isTypeOnly: true
|
|
1148
1377
|
})] }),
|
|
1149
|
-
|
|
1378
|
+
hasFormData && /* @__PURE__ */ jsx(File.Import, {
|
|
1150
1379
|
name: ["buildFormData"],
|
|
1151
1380
|
root: meta.file.path,
|
|
1152
1381
|
path: path.resolve(root, ".kubb/config.ts")
|
|
1153
1382
|
}),
|
|
1383
|
+
zodRequestName && /* @__PURE__ */ jsx(File.Import, {
|
|
1384
|
+
name: ["z"],
|
|
1385
|
+
path: "zod",
|
|
1386
|
+
isTypeOnly: true
|
|
1387
|
+
}),
|
|
1154
1388
|
meta.fileZod && importedZodNames.length > 0 && /* @__PURE__ */ jsx(File.Import, {
|
|
1155
1389
|
name: importedZodNames,
|
|
1156
1390
|
root: meta.file.path,
|
|
@@ -1192,16 +1426,22 @@ const clientGenerator = defineGenerator({
|
|
|
1192
1426
|
});
|
|
1193
1427
|
//#endregion
|
|
1194
1428
|
//#region src/generators/groupedClientGenerator.tsx
|
|
1429
|
+
/**
|
|
1430
|
+
* Emits one aggregate file per tag/group when `group` is configured. Each
|
|
1431
|
+
* file re-exports every client function for that group, so callers can
|
|
1432
|
+
* `import { petController } from './gen/clients'` instead of importing
|
|
1433
|
+
* each operation individually.
|
|
1434
|
+
*/
|
|
1195
1435
|
const groupedClientGenerator = defineGenerator({
|
|
1196
1436
|
name: "groupedClient",
|
|
1197
1437
|
renderer: jsxRenderer,
|
|
1198
1438
|
operations(nodes, ctx) {
|
|
1199
|
-
const { config, resolver,
|
|
1439
|
+
const { config, resolver, root } = ctx;
|
|
1200
1440
|
const { output, group } = ctx.options;
|
|
1201
1441
|
return /* @__PURE__ */ jsx(Fragment, { children: nodes.reduce((acc, operationNode) => {
|
|
1202
1442
|
if (group?.type === "tag") {
|
|
1203
1443
|
const tag = operationNode.tags[0];
|
|
1204
|
-
const name = tag ? group?.name?.({ group: camelCase(tag) }) :
|
|
1444
|
+
const name = tag ? group?.name?.({ group: camelCase(tag) }) : null;
|
|
1205
1445
|
if (!tag || !name) return acc;
|
|
1206
1446
|
const file = resolver.resolveFile({
|
|
1207
1447
|
name,
|
|
@@ -1210,7 +1450,7 @@ const groupedClientGenerator = defineGenerator({
|
|
|
1210
1450
|
}, {
|
|
1211
1451
|
root,
|
|
1212
1452
|
output,
|
|
1213
|
-
group
|
|
1453
|
+
group: group ?? void 0
|
|
1214
1454
|
});
|
|
1215
1455
|
const clientFile = resolver.resolveFile({
|
|
1216
1456
|
name: operationNode.operationId,
|
|
@@ -1220,7 +1460,7 @@ const groupedClientGenerator = defineGenerator({
|
|
|
1220
1460
|
}, {
|
|
1221
1461
|
root,
|
|
1222
1462
|
output,
|
|
1223
|
-
group
|
|
1463
|
+
group: group ?? void 0
|
|
1224
1464
|
});
|
|
1225
1465
|
const client = {
|
|
1226
1466
|
name: resolver.resolveName(operationNode.operationId),
|
|
@@ -1240,13 +1480,23 @@ const groupedClientGenerator = defineGenerator({
|
|
|
1240
1480
|
baseName: file.baseName,
|
|
1241
1481
|
path: file.path,
|
|
1242
1482
|
meta: file.meta,
|
|
1243
|
-
banner: resolver.resolveBanner(
|
|
1483
|
+
banner: resolver.resolveBanner(ctx.meta, {
|
|
1244
1484
|
output,
|
|
1245
|
-
config
|
|
1485
|
+
config,
|
|
1486
|
+
file: {
|
|
1487
|
+
path: file.path,
|
|
1488
|
+
baseName: file.baseName,
|
|
1489
|
+
isAggregation: true
|
|
1490
|
+
}
|
|
1246
1491
|
}),
|
|
1247
|
-
footer: resolver.resolveFooter(
|
|
1492
|
+
footer: resolver.resolveFooter(ctx.meta, {
|
|
1248
1493
|
output,
|
|
1249
|
-
config
|
|
1494
|
+
config,
|
|
1495
|
+
file: {
|
|
1496
|
+
path: file.path,
|
|
1497
|
+
baseName: file.baseName,
|
|
1498
|
+
isAggregation: true
|
|
1499
|
+
}
|
|
1250
1500
|
}),
|
|
1251
1501
|
children: [clients.map((client) => /* @__PURE__ */ jsx(File.Import, {
|
|
1252
1502
|
name: [client.name],
|
|
@@ -1267,33 +1517,17 @@ const groupedClientGenerator = defineGenerator({
|
|
|
1267
1517
|
}
|
|
1268
1518
|
});
|
|
1269
1519
|
//#endregion
|
|
1270
|
-
//#region src/
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
};
|
|
1278
|
-
});
|
|
1279
|
-
return /* @__PURE__ */ jsx(File.Source, {
|
|
1280
|
-
name,
|
|
1281
|
-
isExportable: true,
|
|
1282
|
-
isIndexable: true,
|
|
1283
|
-
children: /* @__PURE__ */ jsx(Const, {
|
|
1284
|
-
name,
|
|
1285
|
-
export: true,
|
|
1286
|
-
children: JSON.stringify(operationsObject, void 0, 2)
|
|
1287
|
-
})
|
|
1288
|
-
});
|
|
1289
|
-
}
|
|
1290
|
-
//#endregion
|
|
1291
|
-
//#region src/generators/operationsGenerator.tsx
|
|
1520
|
+
//#region src/generators/operationsGenerator.ts
|
|
1521
|
+
/**
|
|
1522
|
+
* Generates an `operations.ts` file that re-exports every operation grouped
|
|
1523
|
+
* by HTTP method. Enabled when `pluginClient({ operations: true })`. Useful
|
|
1524
|
+
* for building meta-tooling on top of the generated client (route
|
|
1525
|
+
* registries, API explorers).
|
|
1526
|
+
*/
|
|
1292
1527
|
const operationsGenerator = defineGenerator({
|
|
1293
1528
|
name: "client",
|
|
1294
|
-
renderer: jsxRenderer,
|
|
1295
1529
|
operations(nodes, ctx) {
|
|
1296
|
-
const { config, resolver,
|
|
1530
|
+
const { config, resolver, root } = ctx;
|
|
1297
1531
|
const { output, group } = ctx.options;
|
|
1298
1532
|
const name = "operations";
|
|
1299
1533
|
const file = resolver.resolveFile({
|
|
@@ -1302,37 +1536,65 @@ const operationsGenerator = defineGenerator({
|
|
|
1302
1536
|
}, {
|
|
1303
1537
|
root,
|
|
1304
1538
|
output,
|
|
1305
|
-
group
|
|
1539
|
+
group: group ?? void 0
|
|
1306
1540
|
});
|
|
1307
|
-
|
|
1541
|
+
const operationsObject = {};
|
|
1542
|
+
for (const node of nodes) {
|
|
1543
|
+
if (!ast.isHttpOperationNode(node)) continue;
|
|
1544
|
+
operationsObject[node.operationId] = {
|
|
1545
|
+
path: Url$1.toPath(node.path),
|
|
1546
|
+
method: node.method.toLowerCase()
|
|
1547
|
+
};
|
|
1548
|
+
}
|
|
1549
|
+
return [ast.createFile({
|
|
1308
1550
|
baseName: file.baseName,
|
|
1309
1551
|
path: file.path,
|
|
1310
1552
|
meta: file.meta,
|
|
1311
|
-
banner: resolver.resolveBanner(
|
|
1553
|
+
banner: resolver.resolveBanner(ctx.meta, {
|
|
1312
1554
|
output,
|
|
1313
|
-
config
|
|
1555
|
+
config,
|
|
1556
|
+
file: {
|
|
1557
|
+
path: file.path,
|
|
1558
|
+
baseName: file.baseName
|
|
1559
|
+
}
|
|
1314
1560
|
}),
|
|
1315
|
-
footer: resolver.resolveFooter(
|
|
1561
|
+
footer: resolver.resolveFooter(ctx.meta, {
|
|
1316
1562
|
output,
|
|
1317
|
-
config
|
|
1563
|
+
config,
|
|
1564
|
+
file: {
|
|
1565
|
+
path: file.path,
|
|
1566
|
+
baseName: file.baseName
|
|
1567
|
+
}
|
|
1318
1568
|
}),
|
|
1319
|
-
|
|
1569
|
+
sources: [ast.createSource({
|
|
1320
1570
|
name,
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1571
|
+
isExportable: true,
|
|
1572
|
+
isIndexable: true,
|
|
1573
|
+
nodes: [ast.createConst({
|
|
1574
|
+
name,
|
|
1575
|
+
export: true,
|
|
1576
|
+
nodes: [ast.createText(JSON.stringify(operationsObject, void 0, 2))]
|
|
1577
|
+
})]
|
|
1578
|
+
})]
|
|
1579
|
+
})];
|
|
1324
1580
|
}
|
|
1325
1581
|
});
|
|
1326
1582
|
//#endregion
|
|
1327
1583
|
//#region src/components/StaticClassClient.tsx
|
|
1328
1584
|
const declarationPrinter = functionPrinter({ mode: "declaration" });
|
|
1329
1585
|
function generateMethod({ node, name, tsResolver, zodResolver, baseURL, dataReturnType, parser, paramsType, paramsCasing, pathParamsType }) {
|
|
1330
|
-
|
|
1331
|
-
const contentType = node
|
|
1332
|
-
const isFormData = contentType === "multipart/form-data";
|
|
1333
|
-
const
|
|
1334
|
-
const
|
|
1335
|
-
const
|
|
1586
|
+
if (!ast.isHttpOperationNode(node)) return "";
|
|
1587
|
+
const { defaultContentType: contentType, isMultipleContentTypes, hasFormData } = getContentTypeInfo(node);
|
|
1588
|
+
const isFormData = !isMultipleContentTypes && contentType === "multipart/form-data";
|
|
1589
|
+
const { header: headerParams } = getOperationParameters(node);
|
|
1590
|
+
const headerParamsName = headerParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, headerParams[0]) : null;
|
|
1591
|
+
const headers = isMultipleContentTypes ? headerParamsName ? ["...headers"] : [] : buildHeaders(contentType, !!headerParamsName);
|
|
1592
|
+
const generics = buildGenerics(node, tsResolver, {
|
|
1593
|
+
dataReturnType,
|
|
1594
|
+
zodResolver,
|
|
1595
|
+
parser
|
|
1596
|
+
});
|
|
1597
|
+
const paramsNode = buildClientParamsNode({
|
|
1336
1598
|
paramsType,
|
|
1337
1599
|
paramsCasing,
|
|
1338
1600
|
pathParamsType,
|
|
@@ -1341,31 +1603,47 @@ function generateMethod({ node, name, tsResolver, zodResolver, baseURL, dataRetu
|
|
|
1341
1603
|
isConfigurable: true
|
|
1342
1604
|
});
|
|
1343
1605
|
const paramsSignature = declarationPrinter.print(paramsNode) ?? "";
|
|
1606
|
+
const { query: queryParams } = getOperationParameters(node);
|
|
1607
|
+
const zodQueryParamsName = zodResolver && resolveQueryParamsParser(parser) === "zod" && queryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, queryParams[0]) : null;
|
|
1344
1608
|
const clientParams = buildClassClientParams({
|
|
1345
1609
|
node,
|
|
1346
|
-
path,
|
|
1610
|
+
url: Url$1.toTemplateString(node.path, { casing: paramsCasing }),
|
|
1347
1611
|
baseURL,
|
|
1348
1612
|
tsResolver,
|
|
1349
1613
|
isFormData,
|
|
1350
|
-
|
|
1614
|
+
isMultipleContentTypes,
|
|
1615
|
+
hasFormData,
|
|
1616
|
+
headers,
|
|
1617
|
+
zodQueryParamsName
|
|
1351
1618
|
});
|
|
1352
|
-
const jsdoc = buildJSDoc(
|
|
1619
|
+
const jsdoc = buildJSDoc(buildOperationComments(node, {
|
|
1620
|
+
link: "urlPath",
|
|
1621
|
+
linkPosition: "beforeDeprecated",
|
|
1622
|
+
splitLines: true
|
|
1623
|
+
}));
|
|
1353
1624
|
const requestDataLine = buildRequestDataLine({
|
|
1354
1625
|
parser,
|
|
1355
1626
|
node,
|
|
1356
1627
|
zodResolver
|
|
1357
1628
|
});
|
|
1358
|
-
const
|
|
1629
|
+
const queryParamsLine = buildQueryParamsLine({
|
|
1630
|
+
parser,
|
|
1631
|
+
node,
|
|
1632
|
+
zodResolver
|
|
1633
|
+
});
|
|
1634
|
+
const formDataLine = buildFormDataLine(isFormData || isMultipleContentTypes && hasFormData, !!node.requestBody?.content?.[0]?.schema);
|
|
1359
1635
|
const returnStatement = buildReturnStatement({
|
|
1360
1636
|
dataReturnType,
|
|
1361
1637
|
parser,
|
|
1362
1638
|
node,
|
|
1363
|
-
zodResolver
|
|
1639
|
+
zodResolver,
|
|
1640
|
+
tsResolver
|
|
1364
1641
|
});
|
|
1365
1642
|
return `${jsdoc} static async ${name}(${paramsSignature}) {\n${[
|
|
1366
|
-
|
|
1643
|
+
`const { client: request = client, ${isMultipleContentTypes ? `contentType = ${stringify(contentType)}, ` : ""}...requestConfig } = mergeConfig(this.#config, config)`,
|
|
1367
1644
|
"",
|
|
1368
1645
|
requestDataLine,
|
|
1646
|
+
queryParamsLine,
|
|
1369
1647
|
formDataLine,
|
|
1370
1648
|
`const res = await request<${generics.join(", ")}>(${clientParams.toCall()})`,
|
|
1371
1649
|
returnStatement
|
|
@@ -1391,56 +1669,49 @@ function StaticClassClient({ name, isExportable = true, isIndexable = true, oper
|
|
|
1391
1669
|
children: [classCode, children]
|
|
1392
1670
|
});
|
|
1393
1671
|
}
|
|
1394
|
-
StaticClassClient.getParams = Client.getParams;
|
|
1395
1672
|
//#endregion
|
|
1396
1673
|
//#region src/generators/staticClassClientGenerator.tsx
|
|
1397
1674
|
function resolveTypeImportNames(node, tsResolver) {
|
|
1675
|
+
return resolveOperationTypeNames(node, tsResolver, { order: "body-response-first" });
|
|
1676
|
+
}
|
|
1677
|
+
function resolveZodImportNames(node, zodResolver, parser) {
|
|
1678
|
+
const { query: queryParams } = getOperationParameters(node);
|
|
1398
1679
|
return [
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
...node.parameters.filter((p) => p.in === "query").map((p) => tsResolver.resolveQueryParamsName(node, p)),
|
|
1403
|
-
...node.parameters.filter((p) => p.in === "header").map((p) => tsResolver.resolveHeaderParamsName(node, p)),
|
|
1404
|
-
...node.responses.map((res) => tsResolver.resolveResponseStatusName(node, res.statusCode))
|
|
1680
|
+
resolveResponseParser(parser) === "zod" ? zodResolver.resolveResponseName?.(node) : null,
|
|
1681
|
+
resolveRequestParser(parser) === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null,
|
|
1682
|
+
resolveQueryParamsParser(parser) === "zod" && queryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, queryParams[0]) : null
|
|
1405
1683
|
].filter((n) => Boolean(n));
|
|
1406
1684
|
}
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1685
|
+
/**
|
|
1686
|
+
* Built-in `operations` generator for `@kubb/plugin-client` when
|
|
1687
|
+
* `clientType: 'staticClass'`. Emits one class per tag, with a static method
|
|
1688
|
+
* per operation so callers can use `Pet.getPetById(...)` without
|
|
1689
|
+
* instantiating the class.
|
|
1690
|
+
*/
|
|
1410
1691
|
const staticClassClientGenerator = defineGenerator({
|
|
1411
1692
|
name: "staticClassClient",
|
|
1412
1693
|
renderer: jsxRenderer,
|
|
1413
1694
|
operations(nodes, ctx) {
|
|
1414
|
-
const {
|
|
1695
|
+
const { config, driver, resolver, root } = ctx;
|
|
1415
1696
|
const { output, group, dataReturnType, paramsCasing, paramsType, pathParamsType, parser, importPath } = ctx.options;
|
|
1416
|
-
const baseURL = ctx.options.baseURL ??
|
|
1697
|
+
const baseURL = ctx.options.baseURL ?? ctx.meta.baseURL;
|
|
1417
1698
|
const pluginTs = driver.getPlugin(pluginTsName);
|
|
1418
1699
|
if (!pluginTs) return null;
|
|
1419
1700
|
const tsResolver = driver.getResolver(pluginTsName);
|
|
1420
1701
|
const tsPluginOptions = pluginTs.options;
|
|
1421
|
-
const pluginZod = parser
|
|
1422
|
-
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) :
|
|
1702
|
+
const pluginZod = isParserEnabled(parser) ? driver.getPlugin(pluginZodName) : null;
|
|
1703
|
+
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null;
|
|
1423
1704
|
function buildOperationData(node) {
|
|
1424
|
-
const typeFile = tsResolver.resolveFile({
|
|
1425
|
-
name: node.operationId,
|
|
1426
|
-
extname: ".ts",
|
|
1427
|
-
tag: node.tags[0] ?? "default",
|
|
1428
|
-
path: node.path
|
|
1429
|
-
}, {
|
|
1705
|
+
const typeFile = tsResolver.resolveFile(operationFileEntry(node, node.operationId), {
|
|
1430
1706
|
root,
|
|
1431
1707
|
output: tsPluginOptions?.output ?? output,
|
|
1432
1708
|
group: tsPluginOptions?.group
|
|
1433
1709
|
});
|
|
1434
|
-
const zodFile = zodResolver && pluginZod?.options ? zodResolver.resolveFile({
|
|
1435
|
-
name: node.operationId,
|
|
1436
|
-
extname: ".ts",
|
|
1437
|
-
tag: node.tags[0] ?? "default",
|
|
1438
|
-
path: node.path
|
|
1439
|
-
}, {
|
|
1710
|
+
const zodFile = zodResolver && pluginZod?.options ? zodResolver.resolveFile(operationFileEntry(node, node.operationId), {
|
|
1440
1711
|
root,
|
|
1441
1712
|
output: pluginZod.options?.output ?? output,
|
|
1442
|
-
group: pluginZod.options?.group
|
|
1443
|
-
}) :
|
|
1713
|
+
group: pluginZod.options?.group ?? void 0
|
|
1714
|
+
}) : null;
|
|
1444
1715
|
return {
|
|
1445
1716
|
node,
|
|
1446
1717
|
name: resolver.resolveName(node.operationId),
|
|
@@ -1451,17 +1722,18 @@ const staticClassClientGenerator = defineGenerator({
|
|
|
1451
1722
|
};
|
|
1452
1723
|
}
|
|
1453
1724
|
const controllers = nodes.reduce((acc, operationNode) => {
|
|
1725
|
+
if (!ast.isHttpOperationNode(operationNode)) return acc;
|
|
1454
1726
|
const tag = operationNode.tags[0];
|
|
1455
|
-
const groupName = tag ? group?.name?.({ group: camelCase(tag) }) ??
|
|
1727
|
+
const groupName = tag ? group?.name?.({ group: camelCase(tag) }) ?? resolver.resolveGroupName(tag) : resolver.resolveClassName("Client");
|
|
1456
1728
|
if (!tag && !group) {
|
|
1457
|
-
const name = "ApiClient";
|
|
1729
|
+
const name = resolver.resolveClassName("ApiClient");
|
|
1458
1730
|
const file = resolver.resolveFile({
|
|
1459
1731
|
name,
|
|
1460
1732
|
extname: ".ts"
|
|
1461
1733
|
}, {
|
|
1462
1734
|
root,
|
|
1463
1735
|
output,
|
|
1464
|
-
group
|
|
1736
|
+
group: group ?? void 0
|
|
1465
1737
|
});
|
|
1466
1738
|
const operationData = buildOperationData(operationNode);
|
|
1467
1739
|
const previous = acc.find((item) => item.file.path === file.path);
|
|
@@ -1471,7 +1743,9 @@ const staticClassClientGenerator = defineGenerator({
|
|
|
1471
1743
|
file,
|
|
1472
1744
|
operations: [operationData]
|
|
1473
1745
|
});
|
|
1474
|
-
|
|
1746
|
+
return acc;
|
|
1747
|
+
}
|
|
1748
|
+
if (tag) {
|
|
1475
1749
|
const name = groupName;
|
|
1476
1750
|
const file = resolver.resolveFile({
|
|
1477
1751
|
name,
|
|
@@ -1480,7 +1754,7 @@ const staticClassClientGenerator = defineGenerator({
|
|
|
1480
1754
|
}, {
|
|
1481
1755
|
root,
|
|
1482
1756
|
output,
|
|
1483
|
-
group
|
|
1757
|
+
group: group ?? void 0
|
|
1484
1758
|
});
|
|
1485
1759
|
const operationData = buildOperationData(operationNode);
|
|
1486
1760
|
const previous = acc.find((item) => item.file.path === file.path);
|
|
@@ -1515,7 +1789,7 @@ const staticClassClientGenerator = defineGenerator({
|
|
|
1515
1789
|
const zodFilesByPath = /* @__PURE__ */ new Map();
|
|
1516
1790
|
ops.forEach((op) => {
|
|
1517
1791
|
if (!op.zodFile || !zodResolver) return;
|
|
1518
|
-
const names = resolveZodImportNames(op.node, zodResolver);
|
|
1792
|
+
const names = resolveZodImportNames(op.node, zodResolver, parser);
|
|
1519
1793
|
if (!zodImportsByFile.has(op.zodFile.path)) zodImportsByFile.set(op.zodFile.path, /* @__PURE__ */ new Set());
|
|
1520
1794
|
const imports = zodImportsByFile.get(op.zodFile.path);
|
|
1521
1795
|
names.forEach((n) => {
|
|
@@ -1530,27 +1804,35 @@ const staticClassClientGenerator = defineGenerator({
|
|
|
1530
1804
|
}
|
|
1531
1805
|
return /* @__PURE__ */ jsx(Fragment, { children: controllers.map(({ name, file, operations: ops }) => {
|
|
1532
1806
|
const { typeImportsByFile, typeFilesByPath } = collectTypeImports(ops);
|
|
1533
|
-
const { zodImportsByFile, zodFilesByPath } = parser
|
|
1807
|
+
const { zodImportsByFile, zodFilesByPath } = isParserEnabled(parser) ? collectZodImports(ops) : {
|
|
1534
1808
|
zodImportsByFile: /* @__PURE__ */ new Map(),
|
|
1535
1809
|
zodFilesByPath: /* @__PURE__ */ new Map()
|
|
1536
1810
|
};
|
|
1537
|
-
const hasFormData = ops.some((op) => op.node.requestBody?.content?.
|
|
1811
|
+
const hasFormData = ops.some((op) => op.node.requestBody?.content?.some((e) => e.contentType === "multipart/form-data") ?? false);
|
|
1538
1812
|
return /* @__PURE__ */ jsxs(File, {
|
|
1539
1813
|
baseName: file.baseName,
|
|
1540
1814
|
path: file.path,
|
|
1541
1815
|
meta: file.meta,
|
|
1542
|
-
banner: resolver.resolveBanner(
|
|
1816
|
+
banner: resolver.resolveBanner(ctx.meta, {
|
|
1543
1817
|
output,
|
|
1544
|
-
config
|
|
1818
|
+
config,
|
|
1819
|
+
file: {
|
|
1820
|
+
path: file.path,
|
|
1821
|
+
baseName: file.baseName
|
|
1822
|
+
}
|
|
1545
1823
|
}),
|
|
1546
|
-
footer: resolver.resolveFooter(
|
|
1824
|
+
footer: resolver.resolveFooter(ctx.meta, {
|
|
1547
1825
|
output,
|
|
1548
|
-
config
|
|
1826
|
+
config,
|
|
1827
|
+
file: {
|
|
1828
|
+
path: file.path,
|
|
1829
|
+
baseName: file.baseName
|
|
1830
|
+
}
|
|
1549
1831
|
}),
|
|
1550
1832
|
children: [
|
|
1551
1833
|
importPath ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1552
1834
|
/* @__PURE__ */ jsx(File.Import, {
|
|
1553
|
-
name: "
|
|
1835
|
+
name: "client",
|
|
1554
1836
|
path: importPath
|
|
1555
1837
|
}),
|
|
1556
1838
|
/* @__PURE__ */ jsx(File.Import, {
|
|
@@ -1568,7 +1850,7 @@ const staticClassClientGenerator = defineGenerator({
|
|
|
1568
1850
|
})
|
|
1569
1851
|
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1570
1852
|
/* @__PURE__ */ jsx(File.Import, {
|
|
1571
|
-
name: ["
|
|
1853
|
+
name: ["client"],
|
|
1572
1854
|
root: file.path,
|
|
1573
1855
|
path: path.resolve(root, ".kubb/client.ts")
|
|
1574
1856
|
}),
|
|
@@ -1593,6 +1875,11 @@ const staticClassClientGenerator = defineGenerator({
|
|
|
1593
1875
|
root: file.path,
|
|
1594
1876
|
path: path.resolve(root, ".kubb/config.ts")
|
|
1595
1877
|
}),
|
|
1878
|
+
parser === "zod" && zodResolver && ops.some((op) => op.node.requestBody?.content?.[0]?.schema != null) && /* @__PURE__ */ jsx(File.Import, {
|
|
1879
|
+
name: ["z"],
|
|
1880
|
+
path: "zod",
|
|
1881
|
+
isTypeOnly: true
|
|
1882
|
+
}),
|
|
1596
1883
|
Array.from(typeImportsByFile.entries()).map(([filePath, importSet]) => {
|
|
1597
1884
|
const typeFile = typeFilesByPath.get(filePath);
|
|
1598
1885
|
if (!typeFile) return null;
|
|
@@ -1605,7 +1892,7 @@ const staticClassClientGenerator = defineGenerator({
|
|
|
1605
1892
|
isTypeOnly: true
|
|
1606
1893
|
}, filePath);
|
|
1607
1894
|
}),
|
|
1608
|
-
parser
|
|
1895
|
+
isParserEnabled(parser) && Array.from(zodImportsByFile.entries()).map(([filePath, importSet]) => {
|
|
1609
1896
|
const zodFile = zodFilesByPath.get(filePath);
|
|
1610
1897
|
if (!zodFile) return null;
|
|
1611
1898
|
const importNames = Array.from(importSet).filter(Boolean);
|
|
@@ -1634,64 +1921,95 @@ const staticClassClientGenerator = defineGenerator({
|
|
|
1634
1921
|
//#endregion
|
|
1635
1922
|
//#region src/resolvers/resolverClient.ts
|
|
1636
1923
|
/**
|
|
1637
|
-
*
|
|
1924
|
+
* Default resolver used by `@kubb/plugin-client`. Decides the names and file
|
|
1925
|
+
* paths for every generated client function or class. Functions and files use
|
|
1926
|
+
* camelCase; classes and tag groups use PascalCase.
|
|
1638
1927
|
*
|
|
1639
|
-
*
|
|
1928
|
+
* @example Resolve client function and class names
|
|
1929
|
+
* ```ts
|
|
1930
|
+
* import { resolverClient } from '@kubb/plugin-client'
|
|
1640
1931
|
*
|
|
1641
|
-
*
|
|
1642
|
-
*
|
|
1932
|
+
* resolverClient.default('list pets', 'function') // 'listPets'
|
|
1933
|
+
* resolverClient.resolveClassName('pet') // 'Pet'
|
|
1934
|
+
* resolverClient.resolveGroupName('pet') // 'PetClient'
|
|
1935
|
+
* resolverClient.resolveUrlName(operationNode) // 'getShowPetByIdUrl'
|
|
1936
|
+
* ```
|
|
1643
1937
|
*/
|
|
1644
|
-
const resolverClient = defineResolver((
|
|
1938
|
+
const resolverClient = defineResolver(() => ({
|
|
1645
1939
|
name: "default",
|
|
1646
1940
|
pluginName: "plugin-client",
|
|
1647
1941
|
default(name, type) {
|
|
1648
|
-
|
|
1942
|
+
if (type === "file") return toFilePath(name);
|
|
1943
|
+
return ensureValidVarName(camelCase(name));
|
|
1649
1944
|
},
|
|
1650
1945
|
resolveName(name) {
|
|
1651
|
-
return
|
|
1946
|
+
return this.default(name, "function");
|
|
1947
|
+
},
|
|
1948
|
+
resolvePathName(name, type) {
|
|
1949
|
+
return this.default(name, type);
|
|
1950
|
+
},
|
|
1951
|
+
resolveClassName(name) {
|
|
1952
|
+
return ensureValidVarName(pascalCase(name));
|
|
1953
|
+
},
|
|
1954
|
+
resolveGroupName(name) {
|
|
1955
|
+
return ensureValidVarName(pascalCase(`${name} Client`));
|
|
1956
|
+
},
|
|
1957
|
+
resolveClientPropertyName(name) {
|
|
1958
|
+
return ensureValidVarName(camelCase(name));
|
|
1959
|
+
},
|
|
1960
|
+
resolveUrlName(node) {
|
|
1961
|
+
const name = this.resolveName(node.operationId);
|
|
1962
|
+
return `get${name.charAt(0).toUpperCase()}${name.slice(1)}Url`;
|
|
1652
1963
|
}
|
|
1653
1964
|
}));
|
|
1654
1965
|
//#endregion
|
|
1655
1966
|
//#region src/plugin.ts
|
|
1656
1967
|
/**
|
|
1657
|
-
* Canonical plugin name for `@kubb/plugin-client
|
|
1968
|
+
* Canonical plugin name for `@kubb/plugin-client`. Used for driver lookups and
|
|
1969
|
+
* cross-plugin dependency references.
|
|
1658
1970
|
*/
|
|
1659
1971
|
const pluginClientName = "plugin-client";
|
|
1660
1972
|
/**
|
|
1661
|
-
* Generates
|
|
1662
|
-
*
|
|
1663
|
-
*
|
|
1973
|
+
* Generates one HTTP client function per OpenAPI operation. Each function has
|
|
1974
|
+
* typed path params, query params, body, and response, so callers use the API
|
|
1975
|
+
* like any other typed function. Ships with `axios` and `fetch` runtimes; bring
|
|
1976
|
+
* your own by setting `importPath`.
|
|
1664
1977
|
*
|
|
1665
|
-
* @example
|
|
1978
|
+
* @example
|
|
1666
1979
|
* ```ts
|
|
1667
|
-
* import
|
|
1980
|
+
* import { defineConfig } from 'kubb'
|
|
1981
|
+
* import { pluginTs } from '@kubb/plugin-ts'
|
|
1982
|
+
* import { pluginClient } from '@kubb/plugin-client'
|
|
1983
|
+
*
|
|
1668
1984
|
* export default defineConfig({
|
|
1669
|
-
*
|
|
1985
|
+
* input: { path: './petStore.yaml' },
|
|
1986
|
+
* output: { path: './src/gen' },
|
|
1987
|
+
* plugins: [
|
|
1988
|
+
* pluginTs(),
|
|
1989
|
+
* pluginClient({
|
|
1990
|
+
* output: { path: './clients' },
|
|
1991
|
+
* client: 'fetch',
|
|
1992
|
+
* }),
|
|
1993
|
+
* ],
|
|
1670
1994
|
* })
|
|
1671
1995
|
* ```
|
|
1672
1996
|
*/
|
|
1673
1997
|
const pluginClient = definePlugin((options) => {
|
|
1674
1998
|
const { output = {
|
|
1675
1999
|
path: "clients",
|
|
1676
|
-
|
|
1677
|
-
}, group, exclude = [], include, override = [], urlType = false, dataReturnType = "data", paramsType = "inline", pathParamsType = paramsType === "object" ? "object" : options.pathParamsType || "inline", operations = false, paramsCasing, clientType = options.sdk ? "class" : "function", parser =
|
|
2000
|
+
barrel: { type: "named" }
|
|
2001
|
+
}, group, exclude = [], include, override = [], urlType = false, dataReturnType = "data", paramsType = "inline", pathParamsType = paramsType === "object" ? "object" : options.pathParamsType || "inline", operations = false, paramsCasing, clientType = options.sdk ? "class" : "function", parser = false, client = "axios", importPath, bundle = false, sdk, baseURL, resolver: userResolver, transformer: userTransformer } = options;
|
|
1678
2002
|
const resolvedImportPath = importPath ?? (!bundle ? `@kubb/plugin-client/clients/${client}` : void 0);
|
|
1679
2003
|
const selectedGenerators = options.generators ?? [
|
|
1680
2004
|
clientType === "staticClass" ? staticClassClientGenerator : clientType === "class" ? classClientGenerator : clientGenerator,
|
|
1681
|
-
group && clientType === "function" ? groupedClientGenerator :
|
|
1682
|
-
operations ? operationsGenerator :
|
|
2005
|
+
group && clientType === "function" ? groupedClientGenerator : null,
|
|
2006
|
+
operations ? operationsGenerator : null
|
|
1683
2007
|
].filter((x) => Boolean(x));
|
|
1684
|
-
const groupConfig = group
|
|
1685
|
-
...group,
|
|
1686
|
-
name: group.name ? group.name : (ctx) => {
|
|
1687
|
-
if (group.type === "path") return `${ctx.group.split("/")[1]}`;
|
|
1688
|
-
return `${camelCase(ctx.group)}Controller`;
|
|
1689
|
-
}
|
|
1690
|
-
} : void 0;
|
|
2008
|
+
const groupConfig = createGroupConfig(group);
|
|
1691
2009
|
return {
|
|
1692
2010
|
name: pluginClientName,
|
|
1693
2011
|
options,
|
|
1694
|
-
dependencies: [pluginTsName, parser
|
|
2012
|
+
dependencies: [pluginTsName, isParserEnabled(parser) ? pluginZodName : null].filter((dependency) => Boolean(dependency)),
|
|
1695
2013
|
hooks: { "kubb:plugin:setup"(ctx) {
|
|
1696
2014
|
const resolver = userResolver ? {
|
|
1697
2015
|
...resolverClient,
|
|
@@ -1749,6 +2067,6 @@ const pluginClient = definePlugin((options) => {
|
|
|
1749
2067
|
};
|
|
1750
2068
|
});
|
|
1751
2069
|
//#endregion
|
|
1752
|
-
export { Client, classClientGenerator, clientGenerator, pluginClient as default, pluginClient, groupedClientGenerator, operationsGenerator, pluginClientName, resolverClient, staticClassClientGenerator };
|
|
2070
|
+
export { Client, classClientGenerator, clientGenerator, pluginClient as default, pluginClient, groupedClientGenerator, isParserEnabled, operationsGenerator, pluginClientName, resolveQueryParamsParser, resolveRequestParser, resolveResponseParser, resolverClient, staticClassClientGenerator };
|
|
1753
2071
|
|
|
1754
2072
|
//# sourceMappingURL=index.js.map
|