@kubb/plugin-swr 5.0.0-beta.98 → 5.0.0
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/index.cjs +158 -346
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +155 -343
- package/dist/index.js.map +1 -1
- package/package.json +5 -8
package/dist/index.cjs
CHANGED
|
@@ -23,318 +23,31 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
23
23
|
}
|
|
24
24
|
return to;
|
|
25
25
|
};
|
|
26
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
26
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
|
|
27
27
|
value: mod,
|
|
28
28
|
enumerable: true
|
|
29
29
|
}) : target, mod));
|
|
30
30
|
//#endregion
|
|
31
|
+
let kubb_kit = require("kubb/kit");
|
|
31
32
|
let node_path = require("node:path");
|
|
32
33
|
node_path = __toESM(node_path, 1);
|
|
33
|
-
let kubb_kit = require("kubb/kit");
|
|
34
34
|
let _kubb_plugin_ts = require("@kubb/plugin-ts");
|
|
35
35
|
let kubb_jsx = require("kubb/jsx");
|
|
36
36
|
let kubb_jsx_jsx_runtime = require("kubb/jsx/jsx-runtime");
|
|
37
|
-
//#region ../../internals/utils/src/casing.ts
|
|
38
|
-
/**
|
|
39
|
-
* Shared implementation for camelCase and PascalCase conversion.
|
|
40
|
-
* Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
|
|
41
|
-
* and capitalizes each word according to `pascal`.
|
|
42
|
-
*
|
|
43
|
-
* When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
|
|
44
|
-
*/
|
|
45
|
-
function toCamelOrPascal(text, pascal) {
|
|
46
|
-
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) => {
|
|
47
|
-
if (word.length > 1 && word === word.toUpperCase()) return word;
|
|
48
|
-
return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
|
|
49
|
-
}).join("").replace(/[^a-zA-Z0-9]/g, "");
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Converts `text` to camelCase.
|
|
53
|
-
*
|
|
54
|
-
* @example Word boundaries
|
|
55
|
-
* `camelCase('hello-world') // 'helloWorld'`
|
|
56
|
-
*
|
|
57
|
-
* @example With a prefix
|
|
58
|
-
* `camelCase('tag', { prefix: 'create' }) // 'createTag'`
|
|
59
|
-
*/
|
|
60
|
-
function camelCase(text, { prefix = "", suffix = "" } = {}) {
|
|
61
|
-
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Uppercases only the first character of `text`, leaving the rest untouched.
|
|
65
|
-
* Unlike {@link pascalCase} it never re-splits word boundaries or strips characters.
|
|
66
|
-
*
|
|
67
|
-
* @example
|
|
68
|
-
* `capitalize('getPetById') // 'GetPetById'`
|
|
69
|
-
*/
|
|
70
|
-
function capitalize(text) {
|
|
71
|
-
return `${text.charAt(0).toUpperCase()}${text.slice(1)}`;
|
|
72
|
-
}
|
|
73
|
-
//#endregion
|
|
74
|
-
//#region ../../internals/utils/src/reserved.ts
|
|
75
|
-
/**
|
|
76
|
-
* JavaScript and Java reserved words.
|
|
77
|
-
* @link https://github.com/jonschlinkert/reserved/blob/master/index.js
|
|
78
|
-
*/
|
|
79
|
-
const reservedWords = /* @__PURE__ */ new Set([
|
|
80
|
-
"abstract",
|
|
81
|
-
"arguments",
|
|
82
|
-
"boolean",
|
|
83
|
-
"break",
|
|
84
|
-
"byte",
|
|
85
|
-
"case",
|
|
86
|
-
"catch",
|
|
87
|
-
"char",
|
|
88
|
-
"class",
|
|
89
|
-
"const",
|
|
90
|
-
"continue",
|
|
91
|
-
"debugger",
|
|
92
|
-
"default",
|
|
93
|
-
"delete",
|
|
94
|
-
"do",
|
|
95
|
-
"double",
|
|
96
|
-
"else",
|
|
97
|
-
"enum",
|
|
98
|
-
"eval",
|
|
99
|
-
"export",
|
|
100
|
-
"extends",
|
|
101
|
-
"false",
|
|
102
|
-
"final",
|
|
103
|
-
"finally",
|
|
104
|
-
"float",
|
|
105
|
-
"for",
|
|
106
|
-
"function",
|
|
107
|
-
"goto",
|
|
108
|
-
"if",
|
|
109
|
-
"implements",
|
|
110
|
-
"import",
|
|
111
|
-
"in",
|
|
112
|
-
"instanceof",
|
|
113
|
-
"int",
|
|
114
|
-
"interface",
|
|
115
|
-
"let",
|
|
116
|
-
"long",
|
|
117
|
-
"native",
|
|
118
|
-
"new",
|
|
119
|
-
"null",
|
|
120
|
-
"package",
|
|
121
|
-
"private",
|
|
122
|
-
"protected",
|
|
123
|
-
"public",
|
|
124
|
-
"return",
|
|
125
|
-
"short",
|
|
126
|
-
"static",
|
|
127
|
-
"super",
|
|
128
|
-
"switch",
|
|
129
|
-
"synchronized",
|
|
130
|
-
"this",
|
|
131
|
-
"throw",
|
|
132
|
-
"throws",
|
|
133
|
-
"transient",
|
|
134
|
-
"true",
|
|
135
|
-
"try",
|
|
136
|
-
"typeof",
|
|
137
|
-
"var",
|
|
138
|
-
"void",
|
|
139
|
-
"volatile",
|
|
140
|
-
"while",
|
|
141
|
-
"with",
|
|
142
|
-
"yield",
|
|
143
|
-
"Array",
|
|
144
|
-
"Date",
|
|
145
|
-
"hasOwnProperty",
|
|
146
|
-
"Infinity",
|
|
147
|
-
"isFinite",
|
|
148
|
-
"isNaN",
|
|
149
|
-
"isPrototypeOf",
|
|
150
|
-
"length",
|
|
151
|
-
"Math",
|
|
152
|
-
"name",
|
|
153
|
-
"NaN",
|
|
154
|
-
"Number",
|
|
155
|
-
"Object",
|
|
156
|
-
"prototype",
|
|
157
|
-
"String",
|
|
158
|
-
"toString",
|
|
159
|
-
"undefined",
|
|
160
|
-
"valueOf"
|
|
161
|
-
]);
|
|
162
|
-
/**
|
|
163
|
-
* Returns `true` when `name` is a syntactically valid JavaScript variable name.
|
|
164
|
-
*
|
|
165
|
-
* @example
|
|
166
|
-
* ```ts
|
|
167
|
-
* isValidVarName('status') // true
|
|
168
|
-
* isValidVarName('class') // false (reserved word)
|
|
169
|
-
* isValidVarName('42foo') // false (starts with digit)
|
|
170
|
-
* ```
|
|
171
|
-
*/
|
|
172
|
-
function isValidVarName(name) {
|
|
173
|
-
if (!name || reservedWords.has(name)) return false;
|
|
174
|
-
return isIdentifier(name);
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* Returns `true` when `name` is syntactically a valid identifier, ignoring reserved words.
|
|
178
|
-
*
|
|
179
|
-
* Reserved words and globals (`class`, `name`, `Date`, …) are valid as bare object-literal keys
|
|
180
|
-
* even though they are not valid variable names, so use this (not {@link isValidVarName}) when
|
|
181
|
-
* deciding whether an object key needs quoting.
|
|
182
|
-
*
|
|
183
|
-
* @example
|
|
184
|
-
* ```ts
|
|
185
|
-
* isIdentifier('name') // true
|
|
186
|
-
* isIdentifier('x-total')// false
|
|
187
|
-
* ```
|
|
188
|
-
*/
|
|
189
|
-
function isIdentifier(name) {
|
|
190
|
-
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
|
|
191
|
-
}
|
|
192
|
-
//#endregion
|
|
193
|
-
//#region ../../internals/utils/src/url.ts
|
|
194
|
-
function transformParam(raw, casing) {
|
|
195
|
-
const param = isValidVarName(raw) ? raw : camelCase(raw);
|
|
196
|
-
return casing === "camelcase" ? camelCase(param) : param;
|
|
197
|
-
}
|
|
198
|
-
function toParamsObject(path, { replacer, casing } = {}) {
|
|
199
|
-
const params = {};
|
|
200
|
-
for (const match of path.matchAll(/\{([^}]+)\}/g)) {
|
|
201
|
-
const param = transformParam(match[1], casing);
|
|
202
|
-
const key = replacer ? replacer(param) : param;
|
|
203
|
-
params[key] = key;
|
|
204
|
-
}
|
|
205
|
-
return Object.keys(params).length > 0 ? params : null;
|
|
206
|
-
}
|
|
207
|
-
/**
|
|
208
|
-
* Helpers for OpenAPI/Swagger paths, plus a thin wrapper over the native `URL`.
|
|
209
|
-
*/
|
|
210
|
-
var Url = class Url {
|
|
211
|
-
/**
|
|
212
|
-
* Reports whether `url` is a parseable absolute URL. Delegates to the native `URL.canParse`.
|
|
213
|
-
*
|
|
214
|
-
* @example
|
|
215
|
-
* Url.canParse('https://petstore.swagger.io/v2') // true
|
|
216
|
-
* Url.canParse('/pet/{petId}') // false
|
|
217
|
-
*/
|
|
218
|
-
static canParse(url, base) {
|
|
219
|
-
return URL.canParse(url, base);
|
|
220
|
-
}
|
|
221
|
-
/**
|
|
222
|
-
* Converts an OpenAPI/Swagger path to Express-style colon syntax.
|
|
223
|
-
*
|
|
224
|
-
* @example
|
|
225
|
-
* Url.toPath('/pet/{petId}') // '/pet/:petId'
|
|
226
|
-
*/
|
|
227
|
-
static toPath(path) {
|
|
228
|
-
return path.replace(/\{([^}]+)\}/g, ":$1");
|
|
229
|
-
}
|
|
230
|
-
/**
|
|
231
|
-
* Rewrites OpenAPI placeholder names while keeping the `{...}` braces, so the generated `url`
|
|
232
|
-
* literal aligns with the grouped `path` request option that the runtime client interpolates by
|
|
233
|
-
* key.
|
|
234
|
-
*
|
|
235
|
-
* @example
|
|
236
|
-
* Url.toCasedTemplate('/projects/{project_id}', { casing: 'camelcase' }) // '/projects/{projectId}'
|
|
237
|
-
*/
|
|
238
|
-
static toCasedTemplate(path, { casing } = {}) {
|
|
239
|
-
return path.replace(/\{([^}]+)\}/g, (_, name) => `{${transformParam(name, casing)}}`);
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* Converts an OpenAPI/Swagger path to a TypeScript template literal string.
|
|
243
|
-
* `prefix` is prepended inside the literal, `replacer` transforms each parameter name,
|
|
244
|
-
* and `casing` controls parameter identifier casing.
|
|
245
|
-
*
|
|
246
|
-
* @example
|
|
247
|
-
* Url.toTemplateString('/pet/{petId}') // '`/pet/${petId}`'
|
|
248
|
-
*
|
|
249
|
-
* @example
|
|
250
|
-
* Url.toTemplateString('/pet/{petId}', { prefix: 'https://api' }) // '`https://api/pet/${petId}`'
|
|
251
|
-
*/
|
|
252
|
-
static toTemplateString(path, { prefix, replacer, casing } = {}) {
|
|
253
|
-
const result = path.split(/\{([^}]+)\}/).map((part, i) => {
|
|
254
|
-
if (i % 2 === 0) return part;
|
|
255
|
-
const param = transformParam(part, casing);
|
|
256
|
-
return `\${${replacer ? replacer(param) : param}}`;
|
|
257
|
-
}).join("");
|
|
258
|
-
return `\`${prefix ?? ""}${result}\``;
|
|
259
|
-
}
|
|
260
|
-
/**
|
|
261
|
-
* Converts an OpenAPI/Swagger path to a template literal that reads each parameter off the
|
|
262
|
-
* grouped `path` request option, e.g. `/pet/{petId}` becomes `` `/pet/${path.petId}` ``. Parameter
|
|
263
|
-
* names are camelCased to match the generated `path` type, and `prefix` is prepended inside the
|
|
264
|
-
* literal. Shared by the client and cypress generators that pass a grouped `path` object.
|
|
265
|
-
*
|
|
266
|
-
* @example
|
|
267
|
-
* Url.toGroupedTemplateString('/pet/{petId}') // '`/pet/${path.petId}`'
|
|
268
|
-
*/
|
|
269
|
-
static toGroupedTemplateString(path, { prefix } = {}) {
|
|
270
|
-
return Url.toTemplateString(path, {
|
|
271
|
-
prefix,
|
|
272
|
-
casing: "camelcase",
|
|
273
|
-
replacer: (name) => `path.${name}`
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
/**
|
|
277
|
-
* Returns the path and its extracted params as a structured `URLObject`, or as a stringified
|
|
278
|
-
* expression when `stringify` is set.
|
|
279
|
-
*
|
|
280
|
-
* @example
|
|
281
|
-
* Url.toObject('/pet/{petId}')
|
|
282
|
-
* // { url: '/pet/:petId', params: { petId: 'petId' } }
|
|
283
|
-
*/
|
|
284
|
-
static toObject(path, { type = "path", replacer, stringify, casing } = {}) {
|
|
285
|
-
const object = {
|
|
286
|
-
url: type === "path" ? Url.toPath(path) : Url.toTemplateString(path, {
|
|
287
|
-
replacer,
|
|
288
|
-
casing
|
|
289
|
-
}),
|
|
290
|
-
params: toParamsObject(path, {
|
|
291
|
-
replacer,
|
|
292
|
-
casing
|
|
293
|
-
})
|
|
294
|
-
};
|
|
295
|
-
if (stringify) {
|
|
296
|
-
if (type === "template") return JSON.stringify(object).replaceAll("'", "").replaceAll(`"`, "");
|
|
297
|
-
if (object.params) return `{ url: '${object.url}', params: ${JSON.stringify(object.params).replaceAll("'", "").replaceAll(`"`, "")} }`;
|
|
298
|
-
return `{ url: '${object.url}' }`;
|
|
299
|
-
}
|
|
300
|
-
return object;
|
|
301
|
-
}
|
|
302
|
-
};
|
|
303
|
-
//#endregion
|
|
304
37
|
//#region ../../internals/shared/src/params.ts
|
|
305
|
-
const caseParamsCache = /* @__PURE__ */ new WeakMap();
|
|
306
|
-
/**
|
|
307
|
-
* Applies camelCase to parameter names and returns a new array without mutating the input.
|
|
308
|
-
*
|
|
309
|
-
* Run it before handing parameters to schema builders so output property keys get the right casing
|
|
310
|
-
* while `OperationNode.parameters` stays intact for other consumers. When `casing` is unset, the
|
|
311
|
-
* original array is returned unchanged. Results are cached per input array.
|
|
312
|
-
*/
|
|
313
|
-
function caseParams(params, casing) {
|
|
314
|
-
if (!casing) return params;
|
|
315
|
-
const cached = caseParamsCache.get(params);
|
|
316
|
-
if (cached) return cached;
|
|
317
|
-
const result = params.map((param) => ({
|
|
318
|
-
...param,
|
|
319
|
-
name: camelCase(param.name)
|
|
320
|
-
}));
|
|
321
|
-
caseParamsCache.set(params, result);
|
|
322
|
-
return result;
|
|
323
|
-
}
|
|
324
38
|
/**
|
|
325
|
-
* Drops parameters that
|
|
39
|
+
* Drops parameters that share the same name, keeping the first.
|
|
326
40
|
*
|
|
327
|
-
*
|
|
328
|
-
*
|
|
329
|
-
*
|
|
330
|
-
*
|
|
41
|
+
* A malformed spec can declare the same parameter name twice within one `in` location. Both would
|
|
42
|
+
* resolve to the same output property, so emitting both would yield an object type with a duplicate
|
|
43
|
+
* member, which TypeScript rejects. This is a defensive guard against that case, not a casing guard:
|
|
44
|
+
* parameter names flow through unchanged, so no two distinct names ever collide here anymore.
|
|
331
45
|
*/
|
|
332
|
-
function
|
|
46
|
+
function dedupeParams(params) {
|
|
333
47
|
const seen = /* @__PURE__ */ new Set();
|
|
334
48
|
return params.filter((param) => {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
seen.add(key);
|
|
49
|
+
if (seen.has(param.name)) return false;
|
|
50
|
+
seen.add(param.name);
|
|
338
51
|
return true;
|
|
339
52
|
});
|
|
340
53
|
}
|
|
@@ -359,11 +72,30 @@ function operationFileEntry(node, name, extname = ".ts") {
|
|
|
359
72
|
path: node.path
|
|
360
73
|
};
|
|
361
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Resolves a dependency plugin's generated file for `node.operationId`, cached in `cache` (the
|
|
77
|
+
* current node's `ctx.cache`) under the resolver's own plugin name. Several dependents reading the
|
|
78
|
+
* same dependency for the same operation in one pass (a query plugin's several hook generators, the
|
|
79
|
+
* MCP handler, ...) share one computed name and path instead of each calling `resolver.file` again.
|
|
80
|
+
*
|
|
81
|
+
* @example Cache `plugin-ts`'s file for the current operation
|
|
82
|
+
* ```ts
|
|
83
|
+
* const fileTs = resolveDependencyOperationFile({ cache: ctx.cache, node, resolver: tsResolver, root, output })
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
function resolveDependencyOperationFile(options) {
|
|
87
|
+
const { cache, node, resolver, root, output, group } = options;
|
|
88
|
+
return cache.ensureItem(`${resolver.pluginName}:operationFile`, () => resolver.file({
|
|
89
|
+
...operationFileEntry(node, node.operationId),
|
|
90
|
+
root,
|
|
91
|
+
output,
|
|
92
|
+
group: group ?? void 0
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
362
95
|
function getOperationLink(node, link) {
|
|
363
96
|
if (!link) return null;
|
|
364
97
|
if (typeof link === "function") return link(node) ?? null;
|
|
365
|
-
|
|
366
|
-
return node.path ? `{@link ${node.path.replaceAll("{", ":").replaceAll("}", "")}}` : null;
|
|
98
|
+
return node.path ? `{@link ${kubb_kit.Url.toPath(node.path)}}` : null;
|
|
367
99
|
}
|
|
368
100
|
/**
|
|
369
101
|
* Derives the shared `ContentTypeInfo` shape from a list of content types, tracking whether several
|
|
@@ -453,14 +185,24 @@ function buildOperationComments(node, options = {}) {
|
|
|
453
185
|
if (!splitLines) return filteredComments;
|
|
454
186
|
return filteredComments.flatMap((text) => text.split(/\r?\n/).map((line) => line.trim())).filter((comment) => Boolean(comment));
|
|
455
187
|
}
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
188
|
+
const operationParameterGroupsByNode = /* @__PURE__ */ new WeakMap();
|
|
189
|
+
/**
|
|
190
|
+
* Groups an operation's parameters by location (`path`/`query`/`header`/`cookie`), deduping each
|
|
191
|
+
* group by name. Every plugin generator visiting the same `OperationNode` shares one AST instance
|
|
192
|
+
* (see `KubbDriver`), so the result is cached per node to avoid re-filtering and re-deduping the
|
|
193
|
+
* same parameters once per plugin.
|
|
194
|
+
*/
|
|
195
|
+
function getOperationParameters(node) {
|
|
196
|
+
const cached = operationParameterGroupsByNode.get(node);
|
|
197
|
+
if (cached) return cached;
|
|
198
|
+
const groups = {
|
|
199
|
+
path: dedupeParams(node.parameters.filter((param) => param.in === "path")),
|
|
200
|
+
query: dedupeParams(node.parameters.filter((param) => param.in === "query")),
|
|
201
|
+
header: dedupeParams(node.parameters.filter((param) => param.in === "header")),
|
|
202
|
+
cookie: dedupeParams(node.parameters.filter((param) => param.in === "cookie"))
|
|
463
203
|
};
|
|
204
|
+
operationParameterGroupsByNode.set(node, groups);
|
|
205
|
+
return groups;
|
|
464
206
|
}
|
|
465
207
|
function getStatusCodeNumber(statusCode) {
|
|
466
208
|
const code = Number(statusCode);
|
|
@@ -491,7 +233,7 @@ function resolveStatusCodeNames(node, resolver) {
|
|
|
491
233
|
}
|
|
492
234
|
const typeNamesByResolver = /* @__PURE__ */ new WeakMap();
|
|
493
235
|
function resolveOperationTypeNames(node, resolver, options = {}) {
|
|
494
|
-
const cacheKey = `${node.operationId}\0${options.
|
|
236
|
+
const cacheKey = `${node.operationId}\0${options.order ?? ""}\0${options.responseStatusNames ?? ""}\0${options.includeParams === false ? "noparams" : ""}\0${(options.exclude ?? []).join(",")}`;
|
|
495
237
|
let byResolver = typeNamesByResolver.get(resolver);
|
|
496
238
|
if (byResolver) {
|
|
497
239
|
const cached = byResolver.get(cacheKey);
|
|
@@ -500,7 +242,7 @@ function resolveOperationTypeNames(node, resolver, options = {}) {
|
|
|
500
242
|
byResolver = /* @__PURE__ */ new Map();
|
|
501
243
|
typeNamesByResolver.set(resolver, byResolver);
|
|
502
244
|
}
|
|
503
|
-
const { path, query, header } = getOperationParameters(node
|
|
245
|
+
const { path, query, header } = getOperationParameters(node);
|
|
504
246
|
const responseStatusNames = options.responseStatusNames === "error" ? resolveErrorNames(node, resolver) : options.responseStatusNames === false ? [] : resolveStatusCodeNames(node, resolver);
|
|
505
247
|
const exclude = new Set(options.exclude ?? []);
|
|
506
248
|
const paramNames = options.includeParams === false ? [] : [
|
|
@@ -522,6 +264,43 @@ function resolveOperationTypeNames(node, resolver, options = {}) {
|
|
|
522
264
|
return result;
|
|
523
265
|
}
|
|
524
266
|
//#endregion
|
|
267
|
+
//#region ../../internals/utils/src/casing.ts
|
|
268
|
+
/**
|
|
269
|
+
* Shared implementation for camelCase and PascalCase conversion.
|
|
270
|
+
* Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
|
|
271
|
+
* and capitalizes each word according to `pascal`.
|
|
272
|
+
*
|
|
273
|
+
* When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
|
|
274
|
+
*/
|
|
275
|
+
function toCamelOrPascal(text, pascal) {
|
|
276
|
+
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) => {
|
|
277
|
+
if (word.length > 1 && word === word.toUpperCase()) return word;
|
|
278
|
+
return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
|
|
279
|
+
}).join("").replace(/[^a-zA-Z0-9]/g, "");
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Converts `text` to camelCase.
|
|
283
|
+
*
|
|
284
|
+
* @example Word boundaries
|
|
285
|
+
* `camelCase('hello-world') // 'helloWorld'`
|
|
286
|
+
*
|
|
287
|
+
* @example With a prefix
|
|
288
|
+
* `camelCase('tag', { prefix: 'create' }) // 'createTag'`
|
|
289
|
+
*/
|
|
290
|
+
function camelCase(text, { prefix = "", suffix = "" } = {}) {
|
|
291
|
+
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Uppercases only the first character of `text`, leaving the rest untouched.
|
|
295
|
+
* Unlike {@link pascalCase} it never re-splits word boundaries or strips characters.
|
|
296
|
+
*
|
|
297
|
+
* @example
|
|
298
|
+
* `capitalize('getPetById') // 'GetPetById'`
|
|
299
|
+
*/
|
|
300
|
+
function capitalize(text) {
|
|
301
|
+
return `${text.charAt(0).toUpperCase()}${text.slice(1)}`;
|
|
302
|
+
}
|
|
303
|
+
//#endregion
|
|
525
304
|
//#region ../../internals/shared/src/group.ts
|
|
526
305
|
/**
|
|
527
306
|
* Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
|
|
@@ -641,23 +420,30 @@ function resolveContractClient(options) {
|
|
|
641
420
|
* (plugin-fetch or plugin-axios). Returns `null` when no contract plugin is in play (the inline
|
|
642
421
|
* path). The plugin injects `.kubb/client.ts` at the global output root, the same path consumers
|
|
643
422
|
* read `RequestConfig` / `ResponseErrorConfig` from.
|
|
423
|
+
*
|
|
424
|
+
* The result is cached in the current node's `cache` (`ctx.cache`) under the client plugin's name,
|
|
425
|
+
* so several dependents reading the same client plugin for one operation in a single pass
|
|
426
|
+
* (react-query's query/mutation/infinite generators, vue-query, swr, the MCP handler, ...) share
|
|
427
|
+
* one computed result instead of each re-deriving the name and path.
|
|
644
428
|
*/
|
|
645
429
|
function resolveClientOperation(options) {
|
|
646
|
-
const { clientPlugin, driver, node, root, output } = options;
|
|
430
|
+
const { clientPlugin, driver, node, root, output, cache } = options;
|
|
647
431
|
if (!clientPlugin) return null;
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
432
|
+
return cache.ensureItem(`${clientPlugin.pluginName}:clientOperation`, () => {
|
|
433
|
+
const resolver = driver.getResolver(clientPlugin.pluginName);
|
|
434
|
+
const plugin = driver.getPlugin(clientPlugin.pluginName);
|
|
435
|
+
const file = resolver.file({
|
|
436
|
+
...operationFileEntry(node, node.operationId),
|
|
437
|
+
root,
|
|
438
|
+
output: plugin?.options?.output ?? output,
|
|
439
|
+
group: plugin?.options?.group ?? void 0
|
|
440
|
+
});
|
|
441
|
+
return {
|
|
442
|
+
name: resolver.name(node.operationId),
|
|
443
|
+
path: file.path,
|
|
444
|
+
clientPath: node_path.default.resolve(root, ".kubb/client.ts")
|
|
445
|
+
};
|
|
655
446
|
});
|
|
656
|
-
return {
|
|
657
|
-
name: resolver.name(node.operationId),
|
|
658
|
-
path: file.path,
|
|
659
|
-
clientPath: node_path.default.resolve(root, ".kubb/client.ts")
|
|
660
|
-
};
|
|
661
447
|
}
|
|
662
448
|
//#endregion
|
|
663
449
|
//#region ../../internals/tanstack-query/src/utils.ts
|
|
@@ -725,14 +511,16 @@ function buildGroupedRequestParam(node, options) {
|
|
|
725
511
|
*/
|
|
726
512
|
function buildQueryOptionsParams(node, options) {
|
|
727
513
|
const { resolver, memberTypeWrapper } = options;
|
|
728
|
-
|
|
514
|
+
const groupedParam = buildGroupedRequestParam(node, {
|
|
729
515
|
resolver,
|
|
730
516
|
memberTypeWrapper
|
|
731
|
-
})
|
|
517
|
+
});
|
|
518
|
+
const configParam = (0, _kubb_plugin_ts.createFunctionParameter)({
|
|
732
519
|
name: "config",
|
|
733
520
|
type: `Partial<Omit<RequestConfig, 'path' | 'query' | 'body' | 'headers' | 'url'>>`,
|
|
734
521
|
default: "{}"
|
|
735
|
-
})
|
|
522
|
+
});
|
|
523
|
+
return (0, _kubb_plugin_ts.createFunctionParameters)({ params: [groupedParam, configParam].filter((param) => param !== null) });
|
|
736
524
|
}
|
|
737
525
|
/**
|
|
738
526
|
* Builds the call to a client `<op>` function inside a query/mutation hook body. The function takes
|
|
@@ -790,6 +578,19 @@ function resolveMutationConfig(mutation, options) {
|
|
|
790
578
|
};
|
|
791
579
|
}
|
|
792
580
|
/**
|
|
581
|
+
* Classifies an operation as a query or a mutation from the resolved `query` / `mutation` method lists.
|
|
582
|
+
* `query: false` still marks the operation as a query so the query-family generators keep matching it,
|
|
583
|
+
* and a method already claimed by `query` never counts as a mutation.
|
|
584
|
+
*/
|
|
585
|
+
function classifyOperation(node, { query, mutation }) {
|
|
586
|
+
const isQuery = query === false || !!query && query.methods.some((method) => node.method.toLowerCase() === method.toLowerCase());
|
|
587
|
+
const queryMethods = new Set(query ? query.methods : []);
|
|
588
|
+
return {
|
|
589
|
+
isQuery,
|
|
590
|
+
isMutation: mutation !== false && !isQuery && (mutation ? mutation.methods : []).some((method) => !queryMethods.has(method) && node.method.toLowerCase() === method.toLowerCase())
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
793
594
|
* Build QueryKey params as the grouped `{ path, query, body }` object (NO headers, NO config),
|
|
794
595
|
* typed from the operation's `Options` minus `url`. The query key transformer reads the
|
|
795
596
|
* grouped `path`/`query`/`body` bindings.
|
|
@@ -811,7 +612,7 @@ function buildQueryKeyParams(node, options) {
|
|
|
811
612
|
const declarationPrinter$4 = (0, _kubb_plugin_ts.functionPrinter)({ mode: "declaration" });
|
|
812
613
|
const mutationKeyTransformer = ({ node }) => {
|
|
813
614
|
if (!node.path) return [];
|
|
814
|
-
return [`{ url: '${Url.toPath(node.path)}' }`];
|
|
615
|
+
return [`{ url: '${kubb_kit.Url.toPath(node.path)}' }`];
|
|
815
616
|
};
|
|
816
617
|
function MutationKey$1({ name, node, transformer }) {
|
|
817
618
|
const paramsNode = (0, _kubb_plugin_ts.createFunctionParameters)({ params: [] });
|
|
@@ -896,7 +697,7 @@ const queryKeyTransformer = ({ node }) => {
|
|
|
896
697
|
const hasQueryParams = getOperationParameters(node).query.length > 0;
|
|
897
698
|
const hasRequestBody = !!node.requestBody?.content?.[0]?.schema;
|
|
898
699
|
return [
|
|
899
|
-
hasPathParams ? `{ url: '${Url.toPath(node.path)}', params: path }` : `{ url: '${Url.toPath(node.path)}' }`,
|
|
700
|
+
hasPathParams ? `{ url: '${kubb_kit.Url.toPath(node.path)}', params: path }` : `{ url: '${kubb_kit.Url.toPath(node.path)}' }`,
|
|
900
701
|
hasQueryParams ? "...(query ? [query] : [])" : null,
|
|
901
702
|
hasRequestBody ? "...(body ? [body] : [])" : null
|
|
902
703
|
].filter(Boolean);
|
|
@@ -936,10 +737,12 @@ function buildMutationParamsNode(node, options) {
|
|
|
936
737
|
const { mutationKeyTypeName, mutationArgTypeName, resolver } = options;
|
|
937
738
|
const responseName = resolver.response.response(node);
|
|
938
739
|
const errorNames = resolveErrorNames(node, resolver);
|
|
740
|
+
const TData = responseName;
|
|
741
|
+
const TError = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`;
|
|
939
742
|
return (0, _kubb_plugin_ts.createFunctionParameters)({ params: [(0, _kubb_plugin_ts.createFunctionParameter)({
|
|
940
743
|
name: "options",
|
|
941
744
|
type: `{
|
|
942
|
-
mutation?: SWRMutationConfiguration<${
|
|
745
|
+
mutation?: SWRMutationConfiguration<${TData}, ${TError}, ${mutationKeyTypeName} | null, ${mutationArgTypeName}> & { throwOnError?: boolean },
|
|
943
746
|
client?: ${buildRequestConfigType(node)},
|
|
944
747
|
shouldFetch?: boolean,
|
|
945
748
|
}`,
|
|
@@ -1034,17 +837,20 @@ function buildQueryParamsNode(node, options) {
|
|
|
1034
837
|
const { resolver } = options;
|
|
1035
838
|
const responseName = resolver.response.response(node);
|
|
1036
839
|
const errorNames = resolveErrorNames(node, resolver);
|
|
840
|
+
const TData = responseName;
|
|
841
|
+
const TError = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`;
|
|
1037
842
|
const optionsParam = (0, _kubb_plugin_ts.createFunctionParameter)({
|
|
1038
843
|
name: "options",
|
|
1039
844
|
type: `{
|
|
1040
|
-
query?: SWRConfiguration<${[
|
|
845
|
+
query?: SWRConfiguration<${[TData, TError].join(", ")}>,
|
|
1041
846
|
client?: ${buildClientOptionType()},
|
|
1042
847
|
shouldFetch?: boolean,
|
|
1043
848
|
immutable?: boolean
|
|
1044
849
|
}`,
|
|
1045
850
|
default: "{}"
|
|
1046
851
|
});
|
|
1047
|
-
|
|
852
|
+
const groupedParam = buildGroupedRequestParam(node, { resolver });
|
|
853
|
+
return (0, _kubb_plugin_ts.createFunctionParameters)({ params: [groupedParam, optionsParam].filter((param) => param !== null) });
|
|
1048
854
|
}
|
|
1049
855
|
function Query({ name, queryKeyTypeName, queryOptionsName, queryKeyName, node, tsResolver }) {
|
|
1050
856
|
const responseName = tsResolver.response.response(node);
|
|
@@ -1136,16 +942,19 @@ const mutationGenerator = (0, kubb_kit.defineGenerator)({
|
|
|
1136
942
|
const pluginTs = driver.getPlugin(_kubb_plugin_ts.pluginTsName);
|
|
1137
943
|
if (!pluginTs) return null;
|
|
1138
944
|
const tsResolver = driver.getResolver(_kubb_plugin_ts.pluginTsName);
|
|
1139
|
-
const
|
|
1140
|
-
|
|
1141
|
-
|
|
945
|
+
const { isMutation } = classifyOperation(node, {
|
|
946
|
+
query,
|
|
947
|
+
mutation
|
|
948
|
+
});
|
|
949
|
+
if (!isMutation) return null;
|
|
1142
950
|
const importPath = mutation ? mutation.importPath : "swr/mutation";
|
|
1143
951
|
const contractOp = resolveClientOperation({
|
|
1144
952
|
clientPlugin: { pluginName: client.pluginName },
|
|
1145
953
|
driver,
|
|
1146
954
|
node,
|
|
1147
955
|
root,
|
|
1148
|
-
output
|
|
956
|
+
output,
|
|
957
|
+
cache: ctx.cache
|
|
1149
958
|
});
|
|
1150
959
|
if (!contractOp) return null;
|
|
1151
960
|
const mutationHookName = resolver.mutation.name(node);
|
|
@@ -1162,11 +971,10 @@ const mutationGenerator = (0, kubb_kit.defineGenerator)({
|
|
|
1162
971
|
output,
|
|
1163
972
|
group
|
|
1164
973
|
}),
|
|
1165
|
-
fileTs:
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
path: node.path,
|
|
974
|
+
fileTs: resolveDependencyOperationFile({
|
|
975
|
+
cache: ctx.cache,
|
|
976
|
+
node,
|
|
977
|
+
resolver: tsResolver,
|
|
1170
978
|
root,
|
|
1171
979
|
output: pluginTs.options?.output ?? output,
|
|
1172
980
|
group: pluginTs.options?.group
|
|
@@ -1257,18 +1065,23 @@ const queryGenerator = (0, kubb_kit.defineGenerator)({
|
|
|
1257
1065
|
operation(node, ctx) {
|
|
1258
1066
|
if (!kubb_kit.ast.isHttpOperationNode(node)) return null;
|
|
1259
1067
|
const { config, driver, resolver, root } = ctx;
|
|
1260
|
-
const { output, query, client, group } = ctx.options;
|
|
1068
|
+
const { output, query, mutation, client, group } = ctx.options;
|
|
1261
1069
|
const pluginTs = driver.getPlugin(_kubb_plugin_ts.pluginTsName);
|
|
1262
1070
|
if (!pluginTs) return null;
|
|
1263
1071
|
const tsResolver = driver.getResolver(_kubb_plugin_ts.pluginTsName);
|
|
1264
|
-
|
|
1072
|
+
const { isQuery } = classifyOperation(node, {
|
|
1073
|
+
query,
|
|
1074
|
+
mutation
|
|
1075
|
+
});
|
|
1076
|
+
if (!isQuery) return null;
|
|
1265
1077
|
const importPath = query ? query.importPath : "swr";
|
|
1266
1078
|
const contractOp = resolveClientOperation({
|
|
1267
1079
|
clientPlugin: { pluginName: client.pluginName },
|
|
1268
1080
|
driver,
|
|
1269
1081
|
node,
|
|
1270
1082
|
root,
|
|
1271
|
-
output
|
|
1083
|
+
output,
|
|
1084
|
+
cache: ctx.cache
|
|
1272
1085
|
});
|
|
1273
1086
|
if (!contractOp) return null;
|
|
1274
1087
|
const queryName = resolver.query.name(node);
|
|
@@ -1285,11 +1098,10 @@ const queryGenerator = (0, kubb_kit.defineGenerator)({
|
|
|
1285
1098
|
output,
|
|
1286
1099
|
group
|
|
1287
1100
|
}),
|
|
1288
|
-
fileTs:
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
path: node.path,
|
|
1101
|
+
fileTs: resolveDependencyOperationFile({
|
|
1102
|
+
cache: ctx.cache,
|
|
1103
|
+
node,
|
|
1104
|
+
resolver: tsResolver,
|
|
1293
1105
|
root,
|
|
1294
1106
|
output: pluginTs.options?.output ?? output,
|
|
1295
1107
|
group: pluginTs.options?.group
|