@kubb/plugin-vue-query 5.0.0-beta.99 → 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 +205 -325
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +202 -322
- package/dist/index.js.map +1 -1
- package/package.json +5 -8
package/dist/index.js
CHANGED
|
@@ -1,249 +1,9 @@
|
|
|
1
1
|
import { t as __name } from "./rolldown-runtime-C0LytTxp.js";
|
|
2
|
+
import { Resolver, Url, ast, createResolver, defineGenerator, definePlugin } from "kubb/kit";
|
|
2
3
|
import path from "node:path";
|
|
3
|
-
import { Resolver, ast, createResolver, defineGenerator, definePlugin } from "kubb/kit";
|
|
4
4
|
import { createFunctionParameter, createFunctionParameters, createObjectBindingPattern, createTypeLiteral, functionPrinter, pluginTsName } from "@kubb/plugin-ts";
|
|
5
5
|
import { File, Function, Type, jsxRenderer } from "kubb/jsx";
|
|
6
6
|
import { Fragment, jsx, jsxs } from "kubb/jsx/jsx-runtime";
|
|
7
|
-
//#region ../../internals/utils/src/casing.ts
|
|
8
|
-
/**
|
|
9
|
-
* Shared implementation for camelCase and PascalCase conversion.
|
|
10
|
-
* Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
|
|
11
|
-
* and capitalizes each word according to `pascal`.
|
|
12
|
-
*
|
|
13
|
-
* When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
|
|
14
|
-
*/
|
|
15
|
-
function toCamelOrPascal(text, pascal) {
|
|
16
|
-
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) => {
|
|
17
|
-
if (word.length > 1 && word === word.toUpperCase()) return word;
|
|
18
|
-
return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
|
|
19
|
-
}).join("").replace(/[^a-zA-Z0-9]/g, "");
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Converts `text` to camelCase.
|
|
23
|
-
*
|
|
24
|
-
* @example Word boundaries
|
|
25
|
-
* `camelCase('hello-world') // 'helloWorld'`
|
|
26
|
-
*
|
|
27
|
-
* @example With a prefix
|
|
28
|
-
* `camelCase('tag', { prefix: 'create' }) // 'createTag'`
|
|
29
|
-
*/
|
|
30
|
-
function camelCase(text, { prefix = "", suffix = "" } = {}) {
|
|
31
|
-
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Uppercases only the first character of `text`, leaving the rest untouched.
|
|
35
|
-
* Unlike {@link pascalCase} it never re-splits word boundaries or strips characters.
|
|
36
|
-
*
|
|
37
|
-
* @example
|
|
38
|
-
* `capitalize('getPetById') // 'GetPetById'`
|
|
39
|
-
*/
|
|
40
|
-
function capitalize(text) {
|
|
41
|
-
return `${text.charAt(0).toUpperCase()}${text.slice(1)}`;
|
|
42
|
-
}
|
|
43
|
-
//#endregion
|
|
44
|
-
//#region ../../internals/utils/src/reserved.ts
|
|
45
|
-
/**
|
|
46
|
-
* JavaScript and Java reserved words.
|
|
47
|
-
* @link https://github.com/jonschlinkert/reserved/blob/master/index.js
|
|
48
|
-
*/
|
|
49
|
-
const reservedWords = /* @__PURE__ */ new Set([
|
|
50
|
-
"abstract",
|
|
51
|
-
"arguments",
|
|
52
|
-
"boolean",
|
|
53
|
-
"break",
|
|
54
|
-
"byte",
|
|
55
|
-
"case",
|
|
56
|
-
"catch",
|
|
57
|
-
"char",
|
|
58
|
-
"class",
|
|
59
|
-
"const",
|
|
60
|
-
"continue",
|
|
61
|
-
"debugger",
|
|
62
|
-
"default",
|
|
63
|
-
"delete",
|
|
64
|
-
"do",
|
|
65
|
-
"double",
|
|
66
|
-
"else",
|
|
67
|
-
"enum",
|
|
68
|
-
"eval",
|
|
69
|
-
"export",
|
|
70
|
-
"extends",
|
|
71
|
-
"false",
|
|
72
|
-
"final",
|
|
73
|
-
"finally",
|
|
74
|
-
"float",
|
|
75
|
-
"for",
|
|
76
|
-
"function",
|
|
77
|
-
"goto",
|
|
78
|
-
"if",
|
|
79
|
-
"implements",
|
|
80
|
-
"import",
|
|
81
|
-
"in",
|
|
82
|
-
"instanceof",
|
|
83
|
-
"int",
|
|
84
|
-
"interface",
|
|
85
|
-
"let",
|
|
86
|
-
"long",
|
|
87
|
-
"native",
|
|
88
|
-
"new",
|
|
89
|
-
"null",
|
|
90
|
-
"package",
|
|
91
|
-
"private",
|
|
92
|
-
"protected",
|
|
93
|
-
"public",
|
|
94
|
-
"return",
|
|
95
|
-
"short",
|
|
96
|
-
"static",
|
|
97
|
-
"super",
|
|
98
|
-
"switch",
|
|
99
|
-
"synchronized",
|
|
100
|
-
"this",
|
|
101
|
-
"throw",
|
|
102
|
-
"throws",
|
|
103
|
-
"transient",
|
|
104
|
-
"true",
|
|
105
|
-
"try",
|
|
106
|
-
"typeof",
|
|
107
|
-
"var",
|
|
108
|
-
"void",
|
|
109
|
-
"volatile",
|
|
110
|
-
"while",
|
|
111
|
-
"with",
|
|
112
|
-
"yield",
|
|
113
|
-
"Array",
|
|
114
|
-
"Date",
|
|
115
|
-
"hasOwnProperty",
|
|
116
|
-
"Infinity",
|
|
117
|
-
"isFinite",
|
|
118
|
-
"isNaN",
|
|
119
|
-
"isPrototypeOf",
|
|
120
|
-
"length",
|
|
121
|
-
"Math",
|
|
122
|
-
"name",
|
|
123
|
-
"NaN",
|
|
124
|
-
"Number",
|
|
125
|
-
"Object",
|
|
126
|
-
"prototype",
|
|
127
|
-
"String",
|
|
128
|
-
"toString",
|
|
129
|
-
"undefined",
|
|
130
|
-
"valueOf"
|
|
131
|
-
]);
|
|
132
|
-
/**
|
|
133
|
-
* Returns `true` when `name` is a syntactically valid JavaScript variable name.
|
|
134
|
-
*
|
|
135
|
-
* @example
|
|
136
|
-
* ```ts
|
|
137
|
-
* isValidVarName('status') // true
|
|
138
|
-
* isValidVarName('class') // false (reserved word)
|
|
139
|
-
* isValidVarName('42foo') // false (starts with digit)
|
|
140
|
-
* ```
|
|
141
|
-
*/
|
|
142
|
-
function isValidVarName(name) {
|
|
143
|
-
if (!name || reservedWords.has(name)) return false;
|
|
144
|
-
return isIdentifier(name);
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
147
|
-
* Returns `true` when `name` is syntactically a valid identifier, ignoring reserved words.
|
|
148
|
-
*
|
|
149
|
-
* Reserved words and globals (`class`, `name`, `Date`, …) are valid as bare object-literal keys
|
|
150
|
-
* even though they are not valid variable names, so use this (not {@link isValidVarName}) when
|
|
151
|
-
* deciding whether an object key needs quoting.
|
|
152
|
-
*
|
|
153
|
-
* @example
|
|
154
|
-
* ```ts
|
|
155
|
-
* isIdentifier('name') // true
|
|
156
|
-
* isIdentifier('x-total')// false
|
|
157
|
-
* ```
|
|
158
|
-
*/
|
|
159
|
-
function isIdentifier(name) {
|
|
160
|
-
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
|
|
161
|
-
}
|
|
162
|
-
//#endregion
|
|
163
|
-
//#region ../../internals/utils/src/strings.ts
|
|
164
|
-
/**
|
|
165
|
-
* Renders a dotted path or string array as an optional-chaining accessor expression rooted at
|
|
166
|
-
* `accessor`. Returns `null` for an empty path.
|
|
167
|
-
*
|
|
168
|
-
* @example
|
|
169
|
-
* ```ts
|
|
170
|
-
* getNestedAccessor('pagination.next.id', 'lastPage')
|
|
171
|
-
* // "lastPage?.['pagination']?.['next']?.['id']"
|
|
172
|
-
* ```
|
|
173
|
-
*/
|
|
174
|
-
function getNestedAccessor(param, accessor) {
|
|
175
|
-
const parts = Array.isArray(param) ? param : param.split(".");
|
|
176
|
-
if (parts.length === 0 || parts.length === 1 && parts[0] === "") return null;
|
|
177
|
-
return `${accessor}?.['${`${parts.join("']?.['")}']`}`;
|
|
178
|
-
}
|
|
179
|
-
//#endregion
|
|
180
|
-
//#region ../../internals/utils/src/url.ts
|
|
181
|
-
/**
|
|
182
|
-
* Keeps the OpenAPI parameter name as-is when it is already a valid JS identifier, and
|
|
183
|
-
* camelCases it only enough to become one otherwise (for example a hyphenated path segment).
|
|
184
|
-
*/
|
|
185
|
-
function transformParam(raw) {
|
|
186
|
-
return isValidVarName(raw) ? raw : camelCase(raw);
|
|
187
|
-
}
|
|
188
|
-
/**
|
|
189
|
-
* Helpers for OpenAPI/Swagger paths.
|
|
190
|
-
*/
|
|
191
|
-
var Url = class Url {
|
|
192
|
-
/**
|
|
193
|
-
* Converts an OpenAPI/Swagger path to Express-style colon syntax.
|
|
194
|
-
*
|
|
195
|
-
* @example
|
|
196
|
-
* Url.toPath('/pet/{petId}') // '/pet/:petId'
|
|
197
|
-
*/
|
|
198
|
-
static toPath(path) {
|
|
199
|
-
return path.replace(/\{([^}]+)\}/g, ":$1");
|
|
200
|
-
}
|
|
201
|
-
/**
|
|
202
|
-
* Rewrites OpenAPI placeholder names while keeping the `{...}` braces, so the generated `url`
|
|
203
|
-
* literal aligns with the grouped `path` request option that the runtime client interpolates by
|
|
204
|
-
* key.
|
|
205
|
-
*
|
|
206
|
-
* @example
|
|
207
|
-
* Url.toSafeTemplate('/user/{monetary-account-id}') // '/user/{monetaryAccountId}'
|
|
208
|
-
*/
|
|
209
|
-
static toSafeTemplate(path) {
|
|
210
|
-
return path.replace(/\{([^}]+)\}/g, (_, name) => `{${transformParam(name)}}`);
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* Converts an OpenAPI/Swagger path to a TypeScript template literal string.
|
|
214
|
-
* `prefix` is prepended inside the literal, and `replacer` transforms each parameter name.
|
|
215
|
-
*
|
|
216
|
-
* @example
|
|
217
|
-
* Url.toTemplateString('/pet/{petId}') // '`/pet/${petId}`'
|
|
218
|
-
*
|
|
219
|
-
* @example
|
|
220
|
-
* Url.toTemplateString('/pet/{petId}', { prefix: 'https://api' }) // '`https://api/pet/${petId}`'
|
|
221
|
-
*/
|
|
222
|
-
static toTemplateString(path, { prefix, replacer } = {}) {
|
|
223
|
-
const result = path.split(/\{([^}]+)\}/).map((part, i) => {
|
|
224
|
-
if (i % 2 === 0) return part;
|
|
225
|
-
const param = transformParam(part);
|
|
226
|
-
return `\${${replacer ? replacer(param) : param}}`;
|
|
227
|
-
}).join("");
|
|
228
|
-
return `\`${prefix ?? ""}${result}\``;
|
|
229
|
-
}
|
|
230
|
-
/**
|
|
231
|
-
* Converts an OpenAPI/Swagger path to a template literal that reads each parameter off the
|
|
232
|
-
* grouped `path` request option, e.g. `/pet/{petId}` becomes `` `/pet/${path.petId}` ``. Parameter
|
|
233
|
-
* names match the generated `path` type, and `prefix` is prepended inside the literal. Shared by
|
|
234
|
-
* the client and cypress generators that pass a grouped `path` object.
|
|
235
|
-
*
|
|
236
|
-
* @example
|
|
237
|
-
* Url.toGroupedTemplateString('/pet/{petId}') // '`/pet/${path.petId}`'
|
|
238
|
-
*/
|
|
239
|
-
static toGroupedTemplateString(path, { prefix } = {}) {
|
|
240
|
-
return Url.toTemplateString(path, {
|
|
241
|
-
prefix,
|
|
242
|
-
replacer: (name) => `path.${name}`
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
};
|
|
246
|
-
//#endregion
|
|
247
7
|
//#region ../../internals/shared/src/params.ts
|
|
248
8
|
/**
|
|
249
9
|
* Drops parameters that share the same name, keeping the first.
|
|
@@ -282,11 +42,30 @@ function operationFileEntry(node, name, extname = ".ts") {
|
|
|
282
42
|
path: node.path
|
|
283
43
|
};
|
|
284
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Resolves a dependency plugin's generated file for `node.operationId`, cached in `cache` (the
|
|
47
|
+
* current node's `ctx.cache`) under the resolver's own plugin name. Several dependents reading the
|
|
48
|
+
* same dependency for the same operation in one pass (a query plugin's several hook generators, the
|
|
49
|
+
* MCP handler, ...) share one computed name and path instead of each calling `resolver.file` again.
|
|
50
|
+
*
|
|
51
|
+
* @example Cache `plugin-ts`'s file for the current operation
|
|
52
|
+
* ```ts
|
|
53
|
+
* const fileTs = resolveDependencyOperationFile({ cache: ctx.cache, node, resolver: tsResolver, root, output })
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
function resolveDependencyOperationFile(options) {
|
|
57
|
+
const { cache, node, resolver, root, output, group } = options;
|
|
58
|
+
return cache.ensureItem(`${resolver.pluginName}:operationFile`, () => resolver.file({
|
|
59
|
+
...operationFileEntry(node, node.operationId),
|
|
60
|
+
root,
|
|
61
|
+
output,
|
|
62
|
+
group: group ?? void 0
|
|
63
|
+
}));
|
|
64
|
+
}
|
|
285
65
|
function getOperationLink(node, link) {
|
|
286
66
|
if (!link) return null;
|
|
287
67
|
if (typeof link === "function") return link(node) ?? null;
|
|
288
|
-
|
|
289
|
-
return node.path ? `{@link ${node.path.replaceAll("{", ":").replaceAll("}", "")}}` : null;
|
|
68
|
+
return node.path ? `{@link ${Url.toPath(node.path)}}` : null;
|
|
290
69
|
}
|
|
291
70
|
/**
|
|
292
71
|
* Derives the shared `ContentTypeInfo` shape from a list of content types, tracking whether several
|
|
@@ -376,16 +155,24 @@ function buildOperationComments(node, options = {}) {
|
|
|
376
155
|
if (!splitLines) return filteredComments;
|
|
377
156
|
return filteredComments.flatMap((text) => text.split(/\r?\n/).map((line) => line.trim())).filter((comment) => Boolean(comment));
|
|
378
157
|
}
|
|
158
|
+
const operationParameterGroupsByNode = /* @__PURE__ */ new WeakMap();
|
|
159
|
+
/**
|
|
160
|
+
* Groups an operation's parameters by location (`path`/`query`/`header`/`cookie`), deduping each
|
|
161
|
+
* group by name. Every plugin generator visiting the same `OperationNode` shares one AST instance
|
|
162
|
+
* (see `KubbDriver`), so the result is cached per node to avoid re-filtering and re-deduping the
|
|
163
|
+
* same parameters once per plugin.
|
|
164
|
+
*/
|
|
379
165
|
function getOperationParameters(node) {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
})),
|
|
166
|
+
const cached = operationParameterGroupsByNode.get(node);
|
|
167
|
+
if (cached) return cached;
|
|
168
|
+
const groups = {
|
|
169
|
+
path: dedupeParams(node.parameters.filter((param) => param.in === "path")),
|
|
385
170
|
query: dedupeParams(node.parameters.filter((param) => param.in === "query")),
|
|
386
171
|
header: dedupeParams(node.parameters.filter((param) => param.in === "header")),
|
|
387
172
|
cookie: dedupeParams(node.parameters.filter((param) => param.in === "cookie"))
|
|
388
173
|
};
|
|
174
|
+
operationParameterGroupsByNode.set(node, groups);
|
|
175
|
+
return groups;
|
|
389
176
|
}
|
|
390
177
|
function getStatusCodeNumber(statusCode) {
|
|
391
178
|
const code = Number(statusCode);
|
|
@@ -450,6 +237,60 @@ function resolveOperationTypeNames(node, resolver, options = {}) {
|
|
|
450
237
|
return result;
|
|
451
238
|
}
|
|
452
239
|
//#endregion
|
|
240
|
+
//#region ../../internals/utils/src/casing.ts
|
|
241
|
+
/**
|
|
242
|
+
* Shared implementation for camelCase and PascalCase conversion.
|
|
243
|
+
* Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
|
|
244
|
+
* and capitalizes each word according to `pascal`.
|
|
245
|
+
*
|
|
246
|
+
* When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
|
|
247
|
+
*/
|
|
248
|
+
function toCamelOrPascal(text, pascal) {
|
|
249
|
+
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) => {
|
|
250
|
+
if (word.length > 1 && word === word.toUpperCase()) return word;
|
|
251
|
+
return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
|
|
252
|
+
}).join("").replace(/[^a-zA-Z0-9]/g, "");
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Converts `text` to camelCase.
|
|
256
|
+
*
|
|
257
|
+
* @example Word boundaries
|
|
258
|
+
* `camelCase('hello-world') // 'helloWorld'`
|
|
259
|
+
*
|
|
260
|
+
* @example With a prefix
|
|
261
|
+
* `camelCase('tag', { prefix: 'create' }) // 'createTag'`
|
|
262
|
+
*/
|
|
263
|
+
function camelCase(text, { prefix = "", suffix = "" } = {}) {
|
|
264
|
+
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Uppercases only the first character of `text`, leaving the rest untouched.
|
|
268
|
+
* Unlike {@link pascalCase} it never re-splits word boundaries or strips characters.
|
|
269
|
+
*
|
|
270
|
+
* @example
|
|
271
|
+
* `capitalize('getPetById') // 'GetPetById'`
|
|
272
|
+
*/
|
|
273
|
+
function capitalize(text) {
|
|
274
|
+
return `${text.charAt(0).toUpperCase()}${text.slice(1)}`;
|
|
275
|
+
}
|
|
276
|
+
//#endregion
|
|
277
|
+
//#region ../../internals/utils/src/strings.ts
|
|
278
|
+
/**
|
|
279
|
+
* Renders a dotted path or string array as an optional-chaining accessor expression rooted at
|
|
280
|
+
* `accessor`. Returns `null` for an empty path.
|
|
281
|
+
*
|
|
282
|
+
* @example
|
|
283
|
+
* ```ts
|
|
284
|
+
* getNestedAccessor('pagination.next.id', 'lastPage')
|
|
285
|
+
* // "lastPage?.['pagination']?.['next']?.['id']"
|
|
286
|
+
* ```
|
|
287
|
+
*/
|
|
288
|
+
function getNestedAccessor(param, accessor) {
|
|
289
|
+
const parts = Array.isArray(param) ? param : param.split(".");
|
|
290
|
+
if (parts.length === 0 || parts.length === 1 && parts[0] === "") return null;
|
|
291
|
+
return `${accessor}?.['${`${parts.join("']?.['")}']`}`;
|
|
292
|
+
}
|
|
293
|
+
//#endregion
|
|
453
294
|
//#region ../../internals/shared/src/group.ts
|
|
454
295
|
/**
|
|
455
296
|
* Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
|
|
@@ -559,14 +400,16 @@ function buildGroupedRequestParam(node, options) {
|
|
|
559
400
|
*/
|
|
560
401
|
function buildQueryOptionsParams(node, options) {
|
|
561
402
|
const { resolver, memberTypeWrapper } = options;
|
|
562
|
-
|
|
403
|
+
const groupedParam = buildGroupedRequestParam(node, {
|
|
563
404
|
resolver,
|
|
564
405
|
memberTypeWrapper
|
|
565
|
-
})
|
|
406
|
+
});
|
|
407
|
+
const configParam = createFunctionParameter({
|
|
566
408
|
name: "config",
|
|
567
409
|
type: `Partial<Omit<RequestConfig, 'path' | 'query' | 'body' | 'headers' | 'url'>>`,
|
|
568
410
|
default: "{}"
|
|
569
|
-
})
|
|
411
|
+
});
|
|
412
|
+
return createFunctionParameters({ params: [groupedParam, configParam].filter((param) => param !== null) });
|
|
570
413
|
}
|
|
571
414
|
/**
|
|
572
415
|
* Builds the call to a client `<op>` function inside a query/mutation hook body. The function takes
|
|
@@ -664,6 +507,19 @@ function resolveMutationConfig(mutation, options) {
|
|
|
664
507
|
};
|
|
665
508
|
}
|
|
666
509
|
/**
|
|
510
|
+
* Classifies an operation as a query or a mutation from the resolved `query` / `mutation` method lists.
|
|
511
|
+
* `query: false` still marks the operation as a query so the query-family generators keep matching it,
|
|
512
|
+
* and a method already claimed by `query` never counts as a mutation.
|
|
513
|
+
*/
|
|
514
|
+
function classifyOperation(node, { query, mutation }) {
|
|
515
|
+
const isQuery = query === false || !!query && query.methods.some((method) => node.method.toLowerCase() === method.toLowerCase());
|
|
516
|
+
const queryMethods = new Set(query ? query.methods : []);
|
|
517
|
+
return {
|
|
518
|
+
isQuery,
|
|
519
|
+
isMutation: mutation !== false && !isQuery && (mutation ? mutation.methods : []).some((method) => !queryMethods.has(method) && node.method.toLowerCase() === method.toLowerCase())
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
667
523
|
* Applies the shared infinite-query defaults during plugin setup: a falsy value disables infinite
|
|
668
524
|
* queries, and an object merges over `queryParam: 'id'` and `initialPageParam: 0` with the cursor
|
|
669
525
|
* paths cleared.
|
|
@@ -936,23 +792,30 @@ function resolveContractClient(options) {
|
|
|
936
792
|
* (plugin-fetch or plugin-axios). Returns `null` when no contract plugin is in play (the inline
|
|
937
793
|
* path). The plugin injects `.kubb/client.ts` at the global output root, the same path consumers
|
|
938
794
|
* read `RequestConfig` / `ResponseErrorConfig` from.
|
|
795
|
+
*
|
|
796
|
+
* The result is cached in the current node's `cache` (`ctx.cache`) under the client plugin's name,
|
|
797
|
+
* so several dependents reading the same client plugin for one operation in a single pass
|
|
798
|
+
* (react-query's query/mutation/infinite generators, vue-query, swr, the MCP handler, ...) share
|
|
799
|
+
* one computed result instead of each re-deriving the name and path.
|
|
939
800
|
*/
|
|
940
801
|
function resolveClientOperation(options) {
|
|
941
|
-
const { clientPlugin, driver, node, root, output } = options;
|
|
802
|
+
const { clientPlugin, driver, node, root, output, cache } = options;
|
|
942
803
|
if (!clientPlugin) return null;
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
804
|
+
return cache.ensureItem(`${clientPlugin.pluginName}:clientOperation`, () => {
|
|
805
|
+
const resolver = driver.getResolver(clientPlugin.pluginName);
|
|
806
|
+
const plugin = driver.getPlugin(clientPlugin.pluginName);
|
|
807
|
+
const file = resolver.file({
|
|
808
|
+
...operationFileEntry(node, node.operationId),
|
|
809
|
+
root,
|
|
810
|
+
output: plugin?.options?.output ?? output,
|
|
811
|
+
group: plugin?.options?.group ?? void 0
|
|
812
|
+
});
|
|
813
|
+
return {
|
|
814
|
+
name: resolver.name(node.operationId),
|
|
815
|
+
path: file.path,
|
|
816
|
+
clientPath: path.resolve(root, ".kubb/client.ts")
|
|
817
|
+
};
|
|
950
818
|
});
|
|
951
|
-
return {
|
|
952
|
-
name: resolver.name(node.operationId),
|
|
953
|
-
path: file.path,
|
|
954
|
-
clientPath: path.resolve(root, ".kubb/client.ts")
|
|
955
|
-
};
|
|
956
819
|
}
|
|
957
820
|
//#endregion
|
|
958
821
|
//#region src/utils.ts
|
|
@@ -1076,10 +939,11 @@ function buildInfiniteQueryParamsNode(node, options) {
|
|
|
1076
939
|
}`,
|
|
1077
940
|
default: "{}"
|
|
1078
941
|
});
|
|
1079
|
-
|
|
942
|
+
const groupedParam = buildGroupedRequestParam(node, {
|
|
1080
943
|
resolver,
|
|
1081
944
|
memberTypeWrapper: maybeRefOrGetter
|
|
1082
|
-
})
|
|
945
|
+
});
|
|
946
|
+
return createFunctionParameters({ params: [groupedParam, optionsParam].filter((param) => param !== null) });
|
|
1083
947
|
}
|
|
1084
948
|
function InfiniteQuery({ name, queryKeyTypeName, queryOptionsName, queryKeyName, node, tsResolver }) {
|
|
1085
949
|
const { TData, TError } = buildResponseTypes(node, tsResolver);
|
|
@@ -1148,13 +1012,14 @@ function resolveMutationRequestType(node, resolver) {
|
|
|
1148
1012
|
function buildMutationParamsNode(node, options) {
|
|
1149
1013
|
const { resolver } = options;
|
|
1150
1014
|
const { TData, TError } = buildResponseTypes(node, resolver);
|
|
1015
|
+
const TRequest = resolveMutationRequestType(node, resolver);
|
|
1151
1016
|
return createFunctionParameters({ params: [createFunctionParameter({
|
|
1152
1017
|
name: "options",
|
|
1153
1018
|
type: `{
|
|
1154
1019
|
mutation?: MutationObserverOptions<${[
|
|
1155
1020
|
TData,
|
|
1156
1021
|
TError,
|
|
1157
|
-
|
|
1022
|
+
TRequest,
|
|
1158
1023
|
"TContext"
|
|
1159
1024
|
].join(", ")}> & { client?: QueryClient },
|
|
1160
1025
|
client?: ${buildRequestConfigType(node)},
|
|
@@ -1228,10 +1093,11 @@ function buildQueryParamsNode(node, options) {
|
|
|
1228
1093
|
}`,
|
|
1229
1094
|
default: "{}"
|
|
1230
1095
|
});
|
|
1231
|
-
|
|
1096
|
+
const groupedParam = buildGroupedRequestParam(node, {
|
|
1232
1097
|
resolver,
|
|
1233
1098
|
memberTypeWrapper: maybeRefOrGetter
|
|
1234
|
-
})
|
|
1099
|
+
});
|
|
1100
|
+
return createFunctionParameters({ params: [groupedParam, optionsParam].filter((param) => param !== null) });
|
|
1235
1101
|
}
|
|
1236
1102
|
function Query({ name, queryKeyTypeName, queryOptionsName, queryKeyName, node, tsResolver }) {
|
|
1237
1103
|
const { TData, TError } = buildResponseTypes(node, tsResolver);
|
|
@@ -1279,9 +1145,9 @@ function Query({ name, queryKeyTypeName, queryOptionsName, queryKeyName, node, t
|
|
|
1279
1145
|
//#region src/generators/infiniteQueryGenerator.tsx
|
|
1280
1146
|
/**
|
|
1281
1147
|
* Built-in generator for `useInfiniteQuery` composables. Enabled when
|
|
1282
|
-
* `pluginVueQuery({ infinite: { ... } })`. Emits one
|
|
1283
|
-
* composable per query operation, wiring the
|
|
1284
|
-
* TanStack Query's cursor-based pagination.
|
|
1148
|
+
* `pluginVueQuery({ infinite: { ... }, hooks: true })`. Emits one
|
|
1149
|
+
* `useFooInfiniteQuery` composable per query operation, wiring the
|
|
1150
|
+
* configured cursor path into TanStack Query's cursor-based pagination.
|
|
1285
1151
|
*/
|
|
1286
1152
|
const infiniteQueryGenerator = defineGenerator({
|
|
1287
1153
|
name: "vue-query-infinite",
|
|
@@ -1293,11 +1159,12 @@ const infiniteQueryGenerator = defineGenerator({
|
|
|
1293
1159
|
const pluginTs = driver.getPlugin(pluginTsName);
|
|
1294
1160
|
if (!pluginTs) return null;
|
|
1295
1161
|
const tsResolver = driver.getResolver(pluginTsName);
|
|
1296
|
-
const isQuery
|
|
1297
|
-
|
|
1298
|
-
|
|
1162
|
+
const { isQuery, isMutation } = classifyOperation(node, {
|
|
1163
|
+
query,
|
|
1164
|
+
mutation
|
|
1165
|
+
});
|
|
1299
1166
|
const infiniteOptions = infinite && typeof infinite === "object" ? infinite : null;
|
|
1300
|
-
if (!isQuery || isMutation || !infiniteOptions) return null;
|
|
1167
|
+
if (!isQuery || isMutation || !infiniteOptions || !hooks) return null;
|
|
1301
1168
|
const normalizeKey = (key) => key.replace(/\?$/, "");
|
|
1302
1169
|
const queryParamKeys = getOperationParameters(node).query.map((p) => p.name);
|
|
1303
1170
|
if (!(infiniteOptions.queryParam ? queryParamKeys.some((k) => normalizeKey(k) === infiniteOptions.queryParam) : false)) return null;
|
|
@@ -1307,7 +1174,8 @@ const infiniteQueryGenerator = defineGenerator({
|
|
|
1307
1174
|
driver,
|
|
1308
1175
|
node,
|
|
1309
1176
|
root,
|
|
1310
|
-
output
|
|
1177
|
+
output,
|
|
1178
|
+
cache: ctx.cache
|
|
1311
1179
|
});
|
|
1312
1180
|
if (!contractOp) return null;
|
|
1313
1181
|
const queryName = resolver.infiniteQuery.name(node);
|
|
@@ -1321,15 +1189,20 @@ const infiniteQueryGenerator = defineGenerator({
|
|
|
1321
1189
|
output,
|
|
1322
1190
|
group: group ?? void 0
|
|
1323
1191
|
}),
|
|
1324
|
-
fileTs:
|
|
1325
|
-
|
|
1192
|
+
fileTs: resolveDependencyOperationFile({
|
|
1193
|
+
cache: ctx.cache,
|
|
1194
|
+
node,
|
|
1195
|
+
resolver: tsResolver,
|
|
1326
1196
|
root,
|
|
1327
1197
|
output: pluginTs.options?.output ?? output,
|
|
1328
|
-
group: pluginTs.options?.group
|
|
1198
|
+
group: pluginTs.options?.group
|
|
1329
1199
|
})
|
|
1330
1200
|
};
|
|
1331
|
-
const
|
|
1332
|
-
|
|
1201
|
+
const { queryParamsTypeName } = resolvePageParamType(node, {
|
|
1202
|
+
resolver: tsResolver,
|
|
1203
|
+
initialPageParam: infiniteOptions.initialPageParam,
|
|
1204
|
+
queryParam: infiniteOptions.queryParam
|
|
1205
|
+
});
|
|
1333
1206
|
const importedTypeNames = [
|
|
1334
1207
|
tsResolver.response.options(node),
|
|
1335
1208
|
queryParamsTypeName,
|
|
@@ -1415,32 +1288,30 @@ const infiniteQueryGenerator = defineGenerator({
|
|
|
1415
1288
|
initialPageParam: infiniteOptions.initialPageParam,
|
|
1416
1289
|
queryParam: infiniteOptions.queryParam
|
|
1417
1290
|
}),
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
})
|
|
1443
|
-
] })
|
|
1291
|
+
/* @__PURE__ */ jsx(File.Import, {
|
|
1292
|
+
name: ["useInfiniteQuery"],
|
|
1293
|
+
path: importPath
|
|
1294
|
+
}),
|
|
1295
|
+
/* @__PURE__ */ jsx(File.Import, {
|
|
1296
|
+
name: [
|
|
1297
|
+
"QueryKey",
|
|
1298
|
+
"QueryClient",
|
|
1299
|
+
"UseInfiniteQueryOptions",
|
|
1300
|
+
"UseInfiniteQueryReturnType"
|
|
1301
|
+
],
|
|
1302
|
+
path: importPath,
|
|
1303
|
+
isTypeOnly: true
|
|
1304
|
+
}),
|
|
1305
|
+
/* @__PURE__ */ jsx(InfiniteQuery, {
|
|
1306
|
+
name: queryName,
|
|
1307
|
+
queryOptionsName,
|
|
1308
|
+
queryKeyName,
|
|
1309
|
+
queryKeyTypeName,
|
|
1310
|
+
node,
|
|
1311
|
+
tsResolver,
|
|
1312
|
+
initialPageParam: infiniteOptions.initialPageParam,
|
|
1313
|
+
queryParam: infiniteOptions.queryParam
|
|
1314
|
+
})
|
|
1444
1315
|
]
|
|
1445
1316
|
});
|
|
1446
1317
|
}
|
|
@@ -1462,16 +1333,19 @@ const mutationGenerator = defineGenerator({
|
|
|
1462
1333
|
const pluginTs = driver.getPlugin(pluginTsName);
|
|
1463
1334
|
if (!pluginTs) return null;
|
|
1464
1335
|
const tsResolver = driver.getResolver(pluginTsName);
|
|
1465
|
-
const
|
|
1466
|
-
|
|
1467
|
-
|
|
1336
|
+
const { isMutation } = classifyOperation(node, {
|
|
1337
|
+
query,
|
|
1338
|
+
mutation
|
|
1339
|
+
});
|
|
1340
|
+
if (!isMutation) return null;
|
|
1468
1341
|
const importPath = mutation ? mutation.importPath : "@tanstack/vue-query";
|
|
1469
1342
|
const contractOp = resolveClientOperation({
|
|
1470
1343
|
clientPlugin: { pluginName: client.pluginName },
|
|
1471
1344
|
driver,
|
|
1472
1345
|
node,
|
|
1473
1346
|
root,
|
|
1474
|
-
output
|
|
1347
|
+
output,
|
|
1348
|
+
cache: ctx.cache
|
|
1475
1349
|
});
|
|
1476
1350
|
if (!contractOp) return null;
|
|
1477
1351
|
const mutationHookName = resolver.mutation.name(node);
|
|
@@ -1484,11 +1358,13 @@ const mutationGenerator = defineGenerator({
|
|
|
1484
1358
|
output,
|
|
1485
1359
|
group: group ?? void 0
|
|
1486
1360
|
}),
|
|
1487
|
-
fileTs:
|
|
1488
|
-
|
|
1361
|
+
fileTs: resolveDependencyOperationFile({
|
|
1362
|
+
cache: ctx.cache,
|
|
1363
|
+
node,
|
|
1364
|
+
resolver: tsResolver,
|
|
1489
1365
|
root,
|
|
1490
1366
|
output: pluginTs.options?.output ?? output,
|
|
1491
|
-
group: pluginTs.options?.group
|
|
1367
|
+
group: pluginTs.options?.group
|
|
1492
1368
|
})
|
|
1493
1369
|
};
|
|
1494
1370
|
const importedTypeNames = [tsResolver.response.options(node), ...resolveOperationTypeNames(node, tsResolver, {
|
|
@@ -1590,9 +1466,10 @@ const queryGenerator = defineGenerator({
|
|
|
1590
1466
|
const pluginTs = driver.getPlugin(pluginTsName);
|
|
1591
1467
|
if (!pluginTs) return null;
|
|
1592
1468
|
const tsResolver = driver.getResolver(pluginTsName);
|
|
1593
|
-
const isQuery
|
|
1594
|
-
|
|
1595
|
-
|
|
1469
|
+
const { isQuery, isMutation } = classifyOperation(node, {
|
|
1470
|
+
query,
|
|
1471
|
+
mutation
|
|
1472
|
+
});
|
|
1596
1473
|
if (!isQuery || isMutation) return null;
|
|
1597
1474
|
const importPath = query ? query.importPath : "@tanstack/vue-query";
|
|
1598
1475
|
const contractOp = resolveClientOperation({
|
|
@@ -1600,7 +1477,8 @@ const queryGenerator = defineGenerator({
|
|
|
1600
1477
|
driver,
|
|
1601
1478
|
node,
|
|
1602
1479
|
root,
|
|
1603
|
-
output
|
|
1480
|
+
output,
|
|
1481
|
+
cache: ctx.cache
|
|
1604
1482
|
});
|
|
1605
1483
|
if (!contractOp) return null;
|
|
1606
1484
|
const queryName = resolver.query.name(node);
|
|
@@ -1614,11 +1492,13 @@ const queryGenerator = defineGenerator({
|
|
|
1614
1492
|
output,
|
|
1615
1493
|
group: group ?? void 0
|
|
1616
1494
|
}),
|
|
1617
|
-
fileTs:
|
|
1618
|
-
|
|
1495
|
+
fileTs: resolveDependencyOperationFile({
|
|
1496
|
+
cache: ctx.cache,
|
|
1497
|
+
node,
|
|
1498
|
+
resolver: tsResolver,
|
|
1619
1499
|
root,
|
|
1620
1500
|
output: pluginTs.options?.output ?? output,
|
|
1621
|
-
group: pluginTs.options?.group
|
|
1501
|
+
group: pluginTs.options?.group
|
|
1622
1502
|
})
|
|
1623
1503
|
};
|
|
1624
1504
|
const importedTypeNames = [tsResolver.response.options(node), ...resolveOperationTypeNames(node, tsResolver, {
|