@kubb/plugin-client 5.0.0-beta.3 → 5.0.0-beta.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -4
- package/dist/clients/axios.cjs +25 -3
- package/dist/clients/axios.cjs.map +1 -1
- package/dist/clients/axios.d.ts +9 -2
- package/dist/clients/axios.js +25 -3
- package/dist/clients/axios.js.map +1 -1
- package/dist/clients/fetch.cjs +76 -8
- package/dist/clients/fetch.cjs.map +1 -1
- package/dist/clients/fetch.d.ts +9 -2
- package/dist/clients/fetch.js +76 -8
- package/dist/clients/fetch.js.map +1 -1
- package/dist/index.cjs +627 -353
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +153 -86
- package/dist/index.js +628 -354
- package/dist/index.js.map +1 -1
- package/dist/templates/clients/axios.source.cjs +1 -1
- package/dist/templates/clients/axios.source.js +1 -1
- package/dist/templates/clients/fetch.source.cjs +1 -1
- package/dist/templates/clients/fetch.source.js +1 -1
- package/extension.yaml +1293 -0
- package/package.json +11 -17
- package/src/clients/axios.ts +41 -7
- package/src/clients/fetch.ts +106 -6
- package/src/components/ClassClient.tsx +19 -20
- package/src/components/Client.tsx +74 -53
- package/src/components/Operations.tsx +2 -1
- package/src/components/StaticClassClient.tsx +19 -20
- package/src/components/Url.tsx +8 -9
- package/src/components/WrapperClient.tsx +9 -5
- package/src/functionParams.ts +8 -8
- package/src/generators/classClientGenerator.tsx +51 -47
- package/src/generators/clientGenerator.tsx +37 -48
- package/src/generators/groupedClientGenerator.tsx +14 -8
- package/src/generators/operationsGenerator.tsx +14 -8
- package/src/generators/staticClassClientGenerator.tsx +45 -41
- package/src/plugin.ts +27 -26
- package/src/resolvers/resolverClient.ts +31 -8
- package/src/types.ts +93 -55
- package/src/utils.ts +35 -56
- package/templates/clients/axios.ts +0 -73
- package/templates/clients/fetch.ts +0 -96
- package/templates/config.ts +0 -43
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ 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
7
|
import { functionPrinter, pluginTsName } from "@kubb/plugin-ts";
|
|
8
|
-
import { Const, File, Function,
|
|
8
|
+
import { Const, File, Function, jsxRendererSync } from "@kubb/renderer-jsx";
|
|
9
9
|
import { Fragment, jsx, jsxs } from "@kubb/renderer-jsx/jsx-runtime";
|
|
10
10
|
import { pluginZodName } from "@kubb/plugin-zod";
|
|
11
11
|
//#region ../../internals/utils/src/casing.ts
|
|
@@ -185,6 +185,26 @@ function isValidVarName(name) {
|
|
|
185
185
|
if (!name || reservedWords.has(name)) return false;
|
|
186
186
|
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
|
|
187
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* Returns `name` when it's a syntactically valid JavaScript variable name,
|
|
190
|
+
* otherwise prefixes it with `_` so the result is a valid identifier.
|
|
191
|
+
*
|
|
192
|
+
* Useful for sanitizing OpenAPI schema names or operation IDs that start with
|
|
193
|
+
* a digit (e.g. `409`, `504AccountCancel`) before using them as exported
|
|
194
|
+
* variable, type, or function names.
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* ```ts
|
|
198
|
+
* ensureValidVarName('409') // '_409'
|
|
199
|
+
* ensureValidVarName('504AccountCancel') // '_504AccountCancel'
|
|
200
|
+
* ensureValidVarName('Pet') // 'Pet'
|
|
201
|
+
* ensureValidVarName('class') // '_class'
|
|
202
|
+
* ```
|
|
203
|
+
*/
|
|
204
|
+
function ensureValidVarName(name) {
|
|
205
|
+
if (!name || isValidVarName(name)) return name;
|
|
206
|
+
return `_${name}`;
|
|
207
|
+
}
|
|
188
208
|
//#endregion
|
|
189
209
|
//#region ../../internals/utils/src/urlPath.ts
|
|
190
210
|
/**
|
|
@@ -251,16 +271,16 @@ var URLPath = class {
|
|
|
251
271
|
get object() {
|
|
252
272
|
return this.toObject();
|
|
253
273
|
}
|
|
254
|
-
/** Returns a map of path parameter names, or `
|
|
274
|
+
/** Returns a map of path parameter names, or `null` when the path has no parameters.
|
|
255
275
|
*
|
|
256
276
|
* @example
|
|
257
277
|
* ```ts
|
|
258
278
|
* new URLPath('/pet/{petId}').params // { petId: 'petId' }
|
|
259
|
-
* new URLPath('/pet').params //
|
|
279
|
+
* new URLPath('/pet').params // null
|
|
260
280
|
* ```
|
|
261
281
|
*/
|
|
262
282
|
get params() {
|
|
263
|
-
return this.
|
|
283
|
+
return this.toParamsObject();
|
|
264
284
|
}
|
|
265
285
|
#transformParam(raw) {
|
|
266
286
|
const param = isValidVarName(raw) ? raw : camelCase(raw);
|
|
@@ -278,7 +298,7 @@ var URLPath = class {
|
|
|
278
298
|
toObject({ type = "path", replacer, stringify } = {}) {
|
|
279
299
|
const object = {
|
|
280
300
|
url: type === "path" ? this.toURLPath() : this.toTemplateString({ replacer }),
|
|
281
|
-
params: this.
|
|
301
|
+
params: this.toParamsObject()
|
|
282
302
|
};
|
|
283
303
|
if (stringify) {
|
|
284
304
|
if (type === "template") return JSON.stringify(object).replaceAll("'", "").replaceAll(`"`, "");
|
|
@@ -294,12 +314,13 @@ var URLPath = class {
|
|
|
294
314
|
* @example
|
|
295
315
|
* new URLPath('/pet/{petId}').toTemplateString() // '`/pet/${petId}`'
|
|
296
316
|
*/
|
|
297
|
-
toTemplateString({ prefix
|
|
298
|
-
|
|
317
|
+
toTemplateString({ prefix, replacer } = {}) {
|
|
318
|
+
const result = this.path.split(/\{([^}]+)\}/).map((part, i) => {
|
|
299
319
|
if (i % 2 === 0) return part;
|
|
300
320
|
const param = this.#transformParam(part);
|
|
301
321
|
return `\${${replacer ? replacer(param) : param}}`;
|
|
302
|
-
}).join("")
|
|
322
|
+
}).join("");
|
|
323
|
+
return `\`${prefix ?? ""}${result}\``;
|
|
303
324
|
}
|
|
304
325
|
/**
|
|
305
326
|
* Extracts all `{param}` segments from the path and returns them as a key-value map.
|
|
@@ -308,17 +329,17 @@ var URLPath = class {
|
|
|
308
329
|
*
|
|
309
330
|
* @example
|
|
310
331
|
* ```ts
|
|
311
|
-
* new URLPath('/pet/{petId}/tag/{tagId}').
|
|
332
|
+
* new URLPath('/pet/{petId}/tag/{tagId}').toParamsObject()
|
|
312
333
|
* // { petId: 'petId', tagId: 'tagId' }
|
|
313
334
|
* ```
|
|
314
335
|
*/
|
|
315
|
-
|
|
336
|
+
toParamsObject(replacer) {
|
|
316
337
|
const params = {};
|
|
317
338
|
this.#eachParam((_raw, param) => {
|
|
318
339
|
const key = replacer ? replacer(param) : param;
|
|
319
340
|
params[key] = key;
|
|
320
341
|
});
|
|
321
|
-
return Object.keys(params).length > 0 ? params :
|
|
342
|
+
return Object.keys(params).length > 0 ? params : null;
|
|
322
343
|
}
|
|
323
344
|
/** Converts the OpenAPI path to Express-style colon syntax.
|
|
324
345
|
*
|
|
@@ -332,6 +353,198 @@ var URLPath = class {
|
|
|
332
353
|
}
|
|
333
354
|
};
|
|
334
355
|
//#endregion
|
|
356
|
+
//#region ../../internals/shared/src/operation.ts
|
|
357
|
+
/**
|
|
358
|
+
* Builds the `ResolverFileParams` every operation generator passes to
|
|
359
|
+
* `resolver.resolveFile`: a file named `name`, tagged by the operation's first
|
|
360
|
+
* tag (or `'default'`), at the operation's path. Centralizes the entry object
|
|
361
|
+
* that was repeated at dozens of call sites across the client and query plugins.
|
|
362
|
+
*
|
|
363
|
+
* @example
|
|
364
|
+
* ```ts
|
|
365
|
+
* resolver.resolveFile(operationFileEntry(node, node.operationId), { root, output, group })
|
|
366
|
+
* ```
|
|
367
|
+
*/
|
|
368
|
+
function operationFileEntry(node, name, extname = ".ts") {
|
|
369
|
+
return {
|
|
370
|
+
name,
|
|
371
|
+
extname,
|
|
372
|
+
tag: node.tags[0] ?? "default",
|
|
373
|
+
path: node.path
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
function getOperationLink(node, link) {
|
|
377
|
+
if (!link) return null;
|
|
378
|
+
if (typeof link === "function") return link(node) ?? null;
|
|
379
|
+
if (link === "urlPath") return node.path ? `{@link ${new URLPath(node.path).URL}}` : null;
|
|
380
|
+
return node.path ? `{@link ${node.path.replaceAll("{", ":").replaceAll("}", "")}}` : null;
|
|
381
|
+
}
|
|
382
|
+
function getContentTypeInfo(node) {
|
|
383
|
+
const contentTypes = node.requestBody?.content?.map((e) => e.contentType) ?? [];
|
|
384
|
+
const isMultipleContentTypes = contentTypes.length > 1;
|
|
385
|
+
return {
|
|
386
|
+
contentTypes,
|
|
387
|
+
isMultipleContentTypes,
|
|
388
|
+
contentTypeUnion: isMultipleContentTypes ? contentTypes.map((ct) => JSON.stringify(ct)).join(" | ") : "",
|
|
389
|
+
defaultContentType: contentTypes[0] ?? "application/json",
|
|
390
|
+
hasFormData: contentTypes.some((ct) => ct === "multipart/form-data")
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Derives the default `responseType` for an operation from its primary success response.
|
|
395
|
+
*
|
|
396
|
+
* Returns a value only when that response declares a single non-JSON content type — a binary type
|
|
397
|
+
* (`application/octet-stream`, `application/pdf`, `image/*`, `audio/*`, `video/*`) maps to `'blob'`
|
|
398
|
+
* and other `text/*` maps to `'text'`. Otherwise `undefined`, leaving the runtime client's
|
|
399
|
+
* `Content-Type` auto-detection in charge.
|
|
400
|
+
*/
|
|
401
|
+
function getResponseType(node) {
|
|
402
|
+
const contentTypes = getPrimarySuccessResponse(node)?.content?.map((entry) => entry.contentType) ?? [];
|
|
403
|
+
if (contentTypes.length !== 1) return void 0;
|
|
404
|
+
const baseType = contentTypes[0].split(";")[0].trim().toLowerCase();
|
|
405
|
+
if (baseType === "application/json" || baseType.endsWith("+json") || baseType === "text/json") return void 0;
|
|
406
|
+
if (baseType.startsWith("text/")) return "text";
|
|
407
|
+
if (baseType === "application/octet-stream" || baseType === "application/pdf" || /^(image|audio|video)\//.test(baseType)) return "blob";
|
|
408
|
+
}
|
|
409
|
+
function buildRequestConfigType(node, resolver) {
|
|
410
|
+
const requestName = node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : null;
|
|
411
|
+
const { isMultipleContentTypes, contentTypeUnion } = getContentTypeInfo(node);
|
|
412
|
+
return `${requestName ? `Partial<RequestConfig<${requestName}>>` : "Partial<RequestConfig>"} & { ${["client?: Client", isMultipleContentTypes ? `contentType?: ${contentTypeUnion}` : null].filter(Boolean).join("; ")} }`;
|
|
413
|
+
}
|
|
414
|
+
function buildOperationComments(node, options = {}) {
|
|
415
|
+
const { link = "pathTemplate", linkPosition = "afterDeprecated", splitLines = false } = options;
|
|
416
|
+
const linkComment = getOperationLink(node, link);
|
|
417
|
+
const filteredComments = (linkPosition === "beforeDeprecated" ? [
|
|
418
|
+
node.description && `@description ${node.description}`,
|
|
419
|
+
node.summary && `@summary ${node.summary}`,
|
|
420
|
+
linkComment,
|
|
421
|
+
node.deprecated && "@deprecated"
|
|
422
|
+
] : [
|
|
423
|
+
node.description && `@description ${node.description}`,
|
|
424
|
+
node.summary && `@summary ${node.summary}`,
|
|
425
|
+
node.deprecated && "@deprecated",
|
|
426
|
+
linkComment
|
|
427
|
+
]).filter((comment) => Boolean(comment));
|
|
428
|
+
if (!splitLines) return filteredComments;
|
|
429
|
+
return filteredComments.flatMap((text) => text.split(/\r?\n/).map((line) => line.trim())).filter((comment) => Boolean(comment));
|
|
430
|
+
}
|
|
431
|
+
function getOperationParameters(node, options = {}) {
|
|
432
|
+
const params = ast.caseParams(node.parameters, options.paramsCasing);
|
|
433
|
+
return {
|
|
434
|
+
path: params.filter((param) => param.in === "path"),
|
|
435
|
+
query: params.filter((param) => param.in === "query"),
|
|
436
|
+
header: params.filter((param) => param.in === "header"),
|
|
437
|
+
cookie: params.filter((param) => param.in === "cookie")
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
function getStatusCodeNumber(statusCode) {
|
|
441
|
+
const code = Number(statusCode);
|
|
442
|
+
return Number.isNaN(code) ? null : code;
|
|
443
|
+
}
|
|
444
|
+
function isSuccessStatusCode(statusCode) {
|
|
445
|
+
const code = getStatusCodeNumber(statusCode);
|
|
446
|
+
return code !== null && code >= 200 && code < 300;
|
|
447
|
+
}
|
|
448
|
+
function isErrorStatusCode(statusCode) {
|
|
449
|
+
const code = getStatusCodeNumber(statusCode);
|
|
450
|
+
return code !== null && code >= 400;
|
|
451
|
+
}
|
|
452
|
+
function getSuccessResponses(responses) {
|
|
453
|
+
return responses.filter((response) => isSuccessStatusCode(response.statusCode));
|
|
454
|
+
}
|
|
455
|
+
function getOperationSuccessResponses(node) {
|
|
456
|
+
return getSuccessResponses(node.responses);
|
|
457
|
+
}
|
|
458
|
+
function getPrimarySuccessResponse(node) {
|
|
459
|
+
return getOperationSuccessResponses(node)[0] ?? null;
|
|
460
|
+
}
|
|
461
|
+
function resolveErrorNames(node, resolver) {
|
|
462
|
+
return node.responses.filter((response) => isErrorStatusCode(response.statusCode)).map((response) => resolver.resolveResponseStatusName(node, response.statusCode));
|
|
463
|
+
}
|
|
464
|
+
function resolveSuccessNames(node, resolver) {
|
|
465
|
+
return node.responses.filter((response) => isSuccessStatusCode(response.statusCode)).map((response) => resolver.resolveResponseStatusName(node, response.statusCode));
|
|
466
|
+
}
|
|
467
|
+
function resolveStatusCodeNames(node, resolver) {
|
|
468
|
+
return node.responses.map((response) => resolver.resolveResponseStatusName(node, response.statusCode));
|
|
469
|
+
}
|
|
470
|
+
const typeNamesByResolver = /* @__PURE__ */ new WeakMap();
|
|
471
|
+
function resolveOperationTypeNames(node, resolver, options = {}) {
|
|
472
|
+
const cacheKey = `${node.operationId}\0${options.paramsCasing ?? ""}\0${options.order ?? ""}\0${options.responseStatusNames ?? ""}\0${(options.exclude ?? []).join(",")}`;
|
|
473
|
+
let byResolver = typeNamesByResolver.get(resolver);
|
|
474
|
+
if (byResolver) {
|
|
475
|
+
const cached = byResolver.get(cacheKey);
|
|
476
|
+
if (cached) return cached;
|
|
477
|
+
} else {
|
|
478
|
+
byResolver = /* @__PURE__ */ new Map();
|
|
479
|
+
typeNamesByResolver.set(resolver, byResolver);
|
|
480
|
+
}
|
|
481
|
+
const { path, query, header } = getOperationParameters(node, { paramsCasing: options.paramsCasing });
|
|
482
|
+
const responseStatusNames = options.responseStatusNames === "error" ? resolveErrorNames(node, resolver) : options.responseStatusNames === false ? [] : resolveStatusCodeNames(node, resolver);
|
|
483
|
+
const exclude = new Set(options.exclude ?? []);
|
|
484
|
+
const paramNames = [
|
|
485
|
+
...path.map((param) => resolver.resolvePathParamsName(node, param)),
|
|
486
|
+
...query.map((param) => resolver.resolveQueryParamsName(node, param)),
|
|
487
|
+
...header.map((param) => resolver.resolveHeaderParamsName(node, param))
|
|
488
|
+
];
|
|
489
|
+
const bodyAndResponseNames = [node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : null, resolver.resolveResponseName(node)];
|
|
490
|
+
const result = (options.order === "body-response-first" ? [
|
|
491
|
+
...bodyAndResponseNames,
|
|
492
|
+
...paramNames,
|
|
493
|
+
...responseStatusNames
|
|
494
|
+
] : [
|
|
495
|
+
...paramNames,
|
|
496
|
+
...bodyAndResponseNames,
|
|
497
|
+
...responseStatusNames
|
|
498
|
+
]).filter((name) => Boolean(name) && !exclude.has(name));
|
|
499
|
+
byResolver.set(cacheKey, result);
|
|
500
|
+
return result;
|
|
501
|
+
}
|
|
502
|
+
//#endregion
|
|
503
|
+
//#region ../../internals/shared/src/group.ts
|
|
504
|
+
/**
|
|
505
|
+
* Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
|
|
506
|
+
* shared default naming so every plugin groups output consistently:
|
|
507
|
+
*
|
|
508
|
+
* - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).
|
|
509
|
+
* - other groups use `${camelCase(group)}${suffix}` (e.g. `petController`).
|
|
510
|
+
*
|
|
511
|
+
* Returns `null` when grouping is disabled, matching the per-plugin convention.
|
|
512
|
+
*
|
|
513
|
+
* @param group - The user-supplied group option, or `undefined` to disable grouping.
|
|
514
|
+
* @param options.suffix - Appended to non-`path` group names, e.g. `'Controller'` or `'Requests'`.
|
|
515
|
+
* @param options.honorName - When `true`, a user-provided `group.name` overrides the default namer.
|
|
516
|
+
*
|
|
517
|
+
* @example
|
|
518
|
+
* ```ts
|
|
519
|
+
* createGroupConfig(group, { suffix: 'Controller' }) // plugin-ts, plugin-zod
|
|
520
|
+
* createGroupConfig(group, { suffix: 'Controller', honorName: true }) // plugin-faker, plugin-client, …
|
|
521
|
+
* createGroupConfig(group, { suffix: 'Requests', honorName: true }) // plugin-cypress, plugin-mcp
|
|
522
|
+
* ```
|
|
523
|
+
*/
|
|
524
|
+
function createGroupConfig(group, options) {
|
|
525
|
+
if (!group) return null;
|
|
526
|
+
const defaultName = (ctx) => {
|
|
527
|
+
if (group.type === "path") return `${ctx.group.split("/")[1]}`;
|
|
528
|
+
return `${camelCase(ctx.group)}${options.suffix}`;
|
|
529
|
+
};
|
|
530
|
+
return {
|
|
531
|
+
...group,
|
|
532
|
+
name: options.honorName && group.name ? group.name : defaultName
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
//#endregion
|
|
536
|
+
//#region ../../internals/shared/src/params.ts
|
|
537
|
+
function buildParamsMapping(originalParams, mappedParams) {
|
|
538
|
+
const mapping = {};
|
|
539
|
+
let hasChanged = false;
|
|
540
|
+
originalParams.forEach((param, i) => {
|
|
541
|
+
const mappedName = mappedParams[i]?.name ?? param.name;
|
|
542
|
+
mapping[param.name] = mappedName;
|
|
543
|
+
if (param.name !== mappedName) hasChanged = true;
|
|
544
|
+
});
|
|
545
|
+
return hasChanged ? mapping : null;
|
|
546
|
+
}
|
|
547
|
+
//#endregion
|
|
335
548
|
//#region src/functionParams.ts
|
|
336
549
|
const declarationPrinter$4 = functionPrinter({ mode: "declaration" });
|
|
337
550
|
const callPrinter = functionPrinter({ mode: "call" });
|
|
@@ -342,18 +555,18 @@ function createType(type) {
|
|
|
342
555
|
return type ? ast.createParamsType({
|
|
343
556
|
variant: "reference",
|
|
344
557
|
name: type
|
|
345
|
-
}) :
|
|
558
|
+
}) : null;
|
|
346
559
|
}
|
|
347
560
|
function createDeclarationLeaf(name, spec) {
|
|
348
561
|
if (spec.default !== void 0) return ast.createFunctionParameter({
|
|
349
562
|
name,
|
|
350
|
-
type: createType(spec.type),
|
|
563
|
+
type: createType(spec.type) ?? void 0,
|
|
351
564
|
default: spec.default,
|
|
352
565
|
rest: spec.mode === "inlineSpread"
|
|
353
566
|
});
|
|
354
567
|
return ast.createFunctionParameter({
|
|
355
568
|
name,
|
|
356
|
-
type: createType(spec.type),
|
|
569
|
+
type: createType(spec.type) ?? void 0,
|
|
357
570
|
optional: !!spec.optional,
|
|
358
571
|
rest: spec.mode === "inlineSpread"
|
|
359
572
|
});
|
|
@@ -362,14 +575,14 @@ function createDeclarationParam(name, spec) {
|
|
|
362
575
|
if (isGroup(spec)) return ast.createParameterGroup({
|
|
363
576
|
inline: spec.mode === "inlineSpread",
|
|
364
577
|
default: spec.default,
|
|
365
|
-
properties: Object.entries(spec.children).filter(([, child]) => child
|
|
578
|
+
properties: Object.entries(spec.children).filter(([, child]) => child != null).map(([childName, child]) => createDeclarationLeaf(childName, child))
|
|
366
579
|
});
|
|
367
580
|
return createDeclarationLeaf(name, spec);
|
|
368
581
|
}
|
|
369
582
|
function createCallParam(name, spec) {
|
|
370
583
|
if (isGroup(spec)) return ast.createParameterGroup({
|
|
371
584
|
inline: spec.mode === "inlineSpread",
|
|
372
|
-
properties: Object.entries(spec.children).filter(([, child]) => child
|
|
585
|
+
properties: Object.entries(spec.children).filter(([, child]) => child != null).map(([childName, child]) => ast.createFunctionParameter({
|
|
373
586
|
name: child?.mode === "inlineSpread" ? spec.mode === "inlineSpread" ? child.value ?? childName : `...${child.value ?? childName}` : child?.value ? `${childName}: ${child.value}` : childName,
|
|
374
587
|
rest: spec.mode === "inlineSpread" && child?.mode === "inlineSpread"
|
|
375
588
|
}))
|
|
@@ -384,7 +597,7 @@ function createCallParam(name, spec) {
|
|
|
384
597
|
* Returns utilities to output constructor signatures (`toConstructor()`) or call expressions (`toCall()`).
|
|
385
598
|
*/
|
|
386
599
|
function createFunctionParams(params) {
|
|
387
|
-
const entries = Object.entries(params).filter(([, spec]) => spec
|
|
600
|
+
const entries = Object.entries(params).filter(([, spec]) => spec != null);
|
|
388
601
|
return {
|
|
389
602
|
toConstructor() {
|
|
390
603
|
return declarationPrinter$4.print(ast.createFunctionParameters({ params: entries.map(([name, spec]) => createDeclarationParam(name, spec)) })) ?? "";
|
|
@@ -395,106 +608,9 @@ function createFunctionParams(params) {
|
|
|
395
608
|
};
|
|
396
609
|
}
|
|
397
610
|
//#endregion
|
|
398
|
-
//#region src/utils.ts
|
|
399
|
-
/**
|
|
400
|
-
* Extracts documentation comments from an operation node.
|
|
401
|
-
* Includes description, summary, link, and deprecation information.
|
|
402
|
-
*/
|
|
403
|
-
function getComments(node) {
|
|
404
|
-
return [
|
|
405
|
-
node.description && `@description ${node.description}`,
|
|
406
|
-
node.summary && `@summary ${node.summary}`,
|
|
407
|
-
node.path && `{@link ${new URLPath(node.path).URL}}`,
|
|
408
|
-
node.deprecated && "@deprecated"
|
|
409
|
-
].filter((x) => Boolean(x)).flatMap((text) => text.split(/\r?\n/).map((line) => line.trim())).filter((x) => Boolean(x));
|
|
410
|
-
}
|
|
411
|
-
/**
|
|
412
|
-
* Builds a mapping of original parameter names to their transformed (cased) names.
|
|
413
|
-
* Returns undefined if no names have changed.
|
|
414
|
-
*/
|
|
415
|
-
function buildParamsMapping(originalParams, casedParams) {
|
|
416
|
-
const mapping = {};
|
|
417
|
-
let hasChanged = false;
|
|
418
|
-
originalParams.forEach((param, i) => {
|
|
419
|
-
const casedName = casedParams[i]?.name ?? param.name;
|
|
420
|
-
mapping[param.name] = casedName;
|
|
421
|
-
if (param.name !== casedName) hasChanged = true;
|
|
422
|
-
});
|
|
423
|
-
return hasChanged ? mapping : void 0;
|
|
424
|
-
}
|
|
425
|
-
/**
|
|
426
|
-
* Builds HTTP headers array for a client request.
|
|
427
|
-
* Includes Content-Type (if not default) and spreads header parameters if present.
|
|
428
|
-
*/
|
|
429
|
-
function buildHeaders(contentType, hasHeaderParams) {
|
|
430
|
-
return [contentType !== "application/json" && contentType !== "multipart/form-data" ? `'Content-Type': '${contentType}'` : void 0, hasHeaderParams ? "...headers" : void 0].filter(Boolean);
|
|
431
|
-
}
|
|
432
|
-
/**
|
|
433
|
-
* Builds TypeScript generic parameters for a client method.
|
|
434
|
-
* Includes response type, error type, and optional request type.
|
|
435
|
-
*/
|
|
436
|
-
function buildGenerics(node, tsResolver) {
|
|
437
|
-
const responseName = tsResolver.resolveResponseName(node);
|
|
438
|
-
const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : void 0;
|
|
439
|
-
const errorNames = node.responses.filter((r) => Number.parseInt(r.statusCode, 10) >= 400).map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode));
|
|
440
|
-
return [
|
|
441
|
-
responseName,
|
|
442
|
-
`ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`,
|
|
443
|
-
requestName || "unknown"
|
|
444
|
-
].filter(Boolean);
|
|
445
|
-
}
|
|
446
|
-
/**
|
|
447
|
-
* Builds the parameters object for a class-based client method.
|
|
448
|
-
* Includes URL, method, base URL, headers, and request/response data.
|
|
449
|
-
*/
|
|
450
|
-
function buildClassClientParams({ node, path, baseURL, tsResolver, isFormData, headers }) {
|
|
451
|
-
const queryParamsName = node.parameters.filter((p) => p.in === "query").length > 0 ? tsResolver.resolveQueryParamsName(node, node.parameters.filter((p) => p.in === "query")[0]) : void 0;
|
|
452
|
-
const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : void 0;
|
|
453
|
-
return createFunctionParams({ config: {
|
|
454
|
-
mode: "object",
|
|
455
|
-
children: {
|
|
456
|
-
requestConfig: { mode: "inlineSpread" },
|
|
457
|
-
method: { value: JSON.stringify(node.method.toUpperCase()) },
|
|
458
|
-
url: { value: path.template },
|
|
459
|
-
baseURL: baseURL ? { value: JSON.stringify(baseURL) } : void 0,
|
|
460
|
-
params: queryParamsName ? {} : void 0,
|
|
461
|
-
data: requestName ? { value: isFormData ? "formData as FormData" : "requestData" } : void 0,
|
|
462
|
-
headers: headers.length ? { value: `{ ${headers.join(", ")}, ...requestConfig.headers }` } : void 0
|
|
463
|
-
}
|
|
464
|
-
} });
|
|
465
|
-
}
|
|
466
|
-
/**
|
|
467
|
-
* Builds the request data parsing line for client methods.
|
|
468
|
-
* Applies Zod validation if configured, otherwise uses data directly.
|
|
469
|
-
*/
|
|
470
|
-
function buildRequestDataLine({ parser, node, zodResolver }) {
|
|
471
|
-
const zodRequestName = zodResolver && parser === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : void 0;
|
|
472
|
-
if (parser === "zod" && zodRequestName) return `const requestData = ${zodRequestName}.parse(data)`;
|
|
473
|
-
if (node.requestBody?.content?.[0]?.schema) return "const requestData = data";
|
|
474
|
-
return "";
|
|
475
|
-
}
|
|
476
|
-
/**
|
|
477
|
-
* Builds the form data conversion line for file upload requests.
|
|
478
|
-
* Returns empty string if not applicable.
|
|
479
|
-
*/
|
|
480
|
-
function buildFormDataLine(isFormData, hasRequest) {
|
|
481
|
-
return isFormData && hasRequest ? "const formData = buildFormData(requestData)" : "";
|
|
482
|
-
}
|
|
483
|
-
/**
|
|
484
|
-
* Builds the return statement for a client method.
|
|
485
|
-
* Applies Zod validation to response data if configured, otherwise returns raw response.
|
|
486
|
-
*/
|
|
487
|
-
function buildReturnStatement({ dataReturnType, parser, node, zodResolver }) {
|
|
488
|
-
const zodResponseName = zodResolver && parser === "zod" ? zodResolver.resolveResponseName?.(node) : void 0;
|
|
489
|
-
if (dataReturnType === "full" && parser === "zod" && zodResponseName) return `return {...res, data: ${zodResponseName}.parse(res.data)}`;
|
|
490
|
-
if (dataReturnType === "data" && parser === "zod" && zodResponseName) return `return ${zodResponseName}.parse(res.data)`;
|
|
491
|
-
if (dataReturnType === "full" && parser === "client") return "return res";
|
|
492
|
-
return "return res.data";
|
|
493
|
-
}
|
|
494
|
-
//#endregion
|
|
495
611
|
//#region src/components/Url.tsx
|
|
496
612
|
const declarationPrinter$3 = functionPrinter({ mode: "declaration" });
|
|
497
|
-
function
|
|
613
|
+
function buildUrlParamsNode({ paramsType, paramsCasing, pathParamsType, node, tsResolver }) {
|
|
498
614
|
const urlNode = {
|
|
499
615
|
...node,
|
|
500
616
|
parameters: node.parameters.filter((p) => p.in === "path"),
|
|
@@ -507,10 +623,10 @@ function getParams$1({ paramsType, paramsCasing, pathParamsType, node, tsResolve
|
|
|
507
623
|
resolver: tsResolver
|
|
508
624
|
});
|
|
509
625
|
}
|
|
510
|
-
__name(getParams$1, "getParams");
|
|
511
626
|
function Url({ name, isExportable = true, isIndexable = true, baseURL, paramsType, paramsCasing, pathParamsType, node, tsResolver }) {
|
|
627
|
+
if (!ast.isHttpOperationNode(node)) return null;
|
|
512
628
|
const path = new URLPath(node.path);
|
|
513
|
-
const paramsNode =
|
|
629
|
+
const paramsNode = buildUrlParamsNode({
|
|
514
630
|
paramsType,
|
|
515
631
|
paramsCasing,
|
|
516
632
|
pathParamsType,
|
|
@@ -518,9 +634,9 @@ function Url({ name, isExportable = true, isIndexable = true, baseURL, paramsTyp
|
|
|
518
634
|
tsResolver
|
|
519
635
|
});
|
|
520
636
|
const paramsSignature = declarationPrinter$3.print(paramsNode) ?? "";
|
|
521
|
-
const originalPathParams = node
|
|
522
|
-
const casedPathParams =
|
|
523
|
-
const pathParamsMapping = paramsCasing ? buildParamsMapping(originalPathParams, casedPathParams) :
|
|
637
|
+
const { path: originalPathParams } = getOperationParameters(node);
|
|
638
|
+
const { path: casedPathParams } = getOperationParameters(node, { paramsCasing });
|
|
639
|
+
const pathParamsMapping = paramsCasing ? buildParamsMapping(originalPathParams, casedPathParams) : null;
|
|
524
640
|
return /* @__PURE__ */ jsx(File.Source, {
|
|
525
641
|
name,
|
|
526
642
|
isExportable,
|
|
@@ -542,56 +658,53 @@ function Url({ name, isExportable = true, isIndexable = true, baseURL, paramsTyp
|
|
|
542
658
|
})
|
|
543
659
|
});
|
|
544
660
|
}
|
|
545
|
-
Url.getParams = getParams$1;
|
|
546
661
|
//#endregion
|
|
547
662
|
//#region src/components/Client.tsx
|
|
548
663
|
const declarationPrinter$2 = functionPrinter({ mode: "declaration" });
|
|
549
|
-
function
|
|
550
|
-
const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : void 0;
|
|
664
|
+
function buildClientParamsNode({ paramsType, paramsCasing, pathParamsType, node, tsResolver, isConfigurable }) {
|
|
551
665
|
return ast.createOperationParams(node, {
|
|
552
666
|
paramsType,
|
|
553
667
|
pathParamsType: paramsType === "object" ? "object" : pathParamsType === "object" ? "object" : "inline",
|
|
554
668
|
paramsCasing,
|
|
555
669
|
resolver: tsResolver,
|
|
556
|
-
extraParams: isConfigurable ? [ast.createFunctionParameter({
|
|
670
|
+
extraParams: [...isConfigurable ? [ast.createFunctionParameter({
|
|
557
671
|
name: "config",
|
|
558
672
|
type: ast.createParamsType({
|
|
559
673
|
variant: "reference",
|
|
560
|
-
name:
|
|
674
|
+
name: buildRequestConfigType(node, tsResolver)
|
|
561
675
|
}),
|
|
562
676
|
default: "{}"
|
|
563
|
-
})] : []
|
|
677
|
+
})] : []]
|
|
564
678
|
});
|
|
565
679
|
}
|
|
566
680
|
function Client({ name, isExportable = true, isIndexable = true, returnType, baseURL, dataReturnType, parser, paramsType, paramsCasing, pathParamsType, node, tsResolver, zodResolver, urlName, children, isConfigurable = true }) {
|
|
681
|
+
if (!ast.isHttpOperationNode(node)) return null;
|
|
567
682
|
const path = new URLPath(node.path);
|
|
568
|
-
const contentType = node
|
|
569
|
-
const isFormData = contentType === "multipart/form-data";
|
|
570
|
-
const
|
|
571
|
-
const
|
|
572
|
-
const
|
|
573
|
-
const
|
|
574
|
-
const
|
|
575
|
-
const
|
|
576
|
-
const
|
|
577
|
-
const
|
|
578
|
-
const
|
|
579
|
-
const
|
|
580
|
-
const
|
|
581
|
-
const
|
|
582
|
-
const
|
|
583
|
-
const zodResponseName = zodResolver && parser === "zod" ? zodResolver.resolveResponseName?.(node) : void 0;
|
|
584
|
-
const zodRequestName = zodResolver && parser === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : void 0;
|
|
683
|
+
const { defaultContentType: contentType, isMultipleContentTypes, hasFormData } = getContentTypeInfo(node);
|
|
684
|
+
const isFormData = !isMultipleContentTypes && contentType === "multipart/form-data";
|
|
685
|
+
const responseType = getResponseType(node);
|
|
686
|
+
const { path: originalPathParams, query: originalQueryParams, header: originalHeaderParams } = getOperationParameters(node);
|
|
687
|
+
const { path: casedPathParams, query: casedQueryParams, header: casedHeaderParams } = getOperationParameters(node, { paramsCasing });
|
|
688
|
+
const pathParamsMapping = paramsCasing && !urlName ? buildParamsMapping(originalPathParams, casedPathParams) : null;
|
|
689
|
+
const queryParamsMapping = paramsCasing ? buildParamsMapping(originalQueryParams, casedQueryParams) : null;
|
|
690
|
+
const headerParamsMapping = paramsCasing ? buildParamsMapping(originalHeaderParams, casedHeaderParams) : null;
|
|
691
|
+
const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : null;
|
|
692
|
+
const successNames = resolveSuccessNames(node, tsResolver);
|
|
693
|
+
const responseName = successNames.length > 0 ? successNames.join(" | ") : tsResolver.resolveResponseName(node);
|
|
694
|
+
const queryParamsName = originalQueryParams.length > 0 ? tsResolver.resolveQueryParamsName(node, originalQueryParams[0]) : null;
|
|
695
|
+
const headerParamsName = originalHeaderParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, originalHeaderParams[0]) : null;
|
|
696
|
+
const zodResponseName = zodResolver && parser === "zod" ? zodResolver.resolveResponseName?.(node) : null;
|
|
697
|
+
const zodRequestName = zodResolver && parser === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null;
|
|
585
698
|
const errorNames = node.responses.filter((r) => {
|
|
586
699
|
return Number.parseInt(r.statusCode, 10) >= 400;
|
|
587
700
|
}).map((r) => tsResolver.resolveResponseStatusName(node, r.statusCode));
|
|
588
|
-
const headers = [contentType !== "application/json" && contentType !== "multipart/form-data" ? `'Content-Type': '${contentType}'` :
|
|
701
|
+
const headers = [!isMultipleContentTypes && contentType !== "application/json" && contentType !== "multipart/form-data" ? `'Content-Type': '${contentType}'` : null, headerParamsName ? headerParamsMapping ? "...mappedHeaders" : "...headers" : null].filter(Boolean);
|
|
589
702
|
const generics = [
|
|
590
703
|
responseName,
|
|
591
704
|
`ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`,
|
|
592
705
|
requestName || "unknown"
|
|
593
706
|
].filter(Boolean);
|
|
594
|
-
const paramsNode =
|
|
707
|
+
const paramsNode = buildClientParamsNode({
|
|
595
708
|
paramsType,
|
|
596
709
|
paramsCasing,
|
|
597
710
|
pathParamsType,
|
|
@@ -600,7 +713,7 @@ function Client({ name, isExportable = true, isIndexable = true, returnType, bas
|
|
|
600
713
|
isConfigurable
|
|
601
714
|
});
|
|
602
715
|
const paramsSignature = declarationPrinter$2.print(paramsNode) ?? "";
|
|
603
|
-
const urlParamsNode =
|
|
716
|
+
const urlParamsNode = buildUrlParamsNode({
|
|
604
717
|
paramsType,
|
|
605
718
|
paramsCasing,
|
|
606
719
|
pathParamsType,
|
|
@@ -613,18 +726,20 @@ function Client({ name, isExportable = true, isIndexable = true, returnType, bas
|
|
|
613
726
|
children: {
|
|
614
727
|
method: { value: JSON.stringify(node.method.toUpperCase()) },
|
|
615
728
|
url: { value: urlName ? `${urlName}(${urlParamsCall}).url.toString()` : path.template },
|
|
616
|
-
baseURL: baseURL && !urlName ? { value: `\`${baseURL}\`` } :
|
|
617
|
-
params: queryParamsName ? queryParamsMapping ? { value: "mappedParams" } : {} :
|
|
618
|
-
data: requestName ? { value: isFormData ? "formData as FormData" : "requestData" } :
|
|
619
|
-
|
|
620
|
-
|
|
729
|
+
baseURL: baseURL && !urlName ? { value: `\`${baseURL}\`` } : null,
|
|
730
|
+
params: queryParamsName ? queryParamsMapping ? { value: "mappedParams" } : {} : null,
|
|
731
|
+
data: requestName ? { value: isMultipleContentTypes && hasFormData ? "contentType === 'multipart/form-data' ? formData as FormData : requestData" : isFormData ? "formData as FormData" : "requestData" } : null,
|
|
732
|
+
contentType: isConfigurable && isMultipleContentTypes ? {} : null,
|
|
733
|
+
responseType: responseType ? { value: JSON.stringify(responseType) } : null,
|
|
734
|
+
requestConfig: isConfigurable ? { mode: "inlineSpread" } : null,
|
|
735
|
+
headers: headers.length ? { value: isConfigurable ? `{ ${headers.join(", ")}, ...requestConfig.headers }` : `{ ${headers.join(", ")} }` } : null
|
|
621
736
|
}
|
|
622
737
|
} });
|
|
623
738
|
const childrenElement = children ? children : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
624
739
|
dataReturnType === "full" && parser === "zod" && zodResponseName && `return {...res, data: ${zodResponseName}.parse(res.data)}`,
|
|
625
740
|
dataReturnType === "data" && parser === "zod" && zodResponseName && `return ${zodResponseName}.parse(res.data)`,
|
|
626
|
-
dataReturnType === "full" && parser
|
|
627
|
-
dataReturnType === "data" && parser
|
|
741
|
+
dataReturnType === "full" && parser !== "zod" && "return res",
|
|
742
|
+
dataReturnType === "data" && parser !== "zod" && "return res.data"
|
|
628
743
|
] });
|
|
629
744
|
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("br", {}), /* @__PURE__ */ jsx(File.Source, {
|
|
630
745
|
name,
|
|
@@ -635,10 +750,14 @@ function Client({ name, isExportable = true, isIndexable = true, returnType, bas
|
|
|
635
750
|
async: true,
|
|
636
751
|
export: isExportable,
|
|
637
752
|
params: paramsSignature,
|
|
638
|
-
JSDoc: { comments:
|
|
753
|
+
JSDoc: { comments: buildOperationComments(node, {
|
|
754
|
+
link: "urlPath",
|
|
755
|
+
linkPosition: "beforeDeprecated",
|
|
756
|
+
splitLines: true
|
|
757
|
+
}) },
|
|
639
758
|
returnType,
|
|
640
759
|
children: [
|
|
641
|
-
isConfigurable ?
|
|
760
|
+
isConfigurable ? `const { client: request = client, ${isMultipleContentTypes ? `contentType = ${JSON.stringify(contentType)}, ` : ""}...requestConfig } = config` : "",
|
|
642
761
|
/* @__PURE__ */ jsx("br", {}),
|
|
643
762
|
/* @__PURE__ */ jsx("br", {}),
|
|
644
763
|
pathParamsMapping && Object.entries(pathParamsMapping).filter(([originalName, camelCaseName]) => isValidVarName(originalName) && originalName !== camelCaseName).map(([originalName, camelCaseName]) => `const ${originalName} = ${camelCaseName}`).join("\n"),
|
|
@@ -655,26 +774,104 @@ function Client({ name, isExportable = true, isIndexable = true, returnType, bas
|
|
|
655
774
|
] }),
|
|
656
775
|
parser === "zod" && zodRequestName ? `const requestData = ${zodRequestName}.parse(data)` : requestName && "const requestData = data",
|
|
657
776
|
/* @__PURE__ */ jsx("br", {}),
|
|
658
|
-
isFormData && requestName && "const formData = buildFormData(requestData)",
|
|
777
|
+
(isFormData || isMultipleContentTypes && hasFormData) && requestName && "const formData = buildFormData(requestData)",
|
|
659
778
|
/* @__PURE__ */ jsx("br", {}),
|
|
660
|
-
isConfigurable ? `const res = await request<${generics.join(", ")}>(${clientParams.toCall()})` : `const res = await
|
|
779
|
+
isConfigurable ? `const res = await request<${generics.join(", ")}>(${clientParams.toCall()})` : `const res = await client<${generics.join(", ")}>(${clientParams.toCall()})`,
|
|
661
780
|
/* @__PURE__ */ jsx("br", {}),
|
|
662
781
|
childrenElement
|
|
663
782
|
]
|
|
664
783
|
})
|
|
665
784
|
})] });
|
|
666
785
|
}
|
|
667
|
-
|
|
786
|
+
//#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
|
+
}
|
|
668
862
|
//#endregion
|
|
669
863
|
//#region src/components/ClassClient.tsx
|
|
670
864
|
const declarationPrinter$1 = functionPrinter({ mode: "declaration" });
|
|
671
865
|
function generateMethod$1({ node, name, tsResolver, zodResolver, baseURL, dataReturnType, parser, paramsType, paramsCasing, pathParamsType }) {
|
|
866
|
+
if (!ast.isHttpOperationNode(node)) return "";
|
|
672
867
|
const path = new URLPath(node.path, { casing: paramsCasing });
|
|
673
|
-
const contentType = node
|
|
674
|
-
const isFormData = contentType === "multipart/form-data";
|
|
675
|
-
const
|
|
868
|
+
const { defaultContentType: contentType, isMultipleContentTypes, hasFormData } = getContentTypeInfo(node);
|
|
869
|
+
const isFormData = !isMultipleContentTypes && contentType === "multipart/form-data";
|
|
870
|
+
const { header: headerParams } = getOperationParameters(node);
|
|
871
|
+
const headerParamsName = headerParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, headerParams[0]) : null;
|
|
872
|
+
const headers = isMultipleContentTypes ? headerParamsName ? ["...headers"] : [] : buildHeaders(contentType, !!headerParamsName);
|
|
676
873
|
const generics = buildGenerics(node, tsResolver);
|
|
677
|
-
const paramsNode =
|
|
874
|
+
const paramsNode = buildClientParamsNode({
|
|
678
875
|
paramsType,
|
|
679
876
|
paramsCasing,
|
|
680
877
|
pathParamsType,
|
|
@@ -689,15 +886,21 @@ function generateMethod$1({ node, name, tsResolver, zodResolver, baseURL, dataRe
|
|
|
689
886
|
baseURL,
|
|
690
887
|
tsResolver,
|
|
691
888
|
isFormData,
|
|
889
|
+
isMultipleContentTypes,
|
|
890
|
+
hasFormData,
|
|
692
891
|
headers
|
|
693
892
|
});
|
|
694
|
-
const jsdoc = buildJSDoc(
|
|
893
|
+
const jsdoc = buildJSDoc(buildOperationComments(node, {
|
|
894
|
+
link: "urlPath",
|
|
895
|
+
linkPosition: "beforeDeprecated",
|
|
896
|
+
splitLines: true
|
|
897
|
+
}));
|
|
695
898
|
const requestDataLine = buildRequestDataLine({
|
|
696
899
|
parser,
|
|
697
900
|
node,
|
|
698
901
|
zodResolver
|
|
699
902
|
});
|
|
700
|
-
const formDataLine = buildFormDataLine(isFormData, !!node.requestBody?.content?.[0]?.schema);
|
|
903
|
+
const formDataLine = buildFormDataLine(isFormData || isMultipleContentTypes && hasFormData, !!node.requestBody?.content?.[0]?.schema);
|
|
701
904
|
const returnStatement = buildReturnStatement({
|
|
702
905
|
dataReturnType,
|
|
703
906
|
parser,
|
|
@@ -705,7 +908,7 @@ function generateMethod$1({ node, name, tsResolver, zodResolver, baseURL, dataRe
|
|
|
705
908
|
zodResolver
|
|
706
909
|
});
|
|
707
910
|
return `${jsdoc}async ${name}(${paramsSignature}) {\n${[
|
|
708
|
-
|
|
911
|
+
`const { client: request = client, ${isMultipleContentTypes ? `contentType = ${JSON.stringify(contentType)}, ` : ""}...requestConfig } = mergeConfig(this.#config, config)`,
|
|
709
912
|
"",
|
|
710
913
|
requestDataLine,
|
|
711
914
|
formDataLine,
|
|
@@ -742,15 +945,14 @@ ${operations.map(({ node, name: methodName, tsResolver, zodResolver }) => genera
|
|
|
742
945
|
children: [classCode, children]
|
|
743
946
|
});
|
|
744
947
|
}
|
|
745
|
-
ClassClient.getParams = Client.getParams;
|
|
746
948
|
//#endregion
|
|
747
949
|
//#region src/components/WrapperClient.tsx
|
|
748
|
-
function WrapperClient({ name,
|
|
950
|
+
function WrapperClient({ name, controllers, isExportable = true, isIndexable = true }) {
|
|
749
951
|
const classCode = `export class ${name} {
|
|
750
|
-
${
|
|
952
|
+
${controllers.map(({ className, propertyName }) => ` readonly ${propertyName}: ${className}`).join("\n")}
|
|
751
953
|
|
|
752
954
|
constructor(config: Partial<RequestConfig> & { client?: Client } = {}) {
|
|
753
|
-
${
|
|
955
|
+
${controllers.map(({ className, propertyName }) => ` this.${propertyName} = new ${className}(config)`).join("\n")}
|
|
754
956
|
}
|
|
755
957
|
}`;
|
|
756
958
|
return /* @__PURE__ */ jsx(File.Source, {
|
|
@@ -763,54 +965,42 @@ ${classNames.map((className) => ` this.${camelCase(className)} = new ${classN
|
|
|
763
965
|
//#endregion
|
|
764
966
|
//#region src/generators/classClientGenerator.tsx
|
|
765
967
|
function resolveTypeImportNames$1(node, tsResolver) {
|
|
766
|
-
return
|
|
767
|
-
node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : void 0,
|
|
768
|
-
tsResolver.resolveResponseName(node),
|
|
769
|
-
...node.parameters.filter((p) => p.in === "path").map((p) => tsResolver.resolvePathParamsName(node, p)),
|
|
770
|
-
...node.parameters.filter((p) => p.in === "query").map((p) => tsResolver.resolveQueryParamsName(node, p)),
|
|
771
|
-
...node.parameters.filter((p) => p.in === "header").map((p) => tsResolver.resolveHeaderParamsName(node, p)),
|
|
772
|
-
...node.responses.map((res) => tsResolver.resolveResponseStatusName(node, res.statusCode))
|
|
773
|
-
].filter((n) => Boolean(n));
|
|
968
|
+
return resolveOperationTypeNames(node, tsResolver, { order: "body-response-first" });
|
|
774
969
|
}
|
|
775
970
|
__name(resolveTypeImportNames$1, "resolveTypeImportNames");
|
|
776
971
|
function resolveZodImportNames$1(node, zodResolver) {
|
|
777
|
-
return [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) :
|
|
972
|
+
return [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null].filter((n) => Boolean(n));
|
|
778
973
|
}
|
|
779
974
|
__name(resolveZodImportNames$1, "resolveZodImportNames");
|
|
975
|
+
/**
|
|
976
|
+
* Built-in `operations` generator for `@kubb/plugin-client` when
|
|
977
|
+
* `clientType: 'class'`. Emits one class per tag, with one instance method
|
|
978
|
+
* per operation and a shared constructor for request configuration.
|
|
979
|
+
*/
|
|
780
980
|
const classClientGenerator = defineGenerator({
|
|
781
981
|
name: "classClient",
|
|
782
|
-
renderer:
|
|
982
|
+
renderer: jsxRendererSync,
|
|
783
983
|
operations(nodes, ctx) {
|
|
784
|
-
const {
|
|
984
|
+
const { config, driver, resolver, root } = ctx;
|
|
785
985
|
const { output, group, dataReturnType, paramsCasing, paramsType, pathParamsType, parser, importPath, sdk } = ctx.options;
|
|
786
|
-
const baseURL = ctx.options.baseURL ??
|
|
986
|
+
const baseURL = ctx.options.baseURL ?? ctx.meta.baseURL;
|
|
787
987
|
const pluginTs = driver.getPlugin(pluginTsName);
|
|
788
988
|
if (!pluginTs) return null;
|
|
789
989
|
const tsResolver = driver.getResolver(pluginTsName);
|
|
790
990
|
const tsPluginOptions = pluginTs.options;
|
|
791
|
-
const pluginZod = parser === "zod" ? driver.getPlugin(pluginZodName) :
|
|
792
|
-
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) :
|
|
991
|
+
const pluginZod = parser === "zod" ? driver.getPlugin(pluginZodName) : null;
|
|
992
|
+
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null;
|
|
793
993
|
function buildOperationData(node) {
|
|
794
|
-
const typeFile = tsResolver.resolveFile({
|
|
795
|
-
name: node.operationId,
|
|
796
|
-
extname: ".ts",
|
|
797
|
-
tag: node.tags[0] ?? "default",
|
|
798
|
-
path: node.path
|
|
799
|
-
}, {
|
|
994
|
+
const typeFile = tsResolver.resolveFile(operationFileEntry(node, node.operationId), {
|
|
800
995
|
root,
|
|
801
996
|
output: tsPluginOptions?.output ?? output,
|
|
802
997
|
group: tsPluginOptions?.group
|
|
803
998
|
});
|
|
804
|
-
const zodFile = zodResolver && pluginZod?.options ? zodResolver.resolveFile({
|
|
805
|
-
name: node.operationId,
|
|
806
|
-
extname: ".ts",
|
|
807
|
-
tag: node.tags[0] ?? "default",
|
|
808
|
-
path: node.path
|
|
809
|
-
}, {
|
|
999
|
+
const zodFile = zodResolver && pluginZod?.options ? zodResolver.resolveFile(operationFileEntry(node, node.operationId), {
|
|
810
1000
|
root,
|
|
811
1001
|
output: pluginZod.options?.output ?? output,
|
|
812
|
-
group: pluginZod.options?.group
|
|
813
|
-
}) :
|
|
1002
|
+
group: pluginZod.options?.group ?? void 0
|
|
1003
|
+
}) : null;
|
|
814
1004
|
return {
|
|
815
1005
|
node,
|
|
816
1006
|
name: resolver.resolveName(node.operationId),
|
|
@@ -821,27 +1011,31 @@ const classClientGenerator = defineGenerator({
|
|
|
821
1011
|
};
|
|
822
1012
|
}
|
|
823
1013
|
const controllers = nodes.reduce((acc, operationNode) => {
|
|
1014
|
+
if (!ast.isHttpOperationNode(operationNode)) return acc;
|
|
824
1015
|
const tag = operationNode.tags[0];
|
|
825
|
-
const groupName = tag ? group?.name?.({ group: camelCase(tag) }) ??
|
|
1016
|
+
const groupName = tag ? group?.name?.({ group: camelCase(tag) }) ?? resolver.resolveGroupName(tag) : resolver.resolveGroupName("Client");
|
|
826
1017
|
if (!tag && !group) {
|
|
827
|
-
const name = "ApiClient";
|
|
1018
|
+
const name = resolver.resolveClassName("ApiClient");
|
|
828
1019
|
const file = resolver.resolveFile({
|
|
829
1020
|
name,
|
|
830
1021
|
extname: ".ts"
|
|
831
1022
|
}, {
|
|
832
1023
|
root,
|
|
833
1024
|
output,
|
|
834
|
-
group
|
|
1025
|
+
group: group ?? void 0
|
|
835
1026
|
});
|
|
836
1027
|
const operationData = buildOperationData(operationNode);
|
|
837
1028
|
const previous = acc.find((item) => item.file.path === file.path);
|
|
838
1029
|
if (previous) previous.operations.push(operationData);
|
|
839
1030
|
else acc.push({
|
|
840
1031
|
name,
|
|
1032
|
+
propertyName: resolver.resolveClientPropertyName(name),
|
|
841
1033
|
file,
|
|
842
1034
|
operations: [operationData]
|
|
843
1035
|
});
|
|
844
|
-
|
|
1036
|
+
return acc;
|
|
1037
|
+
}
|
|
1038
|
+
if (tag) {
|
|
845
1039
|
const name = groupName;
|
|
846
1040
|
const file = resolver.resolveFile({
|
|
847
1041
|
name,
|
|
@@ -850,13 +1044,14 @@ const classClientGenerator = defineGenerator({
|
|
|
850
1044
|
}, {
|
|
851
1045
|
root,
|
|
852
1046
|
output,
|
|
853
|
-
group
|
|
1047
|
+
group: group ?? void 0
|
|
854
1048
|
});
|
|
855
1049
|
const operationData = buildOperationData(operationNode);
|
|
856
1050
|
const previous = acc.find((item) => item.file.path === file.path);
|
|
857
1051
|
if (previous) previous.operations.push(operationData);
|
|
858
1052
|
else acc.push({
|
|
859
1053
|
name,
|
|
1054
|
+
propertyName: resolver.resolveClientPropertyName(name),
|
|
860
1055
|
file,
|
|
861
1056
|
operations: [operationData]
|
|
862
1057
|
});
|
|
@@ -904,23 +1099,31 @@ const classClientGenerator = defineGenerator({
|
|
|
904
1099
|
zodImportsByFile: /* @__PURE__ */ new Map(),
|
|
905
1100
|
zodFilesByPath: /* @__PURE__ */ new Map()
|
|
906
1101
|
};
|
|
907
|
-
const hasFormData = ops.some((op) => op.node.requestBody?.content?.
|
|
1102
|
+
const hasFormData = ops.some((op) => op.node.requestBody?.content?.some((e) => e.contentType === "multipart/form-data") ?? false);
|
|
908
1103
|
return /* @__PURE__ */ jsxs(File, {
|
|
909
1104
|
baseName: file.baseName,
|
|
910
1105
|
path: file.path,
|
|
911
1106
|
meta: file.meta,
|
|
912
|
-
banner: resolver.resolveBanner(
|
|
1107
|
+
banner: resolver.resolveBanner(ctx.meta, {
|
|
913
1108
|
output,
|
|
914
|
-
config
|
|
1109
|
+
config,
|
|
1110
|
+
file: {
|
|
1111
|
+
path: file.path,
|
|
1112
|
+
baseName: file.baseName
|
|
1113
|
+
}
|
|
915
1114
|
}),
|
|
916
|
-
footer: resolver.resolveFooter(
|
|
1115
|
+
footer: resolver.resolveFooter(ctx.meta, {
|
|
917
1116
|
output,
|
|
918
|
-
config
|
|
1117
|
+
config,
|
|
1118
|
+
file: {
|
|
1119
|
+
path: file.path,
|
|
1120
|
+
baseName: file.baseName
|
|
1121
|
+
}
|
|
919
1122
|
}),
|
|
920
1123
|
children: [
|
|
921
1124
|
importPath ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
922
1125
|
/* @__PURE__ */ jsx(File.Import, {
|
|
923
|
-
name: "
|
|
1126
|
+
name: "client",
|
|
924
1127
|
path: importPath
|
|
925
1128
|
}),
|
|
926
1129
|
/* @__PURE__ */ jsx(File.Import, {
|
|
@@ -938,7 +1141,7 @@ const classClientGenerator = defineGenerator({
|
|
|
938
1141
|
})
|
|
939
1142
|
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
940
1143
|
/* @__PURE__ */ jsx(File.Import, {
|
|
941
|
-
name: ["
|
|
1144
|
+
name: ["client"],
|
|
942
1145
|
root: file.path,
|
|
943
1146
|
path: path.resolve(root, ".kubb/client.ts")
|
|
944
1147
|
}),
|
|
@@ -1006,19 +1209,27 @@ const classClientGenerator = defineGenerator({
|
|
|
1006
1209
|
}, {
|
|
1007
1210
|
root,
|
|
1008
1211
|
output,
|
|
1009
|
-
group
|
|
1212
|
+
group: group ?? void 0
|
|
1010
1213
|
});
|
|
1011
1214
|
files.push(/* @__PURE__ */ jsxs(File, {
|
|
1012
1215
|
baseName: sdkFile.baseName,
|
|
1013
1216
|
path: sdkFile.path,
|
|
1014
1217
|
meta: sdkFile.meta,
|
|
1015
|
-
banner: resolver.resolveBanner(
|
|
1218
|
+
banner: resolver.resolveBanner(ctx.meta, {
|
|
1016
1219
|
output,
|
|
1017
|
-
config
|
|
1220
|
+
config,
|
|
1221
|
+
file: {
|
|
1222
|
+
path: sdkFile.path,
|
|
1223
|
+
baseName: sdkFile.baseName
|
|
1224
|
+
}
|
|
1018
1225
|
}),
|
|
1019
|
-
footer: resolver.resolveFooter(
|
|
1226
|
+
footer: resolver.resolveFooter(ctx.meta, {
|
|
1020
1227
|
output,
|
|
1021
|
-
config
|
|
1228
|
+
config,
|
|
1229
|
+
file: {
|
|
1230
|
+
path: sdkFile.path,
|
|
1231
|
+
baseName: sdkFile.baseName
|
|
1232
|
+
}
|
|
1022
1233
|
}),
|
|
1023
1234
|
children: [
|
|
1024
1235
|
importPath ? /* @__PURE__ */ jsx(File.Import, {
|
|
@@ -1038,7 +1249,10 @@ const classClientGenerator = defineGenerator({
|
|
|
1038
1249
|
}, name)),
|
|
1039
1250
|
/* @__PURE__ */ jsx(WrapperClient, {
|
|
1040
1251
|
name: sdk.className,
|
|
1041
|
-
|
|
1252
|
+
controllers: controllers.map(({ name, propertyName }) => ({
|
|
1253
|
+
className: name,
|
|
1254
|
+
propertyName
|
|
1255
|
+
}))
|
|
1042
1256
|
})
|
|
1043
1257
|
]
|
|
1044
1258
|
}, sdkFile.path));
|
|
@@ -1048,81 +1262,69 @@ const classClientGenerator = defineGenerator({
|
|
|
1048
1262
|
});
|
|
1049
1263
|
//#endregion
|
|
1050
1264
|
//#region src/generators/clientGenerator.tsx
|
|
1265
|
+
/**
|
|
1266
|
+
* Built-in operation generator for `@kubb/plugin-client`. Emits one async
|
|
1267
|
+
* function per OpenAPI operation, plus the matching URL helper. Used when
|
|
1268
|
+
* `clientType: 'function'` (the default).
|
|
1269
|
+
*/
|
|
1051
1270
|
const clientGenerator = defineGenerator({
|
|
1052
1271
|
name: "client",
|
|
1053
|
-
renderer:
|
|
1272
|
+
renderer: jsxRendererSync,
|
|
1054
1273
|
operation(node, ctx) {
|
|
1055
|
-
|
|
1274
|
+
if (!ast.isHttpOperationNode(node)) return null;
|
|
1275
|
+
const { config, driver, resolver, root } = ctx;
|
|
1056
1276
|
const { output, urlType, dataReturnType, paramsCasing, paramsType, pathParamsType, parser, importPath, group } = ctx.options;
|
|
1057
|
-
const baseURL = ctx.options.baseURL ??
|
|
1277
|
+
const baseURL = ctx.options.baseURL ?? ctx.meta.baseURL;
|
|
1058
1278
|
const pluginTs = driver.getPlugin(pluginTsName);
|
|
1059
1279
|
if (!pluginTs) return null;
|
|
1060
1280
|
const tsResolver = driver.getResolver(pluginTsName);
|
|
1061
|
-
const pluginZod = parser === "zod" ? driver.getPlugin(pluginZodName) :
|
|
1062
|
-
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) :
|
|
1063
|
-
const
|
|
1064
|
-
const
|
|
1065
|
-
const queryParams = casedParams.filter((p) => p.in === "query");
|
|
1066
|
-
const headerParams = casedParams.filter((p) => p.in === "header");
|
|
1067
|
-
const importedTypeNames = [
|
|
1068
|
-
...pathParams.map((p) => tsResolver.resolvePathParamsName(node, p)),
|
|
1069
|
-
...queryParams.map((p) => tsResolver.resolveQueryParamsName(node, p)),
|
|
1070
|
-
...headerParams.map((p) => tsResolver.resolveHeaderParamsName(node, p)),
|
|
1071
|
-
node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : void 0,
|
|
1072
|
-
tsResolver.resolveResponseName(node),
|
|
1073
|
-
...node.responses.map((res) => tsResolver.resolveResponseStatusName(node, res.statusCode))
|
|
1074
|
-
].filter(Boolean);
|
|
1075
|
-
const importedZodNames = zodResolver && parser === "zod" ? [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : void 0].filter(Boolean) : [];
|
|
1281
|
+
const pluginZod = parser === "zod" ? driver.getPlugin(pluginZodName) : null;
|
|
1282
|
+
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null;
|
|
1283
|
+
const importedTypeNames = resolveOperationTypeNames(node, tsResolver, { paramsCasing });
|
|
1284
|
+
const importedZodNames = zodResolver && parser === "zod" ? [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null].filter((name) => Boolean(name)) : [];
|
|
1076
1285
|
const meta = {
|
|
1077
1286
|
name: resolver.resolveName(node.operationId),
|
|
1078
|
-
urlName:
|
|
1079
|
-
file: resolver.resolveFile({
|
|
1080
|
-
name: node.operationId,
|
|
1081
|
-
extname: ".ts",
|
|
1082
|
-
tag: node.tags[0] ?? "default",
|
|
1083
|
-
path: node.path
|
|
1084
|
-
}, {
|
|
1287
|
+
urlName: resolver.resolveUrlName(node),
|
|
1288
|
+
file: resolver.resolveFile(operationFileEntry(node, node.operationId), {
|
|
1085
1289
|
root,
|
|
1086
1290
|
output,
|
|
1087
|
-
group
|
|
1291
|
+
group: group ?? void 0
|
|
1088
1292
|
}),
|
|
1089
|
-
fileTs: tsResolver.resolveFile({
|
|
1090
|
-
name: node.operationId,
|
|
1091
|
-
extname: ".ts",
|
|
1092
|
-
tag: node.tags[0] ?? "default",
|
|
1093
|
-
path: node.path
|
|
1094
|
-
}, {
|
|
1293
|
+
fileTs: tsResolver.resolveFile(operationFileEntry(node, node.operationId), {
|
|
1095
1294
|
root,
|
|
1096
1295
|
output: pluginTs.options?.output ?? output,
|
|
1097
|
-
group: pluginTs.options?.group
|
|
1296
|
+
group: pluginTs.options?.group ?? void 0
|
|
1098
1297
|
}),
|
|
1099
|
-
fileZod: zodResolver && pluginZod?.options ? zodResolver.resolveFile({
|
|
1100
|
-
name: node.operationId,
|
|
1101
|
-
extname: ".ts",
|
|
1102
|
-
tag: node.tags[0] ?? "default",
|
|
1103
|
-
path: node.path
|
|
1104
|
-
}, {
|
|
1298
|
+
fileZod: zodResolver && pluginZod?.options ? zodResolver.resolveFile(operationFileEntry(node, node.operationId), {
|
|
1105
1299
|
root,
|
|
1106
1300
|
output: pluginZod.options.output ?? output,
|
|
1107
|
-
group: pluginZod.options?.group
|
|
1108
|
-
}) :
|
|
1301
|
+
group: pluginZod.options?.group ?? void 0
|
|
1302
|
+
}) : null
|
|
1109
1303
|
};
|
|
1110
|
-
const
|
|
1304
|
+
const hasFormData = node.requestBody?.content?.some((e) => e.contentType === "multipart/form-data") ?? false;
|
|
1111
1305
|
return /* @__PURE__ */ jsxs(File, {
|
|
1112
1306
|
baseName: meta.file.baseName,
|
|
1113
1307
|
path: meta.file.path,
|
|
1114
1308
|
meta: meta.file.meta,
|
|
1115
|
-
banner: resolver.resolveBanner(
|
|
1309
|
+
banner: resolver.resolveBanner(ctx.meta, {
|
|
1116
1310
|
output,
|
|
1117
|
-
config
|
|
1311
|
+
config,
|
|
1312
|
+
file: {
|
|
1313
|
+
path: meta.file.path,
|
|
1314
|
+
baseName: meta.file.baseName
|
|
1315
|
+
}
|
|
1118
1316
|
}),
|
|
1119
|
-
footer: resolver.resolveFooter(
|
|
1317
|
+
footer: resolver.resolveFooter(ctx.meta, {
|
|
1120
1318
|
output,
|
|
1121
|
-
config
|
|
1319
|
+
config,
|
|
1320
|
+
file: {
|
|
1321
|
+
path: meta.file.path,
|
|
1322
|
+
baseName: meta.file.baseName
|
|
1323
|
+
}
|
|
1122
1324
|
}),
|
|
1123
1325
|
children: [
|
|
1124
1326
|
importPath ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(File.Import, {
|
|
1125
|
-
name: "
|
|
1327
|
+
name: "client",
|
|
1126
1328
|
path: importPath
|
|
1127
1329
|
}), /* @__PURE__ */ jsx(File.Import, {
|
|
1128
1330
|
name: [
|
|
@@ -1133,7 +1335,7 @@ const clientGenerator = defineGenerator({
|
|
|
1133
1335
|
path: importPath,
|
|
1134
1336
|
isTypeOnly: true
|
|
1135
1337
|
})] }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(File.Import, {
|
|
1136
|
-
name: ["
|
|
1338
|
+
name: ["client"],
|
|
1137
1339
|
root: meta.file.path,
|
|
1138
1340
|
path: path.resolve(root, ".kubb/client.ts")
|
|
1139
1341
|
}), /* @__PURE__ */ jsx(File.Import, {
|
|
@@ -1146,7 +1348,7 @@ const clientGenerator = defineGenerator({
|
|
|
1146
1348
|
path: path.resolve(root, ".kubb/client.ts"),
|
|
1147
1349
|
isTypeOnly: true
|
|
1148
1350
|
})] }),
|
|
1149
|
-
|
|
1351
|
+
hasFormData && /* @__PURE__ */ jsx(File.Import, {
|
|
1150
1352
|
name: ["buildFormData"],
|
|
1151
1353
|
root: meta.file.path,
|
|
1152
1354
|
path: path.resolve(root, ".kubb/config.ts")
|
|
@@ -1192,16 +1394,22 @@ const clientGenerator = defineGenerator({
|
|
|
1192
1394
|
});
|
|
1193
1395
|
//#endregion
|
|
1194
1396
|
//#region src/generators/groupedClientGenerator.tsx
|
|
1397
|
+
/**
|
|
1398
|
+
* Emits one aggregate file per tag/group when `group` is configured. Each
|
|
1399
|
+
* file re-exports every client function for that group, so callers can
|
|
1400
|
+
* `import { petController } from './gen/clients'` instead of importing
|
|
1401
|
+
* each operation individually.
|
|
1402
|
+
*/
|
|
1195
1403
|
const groupedClientGenerator = defineGenerator({
|
|
1196
1404
|
name: "groupedClient",
|
|
1197
|
-
renderer:
|
|
1405
|
+
renderer: jsxRendererSync,
|
|
1198
1406
|
operations(nodes, ctx) {
|
|
1199
|
-
const { config, resolver,
|
|
1407
|
+
const { config, resolver, root } = ctx;
|
|
1200
1408
|
const { output, group } = ctx.options;
|
|
1201
1409
|
return /* @__PURE__ */ jsx(Fragment, { children: nodes.reduce((acc, operationNode) => {
|
|
1202
1410
|
if (group?.type === "tag") {
|
|
1203
1411
|
const tag = operationNode.tags[0];
|
|
1204
|
-
const name = tag ? group?.name?.({ group: camelCase(tag) }) :
|
|
1412
|
+
const name = tag ? group?.name?.({ group: camelCase(tag) }) : null;
|
|
1205
1413
|
if (!tag || !name) return acc;
|
|
1206
1414
|
const file = resolver.resolveFile({
|
|
1207
1415
|
name,
|
|
@@ -1210,7 +1418,7 @@ const groupedClientGenerator = defineGenerator({
|
|
|
1210
1418
|
}, {
|
|
1211
1419
|
root,
|
|
1212
1420
|
output,
|
|
1213
|
-
group
|
|
1421
|
+
group: group ?? void 0
|
|
1214
1422
|
});
|
|
1215
1423
|
const clientFile = resolver.resolveFile({
|
|
1216
1424
|
name: operationNode.operationId,
|
|
@@ -1220,7 +1428,7 @@ const groupedClientGenerator = defineGenerator({
|
|
|
1220
1428
|
}, {
|
|
1221
1429
|
root,
|
|
1222
1430
|
output,
|
|
1223
|
-
group
|
|
1431
|
+
group: group ?? void 0
|
|
1224
1432
|
});
|
|
1225
1433
|
const client = {
|
|
1226
1434
|
name: resolver.resolveName(operationNode.operationId),
|
|
@@ -1240,13 +1448,23 @@ const groupedClientGenerator = defineGenerator({
|
|
|
1240
1448
|
baseName: file.baseName,
|
|
1241
1449
|
path: file.path,
|
|
1242
1450
|
meta: file.meta,
|
|
1243
|
-
banner: resolver.resolveBanner(
|
|
1451
|
+
banner: resolver.resolveBanner(ctx.meta, {
|
|
1244
1452
|
output,
|
|
1245
|
-
config
|
|
1453
|
+
config,
|
|
1454
|
+
file: {
|
|
1455
|
+
path: file.path,
|
|
1456
|
+
baseName: file.baseName,
|
|
1457
|
+
isAggregation: true
|
|
1458
|
+
}
|
|
1246
1459
|
}),
|
|
1247
|
-
footer: resolver.resolveFooter(
|
|
1460
|
+
footer: resolver.resolveFooter(ctx.meta, {
|
|
1248
1461
|
output,
|
|
1249
|
-
config
|
|
1462
|
+
config,
|
|
1463
|
+
file: {
|
|
1464
|
+
path: file.path,
|
|
1465
|
+
baseName: file.baseName,
|
|
1466
|
+
isAggregation: true
|
|
1467
|
+
}
|
|
1250
1468
|
}),
|
|
1251
1469
|
children: [clients.map((client) => /* @__PURE__ */ jsx(File.Import, {
|
|
1252
1470
|
name: [client.name],
|
|
@@ -1271,6 +1489,7 @@ const groupedClientGenerator = defineGenerator({
|
|
|
1271
1489
|
function Operations({ name, nodes }) {
|
|
1272
1490
|
const operationsObject = {};
|
|
1273
1491
|
nodes.forEach((node) => {
|
|
1492
|
+
if (!ast.isHttpOperationNode(node)) return;
|
|
1274
1493
|
operationsObject[node.operationId] = {
|
|
1275
1494
|
path: new URLPath(node.path).URL,
|
|
1276
1495
|
method: node.method.toLowerCase()
|
|
@@ -1289,11 +1508,17 @@ function Operations({ name, nodes }) {
|
|
|
1289
1508
|
}
|
|
1290
1509
|
//#endregion
|
|
1291
1510
|
//#region src/generators/operationsGenerator.tsx
|
|
1511
|
+
/**
|
|
1512
|
+
* Generates an `operations.ts` file that re-exports every operation grouped
|
|
1513
|
+
* by HTTP method. Enabled when `pluginClient({ operations: true })`. Useful
|
|
1514
|
+
* for building meta-tooling on top of the generated client (route
|
|
1515
|
+
* registries, API explorers).
|
|
1516
|
+
*/
|
|
1292
1517
|
const operationsGenerator = defineGenerator({
|
|
1293
1518
|
name: "client",
|
|
1294
|
-
renderer:
|
|
1519
|
+
renderer: jsxRendererSync,
|
|
1295
1520
|
operations(nodes, ctx) {
|
|
1296
|
-
const { config, resolver,
|
|
1521
|
+
const { config, resolver, root } = ctx;
|
|
1297
1522
|
const { output, group } = ctx.options;
|
|
1298
1523
|
const name = "operations";
|
|
1299
1524
|
const file = resolver.resolveFile({
|
|
@@ -1302,23 +1527,31 @@ const operationsGenerator = defineGenerator({
|
|
|
1302
1527
|
}, {
|
|
1303
1528
|
root,
|
|
1304
1529
|
output,
|
|
1305
|
-
group
|
|
1530
|
+
group: group ?? void 0
|
|
1306
1531
|
});
|
|
1307
1532
|
return /* @__PURE__ */ jsx(File, {
|
|
1308
1533
|
baseName: file.baseName,
|
|
1309
1534
|
path: file.path,
|
|
1310
1535
|
meta: file.meta,
|
|
1311
|
-
banner: resolver.resolveBanner(
|
|
1536
|
+
banner: resolver.resolveBanner(ctx.meta, {
|
|
1312
1537
|
output,
|
|
1313
|
-
config
|
|
1538
|
+
config,
|
|
1539
|
+
file: {
|
|
1540
|
+
path: file.path,
|
|
1541
|
+
baseName: file.baseName
|
|
1542
|
+
}
|
|
1314
1543
|
}),
|
|
1315
|
-
footer: resolver.resolveFooter(
|
|
1544
|
+
footer: resolver.resolveFooter(ctx.meta, {
|
|
1316
1545
|
output,
|
|
1317
|
-
config
|
|
1546
|
+
config,
|
|
1547
|
+
file: {
|
|
1548
|
+
path: file.path,
|
|
1549
|
+
baseName: file.baseName
|
|
1550
|
+
}
|
|
1318
1551
|
}),
|
|
1319
1552
|
children: /* @__PURE__ */ jsx(Operations, {
|
|
1320
1553
|
name,
|
|
1321
|
-
nodes
|
|
1554
|
+
nodes: nodes.filter(ast.isHttpOperationNode)
|
|
1322
1555
|
})
|
|
1323
1556
|
});
|
|
1324
1557
|
}
|
|
@@ -1327,12 +1560,15 @@ const operationsGenerator = defineGenerator({
|
|
|
1327
1560
|
//#region src/components/StaticClassClient.tsx
|
|
1328
1561
|
const declarationPrinter = functionPrinter({ mode: "declaration" });
|
|
1329
1562
|
function generateMethod({ node, name, tsResolver, zodResolver, baseURL, dataReturnType, parser, paramsType, paramsCasing, pathParamsType }) {
|
|
1563
|
+
if (!ast.isHttpOperationNode(node)) return "";
|
|
1330
1564
|
const path = new URLPath(node.path, { casing: paramsCasing });
|
|
1331
|
-
const contentType = node
|
|
1332
|
-
const isFormData = contentType === "multipart/form-data";
|
|
1333
|
-
const
|
|
1565
|
+
const { defaultContentType: contentType, isMultipleContentTypes, hasFormData } = getContentTypeInfo(node);
|
|
1566
|
+
const isFormData = !isMultipleContentTypes && contentType === "multipart/form-data";
|
|
1567
|
+
const { header: headerParams } = getOperationParameters(node);
|
|
1568
|
+
const headerParamsName = headerParams.length > 0 ? tsResolver.resolveHeaderParamsName(node, headerParams[0]) : null;
|
|
1569
|
+
const headers = isMultipleContentTypes ? headerParamsName ? ["...headers"] : [] : buildHeaders(contentType, !!headerParamsName);
|
|
1334
1570
|
const generics = buildGenerics(node, tsResolver);
|
|
1335
|
-
const paramsNode =
|
|
1571
|
+
const paramsNode = buildClientParamsNode({
|
|
1336
1572
|
paramsType,
|
|
1337
1573
|
paramsCasing,
|
|
1338
1574
|
pathParamsType,
|
|
@@ -1347,15 +1583,21 @@ function generateMethod({ node, name, tsResolver, zodResolver, baseURL, dataRetu
|
|
|
1347
1583
|
baseURL,
|
|
1348
1584
|
tsResolver,
|
|
1349
1585
|
isFormData,
|
|
1586
|
+
isMultipleContentTypes,
|
|
1587
|
+
hasFormData,
|
|
1350
1588
|
headers
|
|
1351
1589
|
});
|
|
1352
|
-
const jsdoc = buildJSDoc(
|
|
1590
|
+
const jsdoc = buildJSDoc(buildOperationComments(node, {
|
|
1591
|
+
link: "urlPath",
|
|
1592
|
+
linkPosition: "beforeDeprecated",
|
|
1593
|
+
splitLines: true
|
|
1594
|
+
}));
|
|
1353
1595
|
const requestDataLine = buildRequestDataLine({
|
|
1354
1596
|
parser,
|
|
1355
1597
|
node,
|
|
1356
1598
|
zodResolver
|
|
1357
1599
|
});
|
|
1358
|
-
const formDataLine = buildFormDataLine(isFormData, !!node.requestBody?.content?.[0]?.schema);
|
|
1600
|
+
const formDataLine = buildFormDataLine(isFormData || isMultipleContentTypes && hasFormData, !!node.requestBody?.content?.[0]?.schema);
|
|
1359
1601
|
const returnStatement = buildReturnStatement({
|
|
1360
1602
|
dataReturnType,
|
|
1361
1603
|
parser,
|
|
@@ -1363,7 +1605,7 @@ function generateMethod({ node, name, tsResolver, zodResolver, baseURL, dataRetu
|
|
|
1363
1605
|
zodResolver
|
|
1364
1606
|
});
|
|
1365
1607
|
return `${jsdoc} static async ${name}(${paramsSignature}) {\n${[
|
|
1366
|
-
|
|
1608
|
+
`const { client: request = client, ${isMultipleContentTypes ? `contentType = ${JSON.stringify(contentType)}, ` : ""}...requestConfig } = mergeConfig(this.#config, config)`,
|
|
1367
1609
|
"",
|
|
1368
1610
|
requestDataLine,
|
|
1369
1611
|
formDataLine,
|
|
@@ -1391,56 +1633,44 @@ function StaticClassClient({ name, isExportable = true, isIndexable = true, oper
|
|
|
1391
1633
|
children: [classCode, children]
|
|
1392
1634
|
});
|
|
1393
1635
|
}
|
|
1394
|
-
StaticClassClient.getParams = Client.getParams;
|
|
1395
1636
|
//#endregion
|
|
1396
1637
|
//#region src/generators/staticClassClientGenerator.tsx
|
|
1397
1638
|
function resolveTypeImportNames(node, tsResolver) {
|
|
1398
|
-
return
|
|
1399
|
-
node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : void 0,
|
|
1400
|
-
tsResolver.resolveResponseName(node),
|
|
1401
|
-
...node.parameters.filter((p) => p.in === "path").map((p) => tsResolver.resolvePathParamsName(node, p)),
|
|
1402
|
-
...node.parameters.filter((p) => p.in === "query").map((p) => tsResolver.resolveQueryParamsName(node, p)),
|
|
1403
|
-
...node.parameters.filter((p) => p.in === "header").map((p) => tsResolver.resolveHeaderParamsName(node, p)),
|
|
1404
|
-
...node.responses.map((res) => tsResolver.resolveResponseStatusName(node, res.statusCode))
|
|
1405
|
-
].filter((n) => Boolean(n));
|
|
1639
|
+
return resolveOperationTypeNames(node, tsResolver, { order: "body-response-first" });
|
|
1406
1640
|
}
|
|
1407
1641
|
function resolveZodImportNames(node, zodResolver) {
|
|
1408
|
-
return [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) :
|
|
1642
|
+
return [zodResolver.resolveResponseName?.(node), node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null].filter((n) => Boolean(n));
|
|
1409
1643
|
}
|
|
1644
|
+
/**
|
|
1645
|
+
* Built-in `operations` generator for `@kubb/plugin-client` when
|
|
1646
|
+
* `clientType: 'staticClass'`. Emits one class per tag, with a static method
|
|
1647
|
+
* per operation so callers can use `Pet.getPetById(...)` without
|
|
1648
|
+
* instantiating the class.
|
|
1649
|
+
*/
|
|
1410
1650
|
const staticClassClientGenerator = defineGenerator({
|
|
1411
1651
|
name: "staticClassClient",
|
|
1412
|
-
renderer:
|
|
1652
|
+
renderer: jsxRendererSync,
|
|
1413
1653
|
operations(nodes, ctx) {
|
|
1414
|
-
const {
|
|
1654
|
+
const { config, driver, resolver, root } = ctx;
|
|
1415
1655
|
const { output, group, dataReturnType, paramsCasing, paramsType, pathParamsType, parser, importPath } = ctx.options;
|
|
1416
|
-
const baseURL = ctx.options.baseURL ??
|
|
1656
|
+
const baseURL = ctx.options.baseURL ?? ctx.meta.baseURL;
|
|
1417
1657
|
const pluginTs = driver.getPlugin(pluginTsName);
|
|
1418
1658
|
if (!pluginTs) return null;
|
|
1419
1659
|
const tsResolver = driver.getResolver(pluginTsName);
|
|
1420
1660
|
const tsPluginOptions = pluginTs.options;
|
|
1421
|
-
const pluginZod = parser === "zod" ? driver.getPlugin(pluginZodName) :
|
|
1422
|
-
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) :
|
|
1661
|
+
const pluginZod = parser === "zod" ? driver.getPlugin(pluginZodName) : null;
|
|
1662
|
+
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null;
|
|
1423
1663
|
function buildOperationData(node) {
|
|
1424
|
-
const typeFile = tsResolver.resolveFile({
|
|
1425
|
-
name: node.operationId,
|
|
1426
|
-
extname: ".ts",
|
|
1427
|
-
tag: node.tags[0] ?? "default",
|
|
1428
|
-
path: node.path
|
|
1429
|
-
}, {
|
|
1664
|
+
const typeFile = tsResolver.resolveFile(operationFileEntry(node, node.operationId), {
|
|
1430
1665
|
root,
|
|
1431
1666
|
output: tsPluginOptions?.output ?? output,
|
|
1432
1667
|
group: tsPluginOptions?.group
|
|
1433
1668
|
});
|
|
1434
|
-
const zodFile = zodResolver && pluginZod?.options ? zodResolver.resolveFile({
|
|
1435
|
-
name: node.operationId,
|
|
1436
|
-
extname: ".ts",
|
|
1437
|
-
tag: node.tags[0] ?? "default",
|
|
1438
|
-
path: node.path
|
|
1439
|
-
}, {
|
|
1669
|
+
const zodFile = zodResolver && pluginZod?.options ? zodResolver.resolveFile(operationFileEntry(node, node.operationId), {
|
|
1440
1670
|
root,
|
|
1441
1671
|
output: pluginZod.options?.output ?? output,
|
|
1442
|
-
group: pluginZod.options?.group
|
|
1443
|
-
}) :
|
|
1672
|
+
group: pluginZod.options?.group ?? void 0
|
|
1673
|
+
}) : null;
|
|
1444
1674
|
return {
|
|
1445
1675
|
node,
|
|
1446
1676
|
name: resolver.resolveName(node.operationId),
|
|
@@ -1451,17 +1681,18 @@ const staticClassClientGenerator = defineGenerator({
|
|
|
1451
1681
|
};
|
|
1452
1682
|
}
|
|
1453
1683
|
const controllers = nodes.reduce((acc, operationNode) => {
|
|
1684
|
+
if (!ast.isHttpOperationNode(operationNode)) return acc;
|
|
1454
1685
|
const tag = operationNode.tags[0];
|
|
1455
|
-
const groupName = tag ? group?.name?.({ group: camelCase(tag) }) ??
|
|
1686
|
+
const groupName = tag ? group?.name?.({ group: camelCase(tag) }) ?? resolver.resolveGroupName(tag) : resolver.resolveGroupName("Client");
|
|
1456
1687
|
if (!tag && !group) {
|
|
1457
|
-
const name = "ApiClient";
|
|
1688
|
+
const name = resolver.resolveClassName("ApiClient");
|
|
1458
1689
|
const file = resolver.resolveFile({
|
|
1459
1690
|
name,
|
|
1460
1691
|
extname: ".ts"
|
|
1461
1692
|
}, {
|
|
1462
1693
|
root,
|
|
1463
1694
|
output,
|
|
1464
|
-
group
|
|
1695
|
+
group: group ?? void 0
|
|
1465
1696
|
});
|
|
1466
1697
|
const operationData = buildOperationData(operationNode);
|
|
1467
1698
|
const previous = acc.find((item) => item.file.path === file.path);
|
|
@@ -1471,7 +1702,9 @@ const staticClassClientGenerator = defineGenerator({
|
|
|
1471
1702
|
file,
|
|
1472
1703
|
operations: [operationData]
|
|
1473
1704
|
});
|
|
1474
|
-
|
|
1705
|
+
return acc;
|
|
1706
|
+
}
|
|
1707
|
+
if (tag) {
|
|
1475
1708
|
const name = groupName;
|
|
1476
1709
|
const file = resolver.resolveFile({
|
|
1477
1710
|
name,
|
|
@@ -1480,7 +1713,7 @@ const staticClassClientGenerator = defineGenerator({
|
|
|
1480
1713
|
}, {
|
|
1481
1714
|
root,
|
|
1482
1715
|
output,
|
|
1483
|
-
group
|
|
1716
|
+
group: group ?? void 0
|
|
1484
1717
|
});
|
|
1485
1718
|
const operationData = buildOperationData(operationNode);
|
|
1486
1719
|
const previous = acc.find((item) => item.file.path === file.path);
|
|
@@ -1534,23 +1767,31 @@ const staticClassClientGenerator = defineGenerator({
|
|
|
1534
1767
|
zodImportsByFile: /* @__PURE__ */ new Map(),
|
|
1535
1768
|
zodFilesByPath: /* @__PURE__ */ new Map()
|
|
1536
1769
|
};
|
|
1537
|
-
const hasFormData = ops.some((op) => op.node.requestBody?.content?.
|
|
1770
|
+
const hasFormData = ops.some((op) => op.node.requestBody?.content?.some((e) => e.contentType === "multipart/form-data") ?? false);
|
|
1538
1771
|
return /* @__PURE__ */ jsxs(File, {
|
|
1539
1772
|
baseName: file.baseName,
|
|
1540
1773
|
path: file.path,
|
|
1541
1774
|
meta: file.meta,
|
|
1542
|
-
banner: resolver.resolveBanner(
|
|
1775
|
+
banner: resolver.resolveBanner(ctx.meta, {
|
|
1543
1776
|
output,
|
|
1544
|
-
config
|
|
1777
|
+
config,
|
|
1778
|
+
file: {
|
|
1779
|
+
path: file.path,
|
|
1780
|
+
baseName: file.baseName
|
|
1781
|
+
}
|
|
1545
1782
|
}),
|
|
1546
|
-
footer: resolver.resolveFooter(
|
|
1783
|
+
footer: resolver.resolveFooter(ctx.meta, {
|
|
1547
1784
|
output,
|
|
1548
|
-
config
|
|
1785
|
+
config,
|
|
1786
|
+
file: {
|
|
1787
|
+
path: file.path,
|
|
1788
|
+
baseName: file.baseName
|
|
1789
|
+
}
|
|
1549
1790
|
}),
|
|
1550
1791
|
children: [
|
|
1551
1792
|
importPath ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1552
1793
|
/* @__PURE__ */ jsx(File.Import, {
|
|
1553
|
-
name: "
|
|
1794
|
+
name: "client",
|
|
1554
1795
|
path: importPath
|
|
1555
1796
|
}),
|
|
1556
1797
|
/* @__PURE__ */ jsx(File.Import, {
|
|
@@ -1568,7 +1809,7 @@ const staticClassClientGenerator = defineGenerator({
|
|
|
1568
1809
|
})
|
|
1569
1810
|
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1570
1811
|
/* @__PURE__ */ jsx(File.Import, {
|
|
1571
|
-
name: ["
|
|
1812
|
+
name: ["client"],
|
|
1572
1813
|
root: file.path,
|
|
1573
1814
|
path: path.resolve(root, ".kubb/client.ts")
|
|
1574
1815
|
}),
|
|
@@ -1634,39 +1875,75 @@ const staticClassClientGenerator = defineGenerator({
|
|
|
1634
1875
|
//#endregion
|
|
1635
1876
|
//#region src/resolvers/resolverClient.ts
|
|
1636
1877
|
/**
|
|
1637
|
-
*
|
|
1878
|
+
* Default resolver used by `@kubb/plugin-client`. Decides the names and file
|
|
1879
|
+
* paths for every generated client function or class. Functions and files use
|
|
1880
|
+
* camelCase; classes and tag groups use PascalCase.
|
|
1638
1881
|
*
|
|
1639
|
-
*
|
|
1882
|
+
* @example Resolve client function and class names
|
|
1883
|
+
* ```ts
|
|
1884
|
+
* import { resolverClient } from '@kubb/plugin-client'
|
|
1640
1885
|
*
|
|
1641
|
-
*
|
|
1642
|
-
*
|
|
1886
|
+
* resolverClient.default('list pets', 'function') // 'listPets'
|
|
1887
|
+
* resolverClient.resolveClassName('pet') // 'Pet'
|
|
1888
|
+
* resolverClient.resolveUrlName(operationNode) // 'getShowPetByIdUrl'
|
|
1889
|
+
* ```
|
|
1643
1890
|
*/
|
|
1644
|
-
const resolverClient = defineResolver((
|
|
1891
|
+
const resolverClient = defineResolver(() => ({
|
|
1645
1892
|
name: "default",
|
|
1646
1893
|
pluginName: "plugin-client",
|
|
1647
1894
|
default(name, type) {
|
|
1648
|
-
|
|
1895
|
+
const resolved = camelCase(name, { isFile: type === "file" });
|
|
1896
|
+
return type === "file" ? resolved : ensureValidVarName(resolved);
|
|
1649
1897
|
},
|
|
1650
1898
|
resolveName(name) {
|
|
1651
|
-
return
|
|
1899
|
+
return this.default(name, "function");
|
|
1900
|
+
},
|
|
1901
|
+
resolvePathName(name, type) {
|
|
1902
|
+
return this.default(name, type);
|
|
1903
|
+
},
|
|
1904
|
+
resolveClassName(name) {
|
|
1905
|
+
return ensureValidVarName(pascalCase(name));
|
|
1906
|
+
},
|
|
1907
|
+
resolveGroupName(name) {
|
|
1908
|
+
return ensureValidVarName(pascalCase(name));
|
|
1909
|
+
},
|
|
1910
|
+
resolveClientPropertyName(name) {
|
|
1911
|
+
return ensureValidVarName(camelCase(name));
|
|
1912
|
+
},
|
|
1913
|
+
resolveUrlName(node) {
|
|
1914
|
+
const name = this.resolveName(node.operationId);
|
|
1915
|
+
return `get${name.charAt(0).toUpperCase()}${name.slice(1)}Url`;
|
|
1652
1916
|
}
|
|
1653
1917
|
}));
|
|
1654
1918
|
//#endregion
|
|
1655
1919
|
//#region src/plugin.ts
|
|
1656
1920
|
/**
|
|
1657
|
-
* Canonical plugin name for `@kubb/plugin-client
|
|
1921
|
+
* Canonical plugin name for `@kubb/plugin-client`. Used for driver lookups and
|
|
1922
|
+
* cross-plugin dependency references.
|
|
1658
1923
|
*/
|
|
1659
1924
|
const pluginClientName = "plugin-client";
|
|
1660
1925
|
/**
|
|
1661
|
-
* Generates
|
|
1662
|
-
*
|
|
1663
|
-
*
|
|
1926
|
+
* Generates one HTTP client function per OpenAPI operation. Each function has
|
|
1927
|
+
* typed path params, query params, body, and response, so callers use the API
|
|
1928
|
+
* like any other typed function. Ships with `axios` and `fetch` runtimes; bring
|
|
1929
|
+
* your own by setting `importPath`.
|
|
1664
1930
|
*
|
|
1665
|
-
* @example
|
|
1931
|
+
* @example
|
|
1666
1932
|
* ```ts
|
|
1667
|
-
* import
|
|
1933
|
+
* import { defineConfig } from 'kubb'
|
|
1934
|
+
* import { pluginTs } from '@kubb/plugin-ts'
|
|
1935
|
+
* import { pluginClient } from '@kubb/plugin-client'
|
|
1936
|
+
*
|
|
1668
1937
|
* export default defineConfig({
|
|
1669
|
-
*
|
|
1938
|
+
* input: { path: './petStore.yaml' },
|
|
1939
|
+
* output: { path: './src/gen' },
|
|
1940
|
+
* plugins: [
|
|
1941
|
+
* pluginTs(),
|
|
1942
|
+
* pluginClient({
|
|
1943
|
+
* output: { path: './clients' },
|
|
1944
|
+
* client: 'fetch',
|
|
1945
|
+
* }),
|
|
1946
|
+
* ],
|
|
1670
1947
|
* })
|
|
1671
1948
|
* ```
|
|
1672
1949
|
*/
|
|
@@ -1674,24 +1951,21 @@ const pluginClient = definePlugin((options) => {
|
|
|
1674
1951
|
const { output = {
|
|
1675
1952
|
path: "clients",
|
|
1676
1953
|
barrelType: "named"
|
|
1677
|
-
}, group, exclude = [], include, override = [], urlType = false, dataReturnType = "data", paramsType = "inline", pathParamsType = paramsType === "object" ? "object" : options.pathParamsType || "inline", operations = false, paramsCasing, clientType = options.sdk ? "class" : "function", parser =
|
|
1954
|
+
}, group, exclude = [], include, override = [], urlType = false, dataReturnType = "data", paramsType = "inline", pathParamsType = paramsType === "object" ? "object" : options.pathParamsType || "inline", operations = false, paramsCasing, clientType = options.sdk ? "class" : "function", parser = false, client = "axios", importPath, bundle = false, sdk, baseURL, resolver: userResolver, transformer: userTransformer } = options;
|
|
1678
1955
|
const resolvedImportPath = importPath ?? (!bundle ? `@kubb/plugin-client/clients/${client}` : void 0);
|
|
1679
1956
|
const selectedGenerators = options.generators ?? [
|
|
1680
1957
|
clientType === "staticClass" ? staticClassClientGenerator : clientType === "class" ? classClientGenerator : clientGenerator,
|
|
1681
|
-
group && clientType === "function" ? groupedClientGenerator :
|
|
1682
|
-
operations ? operationsGenerator :
|
|
1958
|
+
group && clientType === "function" ? groupedClientGenerator : null,
|
|
1959
|
+
operations ? operationsGenerator : null
|
|
1683
1960
|
].filter((x) => Boolean(x));
|
|
1684
|
-
const groupConfig = group
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
return `${camelCase(ctx.group)}Controller`;
|
|
1689
|
-
}
|
|
1690
|
-
} : void 0;
|
|
1961
|
+
const groupConfig = createGroupConfig(group, {
|
|
1962
|
+
suffix: "Controller",
|
|
1963
|
+
honorName: true
|
|
1964
|
+
});
|
|
1691
1965
|
return {
|
|
1692
1966
|
name: pluginClientName,
|
|
1693
1967
|
options,
|
|
1694
|
-
dependencies: [pluginTsName, parser === "zod" ? pluginZodName :
|
|
1968
|
+
dependencies: [pluginTsName, parser === "zod" ? pluginZodName : null].filter((dependency) => Boolean(dependency)),
|
|
1695
1969
|
hooks: { "kubb:plugin:setup"(ctx) {
|
|
1696
1970
|
const resolver = userResolver ? {
|
|
1697
1971
|
...resolverClient,
|