@kubb/plugin-client 5.0.0-beta.42 → 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/dist/clients/axios.cjs.map +1 -1
- package/dist/clients/axios.js.map +1 -1
- package/dist/clients/fetch.cjs.map +1 -1
- package/dist/clients/fetch.js.map +1 -1
- package/dist/index.cjs +393 -342
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +57 -22
- package/dist/index.js +389 -342
- package/dist/index.js.map +1 -1
- package/dist/templates/clients/axios.source.cjs.map +1 -1
- package/dist/templates/clients/axios.source.js.map +1 -1
- package/dist/templates/clients/fetch.source.cjs.map +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.js.map +1 -1
- package/package.json +10 -17
- package/src/components/ClassClient.tsx +31 -7
- package/src/components/Client.tsx +33 -25
- package/src/components/StaticClassClient.tsx +31 -7
- package/src/components/Url.tsx +2 -3
- package/src/generators/classClientGenerator.tsx +20 -12
- package/src/generators/clientGenerator.tsx +18 -10
- package/src/generators/groupedClientGenerator.tsx +2 -2
- package/src/generators/operationsGenerator.ts +47 -0
- package/src/generators/staticClassClientGenerator.tsx +20 -12
- package/src/index.ts +2 -1
- package/src/plugin.ts +5 -4
- package/src/resolvers/resolverClient.ts +5 -4
- package/src/types.ts +29 -21
- package/src/utils.ts +122 -22
- package/extension.yaml +0 -1267
- package/src/components/Operations.tsx +0 -29
- package/src/generators/operationsGenerator.tsx +0 -34
package/dist/index.cjs
CHANGED
|
@@ -10,6 +10,7 @@ let node_path = require("node:path");
|
|
|
10
10
|
let node_path$1 = require_chunk.__toESM(node_path, 1);
|
|
11
11
|
node_path = require_chunk.__toESM(node_path);
|
|
12
12
|
let _kubb_core = require("@kubb/core");
|
|
13
|
+
let _kubb_ast_utils = require("@kubb/ast/utils");
|
|
13
14
|
let _kubb_plugin_ts = require("@kubb/plugin-ts");
|
|
14
15
|
let _kubb_renderer_jsx = require("@kubb/renderer-jsx");
|
|
15
16
|
let _kubb_renderer_jsx_jsx_runtime = require("@kubb/renderer-jsx/jsx-runtime");
|
|
@@ -25,68 +26,57 @@ let _kubb_plugin_zod = require("@kubb/plugin-zod");
|
|
|
25
26
|
function toCamelOrPascal(text, pascal) {
|
|
26
27
|
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) => {
|
|
27
28
|
if (word.length > 1 && word === word.toUpperCase()) return word;
|
|
28
|
-
|
|
29
|
-
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
29
|
+
return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
|
|
30
30
|
}).join("").replace(/[^a-zA-Z0-9]/g, "");
|
|
31
31
|
}
|
|
32
32
|
/**
|
|
33
|
-
* Splits `text` on `.` and applies `transformPart` to each segment.
|
|
34
|
-
* The last segment receives `isLast = true`, all earlier segments receive `false`.
|
|
35
|
-
* Segments are joined with `/` to form a file path.
|
|
36
|
-
*
|
|
37
|
-
* Only splits on dots followed by a letter so that version numbers
|
|
38
|
-
* embedded in operationIds (e.g. `v2025.0`) are kept intact.
|
|
39
|
-
*/
|
|
40
|
-
function applyToFileParts(text, transformPart) {
|
|
41
|
-
const parts = text.split(/\.(?=[a-zA-Z])/);
|
|
42
|
-
return parts.map((part, i) => transformPart(part, i === parts.length - 1)).join("/");
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
33
|
* Converts `text` to camelCase.
|
|
46
|
-
* When `isFile` is `true`, dot-separated segments are each cased independently and joined with `/`.
|
|
47
34
|
*
|
|
48
|
-
* @example
|
|
49
|
-
* camelCase('hello-world')
|
|
50
|
-
*
|
|
35
|
+
* @example Word boundaries
|
|
36
|
+
* `camelCase('hello-world') // 'helloWorld'`
|
|
37
|
+
*
|
|
38
|
+
* @example With a prefix
|
|
39
|
+
* `camelCase('tag', { prefix: 'create' }) // 'createTag'`
|
|
51
40
|
*/
|
|
52
|
-
function camelCase(text, {
|
|
53
|
-
if (isFile) return applyToFileParts(text, (part, isLast) => camelCase(part, isLast ? {
|
|
54
|
-
prefix,
|
|
55
|
-
suffix
|
|
56
|
-
} : {}));
|
|
41
|
+
function camelCase(text, { prefix = "", suffix = "" } = {}) {
|
|
57
42
|
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
|
|
58
43
|
}
|
|
59
44
|
/**
|
|
60
45
|
* Converts `text` to PascalCase.
|
|
61
|
-
* When `isFile` is `true`, the last dot-separated segment is PascalCased and earlier segments are camelCased.
|
|
62
46
|
*
|
|
63
|
-
* @example
|
|
64
|
-
* pascalCase('hello-world')
|
|
65
|
-
*
|
|
47
|
+
* @example Word boundaries
|
|
48
|
+
* `pascalCase('hello-world') // 'HelloWorld'`
|
|
49
|
+
*
|
|
50
|
+
* @example With a suffix
|
|
51
|
+
* `pascalCase('tag', { suffix: 'schema' }) // 'TagSchema'`
|
|
66
52
|
*/
|
|
67
|
-
function pascalCase(text, {
|
|
68
|
-
if (isFile) return applyToFileParts(text, (part, isLast) => isLast ? pascalCase(part, {
|
|
69
|
-
prefix,
|
|
70
|
-
suffix
|
|
71
|
-
}) : camelCase(part));
|
|
53
|
+
function pascalCase(text, { prefix = "", suffix = "" } = {}) {
|
|
72
54
|
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
|
|
73
55
|
}
|
|
74
56
|
//#endregion
|
|
75
|
-
//#region ../../internals/utils/src/
|
|
57
|
+
//#region ../../internals/utils/src/fs.ts
|
|
76
58
|
/**
|
|
77
|
-
* Builds a
|
|
78
|
-
*
|
|
59
|
+
* Builds a nested file path from a dotted name. Splits on dots that precede a letter
|
|
60
|
+
* (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
|
|
61
|
+
* every earlier segment, applies `caseLast` to the final segment, and joins with `/`.
|
|
79
62
|
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
63
|
+
* Empty segments are dropped before joining. They arise when the name starts with a dot
|
|
64
|
+
* followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to
|
|
65
|
+
* an empty string). Without this a leading `/` would form, which `path.resolve` reads as an
|
|
66
|
+
* absolute path, letting generated files escape the configured output directory.
|
|
67
|
+
*
|
|
68
|
+
* @example Nested path from a dotted name
|
|
69
|
+
* `toFilePath('pet.petId') // 'pet/petId'`
|
|
70
|
+
*
|
|
71
|
+
* @example PascalCase the final segment
|
|
72
|
+
* `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`
|
|
73
|
+
*
|
|
74
|
+
* @example Suffix applied to the final segment only
|
|
75
|
+
* `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
|
|
85
76
|
*/
|
|
86
|
-
function
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
return `/**\n${comments.map((c) => `${indent}${c}`).join("\n")}\n */${suffix}`;
|
|
77
|
+
function toFilePath(name, caseLast = camelCase) {
|
|
78
|
+
const parts = name.split(/\.(?=[a-zA-Z])/);
|
|
79
|
+
return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
|
|
90
80
|
}
|
|
91
81
|
//#endregion
|
|
92
82
|
//#region ../../internals/utils/src/reserved.ts
|
|
@@ -212,99 +202,83 @@ function ensureValidVarName(name) {
|
|
|
212
202
|
return `_${name}`;
|
|
213
203
|
}
|
|
214
204
|
//#endregion
|
|
215
|
-
//#region ../../internals/utils/src/
|
|
205
|
+
//#region ../../internals/utils/src/url.ts
|
|
206
|
+
function transformParam(raw, casing) {
|
|
207
|
+
const param = isValidVarName(raw) ? raw : camelCase(raw);
|
|
208
|
+
return casing === "camelcase" ? camelCase(param) : param;
|
|
209
|
+
}
|
|
210
|
+
function toParamsObject(path, { replacer, casing } = {}) {
|
|
211
|
+
const params = {};
|
|
212
|
+
for (const match of path.matchAll(/\{([^}]+)\}/g)) {
|
|
213
|
+
const param = transformParam(match[1], casing);
|
|
214
|
+
const key = replacer ? replacer(param) : param;
|
|
215
|
+
params[key] = key;
|
|
216
|
+
}
|
|
217
|
+
return Object.keys(params).length > 0 ? params : null;
|
|
218
|
+
}
|
|
216
219
|
/**
|
|
217
|
-
*
|
|
218
|
-
*
|
|
219
|
-
* @example
|
|
220
|
-
* const p = new URLPath('/pet/{petId}')
|
|
221
|
-
* p.URL // '/pet/:petId'
|
|
222
|
-
* p.template // '`/pet/${petId}`'
|
|
220
|
+
* Helpers for OpenAPI/Swagger paths, plus a thin wrapper over the native `URL`.
|
|
223
221
|
*/
|
|
224
|
-
var
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
*/
|
|
228
|
-
path;
|
|
229
|
-
#options;
|
|
230
|
-
constructor(path, options = {}) {
|
|
231
|
-
this.path = path;
|
|
232
|
-
this.#options = options;
|
|
222
|
+
var Url$1 = class Url$1 {
|
|
223
|
+
static {
|
|
224
|
+
require_chunk.__name(this, "Url");
|
|
233
225
|
}
|
|
234
|
-
/**
|
|
226
|
+
/**
|
|
227
|
+
* Reports whether `url` is a parseable absolute URL. Delegates to the native `URL.canParse`.
|
|
235
228
|
*
|
|
236
229
|
* @example
|
|
237
|
-
*
|
|
238
|
-
*
|
|
239
|
-
* ```
|
|
230
|
+
* Url.canParse('https://petstore.swagger.io/v2') // true
|
|
231
|
+
* Url.canParse('/pet/{petId}') // false
|
|
240
232
|
*/
|
|
241
|
-
|
|
242
|
-
return
|
|
233
|
+
static canParse(url, base) {
|
|
234
|
+
return URL.canParse(url, base);
|
|
243
235
|
}
|
|
244
|
-
/**
|
|
236
|
+
/**
|
|
237
|
+
* Converts an OpenAPI/Swagger path to Express-style colon syntax.
|
|
245
238
|
*
|
|
246
239
|
* @example
|
|
247
|
-
*
|
|
248
|
-
* new URLPath('https://petstore.swagger.io/v2/pet').isURL // true
|
|
249
|
-
* new URLPath('/pet/{petId}').isURL // false
|
|
250
|
-
* ```
|
|
240
|
+
* Url.toPath('/pet/{petId}') // '/pet/:petId'
|
|
251
241
|
*/
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
return !!new URL(this.path).href;
|
|
255
|
-
} catch {
|
|
256
|
-
return false;
|
|
257
|
-
}
|
|
242
|
+
static toPath(path) {
|
|
243
|
+
return path.replace(/\{([^}]+)\}/g, ":$1");
|
|
258
244
|
}
|
|
259
245
|
/**
|
|
260
|
-
* Converts
|
|
246
|
+
* Converts an OpenAPI/Swagger path to a TypeScript template literal string.
|
|
247
|
+
* `prefix` is prepended inside the literal, `replacer` transforms each parameter name,
|
|
248
|
+
* and `casing` controls parameter identifier casing.
|
|
261
249
|
*
|
|
262
250
|
* @example
|
|
263
|
-
*
|
|
264
|
-
* new URLPath('/account/monetary-accountID').template // '`/account/${monetaryAccountId}`'
|
|
265
|
-
*/
|
|
266
|
-
get template() {
|
|
267
|
-
return this.toTemplateString();
|
|
268
|
-
}
|
|
269
|
-
/** Returns the path and its extracted params as a structured `URLObject`, or as a stringified expression when `stringify` is set.
|
|
251
|
+
* Url.toTemplateString('/pet/{petId}') // '`/pet/${petId}`'
|
|
270
252
|
*
|
|
271
253
|
* @example
|
|
272
|
-
*
|
|
273
|
-
* new URLPath('/pet/{petId}').object
|
|
274
|
-
* // { url: '/pet/:petId', params: { petId: 'petId' } }
|
|
275
|
-
* ```
|
|
254
|
+
* Url.toTemplateString('/pet/{petId}', { prefix: 'https://api' }) // '`https://api/pet/${petId}`'
|
|
276
255
|
*/
|
|
277
|
-
|
|
278
|
-
|
|
256
|
+
static toTemplateString(path, { prefix, replacer, casing } = {}) {
|
|
257
|
+
const result = path.split(/\{([^}]+)\}/).map((part, i) => {
|
|
258
|
+
if (i % 2 === 0) return part;
|
|
259
|
+
const param = transformParam(part, casing);
|
|
260
|
+
return `\${${replacer ? replacer(param) : param}}`;
|
|
261
|
+
}).join("");
|
|
262
|
+
return `\`${prefix ?? ""}${result}\``;
|
|
279
263
|
}
|
|
280
|
-
/**
|
|
264
|
+
/**
|
|
265
|
+
* Returns the path and its extracted params as a structured `URLObject`, or as a stringified
|
|
266
|
+
* expression when `stringify` is set.
|
|
281
267
|
*
|
|
282
268
|
* @example
|
|
283
|
-
*
|
|
284
|
-
*
|
|
285
|
-
* new URLPath('/pet').params // null
|
|
286
|
-
* ```
|
|
287
|
-
*/
|
|
288
|
-
get params() {
|
|
289
|
-
return this.toParamsObject();
|
|
290
|
-
}
|
|
291
|
-
#transformParam(raw) {
|
|
292
|
-
const param = isValidVarName(raw) ? raw : camelCase(raw);
|
|
293
|
-
return this.#options.casing === "camelcase" ? camelCase(param) : param;
|
|
294
|
-
}
|
|
295
|
-
/**
|
|
296
|
-
* Iterates over every `{param}` token in `path`, calling `fn` with the raw token and transformed name.
|
|
269
|
+
* Url.toObject('/pet/{petId}')
|
|
270
|
+
* // { url: '/pet/:petId', params: { petId: 'petId' } }
|
|
297
271
|
*/
|
|
298
|
-
|
|
299
|
-
for (const match of this.path.matchAll(/\{([^}]+)\}/g)) {
|
|
300
|
-
const raw = match[1];
|
|
301
|
-
fn(raw, this.#transformParam(raw));
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
toObject({ type = "path", replacer, stringify } = {}) {
|
|
272
|
+
static toObject(path, { type = "path", replacer, stringify, casing } = {}) {
|
|
305
273
|
const object = {
|
|
306
|
-
url: type === "path" ?
|
|
307
|
-
|
|
274
|
+
url: type === "path" ? Url$1.toPath(path) : Url$1.toTemplateString(path, {
|
|
275
|
+
replacer,
|
|
276
|
+
casing
|
|
277
|
+
}),
|
|
278
|
+
params: toParamsObject(path, {
|
|
279
|
+
replacer,
|
|
280
|
+
casing
|
|
281
|
+
})
|
|
308
282
|
};
|
|
309
283
|
if (stringify) {
|
|
310
284
|
if (type === "template") return JSON.stringify(object).replaceAll("'", "").replaceAll(`"`, "");
|
|
@@ -313,50 +287,6 @@ var URLPath = class {
|
|
|
313
287
|
}
|
|
314
288
|
return object;
|
|
315
289
|
}
|
|
316
|
-
/**
|
|
317
|
-
* Converts the OpenAPI path to a TypeScript template literal string.
|
|
318
|
-
* An optional `replacer` can transform each extracted parameter name before interpolation.
|
|
319
|
-
*
|
|
320
|
-
* @example
|
|
321
|
-
* new URLPath('/pet/{petId}').toTemplateString() // '`/pet/${petId}`'
|
|
322
|
-
*/
|
|
323
|
-
toTemplateString({ prefix, replacer } = {}) {
|
|
324
|
-
const result = this.path.split(/\{([^}]+)\}/).map((part, i) => {
|
|
325
|
-
if (i % 2 === 0) return part;
|
|
326
|
-
const param = this.#transformParam(part);
|
|
327
|
-
return `\${${replacer ? replacer(param) : param}}`;
|
|
328
|
-
}).join("");
|
|
329
|
-
return `\`${prefix ?? ""}${result}\``;
|
|
330
|
-
}
|
|
331
|
-
/**
|
|
332
|
-
* Extracts all `{param}` segments from the path and returns them as a key-value map.
|
|
333
|
-
* An optional `replacer` transforms each parameter name in both key and value positions.
|
|
334
|
-
* Returns `undefined` when no path parameters are found.
|
|
335
|
-
*
|
|
336
|
-
* @example
|
|
337
|
-
* ```ts
|
|
338
|
-
* new URLPath('/pet/{petId}/tag/{tagId}').toParamsObject()
|
|
339
|
-
* // { petId: 'petId', tagId: 'tagId' }
|
|
340
|
-
* ```
|
|
341
|
-
*/
|
|
342
|
-
toParamsObject(replacer) {
|
|
343
|
-
const params = {};
|
|
344
|
-
this.#eachParam((_raw, param) => {
|
|
345
|
-
const key = replacer ? replacer(param) : param;
|
|
346
|
-
params[key] = key;
|
|
347
|
-
});
|
|
348
|
-
return Object.keys(params).length > 0 ? params : null;
|
|
349
|
-
}
|
|
350
|
-
/** Converts the OpenAPI path to Express-style colon syntax.
|
|
351
|
-
*
|
|
352
|
-
* @example
|
|
353
|
-
* ```ts
|
|
354
|
-
* new URLPath('/pet/{petId}').toURLPath() // '/pet/:petId'
|
|
355
|
-
* ```
|
|
356
|
-
*/
|
|
357
|
-
toURLPath() {
|
|
358
|
-
return this.path.replace(/\{([^}]+)\}/g, ":$1");
|
|
359
|
-
}
|
|
360
290
|
};
|
|
361
291
|
//#endregion
|
|
362
292
|
//#region ../../internals/shared/src/operation.ts
|
|
@@ -382,7 +312,7 @@ function operationFileEntry(node, name, extname = ".ts") {
|
|
|
382
312
|
function getOperationLink(node, link) {
|
|
383
313
|
if (!link) return null;
|
|
384
314
|
if (typeof link === "function") return link(node) ?? null;
|
|
385
|
-
if (link === "urlPath") return node.path ? `{@link ${
|
|
315
|
+
if (link === "urlPath") return node.path ? `{@link ${Url$1.toPath(node.path)}}` : null;
|
|
386
316
|
return node.path ? `{@link ${node.path.replaceAll("{", ":").replaceAll("}", "")}}` : null;
|
|
387
317
|
}
|
|
388
318
|
function getContentTypeInfo(node) {
|
|
@@ -473,6 +403,19 @@ function resolveSuccessNames(node, resolver) {
|
|
|
473
403
|
function resolveStatusCodeNames(node, resolver) {
|
|
474
404
|
return node.responses.map((response) => resolver.resolveResponseStatusName(node, response.statusCode));
|
|
475
405
|
}
|
|
406
|
+
/**
|
|
407
|
+
* Builds the discriminated union type string for `dataReturnType: 'full'` return shapes.
|
|
408
|
+
* Each member is `{ status: N; data: StatusNType; statusText: string }`.
|
|
409
|
+
*/
|
|
410
|
+
function buildStatusUnionType(node, resolver) {
|
|
411
|
+
const members = node.responses.map((r) => {
|
|
412
|
+
const typeName = resolver.resolveResponseStatusName(node, r.statusCode);
|
|
413
|
+
const statusCode = Number.parseInt(r.statusCode, 10);
|
|
414
|
+
return `{ status: ${Number.isNaN(statusCode) ? "number" : String(statusCode)}; data: ${typeName}; statusText: string }`;
|
|
415
|
+
});
|
|
416
|
+
if (members.length === 1) return members[0];
|
|
417
|
+
return `(${members.join(" | ")})`;
|
|
418
|
+
}
|
|
476
419
|
const typeNamesByResolver = /* @__PURE__ */ new WeakMap();
|
|
477
420
|
function resolveOperationTypeNames(node, resolver, options = {}) {
|
|
478
421
|
const cacheKey = `${node.operationId}\0${options.paramsCasing ?? ""}\0${options.order ?? ""}\0${options.responseStatusNames ?? ""}\0${(options.exclude ?? []).join(",")}`;
|
|
@@ -512,26 +455,24 @@ function resolveOperationTypeNames(node, resolver, options = {}) {
|
|
|
512
455
|
* shared default naming so every plugin groups output consistently:
|
|
513
456
|
*
|
|
514
457
|
* - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).
|
|
515
|
-
* - other groups use
|
|
458
|
+
* - other groups use the camelCased group (`pet store` → `petStore`).
|
|
516
459
|
*
|
|
517
460
|
* A user-provided `group.name` always wins over the default namer, so callers stay in
|
|
518
461
|
* control of their output folders. Returns `null` when grouping is disabled, matching the
|
|
519
462
|
* per-plugin convention.
|
|
520
463
|
*
|
|
521
464
|
* @param group - The user-supplied group option, or `undefined` to disable grouping.
|
|
522
|
-
* @param options.suffix - Appended to non-`path` group names, e.g. `'Controller'` or `'Requests'`.
|
|
523
465
|
*
|
|
524
466
|
* @example
|
|
525
467
|
* ```ts
|
|
526
|
-
* createGroupConfig(group
|
|
527
|
-
* createGroupConfig(group, { suffix: 'Requests' }) // plugin-cypress, plugin-mcp
|
|
468
|
+
* createGroupConfig(group) // shared across every plugin
|
|
528
469
|
* ```
|
|
529
470
|
*/
|
|
530
|
-
function createGroupConfig(group
|
|
471
|
+
function createGroupConfig(group) {
|
|
531
472
|
if (!group) return null;
|
|
532
473
|
const defaultName = (ctx) => {
|
|
533
474
|
if (group.type === "path") return `${ctx.group.split("/")[1]}`;
|
|
534
|
-
return
|
|
475
|
+
return camelCase(ctx.group);
|
|
535
476
|
};
|
|
536
477
|
return {
|
|
537
478
|
...group,
|
|
@@ -614,6 +555,143 @@ function createFunctionParams(params) {
|
|
|
614
555
|
};
|
|
615
556
|
}
|
|
616
557
|
//#endregion
|
|
558
|
+
//#region src/utils.ts
|
|
559
|
+
/**
|
|
560
|
+
* Returns `true` when any direction of the parser uses Zod (used for dependency checks).
|
|
561
|
+
*/
|
|
562
|
+
function isParserEnabled(parser) {
|
|
563
|
+
if (!parser) return false;
|
|
564
|
+
if (parser === "zod") return true;
|
|
565
|
+
return !!(parser.request || parser.response);
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* Returns `'zod'` when request body parsing is enabled, `null` otherwise.
|
|
569
|
+
* The string shorthand `'zod'` also enables request body parsing (existing behavior).
|
|
570
|
+
*/
|
|
571
|
+
function resolveRequestParser(parser) {
|
|
572
|
+
if (!parser) return null;
|
|
573
|
+
if (parser === "zod") return "zod";
|
|
574
|
+
return parser.request ?? null;
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Returns `'zod'` when query-parameters parsing is enabled, `null` otherwise.
|
|
578
|
+
* Only the object form `{ request: 'zod' }` enables query-params parsing.
|
|
579
|
+
* The string shorthand `'zod'` does not, preserving its existing behavior.
|
|
580
|
+
*/
|
|
581
|
+
function resolveQueryParamsParser(parser) {
|
|
582
|
+
if (!parser || parser === "zod") return null;
|
|
583
|
+
return parser.request ?? null;
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* Returns `'zod'` when response-direction parsing is enabled, `null` otherwise.
|
|
587
|
+
* `parser: 'zod'` (string shorthand) maps to response parsing and returns `'zod'`.
|
|
588
|
+
*/
|
|
589
|
+
function resolveResponseParser(parser) {
|
|
590
|
+
if (!parser) return null;
|
|
591
|
+
if (parser === "zod") return "zod";
|
|
592
|
+
return parser.response ?? null;
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Builds HTTP headers array for a client request.
|
|
596
|
+
* Includes Content-Type (if not default) and spreads header parameters if present.
|
|
597
|
+
*/
|
|
598
|
+
function buildHeaders(contentType, hasHeaderParams) {
|
|
599
|
+
return [contentType !== "application/json" && contentType !== "multipart/form-data" ? `'Content-Type': '${contentType}'` : null, hasHeaderParams ? "...headers" : null].filter(Boolean);
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Returns the generic type arguments — response, error, and request body — for a generated
|
|
603
|
+
* client call.
|
|
604
|
+
*
|
|
605
|
+
* When `dataReturnType` is `'full'`, the response generic widens to a union of all documented
|
|
606
|
+
* status types. When `parser` is `'zod'` and a request body schema exists, the request type
|
|
607
|
+
* uses `z.output<typeof schema>` to reflect post-transform types (e.g. date coercion).
|
|
608
|
+
*/
|
|
609
|
+
function buildGenerics(node, tsResolver, options = {}) {
|
|
610
|
+
const allStatusNames = node.responses.map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode));
|
|
611
|
+
const successNames = resolveSuccessNames(node, tsResolver);
|
|
612
|
+
const responseName = options.dataReturnType === "full" ? allStatusNames.length > 0 ? allStatusNames.join(" | ") : tsResolver.resolveResponseName(node) : successNames.length > 0 ? successNames.join(" | ") : tsResolver.resolveResponseName(node);
|
|
613
|
+
const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : null;
|
|
614
|
+
const errorNames = node.responses.filter((r) => Number.parseInt(r.statusCode, 10) >= 400).map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode));
|
|
615
|
+
const TError = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`;
|
|
616
|
+
const zodRequestName = options.parser === "zod" && options.zodResolver && node.requestBody?.content?.[0]?.schema ? options.zodResolver.resolveDataName?.(node) ?? null : null;
|
|
617
|
+
return [
|
|
618
|
+
responseName,
|
|
619
|
+
TError,
|
|
620
|
+
zodRequestName ? `z.output<typeof ${zodRequestName}>` : requestName || "unknown"
|
|
621
|
+
].filter(Boolean);
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Builds the parameters object for a class-based client method.
|
|
625
|
+
* Includes URL, method, base URL, headers, and request/response data.
|
|
626
|
+
*/
|
|
627
|
+
function buildClassClientParams({ node, url, baseURL, tsResolver, isFormData, isMultipleContentTypes, hasFormData, headers, zodQueryParamsName }) {
|
|
628
|
+
const { query: queryParams } = getOperationParameters(node);
|
|
629
|
+
const queryParamsName = queryParams.length > 0 ? tsResolver.resolveQueryParamsName(node, queryParams[0]) : null;
|
|
630
|
+
const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : null;
|
|
631
|
+
const responseType = getResponseType(node);
|
|
632
|
+
return createFunctionParams({ config: {
|
|
633
|
+
mode: "object",
|
|
634
|
+
children: {
|
|
635
|
+
requestConfig: { mode: "inlineSpread" },
|
|
636
|
+
method: { value: JSON.stringify(_kubb_core.ast.isHttpOperationNode(node) ? node.method.toUpperCase() : "") },
|
|
637
|
+
url: { value: url },
|
|
638
|
+
baseURL: baseURL ? { value: JSON.stringify(baseURL) } : null,
|
|
639
|
+
params: queryParamsName ? zodQueryParamsName ? { value: "requestParams" } : {} : null,
|
|
640
|
+
data: requestName ? { value: isMultipleContentTypes && hasFormData ? "contentType === 'multipart/form-data' ? formData as FormData : requestData" : isFormData ? "formData as FormData" : "requestData" } : null,
|
|
641
|
+
contentType: isMultipleContentTypes ? {} : null,
|
|
642
|
+
responseType: responseType ? { value: JSON.stringify(responseType) } : null,
|
|
643
|
+
headers: headers.length ? { value: `{ ${headers.join(", ")}, ...requestConfig.headers }` } : null
|
|
644
|
+
}
|
|
645
|
+
} });
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Builds the request data parsing line for client methods.
|
|
649
|
+
* Applies Zod validation when `parser.request === 'zod'`, otherwise assigns data directly.
|
|
650
|
+
*/
|
|
651
|
+
function buildRequestDataLine({ parser, node, zodResolver }) {
|
|
652
|
+
const requestParser = resolveRequestParser(parser);
|
|
653
|
+
const zodRequestName = zodResolver && requestParser === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null;
|
|
654
|
+
if (requestParser === "zod" && zodRequestName) return `const requestData = ${zodRequestName}.parse(data)`;
|
|
655
|
+
if (node.requestBody?.content?.[0]?.schema) return "const requestData = data";
|
|
656
|
+
return "";
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* Builds the query parameters parsing line for client methods.
|
|
660
|
+
* Returns an empty string when no query params exist or query-params parsing is not enabled.
|
|
661
|
+
* Only the object form `parser: { request: 'zod' }` triggers this. `parser: 'zod'` does not.
|
|
662
|
+
*/
|
|
663
|
+
function buildQueryParamsLine({ parser, node, zodResolver }) {
|
|
664
|
+
if (resolveQueryParamsParser(parser) !== "zod" || !zodResolver) return "";
|
|
665
|
+
const { query: queryParams } = getOperationParameters(node);
|
|
666
|
+
if (queryParams.length === 0) return "";
|
|
667
|
+
const zodQueryParamsName = zodResolver.resolveQueryParamsName?.(node, queryParams[0]);
|
|
668
|
+
if (!zodQueryParamsName) return "";
|
|
669
|
+
return `const requestParams = ${zodQueryParamsName}.parse(params)`;
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Builds the form data conversion line for file upload requests.
|
|
673
|
+
* Returns empty string if not applicable.
|
|
674
|
+
*/
|
|
675
|
+
function buildFormDataLine(isFormData, hasRequest) {
|
|
676
|
+
return isFormData && hasRequest ? "const formData = buildFormData(requestData)" : "";
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* Builds the return statement for a client method.
|
|
680
|
+
* When `dataReturnType` is `'full'`, casts the response to the status-discriminated union type.
|
|
681
|
+
* When `parser.response` is `'zod'`, pipes the response body through the Zod schema before returning.
|
|
682
|
+
*/
|
|
683
|
+
function buildReturnStatement({ dataReturnType, parser, node, zodResolver, tsResolver }) {
|
|
684
|
+
const responseParser = resolveResponseParser(parser);
|
|
685
|
+
const zodResponseName = zodResolver && responseParser === "zod" ? zodResolver.resolveResponseName?.(node) : null;
|
|
686
|
+
if (dataReturnType === "full" && tsResolver) {
|
|
687
|
+
const unionType = buildStatusUnionType(node, tsResolver);
|
|
688
|
+
if (responseParser === "zod" && zodResponseName) return `return {...res, data: ${zodResponseName}.parse(res.data)} as ${unionType}`;
|
|
689
|
+
return `return res as ${unionType}`;
|
|
690
|
+
}
|
|
691
|
+
if (dataReturnType === "data" && responseParser === "zod" && zodResponseName) return `return ${zodResponseName}.parse(res.data)`;
|
|
692
|
+
return "return res.data";
|
|
693
|
+
}
|
|
694
|
+
//#endregion
|
|
617
695
|
//#region src/components/Url.tsx
|
|
618
696
|
const declarationPrinter$3 = (0, _kubb_plugin_ts.functionPrinter)({ mode: "declaration" });
|
|
619
697
|
function buildUrlParamsNode({ paramsType, paramsCasing, pathParamsType, node, tsResolver }) {
|
|
@@ -631,7 +709,6 @@ function buildUrlParamsNode({ paramsType, paramsCasing, pathParamsType, node, ts
|
|
|
631
709
|
}
|
|
632
710
|
function Url({ name, isExportable = true, isIndexable = true, baseURL, paramsType, paramsCasing, pathParamsType, node, tsResolver }) {
|
|
633
711
|
if (!_kubb_core.ast.isHttpOperationNode(node)) return null;
|
|
634
|
-
const path = new URLPath(node.path);
|
|
635
712
|
const paramsNode = buildUrlParamsNode({
|
|
636
713
|
paramsType,
|
|
637
714
|
paramsCasing,
|
|
@@ -656,7 +733,7 @@ function Url({ name, isExportable = true, isIndexable = true, baseURL, paramsTyp
|
|
|
656
733
|
pathParamsMapping && Object.keys(pathParamsMapping).length > 0 && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}),
|
|
657
734
|
/* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.Const, {
|
|
658
735
|
name: "res",
|
|
659
|
-
children: `{ method: '${node.method.toUpperCase()}', url: ${
|
|
736
|
+
children: `{ method: '${node.method.toUpperCase()}', url: ${Url$1.toTemplateString(node.path, { prefix: baseURL })} as const }`
|
|
660
737
|
}),
|
|
661
738
|
/* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}),
|
|
662
739
|
"return res"
|
|
@@ -685,7 +762,6 @@ function buildClientParamsNode({ paramsType, paramsCasing, pathParamsType, node,
|
|
|
685
762
|
}
|
|
686
763
|
function Client({ name, isExportable = true, isIndexable = true, returnType, baseURL, dataReturnType, parser, paramsType, paramsCasing, pathParamsType, node, tsResolver, zodResolver, urlName, children, isConfigurable = true }) {
|
|
687
764
|
if (!_kubb_core.ast.isHttpOperationNode(node)) return null;
|
|
688
|
-
const path = new URLPath(node.path);
|
|
689
765
|
const { defaultContentType: contentType, isMultipleContentTypes, hasFormData } = getContentTypeInfo(node);
|
|
690
766
|
const isFormData = !isMultipleContentTypes && contentType === "multipart/form-data";
|
|
691
767
|
const responseType = getResponseType(node);
|
|
@@ -699,16 +775,22 @@ function Client({ name, isExportable = true, isIndexable = true, returnType, bas
|
|
|
699
775
|
const responseName = successNames.length > 0 ? successNames.join(" | ") : tsResolver.resolveResponseName(node);
|
|
700
776
|
const queryParamsName = originalQueryParams.length > 0 ? tsResolver.resolveQueryParamsName(node, originalQueryParams[0]) : null;
|
|
701
777
|
const headerParamsName = originalHeaderParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, originalHeaderParams[0]) : null;
|
|
702
|
-
const
|
|
703
|
-
const
|
|
778
|
+
const requestParser = resolveRequestParser(parser);
|
|
779
|
+
const responseParser = resolveResponseParser(parser);
|
|
780
|
+
const zodResponseName = zodResolver && responseParser === "zod" ? zodResolver.resolveResponseName?.(node) : null;
|
|
781
|
+
const zodRequestName = zodResolver && requestParser === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null;
|
|
782
|
+
const queryParamsParser = resolveQueryParamsParser(parser);
|
|
783
|
+
const zodQueryParamsName = zodResolver && queryParamsParser === "zod" && originalQueryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, originalQueryParams[0]) : null;
|
|
704
784
|
const errorNames = node.responses.filter((r) => {
|
|
705
785
|
return Number.parseInt(r.statusCode, 10) >= 400;
|
|
706
786
|
}).map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode));
|
|
707
787
|
const headers = [!isMultipleContentTypes && contentType !== "application/json" && contentType !== "multipart/form-data" ? `'Content-Type': '${contentType}'` : null, headerParamsName ? headerParamsMapping ? "...mappedHeaders" : "...headers" : null].filter(Boolean);
|
|
788
|
+
const TError = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`;
|
|
789
|
+
const allStatusNames = node.responses.map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode));
|
|
708
790
|
const generics = [
|
|
709
|
-
responseName,
|
|
710
|
-
|
|
711
|
-
requestName || "unknown"
|
|
791
|
+
dataReturnType === "full" ? allStatusNames.length > 0 ? allStatusNames.join(" | ") : responseName : responseName,
|
|
792
|
+
TError,
|
|
793
|
+
parser === "zod" && zodRequestName ? `z.output<typeof ${zodRequestName}>` : requestName || "unknown"
|
|
712
794
|
].filter(Boolean);
|
|
713
795
|
const paramsNode = buildClientParamsNode({
|
|
714
796
|
paramsType,
|
|
@@ -730,22 +812,23 @@ function Client({ name, isExportable = true, isIndexable = true, returnType, bas
|
|
|
730
812
|
const clientParams = createFunctionParams({ config: {
|
|
731
813
|
mode: "object",
|
|
732
814
|
children: {
|
|
733
|
-
method: { value:
|
|
734
|
-
url: { value: urlName ? `${urlName}(${urlParamsCall}).url.toString()` : path
|
|
815
|
+
method: { value: (0, _kubb_ast_utils.stringify)(node.method.toUpperCase()) },
|
|
816
|
+
url: { value: urlName ? `${urlName}(${urlParamsCall}).url.toString()` : Url$1.toTemplateString(node.path) },
|
|
735
817
|
baseURL: baseURL && !urlName ? { value: `\`${baseURL}\`` } : null,
|
|
736
|
-
params: queryParamsName ? queryParamsMapping ? { value: "mappedParams" } : {} : null,
|
|
818
|
+
params: queryParamsName ? zodQueryParamsName ? { value: "requestParams" } : queryParamsMapping ? { value: "mappedParams" } : {} : null,
|
|
737
819
|
data: requestName ? { value: isMultipleContentTypes && hasFormData ? "contentType === 'multipart/form-data' ? formData as FormData : requestData" : isFormData ? "formData as FormData" : "requestData" } : null,
|
|
738
820
|
contentType: isConfigurable && isMultipleContentTypes ? {} : null,
|
|
739
|
-
responseType: responseType ? { value:
|
|
821
|
+
responseType: responseType ? { value: (0, _kubb_ast_utils.stringify)(responseType) } : null,
|
|
740
822
|
requestConfig: isConfigurable ? { mode: "inlineSpread" } : null,
|
|
741
823
|
headers: headers.length ? { value: isConfigurable ? `{ ${headers.join(", ")}, ...requestConfig.headers }` : `{ ${headers.join(", ")} }` } : null
|
|
742
824
|
}
|
|
743
825
|
} });
|
|
826
|
+
const statusUnionType = dataReturnType === "full" ? buildStatusUnionType(node, tsResolver) : null;
|
|
744
827
|
const childrenElement = children ? children : /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [
|
|
745
|
-
dataReturnType === "full" &&
|
|
746
|
-
dataReturnType === "
|
|
747
|
-
dataReturnType === "
|
|
748
|
-
dataReturnType === "data" &&
|
|
828
|
+
dataReturnType === "full" && responseParser === "zod" && zodResponseName && statusUnionType && `return {...res, data: ${zodResponseName}.parse(res.data)} as ${statusUnionType}`,
|
|
829
|
+
dataReturnType === "full" && responseParser !== "zod" && statusUnionType && `return res as ${statusUnionType}`,
|
|
830
|
+
dataReturnType === "data" && responseParser === "zod" && zodResponseName && `return ${zodResponseName}.parse(res.data)`,
|
|
831
|
+
dataReturnType === "data" && responseParser !== "zod" && "return res.data"
|
|
749
832
|
] });
|
|
750
833
|
return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}), /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
|
|
751
834
|
name,
|
|
@@ -763,23 +846,14 @@ function Client({ name, isExportable = true, isIndexable = true, returnType, bas
|
|
|
763
846
|
}) },
|
|
764
847
|
returnType,
|
|
765
848
|
children: [
|
|
766
|
-
isConfigurable ? `const { client: request = client, ${isMultipleContentTypes ? `contentType = ${
|
|
767
|
-
/* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}),
|
|
849
|
+
isConfigurable ? `const { client: request = client, ${isMultipleContentTypes ? `contentType = ${(0, _kubb_ast_utils.stringify)(contentType)}, ` : ""}...requestConfig } = config` : "",
|
|
768
850
|
/* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}),
|
|
769
851
|
pathParamsMapping && Object.entries(pathParamsMapping).filter(([originalName, camelCaseName]) => isValidVarName(originalName) && originalName !== camelCaseName).map(([originalName, camelCaseName]) => `const ${originalName} = ${camelCaseName}`).join("\n"),
|
|
770
|
-
pathParamsMapping && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.
|
|
771
|
-
queryParamsMapping && queryParamsName && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
] }),
|
|
776
|
-
headerParamsMapping && headerParamsName && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [
|
|
777
|
-
`const mappedHeaders = headers ? { ${Object.entries(headerParamsMapping).map(([originalName, camelCaseName]) => `"${originalName}": headers.${camelCaseName}`).join(", ")} } : undefined`,
|
|
778
|
-
/* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}),
|
|
779
|
-
/* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {})
|
|
780
|
-
] }),
|
|
781
|
-
parser === "zod" && zodRequestName ? `const requestData = ${zodRequestName}.parse(data)` : requestName && "const requestData = data",
|
|
782
|
-
/* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}),
|
|
852
|
+
pathParamsMapping && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}),
|
|
853
|
+
queryParamsMapping && queryParamsName && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [`const mappedParams = params ? { ${Object.entries(queryParamsMapping).map(([originalName, camelCaseName]) => `"${originalName}": params.${camelCaseName}`).join(", ")} } : undefined`, /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {})] }),
|
|
854
|
+
headerParamsMapping && headerParamsName && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [`const mappedHeaders = headers ? { ${Object.entries(headerParamsMapping).map(([originalName, camelCaseName]) => `"${originalName}": headers.${camelCaseName}`).join(", ")} } : undefined`, /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {})] }),
|
|
855
|
+
requestParser === "zod" && zodRequestName ? `const requestData = ${zodRequestName}.parse(data)` : requestName && "const requestData = data",
|
|
856
|
+
zodQueryParamsName && `const requestParams = ${zodQueryParamsName}.parse(params)`,
|
|
783
857
|
(isFormData || isMultipleContentTypes && hasFormData) && requestName && "const formData = buildFormData(requestData)",
|
|
784
858
|
/* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}),
|
|
785
859
|
isConfigurable ? `const res = await request<${generics.join(", ")}>(${clientParams.toCall()})` : `const res = await client<${generics.join(", ")}>(${clientParams.toCall()})`,
|
|
@@ -790,93 +864,20 @@ function Client({ name, isExportable = true, isIndexable = true, returnType, bas
|
|
|
790
864
|
})] });
|
|
791
865
|
}
|
|
792
866
|
//#endregion
|
|
793
|
-
//#region src/utils.ts
|
|
794
|
-
/**
|
|
795
|
-
* Builds HTTP headers array for a client request.
|
|
796
|
-
* Includes Content-Type (if not default) and spreads header parameters if present.
|
|
797
|
-
*/
|
|
798
|
-
function buildHeaders(contentType, hasHeaderParams) {
|
|
799
|
-
return [contentType !== "application/json" && contentType !== "multipart/form-data" ? `'Content-Type': '${contentType}'` : null, hasHeaderParams ? "...headers" : null].filter(Boolean);
|
|
800
|
-
}
|
|
801
|
-
/**
|
|
802
|
-
* Builds TypeScript generic parameters for a client method.
|
|
803
|
-
* Includes response type, error type, and optional request type.
|
|
804
|
-
*/
|
|
805
|
-
function buildGenerics(node, tsResolver) {
|
|
806
|
-
const successNames = resolveSuccessNames(node, tsResolver);
|
|
807
|
-
const responseName = successNames.length > 0 ? successNames.join(" | ") : tsResolver.resolveResponseName(node);
|
|
808
|
-
const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : null;
|
|
809
|
-
const errorNames = node.responses.filter((r) => Number.parseInt(r.statusCode, 10) >= 400).map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode));
|
|
810
|
-
return [
|
|
811
|
-
responseName,
|
|
812
|
-
`ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`,
|
|
813
|
-
requestName || "unknown"
|
|
814
|
-
].filter(Boolean);
|
|
815
|
-
}
|
|
816
|
-
/**
|
|
817
|
-
* Builds the parameters object for a class-based client method.
|
|
818
|
-
* Includes URL, method, base URL, headers, and request/response data.
|
|
819
|
-
*/
|
|
820
|
-
function buildClassClientParams({ node, path, baseURL, tsResolver, isFormData, isMultipleContentTypes, hasFormData, headers }) {
|
|
821
|
-
const { query: queryParams } = getOperationParameters(node);
|
|
822
|
-
const queryParamsName = queryParams.length > 0 ? tsResolver.resolveQueryParamsName(node, queryParams[0]) : null;
|
|
823
|
-
const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : null;
|
|
824
|
-
const responseType = getResponseType(node);
|
|
825
|
-
return createFunctionParams({ config: {
|
|
826
|
-
mode: "object",
|
|
827
|
-
children: {
|
|
828
|
-
requestConfig: { mode: "inlineSpread" },
|
|
829
|
-
method: { value: JSON.stringify(_kubb_core.ast.isHttpOperationNode(node) ? node.method.toUpperCase() : "") },
|
|
830
|
-
url: { value: path.template },
|
|
831
|
-
baseURL: baseURL ? { value: JSON.stringify(baseURL) } : null,
|
|
832
|
-
params: queryParamsName ? {} : null,
|
|
833
|
-
data: requestName ? { value: isMultipleContentTypes && hasFormData ? "contentType === 'multipart/form-data' ? formData as FormData : requestData" : isFormData ? "formData as FormData" : "requestData" } : null,
|
|
834
|
-
contentType: isMultipleContentTypes ? {} : null,
|
|
835
|
-
responseType: responseType ? { value: JSON.stringify(responseType) } : null,
|
|
836
|
-
headers: headers.length ? { value: `{ ${headers.join(", ")}, ...requestConfig.headers }` } : null
|
|
837
|
-
}
|
|
838
|
-
} });
|
|
839
|
-
}
|
|
840
|
-
/**
|
|
841
|
-
* Builds the request data parsing line for client methods.
|
|
842
|
-
* Applies Zod validation if configured, otherwise uses data directly.
|
|
843
|
-
*/
|
|
844
|
-
function buildRequestDataLine({ parser, node, zodResolver }) {
|
|
845
|
-
const zodRequestName = zodResolver && parser === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null;
|
|
846
|
-
if (parser === "zod" && zodRequestName) return `const requestData = ${zodRequestName}.parse(data)`;
|
|
847
|
-
if (node.requestBody?.content?.[0]?.schema) return "const requestData = data";
|
|
848
|
-
return "";
|
|
849
|
-
}
|
|
850
|
-
/**
|
|
851
|
-
* Builds the form data conversion line for file upload requests.
|
|
852
|
-
* Returns empty string if not applicable.
|
|
853
|
-
*/
|
|
854
|
-
function buildFormDataLine(isFormData, hasRequest) {
|
|
855
|
-
return isFormData && hasRequest ? "const formData = buildFormData(requestData)" : "";
|
|
856
|
-
}
|
|
857
|
-
/**
|
|
858
|
-
* Builds the return statement for a client method.
|
|
859
|
-
* Applies Zod validation to response data if configured, otherwise returns raw response.
|
|
860
|
-
*/
|
|
861
|
-
function buildReturnStatement({ dataReturnType, parser, node, zodResolver }) {
|
|
862
|
-
const zodResponseName = zodResolver && parser === "zod" ? zodResolver.resolveResponseName?.(node) : null;
|
|
863
|
-
if (dataReturnType === "full" && parser === "zod" && zodResponseName) return `return {...res, data: ${zodResponseName}.parse(res.data)}`;
|
|
864
|
-
if (dataReturnType === "data" && parser === "zod" && zodResponseName) return `return ${zodResponseName}.parse(res.data)`;
|
|
865
|
-
if (dataReturnType === "full" && parser !== "zod") return "return res";
|
|
866
|
-
return "return res.data";
|
|
867
|
-
}
|
|
868
|
-
//#endregion
|
|
869
867
|
//#region src/components/ClassClient.tsx
|
|
870
868
|
const declarationPrinter$1 = (0, _kubb_plugin_ts.functionPrinter)({ mode: "declaration" });
|
|
871
869
|
function generateMethod$1({ node, name, tsResolver, zodResolver, baseURL, dataReturnType, parser, paramsType, paramsCasing, pathParamsType }) {
|
|
872
870
|
if (!_kubb_core.ast.isHttpOperationNode(node)) return "";
|
|
873
|
-
const path = new URLPath(node.path, { casing: paramsCasing });
|
|
874
871
|
const { defaultContentType: contentType, isMultipleContentTypes, hasFormData } = getContentTypeInfo(node);
|
|
875
872
|
const isFormData = !isMultipleContentTypes && contentType === "multipart/form-data";
|
|
876
873
|
const { header: headerParams } = getOperationParameters(node);
|
|
877
874
|
const headerParamsName = headerParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, headerParams[0]) : null;
|
|
878
875
|
const headers = isMultipleContentTypes ? headerParamsName ? ["...headers"] : [] : buildHeaders(contentType, !!headerParamsName);
|
|
879
|
-
const generics = buildGenerics(node, tsResolver
|
|
876
|
+
const generics = buildGenerics(node, tsResolver, {
|
|
877
|
+
dataReturnType,
|
|
878
|
+
zodResolver,
|
|
879
|
+
parser
|
|
880
|
+
});
|
|
880
881
|
const paramsNode = buildClientParamsNode({
|
|
881
882
|
paramsType,
|
|
882
883
|
paramsCasing,
|
|
@@ -886,17 +887,20 @@ function generateMethod$1({ node, name, tsResolver, zodResolver, baseURL, dataRe
|
|
|
886
887
|
isConfigurable: true
|
|
887
888
|
});
|
|
888
889
|
const paramsSignature = declarationPrinter$1.print(paramsNode) ?? "";
|
|
890
|
+
const { query: queryParams } = getOperationParameters(node);
|
|
891
|
+
const zodQueryParamsName = zodResolver && resolveQueryParamsParser(parser) === "zod" && queryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, queryParams[0]) : null;
|
|
889
892
|
const clientParams = buildClassClientParams({
|
|
890
893
|
node,
|
|
891
|
-
path,
|
|
894
|
+
url: Url$1.toTemplateString(node.path, { casing: paramsCasing }),
|
|
892
895
|
baseURL,
|
|
893
896
|
tsResolver,
|
|
894
897
|
isFormData,
|
|
895
898
|
isMultipleContentTypes,
|
|
896
899
|
hasFormData,
|
|
897
|
-
headers
|
|
900
|
+
headers,
|
|
901
|
+
zodQueryParamsName
|
|
898
902
|
});
|
|
899
|
-
const jsdoc = buildJSDoc(buildOperationComments(node, {
|
|
903
|
+
const jsdoc = (0, _kubb_ast_utils.buildJSDoc)(buildOperationComments(node, {
|
|
900
904
|
link: "urlPath",
|
|
901
905
|
linkPosition: "beforeDeprecated",
|
|
902
906
|
splitLines: true
|
|
@@ -906,17 +910,24 @@ function generateMethod$1({ node, name, tsResolver, zodResolver, baseURL, dataRe
|
|
|
906
910
|
node,
|
|
907
911
|
zodResolver
|
|
908
912
|
});
|
|
913
|
+
const queryParamsLine = buildQueryParamsLine({
|
|
914
|
+
parser,
|
|
915
|
+
node,
|
|
916
|
+
zodResolver
|
|
917
|
+
});
|
|
909
918
|
const formDataLine = buildFormDataLine(isFormData || isMultipleContentTypes && hasFormData, !!node.requestBody?.content?.[0]?.schema);
|
|
910
919
|
const returnStatement = buildReturnStatement({
|
|
911
920
|
dataReturnType,
|
|
912
921
|
parser,
|
|
913
922
|
node,
|
|
914
|
-
zodResolver
|
|
923
|
+
zodResolver,
|
|
924
|
+
tsResolver
|
|
915
925
|
});
|
|
916
926
|
return `${jsdoc}async ${name}(${paramsSignature}) {\n${[
|
|
917
|
-
`const { client: request = client, ${isMultipleContentTypes ? `contentType = ${
|
|
927
|
+
`const { client: request = client, ${isMultipleContentTypes ? `contentType = ${(0, _kubb_ast_utils.stringify)(contentType)}, ` : ""}...requestConfig } = mergeConfig(this.#config, config)`,
|
|
918
928
|
"",
|
|
919
929
|
requestDataLine,
|
|
930
|
+
queryParamsLine,
|
|
920
931
|
formDataLine,
|
|
921
932
|
`const res = await request<${generics.join(", ")}>(${clientParams.toCall()})`,
|
|
922
933
|
returnStatement
|
|
@@ -974,8 +985,13 @@ function resolveTypeImportNames$1(node, tsResolver) {
|
|
|
974
985
|
return resolveOperationTypeNames(node, tsResolver, { order: "body-response-first" });
|
|
975
986
|
}
|
|
976
987
|
require_chunk.__name(resolveTypeImportNames$1, "resolveTypeImportNames");
|
|
977
|
-
function resolveZodImportNames$1(node, zodResolver) {
|
|
978
|
-
|
|
988
|
+
function resolveZodImportNames$1(node, zodResolver, parser) {
|
|
989
|
+
const { query: queryParams } = getOperationParameters(node);
|
|
990
|
+
return [
|
|
991
|
+
resolveResponseParser(parser) === "zod" ? zodResolver.resolveResponseName?.(node) : null,
|
|
992
|
+
resolveRequestParser(parser) === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null,
|
|
993
|
+
resolveQueryParamsParser(parser) === "zod" && queryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, queryParams[0]) : null
|
|
994
|
+
].filter((n) => Boolean(n));
|
|
979
995
|
}
|
|
980
996
|
require_chunk.__name(resolveZodImportNames$1, "resolveZodImportNames");
|
|
981
997
|
/**
|
|
@@ -985,7 +1001,7 @@ require_chunk.__name(resolveZodImportNames$1, "resolveZodImportNames");
|
|
|
985
1001
|
*/
|
|
986
1002
|
const classClientGenerator = (0, _kubb_core.defineGenerator)({
|
|
987
1003
|
name: "classClient",
|
|
988
|
-
renderer: _kubb_renderer_jsx.
|
|
1004
|
+
renderer: _kubb_renderer_jsx.jsxRenderer,
|
|
989
1005
|
operations(nodes, ctx) {
|
|
990
1006
|
const { config, driver, resolver, root } = ctx;
|
|
991
1007
|
const { output, group, dataReturnType, paramsCasing, paramsType, pathParamsType, parser, importPath, sdk } = ctx.options;
|
|
@@ -994,7 +1010,7 @@ const classClientGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
994
1010
|
if (!pluginTs) return null;
|
|
995
1011
|
const tsResolver = driver.getResolver(_kubb_plugin_ts.pluginTsName);
|
|
996
1012
|
const tsPluginOptions = pluginTs.options;
|
|
997
|
-
const pluginZod = parser
|
|
1013
|
+
const pluginZod = isParserEnabled(parser) ? driver.getPlugin(_kubb_plugin_zod.pluginZodName) : null;
|
|
998
1014
|
const zodResolver = pluginZod ? driver.getResolver(_kubb_plugin_zod.pluginZodName) : null;
|
|
999
1015
|
function buildOperationData(node) {
|
|
1000
1016
|
const typeFile = tsResolver.resolveFile(operationFileEntry(node, node.operationId), {
|
|
@@ -1019,7 +1035,7 @@ const classClientGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1019
1035
|
const controllers = nodes.reduce((acc, operationNode) => {
|
|
1020
1036
|
if (!_kubb_core.ast.isHttpOperationNode(operationNode)) return acc;
|
|
1021
1037
|
const tag = operationNode.tags[0];
|
|
1022
|
-
const groupName = tag ? group?.name?.({ group: camelCase(tag) }) ?? resolver.resolveGroupName(tag) : resolver.
|
|
1038
|
+
const groupName = tag ? group?.name?.({ group: camelCase(tag) }) ?? resolver.resolveGroupName(tag) : resolver.resolveClassName("Client");
|
|
1023
1039
|
if (!tag && !group) {
|
|
1024
1040
|
const name = resolver.resolveClassName("ApiClient");
|
|
1025
1041
|
const file = resolver.resolveFile({
|
|
@@ -1086,7 +1102,7 @@ const classClientGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1086
1102
|
const zodFilesByPath = /* @__PURE__ */ new Map();
|
|
1087
1103
|
ops.forEach((op) => {
|
|
1088
1104
|
if (!op.zodFile || !zodResolver) return;
|
|
1089
|
-
const names = resolveZodImportNames$1(op.node, zodResolver);
|
|
1105
|
+
const names = resolveZodImportNames$1(op.node, zodResolver, parser);
|
|
1090
1106
|
if (!zodImportsByFile.has(op.zodFile.path)) zodImportsByFile.set(op.zodFile.path, /* @__PURE__ */ new Set());
|
|
1091
1107
|
const imports = zodImportsByFile.get(op.zodFile.path);
|
|
1092
1108
|
names.forEach((n) => {
|
|
@@ -1101,7 +1117,7 @@ const classClientGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1101
1117
|
}
|
|
1102
1118
|
const files = controllers.map(({ name, file, operations: ops }) => {
|
|
1103
1119
|
const { typeImportsByFile, typeFilesByPath } = collectTypeImports(ops);
|
|
1104
|
-
const { zodImportsByFile, zodFilesByPath } = parser
|
|
1120
|
+
const { zodImportsByFile, zodFilesByPath } = isParserEnabled(parser) ? collectZodImports(ops) : {
|
|
1105
1121
|
zodImportsByFile: /* @__PURE__ */ new Map(),
|
|
1106
1122
|
zodFilesByPath: /* @__PURE__ */ new Map()
|
|
1107
1123
|
};
|
|
@@ -1172,6 +1188,11 @@ const classClientGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1172
1188
|
root: file.path,
|
|
1173
1189
|
path: node_path.default.resolve(root, ".kubb/config.ts")
|
|
1174
1190
|
}),
|
|
1191
|
+
parser === "zod" && zodResolver && ops.some((op) => op.node.requestBody?.content?.[0]?.schema != null) && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
|
|
1192
|
+
name: ["z"],
|
|
1193
|
+
path: "zod",
|
|
1194
|
+
isTypeOnly: true
|
|
1195
|
+
}),
|
|
1175
1196
|
Array.from(typeImportsByFile.entries()).map(([filePath, importSet]) => {
|
|
1176
1197
|
const typeFile = typeFilesByPath.get(filePath);
|
|
1177
1198
|
if (!typeFile) return null;
|
|
@@ -1184,7 +1205,7 @@ const classClientGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1184
1205
|
isTypeOnly: true
|
|
1185
1206
|
}, filePath);
|
|
1186
1207
|
}),
|
|
1187
|
-
parser
|
|
1208
|
+
isParserEnabled(parser) && Array.from(zodImportsByFile.entries()).map(([filePath, importSet]) => {
|
|
1188
1209
|
const zodFile = zodFilesByPath.get(filePath);
|
|
1189
1210
|
if (!zodFile) return null;
|
|
1190
1211
|
const importNames = Array.from(importSet).filter(Boolean);
|
|
@@ -1275,7 +1296,7 @@ const classClientGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1275
1296
|
*/
|
|
1276
1297
|
const clientGenerator = (0, _kubb_core.defineGenerator)({
|
|
1277
1298
|
name: "client",
|
|
1278
|
-
renderer: _kubb_renderer_jsx.
|
|
1299
|
+
renderer: _kubb_renderer_jsx.jsxRenderer,
|
|
1279
1300
|
operation(node, ctx) {
|
|
1280
1301
|
if (!_kubb_core.ast.isHttpOperationNode(node)) return null;
|
|
1281
1302
|
const { config, driver, resolver, root } = ctx;
|
|
@@ -1284,10 +1305,16 @@ const clientGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1284
1305
|
const pluginTs = driver.getPlugin(_kubb_plugin_ts.pluginTsName);
|
|
1285
1306
|
if (!pluginTs) return null;
|
|
1286
1307
|
const tsResolver = driver.getResolver(_kubb_plugin_ts.pluginTsName);
|
|
1287
|
-
const pluginZod = parser
|
|
1308
|
+
const pluginZod = isParserEnabled(parser) ? driver.getPlugin(_kubb_plugin_zod.pluginZodName) : null;
|
|
1288
1309
|
const zodResolver = pluginZod ? driver.getResolver(_kubb_plugin_zod.pluginZodName) : null;
|
|
1289
1310
|
const importedTypeNames = resolveOperationTypeNames(node, tsResolver, { paramsCasing });
|
|
1290
|
-
const
|
|
1311
|
+
const { query: queryParams } = getOperationParameters(node);
|
|
1312
|
+
const importedZodNames = zodResolver ? [
|
|
1313
|
+
resolveResponseParser(parser) === "zod" ? zodResolver.resolveResponseName?.(node) : null,
|
|
1314
|
+
resolveRequestParser(parser) === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null,
|
|
1315
|
+
resolveQueryParamsParser(parser) === "zod" && queryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, queryParams[0]) : null
|
|
1316
|
+
].filter((name) => Boolean(name)) : [];
|
|
1317
|
+
const zodRequestName = zodResolver && parser === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) ?? null : null;
|
|
1291
1318
|
const meta = {
|
|
1292
1319
|
name: resolver.resolveName(node.operationId),
|
|
1293
1320
|
urlName: resolver.resolveUrlName(node),
|
|
@@ -1359,6 +1386,11 @@ const clientGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1359
1386
|
root: meta.file.path,
|
|
1360
1387
|
path: node_path.default.resolve(root, ".kubb/config.ts")
|
|
1361
1388
|
}),
|
|
1389
|
+
zodRequestName && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
|
|
1390
|
+
name: ["z"],
|
|
1391
|
+
path: "zod",
|
|
1392
|
+
isTypeOnly: true
|
|
1393
|
+
}),
|
|
1362
1394
|
meta.fileZod && importedZodNames.length > 0 && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
|
|
1363
1395
|
name: importedZodNames,
|
|
1364
1396
|
root: meta.file.path,
|
|
@@ -1408,7 +1440,7 @@ const clientGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1408
1440
|
*/
|
|
1409
1441
|
const groupedClientGenerator = (0, _kubb_core.defineGenerator)({
|
|
1410
1442
|
name: "groupedClient",
|
|
1411
|
-
renderer: _kubb_renderer_jsx.
|
|
1443
|
+
renderer: _kubb_renderer_jsx.jsxRenderer,
|
|
1412
1444
|
operations(nodes, ctx) {
|
|
1413
1445
|
const { config, resolver, root } = ctx;
|
|
1414
1446
|
const { output, group } = ctx.options;
|
|
@@ -1491,29 +1523,7 @@ const groupedClientGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1491
1523
|
}
|
|
1492
1524
|
});
|
|
1493
1525
|
//#endregion
|
|
1494
|
-
//#region src/
|
|
1495
|
-
function Operations({ name, nodes }) {
|
|
1496
|
-
const operationsObject = {};
|
|
1497
|
-
nodes.forEach((node) => {
|
|
1498
|
-
if (!_kubb_core.ast.isHttpOperationNode(node)) return;
|
|
1499
|
-
operationsObject[node.operationId] = {
|
|
1500
|
-
path: new URLPath(node.path).URL,
|
|
1501
|
-
method: node.method.toLowerCase()
|
|
1502
|
-
};
|
|
1503
|
-
});
|
|
1504
|
-
return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
|
|
1505
|
-
name,
|
|
1506
|
-
isExportable: true,
|
|
1507
|
-
isIndexable: true,
|
|
1508
|
-
children: /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.Const, {
|
|
1509
|
-
name,
|
|
1510
|
-
export: true,
|
|
1511
|
-
children: JSON.stringify(operationsObject, void 0, 2)
|
|
1512
|
-
})
|
|
1513
|
-
});
|
|
1514
|
-
}
|
|
1515
|
-
//#endregion
|
|
1516
|
-
//#region src/generators/operationsGenerator.tsx
|
|
1526
|
+
//#region src/generators/operationsGenerator.ts
|
|
1517
1527
|
/**
|
|
1518
1528
|
* Generates an `operations.ts` file that re-exports every operation grouped
|
|
1519
1529
|
* by HTTP method. Enabled when `pluginClient({ operations: true })`. Useful
|
|
@@ -1522,7 +1532,6 @@ function Operations({ name, nodes }) {
|
|
|
1522
1532
|
*/
|
|
1523
1533
|
const operationsGenerator = (0, _kubb_core.defineGenerator)({
|
|
1524
1534
|
name: "client",
|
|
1525
|
-
renderer: _kubb_renderer_jsx.jsxRendererSync,
|
|
1526
1535
|
operations(nodes, ctx) {
|
|
1527
1536
|
const { config, resolver, root } = ctx;
|
|
1528
1537
|
const { output, group } = ctx.options;
|
|
@@ -1535,7 +1544,15 @@ const operationsGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1535
1544
|
output,
|
|
1536
1545
|
group: group ?? void 0
|
|
1537
1546
|
});
|
|
1538
|
-
|
|
1547
|
+
const operationsObject = {};
|
|
1548
|
+
for (const node of nodes) {
|
|
1549
|
+
if (!_kubb_core.ast.isHttpOperationNode(node)) continue;
|
|
1550
|
+
operationsObject[node.operationId] = {
|
|
1551
|
+
path: Url$1.toPath(node.path),
|
|
1552
|
+
method: node.method.toLowerCase()
|
|
1553
|
+
};
|
|
1554
|
+
}
|
|
1555
|
+
return [_kubb_core.ast.createFile({
|
|
1539
1556
|
baseName: file.baseName,
|
|
1540
1557
|
path: file.path,
|
|
1541
1558
|
meta: file.meta,
|
|
@@ -1555,11 +1572,17 @@ const operationsGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1555
1572
|
baseName: file.baseName
|
|
1556
1573
|
}
|
|
1557
1574
|
}),
|
|
1558
|
-
|
|
1575
|
+
sources: [_kubb_core.ast.createSource({
|
|
1559
1576
|
name,
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1577
|
+
isExportable: true,
|
|
1578
|
+
isIndexable: true,
|
|
1579
|
+
nodes: [_kubb_core.ast.createConst({
|
|
1580
|
+
name,
|
|
1581
|
+
export: true,
|
|
1582
|
+
nodes: [_kubb_core.ast.createText(JSON.stringify(operationsObject, void 0, 2))]
|
|
1583
|
+
})]
|
|
1584
|
+
})]
|
|
1585
|
+
})];
|
|
1563
1586
|
}
|
|
1564
1587
|
});
|
|
1565
1588
|
//#endregion
|
|
@@ -1567,13 +1590,16 @@ const operationsGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1567
1590
|
const declarationPrinter = (0, _kubb_plugin_ts.functionPrinter)({ mode: "declaration" });
|
|
1568
1591
|
function generateMethod({ node, name, tsResolver, zodResolver, baseURL, dataReturnType, parser, paramsType, paramsCasing, pathParamsType }) {
|
|
1569
1592
|
if (!_kubb_core.ast.isHttpOperationNode(node)) return "";
|
|
1570
|
-
const path = new URLPath(node.path, { casing: paramsCasing });
|
|
1571
1593
|
const { defaultContentType: contentType, isMultipleContentTypes, hasFormData } = getContentTypeInfo(node);
|
|
1572
1594
|
const isFormData = !isMultipleContentTypes && contentType === "multipart/form-data";
|
|
1573
1595
|
const { header: headerParams } = getOperationParameters(node);
|
|
1574
1596
|
const headerParamsName = headerParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, headerParams[0]) : null;
|
|
1575
1597
|
const headers = isMultipleContentTypes ? headerParamsName ? ["...headers"] : [] : buildHeaders(contentType, !!headerParamsName);
|
|
1576
|
-
const generics = buildGenerics(node, tsResolver
|
|
1598
|
+
const generics = buildGenerics(node, tsResolver, {
|
|
1599
|
+
dataReturnType,
|
|
1600
|
+
zodResolver,
|
|
1601
|
+
parser
|
|
1602
|
+
});
|
|
1577
1603
|
const paramsNode = buildClientParamsNode({
|
|
1578
1604
|
paramsType,
|
|
1579
1605
|
paramsCasing,
|
|
@@ -1583,17 +1609,20 @@ function generateMethod({ node, name, tsResolver, zodResolver, baseURL, dataRetu
|
|
|
1583
1609
|
isConfigurable: true
|
|
1584
1610
|
});
|
|
1585
1611
|
const paramsSignature = declarationPrinter.print(paramsNode) ?? "";
|
|
1612
|
+
const { query: queryParams } = getOperationParameters(node);
|
|
1613
|
+
const zodQueryParamsName = zodResolver && resolveQueryParamsParser(parser) === "zod" && queryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, queryParams[0]) : null;
|
|
1586
1614
|
const clientParams = buildClassClientParams({
|
|
1587
1615
|
node,
|
|
1588
|
-
path,
|
|
1616
|
+
url: Url$1.toTemplateString(node.path, { casing: paramsCasing }),
|
|
1589
1617
|
baseURL,
|
|
1590
1618
|
tsResolver,
|
|
1591
1619
|
isFormData,
|
|
1592
1620
|
isMultipleContentTypes,
|
|
1593
1621
|
hasFormData,
|
|
1594
|
-
headers
|
|
1622
|
+
headers,
|
|
1623
|
+
zodQueryParamsName
|
|
1595
1624
|
});
|
|
1596
|
-
const jsdoc = buildJSDoc(buildOperationComments(node, {
|
|
1625
|
+
const jsdoc = (0, _kubb_ast_utils.buildJSDoc)(buildOperationComments(node, {
|
|
1597
1626
|
link: "urlPath",
|
|
1598
1627
|
linkPosition: "beforeDeprecated",
|
|
1599
1628
|
splitLines: true
|
|
@@ -1603,17 +1632,24 @@ function generateMethod({ node, name, tsResolver, zodResolver, baseURL, dataRetu
|
|
|
1603
1632
|
node,
|
|
1604
1633
|
zodResolver
|
|
1605
1634
|
});
|
|
1635
|
+
const queryParamsLine = buildQueryParamsLine({
|
|
1636
|
+
parser,
|
|
1637
|
+
node,
|
|
1638
|
+
zodResolver
|
|
1639
|
+
});
|
|
1606
1640
|
const formDataLine = buildFormDataLine(isFormData || isMultipleContentTypes && hasFormData, !!node.requestBody?.content?.[0]?.schema);
|
|
1607
1641
|
const returnStatement = buildReturnStatement({
|
|
1608
1642
|
dataReturnType,
|
|
1609
1643
|
parser,
|
|
1610
1644
|
node,
|
|
1611
|
-
zodResolver
|
|
1645
|
+
zodResolver,
|
|
1646
|
+
tsResolver
|
|
1612
1647
|
});
|
|
1613
1648
|
return `${jsdoc} static async ${name}(${paramsSignature}) {\n${[
|
|
1614
|
-
`const { client: request = client, ${isMultipleContentTypes ? `contentType = ${
|
|
1649
|
+
`const { client: request = client, ${isMultipleContentTypes ? `contentType = ${(0, _kubb_ast_utils.stringify)(contentType)}, ` : ""}...requestConfig } = mergeConfig(this.#config, config)`,
|
|
1615
1650
|
"",
|
|
1616
1651
|
requestDataLine,
|
|
1652
|
+
queryParamsLine,
|
|
1617
1653
|
formDataLine,
|
|
1618
1654
|
`const res = await request<${generics.join(", ")}>(${clientParams.toCall()})`,
|
|
1619
1655
|
returnStatement
|
|
@@ -1644,8 +1680,13 @@ function StaticClassClient({ name, isExportable = true, isIndexable = true, oper
|
|
|
1644
1680
|
function resolveTypeImportNames(node, tsResolver) {
|
|
1645
1681
|
return resolveOperationTypeNames(node, tsResolver, { order: "body-response-first" });
|
|
1646
1682
|
}
|
|
1647
|
-
function resolveZodImportNames(node, zodResolver) {
|
|
1648
|
-
|
|
1683
|
+
function resolveZodImportNames(node, zodResolver, parser) {
|
|
1684
|
+
const { query: queryParams } = getOperationParameters(node);
|
|
1685
|
+
return [
|
|
1686
|
+
resolveResponseParser(parser) === "zod" ? zodResolver.resolveResponseName?.(node) : null,
|
|
1687
|
+
resolveRequestParser(parser) === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null,
|
|
1688
|
+
resolveQueryParamsParser(parser) === "zod" && queryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, queryParams[0]) : null
|
|
1689
|
+
].filter((n) => Boolean(n));
|
|
1649
1690
|
}
|
|
1650
1691
|
/**
|
|
1651
1692
|
* Built-in `operations` generator for `@kubb/plugin-client` when
|
|
@@ -1655,7 +1696,7 @@ function resolveZodImportNames(node, zodResolver) {
|
|
|
1655
1696
|
*/
|
|
1656
1697
|
const staticClassClientGenerator = (0, _kubb_core.defineGenerator)({
|
|
1657
1698
|
name: "staticClassClient",
|
|
1658
|
-
renderer: _kubb_renderer_jsx.
|
|
1699
|
+
renderer: _kubb_renderer_jsx.jsxRenderer,
|
|
1659
1700
|
operations(nodes, ctx) {
|
|
1660
1701
|
const { config, driver, resolver, root } = ctx;
|
|
1661
1702
|
const { output, group, dataReturnType, paramsCasing, paramsType, pathParamsType, parser, importPath } = ctx.options;
|
|
@@ -1664,7 +1705,7 @@ const staticClassClientGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1664
1705
|
if (!pluginTs) return null;
|
|
1665
1706
|
const tsResolver = driver.getResolver(_kubb_plugin_ts.pluginTsName);
|
|
1666
1707
|
const tsPluginOptions = pluginTs.options;
|
|
1667
|
-
const pluginZod = parser
|
|
1708
|
+
const pluginZod = isParserEnabled(parser) ? driver.getPlugin(_kubb_plugin_zod.pluginZodName) : null;
|
|
1668
1709
|
const zodResolver = pluginZod ? driver.getResolver(_kubb_plugin_zod.pluginZodName) : null;
|
|
1669
1710
|
function buildOperationData(node) {
|
|
1670
1711
|
const typeFile = tsResolver.resolveFile(operationFileEntry(node, node.operationId), {
|
|
@@ -1689,7 +1730,7 @@ const staticClassClientGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1689
1730
|
const controllers = nodes.reduce((acc, operationNode) => {
|
|
1690
1731
|
if (!_kubb_core.ast.isHttpOperationNode(operationNode)) return acc;
|
|
1691
1732
|
const tag = operationNode.tags[0];
|
|
1692
|
-
const groupName = tag ? group?.name?.({ group: camelCase(tag) }) ?? resolver.resolveGroupName(tag) : resolver.
|
|
1733
|
+
const groupName = tag ? group?.name?.({ group: camelCase(tag) }) ?? resolver.resolveGroupName(tag) : resolver.resolveClassName("Client");
|
|
1693
1734
|
if (!tag && !group) {
|
|
1694
1735
|
const name = resolver.resolveClassName("ApiClient");
|
|
1695
1736
|
const file = resolver.resolveFile({
|
|
@@ -1754,7 +1795,7 @@ const staticClassClientGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1754
1795
|
const zodFilesByPath = /* @__PURE__ */ new Map();
|
|
1755
1796
|
ops.forEach((op) => {
|
|
1756
1797
|
if (!op.zodFile || !zodResolver) return;
|
|
1757
|
-
const names = resolveZodImportNames(op.node, zodResolver);
|
|
1798
|
+
const names = resolveZodImportNames(op.node, zodResolver, parser);
|
|
1758
1799
|
if (!zodImportsByFile.has(op.zodFile.path)) zodImportsByFile.set(op.zodFile.path, /* @__PURE__ */ new Set());
|
|
1759
1800
|
const imports = zodImportsByFile.get(op.zodFile.path);
|
|
1760
1801
|
names.forEach((n) => {
|
|
@@ -1769,7 +1810,7 @@ const staticClassClientGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1769
1810
|
}
|
|
1770
1811
|
return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: controllers.map(({ name, file, operations: ops }) => {
|
|
1771
1812
|
const { typeImportsByFile, typeFilesByPath } = collectTypeImports(ops);
|
|
1772
|
-
const { zodImportsByFile, zodFilesByPath } = parser
|
|
1813
|
+
const { zodImportsByFile, zodFilesByPath } = isParserEnabled(parser) ? collectZodImports(ops) : {
|
|
1773
1814
|
zodImportsByFile: /* @__PURE__ */ new Map(),
|
|
1774
1815
|
zodFilesByPath: /* @__PURE__ */ new Map()
|
|
1775
1816
|
};
|
|
@@ -1840,6 +1881,11 @@ const staticClassClientGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1840
1881
|
root: file.path,
|
|
1841
1882
|
path: node_path.default.resolve(root, ".kubb/config.ts")
|
|
1842
1883
|
}),
|
|
1884
|
+
parser === "zod" && zodResolver && ops.some((op) => op.node.requestBody?.content?.[0]?.schema != null) && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
|
|
1885
|
+
name: ["z"],
|
|
1886
|
+
path: "zod",
|
|
1887
|
+
isTypeOnly: true
|
|
1888
|
+
}),
|
|
1843
1889
|
Array.from(typeImportsByFile.entries()).map(([filePath, importSet]) => {
|
|
1844
1890
|
const typeFile = typeFilesByPath.get(filePath);
|
|
1845
1891
|
if (!typeFile) return null;
|
|
@@ -1852,7 +1898,7 @@ const staticClassClientGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1852
1898
|
isTypeOnly: true
|
|
1853
1899
|
}, filePath);
|
|
1854
1900
|
}),
|
|
1855
|
-
parser
|
|
1901
|
+
isParserEnabled(parser) && Array.from(zodImportsByFile.entries()).map(([filePath, importSet]) => {
|
|
1856
1902
|
const zodFile = zodFilesByPath.get(filePath);
|
|
1857
1903
|
if (!zodFile) return null;
|
|
1858
1904
|
const importNames = Array.from(importSet).filter(Boolean);
|
|
@@ -1891,6 +1937,7 @@ const staticClassClientGenerator = (0, _kubb_core.defineGenerator)({
|
|
|
1891
1937
|
*
|
|
1892
1938
|
* resolverClient.default('list pets', 'function') // 'listPets'
|
|
1893
1939
|
* resolverClient.resolveClassName('pet') // 'Pet'
|
|
1940
|
+
* resolverClient.resolveGroupName('pet') // 'PetClient'
|
|
1894
1941
|
* resolverClient.resolveUrlName(operationNode) // 'getShowPetByIdUrl'
|
|
1895
1942
|
* ```
|
|
1896
1943
|
*/
|
|
@@ -1898,8 +1945,8 @@ const resolverClient = (0, _kubb_core.defineResolver)(() => ({
|
|
|
1898
1945
|
name: "default",
|
|
1899
1946
|
pluginName: "plugin-client",
|
|
1900
1947
|
default(name, type) {
|
|
1901
|
-
|
|
1902
|
-
return
|
|
1948
|
+
if (type === "file") return toFilePath(name);
|
|
1949
|
+
return ensureValidVarName(camelCase(name));
|
|
1903
1950
|
},
|
|
1904
1951
|
resolveName(name) {
|
|
1905
1952
|
return this.default(name, "function");
|
|
@@ -1911,7 +1958,7 @@ const resolverClient = (0, _kubb_core.defineResolver)(() => ({
|
|
|
1911
1958
|
return ensureValidVarName(pascalCase(name));
|
|
1912
1959
|
},
|
|
1913
1960
|
resolveGroupName(name) {
|
|
1914
|
-
return ensureValidVarName(pascalCase(name));
|
|
1961
|
+
return ensureValidVarName(pascalCase(`${name} Client`));
|
|
1915
1962
|
},
|
|
1916
1963
|
resolveClientPropertyName(name) {
|
|
1917
1964
|
return ensureValidVarName(camelCase(name));
|
|
@@ -1956,7 +2003,7 @@ const pluginClientName = "plugin-client";
|
|
|
1956
2003
|
const pluginClient = (0, _kubb_core.definePlugin)((options) => {
|
|
1957
2004
|
const { output = {
|
|
1958
2005
|
path: "clients",
|
|
1959
|
-
|
|
2006
|
+
barrel: { type: "named" }
|
|
1960
2007
|
}, 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;
|
|
1961
2008
|
const resolvedImportPath = importPath ?? (!bundle ? `@kubb/plugin-client/clients/${client}` : void 0);
|
|
1962
2009
|
const selectedGenerators = options.generators ?? [
|
|
@@ -1964,11 +2011,11 @@ const pluginClient = (0, _kubb_core.definePlugin)((options) => {
|
|
|
1964
2011
|
group && clientType === "function" ? groupedClientGenerator : null,
|
|
1965
2012
|
operations ? operationsGenerator : null
|
|
1966
2013
|
].filter((x) => Boolean(x));
|
|
1967
|
-
const groupConfig = createGroupConfig(group
|
|
2014
|
+
const groupConfig = createGroupConfig(group);
|
|
1968
2015
|
return {
|
|
1969
2016
|
name: pluginClientName,
|
|
1970
2017
|
options,
|
|
1971
|
-
dependencies: [_kubb_plugin_ts.pluginTsName, parser
|
|
2018
|
+
dependencies: [_kubb_plugin_ts.pluginTsName, isParserEnabled(parser) ? _kubb_plugin_zod.pluginZodName : null].filter((dependency) => Boolean(dependency)),
|
|
1972
2019
|
hooks: { "kubb:plugin:setup"(ctx) {
|
|
1973
2020
|
const resolver = userResolver ? {
|
|
1974
2021
|
...resolverClient,
|
|
@@ -2031,9 +2078,13 @@ exports.classClientGenerator = classClientGenerator;
|
|
|
2031
2078
|
exports.clientGenerator = clientGenerator;
|
|
2032
2079
|
exports.default = pluginClient;
|
|
2033
2080
|
exports.groupedClientGenerator = groupedClientGenerator;
|
|
2081
|
+
exports.isParserEnabled = isParserEnabled;
|
|
2034
2082
|
exports.operationsGenerator = operationsGenerator;
|
|
2035
2083
|
exports.pluginClient = pluginClient;
|
|
2036
2084
|
exports.pluginClientName = pluginClientName;
|
|
2085
|
+
exports.resolveQueryParamsParser = resolveQueryParamsParser;
|
|
2086
|
+
exports.resolveRequestParser = resolveRequestParser;
|
|
2087
|
+
exports.resolveResponseParser = resolveResponseParser;
|
|
2037
2088
|
exports.resolverClient = resolverClient;
|
|
2038
2089
|
exports.staticClassClientGenerator = staticClassClientGenerator;
|
|
2039
2090
|
|