@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.js
CHANGED
|
@@ -4,8 +4,9 @@ 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
|
-
import { Const, File, Function,
|
|
9
|
+
import { Const, File, Function, jsxRenderer } from "@kubb/renderer-jsx";
|
|
9
10
|
import { Fragment, jsx, jsxs } from "@kubb/renderer-jsx/jsx-runtime";
|
|
10
11
|
import { pluginZodName } from "@kubb/plugin-zod";
|
|
11
12
|
//#region ../../internals/utils/src/casing.ts
|
|
@@ -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
|
|
@@ -206,99 +196,83 @@ function ensureValidVarName(name) {
|
|
|
206
196
|
return `_${name}`;
|
|
207
197
|
}
|
|
208
198
|
//#endregion
|
|
209
|
-
//#region ../../internals/utils/src/
|
|
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;
|
|
210
|
+
}
|
|
211
|
+
return Object.keys(params).length > 0 ? params : null;
|
|
212
|
+
}
|
|
210
213
|
/**
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
* @example
|
|
214
|
-
* const p = new URLPath('/pet/{petId}')
|
|
215
|
-
* p.URL // '/pet/:petId'
|
|
216
|
-
* p.template // '`/pet/${petId}`'
|
|
214
|
+
* Helpers for OpenAPI/Swagger paths, plus a thin wrapper over the native `URL`.
|
|
217
215
|
*/
|
|
218
|
-
var
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
*/
|
|
222
|
-
path;
|
|
223
|
-
#options;
|
|
224
|
-
constructor(path, options = {}) {
|
|
225
|
-
this.path = path;
|
|
226
|
-
this.#options = options;
|
|
216
|
+
var Url$1 = class Url$1 {
|
|
217
|
+
static {
|
|
218
|
+
__name(this, "Url");
|
|
227
219
|
}
|
|
228
|
-
/**
|
|
220
|
+
/**
|
|
221
|
+
* Reports whether `url` is a parseable absolute URL. Delegates to the native `URL.canParse`.
|
|
229
222
|
*
|
|
230
223
|
* @example
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
* ```
|
|
224
|
+
* Url.canParse('https://petstore.swagger.io/v2') // true
|
|
225
|
+
* Url.canParse('/pet/{petId}') // false
|
|
234
226
|
*/
|
|
235
|
-
|
|
236
|
-
return
|
|
227
|
+
static canParse(url, base) {
|
|
228
|
+
return URL.canParse(url, base);
|
|
237
229
|
}
|
|
238
|
-
/**
|
|
230
|
+
/**
|
|
231
|
+
* Converts an OpenAPI/Swagger path to Express-style colon syntax.
|
|
239
232
|
*
|
|
240
233
|
* @example
|
|
241
|
-
*
|
|
242
|
-
* new URLPath('https://petstore.swagger.io/v2/pet').isURL // true
|
|
243
|
-
* new URLPath('/pet/{petId}').isURL // false
|
|
244
|
-
* ```
|
|
234
|
+
* Url.toPath('/pet/{petId}') // '/pet/:petId'
|
|
245
235
|
*/
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
return !!new URL(this.path).href;
|
|
249
|
-
} catch {
|
|
250
|
-
return false;
|
|
251
|
-
}
|
|
236
|
+
static toPath(path) {
|
|
237
|
+
return path.replace(/\{([^}]+)\}/g, ":$1");
|
|
252
238
|
}
|
|
253
239
|
/**
|
|
254
|
-
* Converts
|
|
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.
|
|
255
243
|
*
|
|
256
244
|
* @example
|
|
257
|
-
*
|
|
258
|
-
* new URLPath('/account/monetary-accountID').template // '`/account/${monetaryAccountId}`'
|
|
259
|
-
*/
|
|
260
|
-
get template() {
|
|
261
|
-
return this.toTemplateString();
|
|
262
|
-
}
|
|
263
|
-
/** Returns the path and its extracted params as a structured `URLObject`, or as a stringified expression when `stringify` is set.
|
|
245
|
+
* Url.toTemplateString('/pet/{petId}') // '`/pet/${petId}`'
|
|
264
246
|
*
|
|
265
247
|
* @example
|
|
266
|
-
*
|
|
267
|
-
* new URLPath('/pet/{petId}').object
|
|
268
|
-
* // { url: '/pet/:petId', params: { petId: 'petId' } }
|
|
269
|
-
* ```
|
|
248
|
+
* Url.toTemplateString('/pet/{petId}', { prefix: 'https://api' }) // '`https://api/pet/${petId}`'
|
|
270
249
|
*/
|
|
271
|
-
|
|
272
|
-
|
|
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}\``;
|
|
273
257
|
}
|
|
274
|
-
/**
|
|
258
|
+
/**
|
|
259
|
+
* Returns the path and its extracted params as a structured `URLObject`, or as a stringified
|
|
260
|
+
* expression when `stringify` is set.
|
|
275
261
|
*
|
|
276
262
|
* @example
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
* new URLPath('/pet').params // null
|
|
280
|
-
* ```
|
|
281
|
-
*/
|
|
282
|
-
get params() {
|
|
283
|
-
return this.toParamsObject();
|
|
284
|
-
}
|
|
285
|
-
#transformParam(raw) {
|
|
286
|
-
const param = isValidVarName(raw) ? raw : camelCase(raw);
|
|
287
|
-
return this.#options.casing === "camelcase" ? camelCase(param) : param;
|
|
288
|
-
}
|
|
289
|
-
/**
|
|
290
|
-
* Iterates over every `{param}` token in `path`, calling `fn` with the raw token and transformed name.
|
|
263
|
+
* Url.toObject('/pet/{petId}')
|
|
264
|
+
* // { url: '/pet/:petId', params: { petId: 'petId' } }
|
|
291
265
|
*/
|
|
292
|
-
|
|
293
|
-
for (const match of this.path.matchAll(/\{([^}]+)\}/g)) {
|
|
294
|
-
const raw = match[1];
|
|
295
|
-
fn(raw, this.#transformParam(raw));
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
toObject({ type = "path", replacer, stringify } = {}) {
|
|
266
|
+
static toObject(path, { type = "path", replacer, stringify, casing } = {}) {
|
|
299
267
|
const object = {
|
|
300
|
-
url: type === "path" ?
|
|
301
|
-
|
|
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
|
+
})
|
|
302
276
|
};
|
|
303
277
|
if (stringify) {
|
|
304
278
|
if (type === "template") return JSON.stringify(object).replaceAll("'", "").replaceAll(`"`, "");
|
|
@@ -307,50 +281,6 @@ var URLPath = class {
|
|
|
307
281
|
}
|
|
308
282
|
return object;
|
|
309
283
|
}
|
|
310
|
-
/**
|
|
311
|
-
* Converts the OpenAPI path to a TypeScript template literal string.
|
|
312
|
-
* An optional `replacer` can transform each extracted parameter name before interpolation.
|
|
313
|
-
*
|
|
314
|
-
* @example
|
|
315
|
-
* new URLPath('/pet/{petId}').toTemplateString() // '`/pet/${petId}`'
|
|
316
|
-
*/
|
|
317
|
-
toTemplateString({ prefix, replacer } = {}) {
|
|
318
|
-
const result = this.path.split(/\{([^}]+)\}/).map((part, i) => {
|
|
319
|
-
if (i % 2 === 0) return part;
|
|
320
|
-
const param = this.#transformParam(part);
|
|
321
|
-
return `\${${replacer ? replacer(param) : param}}`;
|
|
322
|
-
}).join("");
|
|
323
|
-
return `\`${prefix ?? ""}${result}\``;
|
|
324
|
-
}
|
|
325
|
-
/**
|
|
326
|
-
* Extracts all `{param}` segments from the path and returns them as a key-value map.
|
|
327
|
-
* An optional `replacer` transforms each parameter name in both key and value positions.
|
|
328
|
-
* Returns `undefined` when no path parameters are found.
|
|
329
|
-
*
|
|
330
|
-
* @example
|
|
331
|
-
* ```ts
|
|
332
|
-
* new URLPath('/pet/{petId}/tag/{tagId}').toParamsObject()
|
|
333
|
-
* // { petId: 'petId', tagId: 'tagId' }
|
|
334
|
-
* ```
|
|
335
|
-
*/
|
|
336
|
-
toParamsObject(replacer) {
|
|
337
|
-
const params = {};
|
|
338
|
-
this.#eachParam((_raw, param) => {
|
|
339
|
-
const key = replacer ? replacer(param) : param;
|
|
340
|
-
params[key] = key;
|
|
341
|
-
});
|
|
342
|
-
return Object.keys(params).length > 0 ? params : null;
|
|
343
|
-
}
|
|
344
|
-
/** Converts the OpenAPI path to Express-style colon syntax.
|
|
345
|
-
*
|
|
346
|
-
* @example
|
|
347
|
-
* ```ts
|
|
348
|
-
* new URLPath('/pet/{petId}').toURLPath() // '/pet/:petId'
|
|
349
|
-
* ```
|
|
350
|
-
*/
|
|
351
|
-
toURLPath() {
|
|
352
|
-
return this.path.replace(/\{([^}]+)\}/g, ":$1");
|
|
353
|
-
}
|
|
354
284
|
};
|
|
355
285
|
//#endregion
|
|
356
286
|
//#region ../../internals/shared/src/operation.ts
|
|
@@ -376,7 +306,7 @@ function operationFileEntry(node, name, extname = ".ts") {
|
|
|
376
306
|
function getOperationLink(node, link) {
|
|
377
307
|
if (!link) return null;
|
|
378
308
|
if (typeof link === "function") return link(node) ?? null;
|
|
379
|
-
if (link === "urlPath") return node.path ? `{@link ${
|
|
309
|
+
if (link === "urlPath") return node.path ? `{@link ${Url$1.toPath(node.path)}}` : null;
|
|
380
310
|
return node.path ? `{@link ${node.path.replaceAll("{", ":").replaceAll("}", "")}}` : null;
|
|
381
311
|
}
|
|
382
312
|
function getContentTypeInfo(node) {
|
|
@@ -467,6 +397,19 @@ function resolveSuccessNames(node, resolver) {
|
|
|
467
397
|
function resolveStatusCodeNames(node, resolver) {
|
|
468
398
|
return node.responses.map((response) => resolver.resolveResponseStatusName(node, response.statusCode));
|
|
469
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
|
+
}
|
|
470
413
|
const typeNamesByResolver = /* @__PURE__ */ new WeakMap();
|
|
471
414
|
function resolveOperationTypeNames(node, resolver, options = {}) {
|
|
472
415
|
const cacheKey = `${node.operationId}\0${options.paramsCasing ?? ""}\0${options.order ?? ""}\0${options.responseStatusNames ?? ""}\0${(options.exclude ?? []).join(",")}`;
|
|
@@ -506,26 +449,24 @@ function resolveOperationTypeNames(node, resolver, options = {}) {
|
|
|
506
449
|
* shared default naming so every plugin groups output consistently:
|
|
507
450
|
*
|
|
508
451
|
* - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).
|
|
509
|
-
* - other groups use
|
|
452
|
+
* - other groups use the camelCased group (`pet store` → `petStore`).
|
|
510
453
|
*
|
|
511
454
|
* A user-provided `group.name` always wins over the default namer, so callers stay in
|
|
512
455
|
* control of their output folders. Returns `null` when grouping is disabled, matching the
|
|
513
456
|
* per-plugin convention.
|
|
514
457
|
*
|
|
515
458
|
* @param group - The user-supplied group option, or `undefined` to disable grouping.
|
|
516
|
-
* @param options.suffix - Appended to non-`path` group names, e.g. `'Controller'` or `'Requests'`.
|
|
517
459
|
*
|
|
518
460
|
* @example
|
|
519
461
|
* ```ts
|
|
520
|
-
* createGroupConfig(group
|
|
521
|
-
* createGroupConfig(group, { suffix: 'Requests' }) // plugin-cypress, plugin-mcp
|
|
462
|
+
* createGroupConfig(group) // shared across every plugin
|
|
522
463
|
* ```
|
|
523
464
|
*/
|
|
524
|
-
function createGroupConfig(group
|
|
465
|
+
function createGroupConfig(group) {
|
|
525
466
|
if (!group) return null;
|
|
526
467
|
const defaultName = (ctx) => {
|
|
527
468
|
if (group.type === "path") return `${ctx.group.split("/")[1]}`;
|
|
528
|
-
return
|
|
469
|
+
return camelCase(ctx.group);
|
|
529
470
|
};
|
|
530
471
|
return {
|
|
531
472
|
...group,
|
|
@@ -608,6 +549,143 @@ function createFunctionParams(params) {
|
|
|
608
549
|
};
|
|
609
550
|
}
|
|
610
551
|
//#endregion
|
|
552
|
+
//#region src/utils.ts
|
|
553
|
+
/**
|
|
554
|
+
* Returns `true` when any direction of the parser uses Zod (used for dependency checks).
|
|
555
|
+
*/
|
|
556
|
+
function isParserEnabled(parser) {
|
|
557
|
+
if (!parser) return false;
|
|
558
|
+
if (parser === "zod") return true;
|
|
559
|
+
return !!(parser.request || parser.response);
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Returns `'zod'` when request body parsing is enabled, `null` otherwise.
|
|
563
|
+
* The string shorthand `'zod'` also enables request body parsing (existing behavior).
|
|
564
|
+
*/
|
|
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;
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* Builds HTTP headers array for a client request.
|
|
590
|
+
* Includes Content-Type (if not default) and spreads header parameters if present.
|
|
591
|
+
*/
|
|
592
|
+
function buildHeaders(contentType, hasHeaderParams) {
|
|
593
|
+
return [contentType !== "application/json" && contentType !== "multipart/form-data" ? `'Content-Type': '${contentType}'` : null, hasHeaderParams ? "...headers" : null].filter(Boolean);
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
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).
|
|
602
|
+
*/
|
|
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;
|
|
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;
|
|
611
|
+
return [
|
|
612
|
+
responseName,
|
|
613
|
+
TError,
|
|
614
|
+
zodRequestName ? `z.output<typeof ${zodRequestName}>` : requestName || "unknown"
|
|
615
|
+
].filter(Boolean);
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* Builds the parameters object for a class-based client method.
|
|
619
|
+
* Includes URL, method, base URL, headers, and request/response data.
|
|
620
|
+
*/
|
|
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);
|
|
626
|
+
return createFunctionParams({ config: {
|
|
627
|
+
mode: "object",
|
|
628
|
+
children: {
|
|
629
|
+
requestConfig: { mode: "inlineSpread" },
|
|
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
|
|
638
|
+
}
|
|
639
|
+
} });
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
* Builds the request data parsing line for client methods.
|
|
643
|
+
* Applies Zod validation when `parser.request === 'zod'`, otherwise assigns data directly.
|
|
644
|
+
*/
|
|
645
|
+
function buildRequestDataLine({ parser, node, zodResolver }) {
|
|
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)`;
|
|
649
|
+
if (node.requestBody?.content?.[0]?.schema) return "const requestData = data";
|
|
650
|
+
return "";
|
|
651
|
+
}
|
|
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
|
+
/**
|
|
666
|
+
* Builds the form data conversion line for file upload requests.
|
|
667
|
+
* Returns empty string if not applicable.
|
|
668
|
+
*/
|
|
669
|
+
function buildFormDataLine(isFormData, hasRequest) {
|
|
670
|
+
return isFormData && hasRequest ? "const formData = buildFormData(requestData)" : "";
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* Builds the return statement for a client method.
|
|
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.
|
|
676
|
+
*/
|
|
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)`;
|
|
686
|
+
return "return res.data";
|
|
687
|
+
}
|
|
688
|
+
//#endregion
|
|
611
689
|
//#region src/components/Url.tsx
|
|
612
690
|
const declarationPrinter$3 = functionPrinter({ mode: "declaration" });
|
|
613
691
|
function buildUrlParamsNode({ paramsType, paramsCasing, pathParamsType, node, tsResolver }) {
|
|
@@ -625,7 +703,6 @@ function buildUrlParamsNode({ paramsType, paramsCasing, pathParamsType, node, ts
|
|
|
625
703
|
}
|
|
626
704
|
function Url({ name, isExportable = true, isIndexable = true, baseURL, paramsType, paramsCasing, pathParamsType, node, tsResolver }) {
|
|
627
705
|
if (!ast.isHttpOperationNode(node)) return null;
|
|
628
|
-
const path = new URLPath(node.path);
|
|
629
706
|
const paramsNode = buildUrlParamsNode({
|
|
630
707
|
paramsType,
|
|
631
708
|
paramsCasing,
|
|
@@ -650,7 +727,7 @@ function Url({ name, isExportable = true, isIndexable = true, baseURL, paramsTyp
|
|
|
650
727
|
pathParamsMapping && Object.keys(pathParamsMapping).length > 0 && /* @__PURE__ */ jsx("br", {}),
|
|
651
728
|
/* @__PURE__ */ jsx(Const, {
|
|
652
729
|
name: "res",
|
|
653
|
-
children: `{ method: '${node.method.toUpperCase()}', url: ${
|
|
730
|
+
children: `{ method: '${node.method.toUpperCase()}', url: ${Url$1.toTemplateString(node.path, { prefix: baseURL })} as const }`
|
|
654
731
|
}),
|
|
655
732
|
/* @__PURE__ */ jsx("br", {}),
|
|
656
733
|
"return res"
|
|
@@ -679,7 +756,6 @@ function buildClientParamsNode({ paramsType, paramsCasing, pathParamsType, node,
|
|
|
679
756
|
}
|
|
680
757
|
function Client({ name, isExportable = true, isIndexable = true, returnType, baseURL, dataReturnType, parser, paramsType, paramsCasing, pathParamsType, node, tsResolver, zodResolver, urlName, children, isConfigurable = true }) {
|
|
681
758
|
if (!ast.isHttpOperationNode(node)) return null;
|
|
682
|
-
const path = new URLPath(node.path);
|
|
683
759
|
const { defaultContentType: contentType, isMultipleContentTypes, hasFormData } = getContentTypeInfo(node);
|
|
684
760
|
const isFormData = !isMultipleContentTypes && contentType === "multipart/form-data";
|
|
685
761
|
const responseType = getResponseType(node);
|
|
@@ -693,16 +769,22 @@ function Client({ name, isExportable = true, isIndexable = true, returnType, bas
|
|
|
693
769
|
const responseName = successNames.length > 0 ? successNames.join(" | ") : tsResolver.resolveResponseName(node);
|
|
694
770
|
const queryParamsName = originalQueryParams.length > 0 ? tsResolver.resolveQueryParamsName(node, originalQueryParams[0]) : null;
|
|
695
771
|
const headerParamsName = originalHeaderParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, originalHeaderParams[0]) : null;
|
|
696
|
-
const
|
|
697
|
-
const
|
|
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;
|
|
698
778
|
const errorNames = node.responses.filter((r) => {
|
|
699
779
|
return Number.parseInt(r.statusCode, 10) >= 400;
|
|
700
780
|
}).map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode));
|
|
701
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));
|
|
702
784
|
const generics = [
|
|
703
|
-
responseName,
|
|
704
|
-
|
|
705
|
-
requestName || "unknown"
|
|
785
|
+
dataReturnType === "full" ? allStatusNames.length > 0 ? allStatusNames.join(" | ") : responseName : responseName,
|
|
786
|
+
TError,
|
|
787
|
+
parser === "zod" && zodRequestName ? `z.output<typeof ${zodRequestName}>` : requestName || "unknown"
|
|
706
788
|
].filter(Boolean);
|
|
707
789
|
const paramsNode = buildClientParamsNode({
|
|
708
790
|
paramsType,
|
|
@@ -724,22 +806,23 @@ function Client({ name, isExportable = true, isIndexable = true, returnType, bas
|
|
|
724
806
|
const clientParams = createFunctionParams({ config: {
|
|
725
807
|
mode: "object",
|
|
726
808
|
children: {
|
|
727
|
-
method: { value:
|
|
728
|
-
url: { value: urlName ? `${urlName}(${urlParamsCall}).url.toString()` : path
|
|
809
|
+
method: { value: stringify(node.method.toUpperCase()) },
|
|
810
|
+
url: { value: urlName ? `${urlName}(${urlParamsCall}).url.toString()` : Url$1.toTemplateString(node.path) },
|
|
729
811
|
baseURL: baseURL && !urlName ? { value: `\`${baseURL}\`` } : null,
|
|
730
|
-
params: queryParamsName ? queryParamsMapping ? { value: "mappedParams" } : {} : null,
|
|
812
|
+
params: queryParamsName ? zodQueryParamsName ? { value: "requestParams" } : queryParamsMapping ? { value: "mappedParams" } : {} : null,
|
|
731
813
|
data: requestName ? { value: isMultipleContentTypes && hasFormData ? "contentType === 'multipart/form-data' ? formData as FormData : requestData" : isFormData ? "formData as FormData" : "requestData" } : null,
|
|
732
814
|
contentType: isConfigurable && isMultipleContentTypes ? {} : null,
|
|
733
|
-
responseType: responseType ? { value:
|
|
815
|
+
responseType: responseType ? { value: stringify(responseType) } : null,
|
|
734
816
|
requestConfig: isConfigurable ? { mode: "inlineSpread" } : null,
|
|
735
817
|
headers: headers.length ? { value: isConfigurable ? `{ ${headers.join(", ")}, ...requestConfig.headers }` : `{ ${headers.join(", ")} }` } : null
|
|
736
818
|
}
|
|
737
819
|
} });
|
|
820
|
+
const statusUnionType = dataReturnType === "full" ? buildStatusUnionType(node, tsResolver) : null;
|
|
738
821
|
const childrenElement = children ? children : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
739
|
-
dataReturnType === "full" &&
|
|
740
|
-
dataReturnType === "
|
|
741
|
-
dataReturnType === "
|
|
742
|
-
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"
|
|
743
826
|
] });
|
|
744
827
|
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("br", {}), /* @__PURE__ */ jsx(File.Source, {
|
|
745
828
|
name,
|
|
@@ -757,23 +840,14 @@ function Client({ name, isExportable = true, isIndexable = true, returnType, bas
|
|
|
757
840
|
}) },
|
|
758
841
|
returnType,
|
|
759
842
|
children: [
|
|
760
|
-
isConfigurable ? `const { client: request = client, ${isMultipleContentTypes ? `contentType = ${
|
|
761
|
-
/* @__PURE__ */ jsx("br", {}),
|
|
843
|
+
isConfigurable ? `const { client: request = client, ${isMultipleContentTypes ? `contentType = ${stringify(contentType)}, ` : ""}...requestConfig } = config` : "",
|
|
762
844
|
/* @__PURE__ */ jsx("br", {}),
|
|
763
845
|
pathParamsMapping && Object.entries(pathParamsMapping).filter(([originalName, camelCaseName]) => isValidVarName(originalName) && originalName !== camelCaseName).map(([originalName, camelCaseName]) => `const ${originalName} = ${camelCaseName}`).join("\n"),
|
|
764
|
-
pathParamsMapping && /* @__PURE__ */
|
|
765
|
-
queryParamsMapping && queryParamsName && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
] }),
|
|
770
|
-
headerParamsMapping && headerParamsName && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
771
|
-
`const mappedHeaders = headers ? { ${Object.entries(headerParamsMapping).map(([originalName, camelCaseName]) => `"${originalName}": headers.${camelCaseName}`).join(", ")} } : undefined`,
|
|
772
|
-
/* @__PURE__ */ jsx("br", {}),
|
|
773
|
-
/* @__PURE__ */ jsx("br", {})
|
|
774
|
-
] }),
|
|
775
|
-
parser === "zod" && zodRequestName ? `const requestData = ${zodRequestName}.parse(data)` : requestName && "const requestData = data",
|
|
776
|
-
/* @__PURE__ */ jsx("br", {}),
|
|
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)`,
|
|
777
851
|
(isFormData || isMultipleContentTypes && hasFormData) && requestName && "const formData = buildFormData(requestData)",
|
|
778
852
|
/* @__PURE__ */ jsx("br", {}),
|
|
779
853
|
isConfigurable ? `const res = await request<${generics.join(", ")}>(${clientParams.toCall()})` : `const res = await client<${generics.join(", ")}>(${clientParams.toCall()})`,
|
|
@@ -784,93 +858,20 @@ function Client({ name, isExportable = true, isIndexable = true, returnType, bas
|
|
|
784
858
|
})] });
|
|
785
859
|
}
|
|
786
860
|
//#endregion
|
|
787
|
-
//#region src/utils.ts
|
|
788
|
-
/**
|
|
789
|
-
* Builds HTTP headers array for a client request.
|
|
790
|
-
* Includes Content-Type (if not default) and spreads header parameters if present.
|
|
791
|
-
*/
|
|
792
|
-
function buildHeaders(contentType, hasHeaderParams) {
|
|
793
|
-
return [contentType !== "application/json" && contentType !== "multipart/form-data" ? `'Content-Type': '${contentType}'` : null, hasHeaderParams ? "...headers" : null].filter(Boolean);
|
|
794
|
-
}
|
|
795
|
-
/**
|
|
796
|
-
* Builds TypeScript generic parameters for a client method.
|
|
797
|
-
* Includes response type, error type, and optional request type.
|
|
798
|
-
*/
|
|
799
|
-
function buildGenerics(node, tsResolver) {
|
|
800
|
-
const successNames = resolveSuccessNames(node, tsResolver);
|
|
801
|
-
const responseName = successNames.length > 0 ? successNames.join(" | ") : tsResolver.resolveResponseName(node);
|
|
802
|
-
const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : null;
|
|
803
|
-
const errorNames = node.responses.filter((r) => Number.parseInt(r.statusCode, 10) >= 400).map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode));
|
|
804
|
-
return [
|
|
805
|
-
responseName,
|
|
806
|
-
`ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`,
|
|
807
|
-
requestName || "unknown"
|
|
808
|
-
].filter(Boolean);
|
|
809
|
-
}
|
|
810
|
-
/**
|
|
811
|
-
* Builds the parameters object for a class-based client method.
|
|
812
|
-
* Includes URL, method, base URL, headers, and request/response data.
|
|
813
|
-
*/
|
|
814
|
-
function buildClassClientParams({ node, path, baseURL, tsResolver, isFormData, isMultipleContentTypes, hasFormData, headers }) {
|
|
815
|
-
const { query: queryParams } = getOperationParameters(node);
|
|
816
|
-
const queryParamsName = queryParams.length > 0 ? tsResolver.resolveQueryParamsName(node, queryParams[0]) : null;
|
|
817
|
-
const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : null;
|
|
818
|
-
const responseType = getResponseType(node);
|
|
819
|
-
return createFunctionParams({ config: {
|
|
820
|
-
mode: "object",
|
|
821
|
-
children: {
|
|
822
|
-
requestConfig: { mode: "inlineSpread" },
|
|
823
|
-
method: { value: JSON.stringify(ast.isHttpOperationNode(node) ? node.method.toUpperCase() : "") },
|
|
824
|
-
url: { value: path.template },
|
|
825
|
-
baseURL: baseURL ? { value: JSON.stringify(baseURL) } : null,
|
|
826
|
-
params: queryParamsName ? {} : null,
|
|
827
|
-
data: requestName ? { value: isMultipleContentTypes && hasFormData ? "contentType === 'multipart/form-data' ? formData as FormData : requestData" : isFormData ? "formData as FormData" : "requestData" } : null,
|
|
828
|
-
contentType: isMultipleContentTypes ? {} : null,
|
|
829
|
-
responseType: responseType ? { value: JSON.stringify(responseType) } : null,
|
|
830
|
-
headers: headers.length ? { value: `{ ${headers.join(", ")}, ...requestConfig.headers }` } : null
|
|
831
|
-
}
|
|
832
|
-
} });
|
|
833
|
-
}
|
|
834
|
-
/**
|
|
835
|
-
* Builds the request data parsing line for client methods.
|
|
836
|
-
* Applies Zod validation if configured, otherwise uses data directly.
|
|
837
|
-
*/
|
|
838
|
-
function buildRequestDataLine({ parser, node, zodResolver }) {
|
|
839
|
-
const zodRequestName = zodResolver && parser === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null;
|
|
840
|
-
if (parser === "zod" && zodRequestName) return `const requestData = ${zodRequestName}.parse(data)`;
|
|
841
|
-
if (node.requestBody?.content?.[0]?.schema) return "const requestData = data";
|
|
842
|
-
return "";
|
|
843
|
-
}
|
|
844
|
-
/**
|
|
845
|
-
* Builds the form data conversion line for file upload requests.
|
|
846
|
-
* Returns empty string if not applicable.
|
|
847
|
-
*/
|
|
848
|
-
function buildFormDataLine(isFormData, hasRequest) {
|
|
849
|
-
return isFormData && hasRequest ? "const formData = buildFormData(requestData)" : "";
|
|
850
|
-
}
|
|
851
|
-
/**
|
|
852
|
-
* Builds the return statement for a client method.
|
|
853
|
-
* Applies Zod validation to response data if configured, otherwise returns raw response.
|
|
854
|
-
*/
|
|
855
|
-
function buildReturnStatement({ dataReturnType, parser, node, zodResolver }) {
|
|
856
|
-
const zodResponseName = zodResolver && parser === "zod" ? zodResolver.resolveResponseName?.(node) : null;
|
|
857
|
-
if (dataReturnType === "full" && parser === "zod" && zodResponseName) return `return {...res, data: ${zodResponseName}.parse(res.data)}`;
|
|
858
|
-
if (dataReturnType === "data" && parser === "zod" && zodResponseName) return `return ${zodResponseName}.parse(res.data)`;
|
|
859
|
-
if (dataReturnType === "full" && parser !== "zod") return "return res";
|
|
860
|
-
return "return res.data";
|
|
861
|
-
}
|
|
862
|
-
//#endregion
|
|
863
861
|
//#region src/components/ClassClient.tsx
|
|
864
862
|
const declarationPrinter$1 = functionPrinter({ mode: "declaration" });
|
|
865
863
|
function generateMethod$1({ node, name, tsResolver, zodResolver, baseURL, dataReturnType, parser, paramsType, paramsCasing, pathParamsType }) {
|
|
866
864
|
if (!ast.isHttpOperationNode(node)) return "";
|
|
867
|
-
const path = new URLPath(node.path, { casing: paramsCasing });
|
|
868
865
|
const { defaultContentType: contentType, isMultipleContentTypes, hasFormData } = getContentTypeInfo(node);
|
|
869
866
|
const isFormData = !isMultipleContentTypes && contentType === "multipart/form-data";
|
|
870
867
|
const { header: headerParams } = getOperationParameters(node);
|
|
871
868
|
const headerParamsName = headerParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, headerParams[0]) : null;
|
|
872
869
|
const headers = isMultipleContentTypes ? headerParamsName ? ["...headers"] : [] : buildHeaders(contentType, !!headerParamsName);
|
|
873
|
-
const generics = buildGenerics(node, tsResolver
|
|
870
|
+
const generics = buildGenerics(node, tsResolver, {
|
|
871
|
+
dataReturnType,
|
|
872
|
+
zodResolver,
|
|
873
|
+
parser
|
|
874
|
+
});
|
|
874
875
|
const paramsNode = buildClientParamsNode({
|
|
875
876
|
paramsType,
|
|
876
877
|
paramsCasing,
|
|
@@ -880,15 +881,18 @@ function generateMethod$1({ node, name, tsResolver, zodResolver, baseURL, dataRe
|
|
|
880
881
|
isConfigurable: true
|
|
881
882
|
});
|
|
882
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;
|
|
883
886
|
const clientParams = buildClassClientParams({
|
|
884
887
|
node,
|
|
885
|
-
path,
|
|
888
|
+
url: Url$1.toTemplateString(node.path, { casing: paramsCasing }),
|
|
886
889
|
baseURL,
|
|
887
890
|
tsResolver,
|
|
888
891
|
isFormData,
|
|
889
892
|
isMultipleContentTypes,
|
|
890
893
|
hasFormData,
|
|
891
|
-
headers
|
|
894
|
+
headers,
|
|
895
|
+
zodQueryParamsName
|
|
892
896
|
});
|
|
893
897
|
const jsdoc = buildJSDoc(buildOperationComments(node, {
|
|
894
898
|
link: "urlPath",
|
|
@@ -900,17 +904,24 @@ function generateMethod$1({ node, name, tsResolver, zodResolver, baseURL, dataRe
|
|
|
900
904
|
node,
|
|
901
905
|
zodResolver
|
|
902
906
|
});
|
|
907
|
+
const queryParamsLine = buildQueryParamsLine({
|
|
908
|
+
parser,
|
|
909
|
+
node,
|
|
910
|
+
zodResolver
|
|
911
|
+
});
|
|
903
912
|
const formDataLine = buildFormDataLine(isFormData || isMultipleContentTypes && hasFormData, !!node.requestBody?.content?.[0]?.schema);
|
|
904
913
|
const returnStatement = buildReturnStatement({
|
|
905
914
|
dataReturnType,
|
|
906
915
|
parser,
|
|
907
916
|
node,
|
|
908
|
-
zodResolver
|
|
917
|
+
zodResolver,
|
|
918
|
+
tsResolver
|
|
909
919
|
});
|
|
910
920
|
return `${jsdoc}async ${name}(${paramsSignature}) {\n${[
|
|
911
|
-
`const { client: request = client, ${isMultipleContentTypes ? `contentType = ${
|
|
921
|
+
`const { client: request = client, ${isMultipleContentTypes ? `contentType = ${stringify(contentType)}, ` : ""}...requestConfig } = mergeConfig(this.#config, config)`,
|
|
912
922
|
"",
|
|
913
923
|
requestDataLine,
|
|
924
|
+
queryParamsLine,
|
|
914
925
|
formDataLine,
|
|
915
926
|
`const res = await request<${generics.join(", ")}>(${clientParams.toCall()})`,
|
|
916
927
|
returnStatement
|
|
@@ -968,8 +979,13 @@ function resolveTypeImportNames$1(node, tsResolver) {
|
|
|
968
979
|
return resolveOperationTypeNames(node, tsResolver, { order: "body-response-first" });
|
|
969
980
|
}
|
|
970
981
|
__name(resolveTypeImportNames$1, "resolveTypeImportNames");
|
|
971
|
-
function resolveZodImportNames$1(node, zodResolver) {
|
|
972
|
-
|
|
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));
|
|
973
989
|
}
|
|
974
990
|
__name(resolveZodImportNames$1, "resolveZodImportNames");
|
|
975
991
|
/**
|
|
@@ -979,7 +995,7 @@ __name(resolveZodImportNames$1, "resolveZodImportNames");
|
|
|
979
995
|
*/
|
|
980
996
|
const classClientGenerator = defineGenerator({
|
|
981
997
|
name: "classClient",
|
|
982
|
-
renderer:
|
|
998
|
+
renderer: jsxRenderer,
|
|
983
999
|
operations(nodes, ctx) {
|
|
984
1000
|
const { config, driver, resolver, root } = ctx;
|
|
985
1001
|
const { output, group, dataReturnType, paramsCasing, paramsType, pathParamsType, parser, importPath, sdk } = ctx.options;
|
|
@@ -988,7 +1004,7 @@ const classClientGenerator = defineGenerator({
|
|
|
988
1004
|
if (!pluginTs) return null;
|
|
989
1005
|
const tsResolver = driver.getResolver(pluginTsName);
|
|
990
1006
|
const tsPluginOptions = pluginTs.options;
|
|
991
|
-
const pluginZod = parser
|
|
1007
|
+
const pluginZod = isParserEnabled(parser) ? driver.getPlugin(pluginZodName) : null;
|
|
992
1008
|
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null;
|
|
993
1009
|
function buildOperationData(node) {
|
|
994
1010
|
const typeFile = tsResolver.resolveFile(operationFileEntry(node, node.operationId), {
|
|
@@ -1013,7 +1029,7 @@ const classClientGenerator = defineGenerator({
|
|
|
1013
1029
|
const controllers = nodes.reduce((acc, operationNode) => {
|
|
1014
1030
|
if (!ast.isHttpOperationNode(operationNode)) return acc;
|
|
1015
1031
|
const tag = operationNode.tags[0];
|
|
1016
|
-
const groupName = tag ? group?.name?.({ group: camelCase(tag) }) ?? resolver.resolveGroupName(tag) : resolver.
|
|
1032
|
+
const groupName = tag ? group?.name?.({ group: camelCase(tag) }) ?? resolver.resolveGroupName(tag) : resolver.resolveClassName("Client");
|
|
1017
1033
|
if (!tag && !group) {
|
|
1018
1034
|
const name = resolver.resolveClassName("ApiClient");
|
|
1019
1035
|
const file = resolver.resolveFile({
|
|
@@ -1080,7 +1096,7 @@ const classClientGenerator = defineGenerator({
|
|
|
1080
1096
|
const zodFilesByPath = /* @__PURE__ */ new Map();
|
|
1081
1097
|
ops.forEach((op) => {
|
|
1082
1098
|
if (!op.zodFile || !zodResolver) return;
|
|
1083
|
-
const names = resolveZodImportNames$1(op.node, zodResolver);
|
|
1099
|
+
const names = resolveZodImportNames$1(op.node, zodResolver, parser);
|
|
1084
1100
|
if (!zodImportsByFile.has(op.zodFile.path)) zodImportsByFile.set(op.zodFile.path, /* @__PURE__ */ new Set());
|
|
1085
1101
|
const imports = zodImportsByFile.get(op.zodFile.path);
|
|
1086
1102
|
names.forEach((n) => {
|
|
@@ -1095,7 +1111,7 @@ const classClientGenerator = defineGenerator({
|
|
|
1095
1111
|
}
|
|
1096
1112
|
const files = controllers.map(({ name, file, operations: ops }) => {
|
|
1097
1113
|
const { typeImportsByFile, typeFilesByPath } = collectTypeImports(ops);
|
|
1098
|
-
const { zodImportsByFile, zodFilesByPath } = parser
|
|
1114
|
+
const { zodImportsByFile, zodFilesByPath } = isParserEnabled(parser) ? collectZodImports(ops) : {
|
|
1099
1115
|
zodImportsByFile: /* @__PURE__ */ new Map(),
|
|
1100
1116
|
zodFilesByPath: /* @__PURE__ */ new Map()
|
|
1101
1117
|
};
|
|
@@ -1166,6 +1182,11 @@ const classClientGenerator = defineGenerator({
|
|
|
1166
1182
|
root: file.path,
|
|
1167
1183
|
path: path.resolve(root, ".kubb/config.ts")
|
|
1168
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
|
+
}),
|
|
1169
1190
|
Array.from(typeImportsByFile.entries()).map(([filePath, importSet]) => {
|
|
1170
1191
|
const typeFile = typeFilesByPath.get(filePath);
|
|
1171
1192
|
if (!typeFile) return null;
|
|
@@ -1178,7 +1199,7 @@ const classClientGenerator = defineGenerator({
|
|
|
1178
1199
|
isTypeOnly: true
|
|
1179
1200
|
}, filePath);
|
|
1180
1201
|
}),
|
|
1181
|
-
parser
|
|
1202
|
+
isParserEnabled(parser) && Array.from(zodImportsByFile.entries()).map(([filePath, importSet]) => {
|
|
1182
1203
|
const zodFile = zodFilesByPath.get(filePath);
|
|
1183
1204
|
if (!zodFile) return null;
|
|
1184
1205
|
const importNames = Array.from(importSet).filter(Boolean);
|
|
@@ -1269,7 +1290,7 @@ const classClientGenerator = defineGenerator({
|
|
|
1269
1290
|
*/
|
|
1270
1291
|
const clientGenerator = defineGenerator({
|
|
1271
1292
|
name: "client",
|
|
1272
|
-
renderer:
|
|
1293
|
+
renderer: jsxRenderer,
|
|
1273
1294
|
operation(node, ctx) {
|
|
1274
1295
|
if (!ast.isHttpOperationNode(node)) return null;
|
|
1275
1296
|
const { config, driver, resolver, root } = ctx;
|
|
@@ -1278,10 +1299,16 @@ const clientGenerator = defineGenerator({
|
|
|
1278
1299
|
const pluginTs = driver.getPlugin(pluginTsName);
|
|
1279
1300
|
if (!pluginTs) return null;
|
|
1280
1301
|
const tsResolver = driver.getResolver(pluginTsName);
|
|
1281
|
-
const pluginZod = parser
|
|
1302
|
+
const pluginZod = isParserEnabled(parser) ? driver.getPlugin(pluginZodName) : null;
|
|
1282
1303
|
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null;
|
|
1283
1304
|
const importedTypeNames = resolveOperationTypeNames(node, tsResolver, { paramsCasing });
|
|
1284
|
-
const
|
|
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;
|
|
1285
1312
|
const meta = {
|
|
1286
1313
|
name: resolver.resolveName(node.operationId),
|
|
1287
1314
|
urlName: resolver.resolveUrlName(node),
|
|
@@ -1353,6 +1380,11 @@ const clientGenerator = defineGenerator({
|
|
|
1353
1380
|
root: meta.file.path,
|
|
1354
1381
|
path: path.resolve(root, ".kubb/config.ts")
|
|
1355
1382
|
}),
|
|
1383
|
+
zodRequestName && /* @__PURE__ */ jsx(File.Import, {
|
|
1384
|
+
name: ["z"],
|
|
1385
|
+
path: "zod",
|
|
1386
|
+
isTypeOnly: true
|
|
1387
|
+
}),
|
|
1356
1388
|
meta.fileZod && importedZodNames.length > 0 && /* @__PURE__ */ jsx(File.Import, {
|
|
1357
1389
|
name: importedZodNames,
|
|
1358
1390
|
root: meta.file.path,
|
|
@@ -1402,7 +1434,7 @@ const clientGenerator = defineGenerator({
|
|
|
1402
1434
|
*/
|
|
1403
1435
|
const groupedClientGenerator = defineGenerator({
|
|
1404
1436
|
name: "groupedClient",
|
|
1405
|
-
renderer:
|
|
1437
|
+
renderer: jsxRenderer,
|
|
1406
1438
|
operations(nodes, ctx) {
|
|
1407
1439
|
const { config, resolver, root } = ctx;
|
|
1408
1440
|
const { output, group } = ctx.options;
|
|
@@ -1485,29 +1517,7 @@ const groupedClientGenerator = defineGenerator({
|
|
|
1485
1517
|
}
|
|
1486
1518
|
});
|
|
1487
1519
|
//#endregion
|
|
1488
|
-
//#region src/
|
|
1489
|
-
function Operations({ name, nodes }) {
|
|
1490
|
-
const operationsObject = {};
|
|
1491
|
-
nodes.forEach((node) => {
|
|
1492
|
-
if (!ast.isHttpOperationNode(node)) return;
|
|
1493
|
-
operationsObject[node.operationId] = {
|
|
1494
|
-
path: new URLPath(node.path).URL,
|
|
1495
|
-
method: node.method.toLowerCase()
|
|
1496
|
-
};
|
|
1497
|
-
});
|
|
1498
|
-
return /* @__PURE__ */ jsx(File.Source, {
|
|
1499
|
-
name,
|
|
1500
|
-
isExportable: true,
|
|
1501
|
-
isIndexable: true,
|
|
1502
|
-
children: /* @__PURE__ */ jsx(Const, {
|
|
1503
|
-
name,
|
|
1504
|
-
export: true,
|
|
1505
|
-
children: JSON.stringify(operationsObject, void 0, 2)
|
|
1506
|
-
})
|
|
1507
|
-
});
|
|
1508
|
-
}
|
|
1509
|
-
//#endregion
|
|
1510
|
-
//#region src/generators/operationsGenerator.tsx
|
|
1520
|
+
//#region src/generators/operationsGenerator.ts
|
|
1511
1521
|
/**
|
|
1512
1522
|
* Generates an `operations.ts` file that re-exports every operation grouped
|
|
1513
1523
|
* by HTTP method. Enabled when `pluginClient({ operations: true })`. Useful
|
|
@@ -1516,7 +1526,6 @@ function Operations({ name, nodes }) {
|
|
|
1516
1526
|
*/
|
|
1517
1527
|
const operationsGenerator = defineGenerator({
|
|
1518
1528
|
name: "client",
|
|
1519
|
-
renderer: jsxRendererSync,
|
|
1520
1529
|
operations(nodes, ctx) {
|
|
1521
1530
|
const { config, resolver, root } = ctx;
|
|
1522
1531
|
const { output, group } = ctx.options;
|
|
@@ -1529,7 +1538,15 @@ const operationsGenerator = defineGenerator({
|
|
|
1529
1538
|
output,
|
|
1530
1539
|
group: group ?? void 0
|
|
1531
1540
|
});
|
|
1532
|
-
|
|
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({
|
|
1533
1550
|
baseName: file.baseName,
|
|
1534
1551
|
path: file.path,
|
|
1535
1552
|
meta: file.meta,
|
|
@@ -1549,11 +1566,17 @@ const operationsGenerator = defineGenerator({
|
|
|
1549
1566
|
baseName: file.baseName
|
|
1550
1567
|
}
|
|
1551
1568
|
}),
|
|
1552
|
-
|
|
1569
|
+
sources: [ast.createSource({
|
|
1553
1570
|
name,
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
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
|
+
})];
|
|
1557
1580
|
}
|
|
1558
1581
|
});
|
|
1559
1582
|
//#endregion
|
|
@@ -1561,13 +1584,16 @@ const operationsGenerator = defineGenerator({
|
|
|
1561
1584
|
const declarationPrinter = functionPrinter({ mode: "declaration" });
|
|
1562
1585
|
function generateMethod({ node, name, tsResolver, zodResolver, baseURL, dataReturnType, parser, paramsType, paramsCasing, pathParamsType }) {
|
|
1563
1586
|
if (!ast.isHttpOperationNode(node)) return "";
|
|
1564
|
-
const path = new URLPath(node.path, { casing: paramsCasing });
|
|
1565
1587
|
const { defaultContentType: contentType, isMultipleContentTypes, hasFormData } = getContentTypeInfo(node);
|
|
1566
1588
|
const isFormData = !isMultipleContentTypes && contentType === "multipart/form-data";
|
|
1567
1589
|
const { header: headerParams } = getOperationParameters(node);
|
|
1568
1590
|
const headerParamsName = headerParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, headerParams[0]) : null;
|
|
1569
1591
|
const headers = isMultipleContentTypes ? headerParamsName ? ["...headers"] : [] : buildHeaders(contentType, !!headerParamsName);
|
|
1570
|
-
const generics = buildGenerics(node, tsResolver
|
|
1592
|
+
const generics = buildGenerics(node, tsResolver, {
|
|
1593
|
+
dataReturnType,
|
|
1594
|
+
zodResolver,
|
|
1595
|
+
parser
|
|
1596
|
+
});
|
|
1571
1597
|
const paramsNode = buildClientParamsNode({
|
|
1572
1598
|
paramsType,
|
|
1573
1599
|
paramsCasing,
|
|
@@ -1577,15 +1603,18 @@ function generateMethod({ node, name, tsResolver, zodResolver, baseURL, dataRetu
|
|
|
1577
1603
|
isConfigurable: true
|
|
1578
1604
|
});
|
|
1579
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;
|
|
1580
1608
|
const clientParams = buildClassClientParams({
|
|
1581
1609
|
node,
|
|
1582
|
-
path,
|
|
1610
|
+
url: Url$1.toTemplateString(node.path, { casing: paramsCasing }),
|
|
1583
1611
|
baseURL,
|
|
1584
1612
|
tsResolver,
|
|
1585
1613
|
isFormData,
|
|
1586
1614
|
isMultipleContentTypes,
|
|
1587
1615
|
hasFormData,
|
|
1588
|
-
headers
|
|
1616
|
+
headers,
|
|
1617
|
+
zodQueryParamsName
|
|
1589
1618
|
});
|
|
1590
1619
|
const jsdoc = buildJSDoc(buildOperationComments(node, {
|
|
1591
1620
|
link: "urlPath",
|
|
@@ -1597,17 +1626,24 @@ function generateMethod({ node, name, tsResolver, zodResolver, baseURL, dataRetu
|
|
|
1597
1626
|
node,
|
|
1598
1627
|
zodResolver
|
|
1599
1628
|
});
|
|
1629
|
+
const queryParamsLine = buildQueryParamsLine({
|
|
1630
|
+
parser,
|
|
1631
|
+
node,
|
|
1632
|
+
zodResolver
|
|
1633
|
+
});
|
|
1600
1634
|
const formDataLine = buildFormDataLine(isFormData || isMultipleContentTypes && hasFormData, !!node.requestBody?.content?.[0]?.schema);
|
|
1601
1635
|
const returnStatement = buildReturnStatement({
|
|
1602
1636
|
dataReturnType,
|
|
1603
1637
|
parser,
|
|
1604
1638
|
node,
|
|
1605
|
-
zodResolver
|
|
1639
|
+
zodResolver,
|
|
1640
|
+
tsResolver
|
|
1606
1641
|
});
|
|
1607
1642
|
return `${jsdoc} static async ${name}(${paramsSignature}) {\n${[
|
|
1608
|
-
`const { client: request = client, ${isMultipleContentTypes ? `contentType = ${
|
|
1643
|
+
`const { client: request = client, ${isMultipleContentTypes ? `contentType = ${stringify(contentType)}, ` : ""}...requestConfig } = mergeConfig(this.#config, config)`,
|
|
1609
1644
|
"",
|
|
1610
1645
|
requestDataLine,
|
|
1646
|
+
queryParamsLine,
|
|
1611
1647
|
formDataLine,
|
|
1612
1648
|
`const res = await request<${generics.join(", ")}>(${clientParams.toCall()})`,
|
|
1613
1649
|
returnStatement
|
|
@@ -1638,8 +1674,13 @@ function StaticClassClient({ name, isExportable = true, isIndexable = true, oper
|
|
|
1638
1674
|
function resolveTypeImportNames(node, tsResolver) {
|
|
1639
1675
|
return resolveOperationTypeNames(node, tsResolver, { order: "body-response-first" });
|
|
1640
1676
|
}
|
|
1641
|
-
function resolveZodImportNames(node, zodResolver) {
|
|
1642
|
-
|
|
1677
|
+
function resolveZodImportNames(node, zodResolver, parser) {
|
|
1678
|
+
const { query: queryParams } = getOperationParameters(node);
|
|
1679
|
+
return [
|
|
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
|
|
1683
|
+
].filter((n) => Boolean(n));
|
|
1643
1684
|
}
|
|
1644
1685
|
/**
|
|
1645
1686
|
* Built-in `operations` generator for `@kubb/plugin-client` when
|
|
@@ -1649,7 +1690,7 @@ function resolveZodImportNames(node, zodResolver) {
|
|
|
1649
1690
|
*/
|
|
1650
1691
|
const staticClassClientGenerator = defineGenerator({
|
|
1651
1692
|
name: "staticClassClient",
|
|
1652
|
-
renderer:
|
|
1693
|
+
renderer: jsxRenderer,
|
|
1653
1694
|
operations(nodes, ctx) {
|
|
1654
1695
|
const { config, driver, resolver, root } = ctx;
|
|
1655
1696
|
const { output, group, dataReturnType, paramsCasing, paramsType, pathParamsType, parser, importPath } = ctx.options;
|
|
@@ -1658,7 +1699,7 @@ const staticClassClientGenerator = defineGenerator({
|
|
|
1658
1699
|
if (!pluginTs) return null;
|
|
1659
1700
|
const tsResolver = driver.getResolver(pluginTsName);
|
|
1660
1701
|
const tsPluginOptions = pluginTs.options;
|
|
1661
|
-
const pluginZod = parser
|
|
1702
|
+
const pluginZod = isParserEnabled(parser) ? driver.getPlugin(pluginZodName) : null;
|
|
1662
1703
|
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null;
|
|
1663
1704
|
function buildOperationData(node) {
|
|
1664
1705
|
const typeFile = tsResolver.resolveFile(operationFileEntry(node, node.operationId), {
|
|
@@ -1683,7 +1724,7 @@ const staticClassClientGenerator = defineGenerator({
|
|
|
1683
1724
|
const controllers = nodes.reduce((acc, operationNode) => {
|
|
1684
1725
|
if (!ast.isHttpOperationNode(operationNode)) return acc;
|
|
1685
1726
|
const tag = operationNode.tags[0];
|
|
1686
|
-
const groupName = tag ? group?.name?.({ group: camelCase(tag) }) ?? resolver.resolveGroupName(tag) : resolver.
|
|
1727
|
+
const groupName = tag ? group?.name?.({ group: camelCase(tag) }) ?? resolver.resolveGroupName(tag) : resolver.resolveClassName("Client");
|
|
1687
1728
|
if (!tag && !group) {
|
|
1688
1729
|
const name = resolver.resolveClassName("ApiClient");
|
|
1689
1730
|
const file = resolver.resolveFile({
|
|
@@ -1748,7 +1789,7 @@ const staticClassClientGenerator = defineGenerator({
|
|
|
1748
1789
|
const zodFilesByPath = /* @__PURE__ */ new Map();
|
|
1749
1790
|
ops.forEach((op) => {
|
|
1750
1791
|
if (!op.zodFile || !zodResolver) return;
|
|
1751
|
-
const names = resolveZodImportNames(op.node, zodResolver);
|
|
1792
|
+
const names = resolveZodImportNames(op.node, zodResolver, parser);
|
|
1752
1793
|
if (!zodImportsByFile.has(op.zodFile.path)) zodImportsByFile.set(op.zodFile.path, /* @__PURE__ */ new Set());
|
|
1753
1794
|
const imports = zodImportsByFile.get(op.zodFile.path);
|
|
1754
1795
|
names.forEach((n) => {
|
|
@@ -1763,7 +1804,7 @@ const staticClassClientGenerator = defineGenerator({
|
|
|
1763
1804
|
}
|
|
1764
1805
|
return /* @__PURE__ */ jsx(Fragment, { children: controllers.map(({ name, file, operations: ops }) => {
|
|
1765
1806
|
const { typeImportsByFile, typeFilesByPath } = collectTypeImports(ops);
|
|
1766
|
-
const { zodImportsByFile, zodFilesByPath } = parser
|
|
1807
|
+
const { zodImportsByFile, zodFilesByPath } = isParserEnabled(parser) ? collectZodImports(ops) : {
|
|
1767
1808
|
zodImportsByFile: /* @__PURE__ */ new Map(),
|
|
1768
1809
|
zodFilesByPath: /* @__PURE__ */ new Map()
|
|
1769
1810
|
};
|
|
@@ -1834,6 +1875,11 @@ const staticClassClientGenerator = defineGenerator({
|
|
|
1834
1875
|
root: file.path,
|
|
1835
1876
|
path: path.resolve(root, ".kubb/config.ts")
|
|
1836
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
|
+
}),
|
|
1837
1883
|
Array.from(typeImportsByFile.entries()).map(([filePath, importSet]) => {
|
|
1838
1884
|
const typeFile = typeFilesByPath.get(filePath);
|
|
1839
1885
|
if (!typeFile) return null;
|
|
@@ -1846,7 +1892,7 @@ const staticClassClientGenerator = defineGenerator({
|
|
|
1846
1892
|
isTypeOnly: true
|
|
1847
1893
|
}, filePath);
|
|
1848
1894
|
}),
|
|
1849
|
-
parser
|
|
1895
|
+
isParserEnabled(parser) && Array.from(zodImportsByFile.entries()).map(([filePath, importSet]) => {
|
|
1850
1896
|
const zodFile = zodFilesByPath.get(filePath);
|
|
1851
1897
|
if (!zodFile) return null;
|
|
1852
1898
|
const importNames = Array.from(importSet).filter(Boolean);
|
|
@@ -1885,6 +1931,7 @@ const staticClassClientGenerator = defineGenerator({
|
|
|
1885
1931
|
*
|
|
1886
1932
|
* resolverClient.default('list pets', 'function') // 'listPets'
|
|
1887
1933
|
* resolverClient.resolveClassName('pet') // 'Pet'
|
|
1934
|
+
* resolverClient.resolveGroupName('pet') // 'PetClient'
|
|
1888
1935
|
* resolverClient.resolveUrlName(operationNode) // 'getShowPetByIdUrl'
|
|
1889
1936
|
* ```
|
|
1890
1937
|
*/
|
|
@@ -1892,8 +1939,8 @@ const resolverClient = defineResolver(() => ({
|
|
|
1892
1939
|
name: "default",
|
|
1893
1940
|
pluginName: "plugin-client",
|
|
1894
1941
|
default(name, type) {
|
|
1895
|
-
|
|
1896
|
-
return
|
|
1942
|
+
if (type === "file") return toFilePath(name);
|
|
1943
|
+
return ensureValidVarName(camelCase(name));
|
|
1897
1944
|
},
|
|
1898
1945
|
resolveName(name) {
|
|
1899
1946
|
return this.default(name, "function");
|
|
@@ -1905,7 +1952,7 @@ const resolverClient = defineResolver(() => ({
|
|
|
1905
1952
|
return ensureValidVarName(pascalCase(name));
|
|
1906
1953
|
},
|
|
1907
1954
|
resolveGroupName(name) {
|
|
1908
|
-
return ensureValidVarName(pascalCase(name));
|
|
1955
|
+
return ensureValidVarName(pascalCase(`${name} Client`));
|
|
1909
1956
|
},
|
|
1910
1957
|
resolveClientPropertyName(name) {
|
|
1911
1958
|
return ensureValidVarName(camelCase(name));
|
|
@@ -1950,7 +1997,7 @@ const pluginClientName = "plugin-client";
|
|
|
1950
1997
|
const pluginClient = definePlugin((options) => {
|
|
1951
1998
|
const { output = {
|
|
1952
1999
|
path: "clients",
|
|
1953
|
-
|
|
2000
|
+
barrel: { type: "named" }
|
|
1954
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;
|
|
1955
2002
|
const resolvedImportPath = importPath ?? (!bundle ? `@kubb/plugin-client/clients/${client}` : void 0);
|
|
1956
2003
|
const selectedGenerators = options.generators ?? [
|
|
@@ -1958,11 +2005,11 @@ const pluginClient = definePlugin((options) => {
|
|
|
1958
2005
|
group && clientType === "function" ? groupedClientGenerator : null,
|
|
1959
2006
|
operations ? operationsGenerator : null
|
|
1960
2007
|
].filter((x) => Boolean(x));
|
|
1961
|
-
const groupConfig = createGroupConfig(group
|
|
2008
|
+
const groupConfig = createGroupConfig(group);
|
|
1962
2009
|
return {
|
|
1963
2010
|
name: pluginClientName,
|
|
1964
2011
|
options,
|
|
1965
|
-
dependencies: [pluginTsName, parser
|
|
2012
|
+
dependencies: [pluginTsName, isParserEnabled(parser) ? pluginZodName : null].filter((dependency) => Boolean(dependency)),
|
|
1966
2013
|
hooks: { "kubb:plugin:setup"(ctx) {
|
|
1967
2014
|
const resolver = userResolver ? {
|
|
1968
2015
|
...resolverClient,
|
|
@@ -2020,6 +2067,6 @@ const pluginClient = definePlugin((options) => {
|
|
|
2020
2067
|
};
|
|
2021
2068
|
});
|
|
2022
2069
|
//#endregion
|
|
2023
|
-
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 };
|
|
2024
2071
|
|
|
2025
2072
|
//# sourceMappingURL=index.js.map
|