@kubb/plugin-swr 5.0.0-beta.99 → 5.0.1

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.js CHANGED
@@ -1,232 +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/url.ts
164
- /**
165
- * Keeps the OpenAPI parameter name as-is when it is already a valid JS identifier, and
166
- * camelCases it only enough to become one otherwise (for example a hyphenated path segment).
167
- */
168
- function transformParam(raw) {
169
- return isValidVarName(raw) ? raw : camelCase(raw);
170
- }
171
- /**
172
- * Helpers for OpenAPI/Swagger paths.
173
- */
174
- var Url = class Url {
175
- /**
176
- * Converts an OpenAPI/Swagger path to Express-style colon syntax.
177
- *
178
- * @example
179
- * Url.toPath('/pet/{petId}') // '/pet/:petId'
180
- */
181
- static toPath(path) {
182
- return path.replace(/\{([^}]+)\}/g, ":$1");
183
- }
184
- /**
185
- * Rewrites OpenAPI placeholder names while keeping the `{...}` braces, so the generated `url`
186
- * literal aligns with the grouped `path` request option that the runtime client interpolates by
187
- * key.
188
- *
189
- * @example
190
- * Url.toSafeTemplate('/user/{monetary-account-id}') // '/user/{monetaryAccountId}'
191
- */
192
- static toSafeTemplate(path) {
193
- return path.replace(/\{([^}]+)\}/g, (_, name) => `{${transformParam(name)}}`);
194
- }
195
- /**
196
- * Converts an OpenAPI/Swagger path to a TypeScript template literal string.
197
- * `prefix` is prepended inside the literal, and `replacer` transforms each parameter name.
198
- *
199
- * @example
200
- * Url.toTemplateString('/pet/{petId}') // '`/pet/${petId}`'
201
- *
202
- * @example
203
- * Url.toTemplateString('/pet/{petId}', { prefix: 'https://api' }) // '`https://api/pet/${petId}`'
204
- */
205
- static toTemplateString(path, { prefix, replacer } = {}) {
206
- const result = path.split(/\{([^}]+)\}/).map((part, i) => {
207
- if (i % 2 === 0) return part;
208
- const param = transformParam(part);
209
- return `\${${replacer ? replacer(param) : param}}`;
210
- }).join("");
211
- return `\`${prefix ?? ""}${result}\``;
212
- }
213
- /**
214
- * Converts an OpenAPI/Swagger path to a template literal that reads each parameter off the
215
- * grouped `path` request option, e.g. `/pet/{petId}` becomes `` `/pet/${path.petId}` ``. Parameter
216
- * names match the generated `path` type, and `prefix` is prepended inside the literal. Shared by
217
- * the client and cypress generators that pass a grouped `path` object.
218
- *
219
- * @example
220
- * Url.toGroupedTemplateString('/pet/{petId}') // '`/pet/${path.petId}`'
221
- */
222
- static toGroupedTemplateString(path, { prefix } = {}) {
223
- return Url.toTemplateString(path, {
224
- prefix,
225
- replacer: (name) => `path.${name}`
226
- });
227
- }
228
- };
229
- //#endregion
230
7
  //#region ../../internals/shared/src/params.ts
231
8
  /**
232
9
  * Drops parameters that share the same name, keeping the first.
@@ -265,11 +42,30 @@ function operationFileEntry(node, name, extname = ".ts") {
265
42
  path: node.path
266
43
  };
267
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
+ }
268
65
  function getOperationLink(node, link) {
269
66
  if (!link) return null;
270
67
  if (typeof link === "function") return link(node) ?? null;
271
- if (link === "urlPath") return node.path ? `{@link ${Url.toPath(node.path)}}` : null;
272
- return node.path ? `{@link ${node.path.replaceAll("{", ":").replaceAll("}", "")}}` : null;
68
+ return node.path ? `{@link ${Url.toPath(node.path)}}` : null;
273
69
  }
274
70
  /**
275
71
  * Derives the shared `ContentTypeInfo` shape from a list of content types, tracking whether several
@@ -359,16 +155,24 @@ function buildOperationComments(node, options = {}) {
359
155
  if (!splitLines) return filteredComments;
360
156
  return filteredComments.flatMap((text) => text.split(/\r?\n/).map((line) => line.trim())).filter((comment) => Boolean(comment));
361
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
+ */
362
165
  function getOperationParameters(node) {
363
- return {
364
- path: dedupeParams(node.parameters.filter((param) => param.in === "path").map((param) => isValidVarName(param.name) ? param : {
365
- ...param,
366
- name: camelCase(param.name)
367
- })),
166
+ const cached = operationParameterGroupsByNode.get(node);
167
+ if (cached) return cached;
168
+ const groups = {
169
+ path: dedupeParams(node.parameters.filter((param) => param.in === "path")),
368
170
  query: dedupeParams(node.parameters.filter((param) => param.in === "query")),
369
171
  header: dedupeParams(node.parameters.filter((param) => param.in === "header")),
370
172
  cookie: dedupeParams(node.parameters.filter((param) => param.in === "cookie"))
371
173
  };
174
+ operationParameterGroupsByNode.set(node, groups);
175
+ return groups;
372
176
  }
373
177
  function getStatusCodeNumber(statusCode) {
374
178
  const code = Number(statusCode);
@@ -430,6 +234,43 @@ function resolveOperationTypeNames(node, resolver, options = {}) {
430
234
  return result;
431
235
  }
432
236
  //#endregion
237
+ //#region ../../internals/utils/src/casing.ts
238
+ /**
239
+ * Shared implementation for camelCase and PascalCase conversion.
240
+ * Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
241
+ * and capitalizes each word according to `pascal`.
242
+ *
243
+ * When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
244
+ */
245
+ function toCamelOrPascal(text, pascal) {
246
+ 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) => {
247
+ if (word.length > 1 && word === word.toUpperCase()) return word;
248
+ return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
249
+ }).join("").replace(/[^a-zA-Z0-9]/g, "");
250
+ }
251
+ /**
252
+ * Converts `text` to camelCase.
253
+ *
254
+ * @example Word boundaries
255
+ * `camelCase('hello-world') // 'helloWorld'`
256
+ *
257
+ * @example With a prefix
258
+ * `camelCase('tag', { prefix: 'create' }) // 'createTag'`
259
+ */
260
+ function camelCase(text, { prefix = "", suffix = "" } = {}) {
261
+ return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
262
+ }
263
+ /**
264
+ * Uppercases only the first character of `text`, leaving the rest untouched.
265
+ * Unlike {@link pascalCase} it never re-splits word boundaries or strips characters.
266
+ *
267
+ * @example
268
+ * `capitalize('getPetById') // 'GetPetById'`
269
+ */
270
+ function capitalize(text) {
271
+ return `${text.charAt(0).toUpperCase()}${text.slice(1)}`;
272
+ }
273
+ //#endregion
433
274
  //#region ../../internals/shared/src/group.ts
434
275
  /**
435
276
  * Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
@@ -549,23 +390,30 @@ function resolveContractClient(options) {
549
390
  * (plugin-fetch or plugin-axios). Returns `null` when no contract plugin is in play (the inline
550
391
  * path). The plugin injects `.kubb/client.ts` at the global output root, the same path consumers
551
392
  * read `RequestConfig` / `ResponseErrorConfig` from.
393
+ *
394
+ * The result is cached in the current node's `cache` (`ctx.cache`) under the client plugin's name,
395
+ * so several dependents reading the same client plugin for one operation in a single pass
396
+ * (react-query's query/mutation/infinite generators, vue-query, swr, the MCP handler, ...) share
397
+ * one computed result instead of each re-deriving the name and path.
552
398
  */
553
399
  function resolveClientOperation(options) {
554
- const { clientPlugin, driver, node, root, output } = options;
400
+ const { clientPlugin, driver, node, root, output, cache } = options;
555
401
  if (!clientPlugin) return null;
556
- const resolver = driver.getResolver(clientPlugin.pluginName);
557
- const plugin = driver.getPlugin(clientPlugin.pluginName);
558
- const file = resolver.file({
559
- ...operationFileEntry(node, node.operationId),
560
- root,
561
- output: plugin?.options?.output ?? output,
562
- group: plugin?.options?.group ?? void 0
402
+ return cache.ensureItem(`${clientPlugin.pluginName}:clientOperation`, () => {
403
+ const resolver = driver.getResolver(clientPlugin.pluginName);
404
+ const plugin = driver.getPlugin(clientPlugin.pluginName);
405
+ const file = resolver.file({
406
+ ...operationFileEntry(node, node.operationId),
407
+ root,
408
+ output: plugin?.options?.output ?? output,
409
+ group: plugin?.options?.group ?? void 0
410
+ });
411
+ return {
412
+ name: resolver.name(node.operationId),
413
+ path: file.path,
414
+ clientPath: path.resolve(root, ".kubb/client.ts")
415
+ };
563
416
  });
564
- return {
565
- name: resolver.name(node.operationId),
566
- path: file.path,
567
- clientPath: path.resolve(root, ".kubb/client.ts")
568
- };
569
417
  }
570
418
  //#endregion
571
419
  //#region ../../internals/tanstack-query/src/utils.ts
@@ -633,14 +481,16 @@ function buildGroupedRequestParam(node, options) {
633
481
  */
634
482
  function buildQueryOptionsParams(node, options) {
635
483
  const { resolver, memberTypeWrapper } = options;
636
- return createFunctionParameters({ params: [buildGroupedRequestParam(node, {
484
+ const groupedParam = buildGroupedRequestParam(node, {
637
485
  resolver,
638
486
  memberTypeWrapper
639
- }), createFunctionParameter({
487
+ });
488
+ const configParam = createFunctionParameter({
640
489
  name: "config",
641
490
  type: `Partial<Omit<RequestConfig, 'path' | 'query' | 'body' | 'headers' | 'url'>>`,
642
491
  default: "{}"
643
- })].filter((param) => param !== null) });
492
+ });
493
+ return createFunctionParameters({ params: [groupedParam, configParam].filter((param) => param !== null) });
644
494
  }
645
495
  /**
646
496
  * Builds the call to a client `<op>` function inside a query/mutation hook body. The function takes
@@ -698,6 +548,29 @@ function resolveMutationConfig(mutation, options) {
698
548
  };
699
549
  }
700
550
  /**
551
+ * Classifies an operation as a query or a mutation from the resolved `query` / `mutation` method lists.
552
+ * `query: false` still marks the operation as a query so the query-family generators keep matching it,
553
+ * and a method already claimed by `query` never counts as a mutation.
554
+ */
555
+ function classifyOperation(node, { query, mutation }) {
556
+ const isQuery = query === false || !!query && query.methods.some((method) => node.method.toLowerCase() === method.toLowerCase());
557
+ const queryMethods = new Set(query ? query.methods : []);
558
+ return {
559
+ isQuery,
560
+ isMutation: mutation !== false && !isQuery && (mutation ? mutation.methods : []).some((method) => !queryMethods.has(method) && node.method.toLowerCase() === method.toLowerCase())
561
+ };
562
+ }
563
+ /**
564
+ * The request groups a query key is built from, matching `buildQueryKeyParams`. Headers are
565
+ * deliberately absent: they do not identify a cache entry, so the key factory takes no parameter
566
+ * for an operation that only carries headers.
567
+ */
568
+ const queryKeyGroupOrder = [
569
+ "path",
570
+ "query",
571
+ "body"
572
+ ];
573
+ /**
701
574
  * Build QueryKey params as the grouped `{ path, query, body }` object (NO headers, NO config),
702
575
  * typed from the operation's `Options` minus `url`. The query key transformer reads the
703
576
  * grouped `path`/`query`/`body` bindings.
@@ -706,11 +579,7 @@ function buildQueryKeyParams(node, options) {
706
579
  const { resolver } = options;
707
580
  const groupedParam = buildGroupedRequestParam(node, {
708
581
  resolver,
709
- keys: [
710
- "path",
711
- "query",
712
- "body"
713
- ]
582
+ keys: queryKeyGroupOrder
714
583
  });
715
584
  return createFunctionParameters({ params: groupedParam ? [groupedParam] : [] });
716
585
  }
@@ -844,10 +713,12 @@ function buildMutationParamsNode(node, options) {
844
713
  const { mutationKeyTypeName, mutationArgTypeName, resolver } = options;
845
714
  const responseName = resolver.response.response(node);
846
715
  const errorNames = resolveErrorNames(node, resolver);
716
+ const TData = responseName;
717
+ const TError = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`;
847
718
  return createFunctionParameters({ params: [createFunctionParameter({
848
719
  name: "options",
849
720
  type: `{
850
- mutation?: SWRMutationConfiguration<${responseName}, ${`ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`}, ${mutationKeyTypeName} | null, ${mutationArgTypeName}> & { throwOnError?: boolean },
721
+ mutation?: SWRMutationConfiguration<${TData}, ${TError}, ${mutationKeyTypeName} | null, ${mutationArgTypeName}> & { throwOnError?: boolean },
851
722
  client?: ${buildRequestConfigType(node)},
852
723
  shouldFetch?: boolean,
853
724
  }`,
@@ -864,11 +735,10 @@ function Mutation({ name, clientName, mutationKeyName, mutationKeyTypeName, muta
864
735
  const groupedParamsNode = createFunctionParameters({ params: groupedParam ? [groupedParam] : [] });
865
736
  const argTypeBody = hasMutationParams ? tsResolver.response.options(node) : "";
866
737
  const argBindingStr = hasMutationParams ? callPrinter$1.print(groupedParamsNode) ?? "" : "";
867
- const mutationFnBody = `const { data } = await ${buildClientCall(node, {
738
+ const mutationFnBody = `return ${buildClientCall(node, {
868
739
  clientName,
869
740
  signal: false
870
- })}
871
- return data`;
741
+ })}.unwrap()`;
872
742
  const generics = [
873
743
  TData,
874
744
  TError,
@@ -942,17 +812,20 @@ function buildQueryParamsNode(node, options) {
942
812
  const { resolver } = options;
943
813
  const responseName = resolver.response.response(node);
944
814
  const errorNames = resolveErrorNames(node, resolver);
815
+ const TData = responseName;
816
+ const TError = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`;
945
817
  const optionsParam = createFunctionParameter({
946
818
  name: "options",
947
819
  type: `{
948
- query?: SWRConfiguration<${[responseName, `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`].join(", ")}>,
820
+ query?: SWRConfiguration<${[TData, TError].join(", ")}>,
949
821
  client?: ${buildClientOptionType()},
950
822
  shouldFetch?: boolean,
951
823
  immutable?: boolean
952
824
  }`,
953
825
  default: "{}"
954
826
  });
955
- return createFunctionParameters({ params: [buildGroupedRequestParam(node, { resolver }), optionsParam].filter((param) => param !== null) });
827
+ const groupedParam = buildGroupedRequestParam(node, { resolver });
828
+ return createFunctionParameters({ params: [groupedParam, optionsParam].filter((param) => param !== null) });
956
829
  }
957
830
  function Query({ name, queryKeyTypeName, queryOptionsName, queryKeyName, node, tsResolver }) {
958
831
  const responseName = tsResolver.response.response(node);
@@ -1004,11 +877,10 @@ const declarationPrinter = functionPrinter({ mode: "declaration" });
1004
877
  function QueryOptions({ name, clientName, node, tsResolver }) {
1005
878
  const paramsNode = buildQueryOptionsParams(node, { resolver: tsResolver });
1006
879
  const paramsSignature = declarationPrinter.print(paramsNode) ?? "";
1007
- const fetcherBody = `const { data } = await ${buildClientCall(node, {
880
+ const fetcherBody = `return ${buildClientCall(node, {
1008
881
  clientName,
1009
882
  signal: false
1010
- })}
1011
- return data`;
883
+ })}.unwrap()`;
1012
884
  return /* @__PURE__ */ jsx(File.Source, {
1013
885
  name,
1014
886
  isExportable: true,
@@ -1044,16 +916,19 @@ const mutationGenerator = defineGenerator({
1044
916
  const pluginTs = driver.getPlugin(pluginTsName);
1045
917
  if (!pluginTs) return null;
1046
918
  const tsResolver = driver.getResolver(pluginTsName);
1047
- const isQuery = query === false || !!query && query.methods.some((method) => node.method.toLowerCase() === method.toLowerCase());
1048
- const queryMethods = new Set(query ? query.methods : []);
1049
- if (!(mutation !== false && !isQuery && (mutation ? mutation.methods : []).some((method) => !queryMethods.has(method) && node.method.toLowerCase() === method.toLowerCase()))) return null;
919
+ const { isMutation } = classifyOperation(node, {
920
+ query,
921
+ mutation
922
+ });
923
+ if (!isMutation) return null;
1050
924
  const importPath = mutation ? mutation.importPath : "swr/mutation";
1051
925
  const contractOp = resolveClientOperation({
1052
926
  clientPlugin: { pluginName: client.pluginName },
1053
927
  driver,
1054
928
  node,
1055
929
  root,
1056
- output
930
+ output,
931
+ cache: ctx.cache
1057
932
  });
1058
933
  if (!contractOp) return null;
1059
934
  const mutationHookName = resolver.mutation.name(node);
@@ -1070,11 +945,10 @@ const mutationGenerator = defineGenerator({
1070
945
  output,
1071
946
  group
1072
947
  }),
1073
- fileTs: tsResolver.file({
1074
- name: node.operationId,
1075
- extname: ".ts",
1076
- tag: node.tags[0] ?? "default",
1077
- path: node.path,
948
+ fileTs: resolveDependencyOperationFile({
949
+ cache: ctx.cache,
950
+ node,
951
+ resolver: tsResolver,
1078
952
  root,
1079
953
  output: pluginTs.options?.output ?? output,
1080
954
  group: pluginTs.options?.group
@@ -1165,18 +1039,23 @@ const queryGenerator = defineGenerator({
1165
1039
  operation(node, ctx) {
1166
1040
  if (!ast.isHttpOperationNode(node)) return null;
1167
1041
  const { config, driver, resolver, root } = ctx;
1168
- const { output, query, client, group } = ctx.options;
1042
+ const { output, query, mutation, client, group } = ctx.options;
1169
1043
  const pluginTs = driver.getPlugin(pluginTsName);
1170
1044
  if (!pluginTs) return null;
1171
1045
  const tsResolver = driver.getResolver(pluginTsName);
1172
- if (!(query === false || !!query && query.methods.some((method) => node.method.toLowerCase() === method.toLowerCase()))) return null;
1046
+ const { isQuery } = classifyOperation(node, {
1047
+ query,
1048
+ mutation
1049
+ });
1050
+ if (!isQuery) return null;
1173
1051
  const importPath = query ? query.importPath : "swr";
1174
1052
  const contractOp = resolveClientOperation({
1175
1053
  clientPlugin: { pluginName: client.pluginName },
1176
1054
  driver,
1177
1055
  node,
1178
1056
  root,
1179
- output
1057
+ output,
1058
+ cache: ctx.cache
1180
1059
  });
1181
1060
  if (!contractOp) return null;
1182
1061
  const queryName = resolver.query.name(node);
@@ -1193,11 +1072,10 @@ const queryGenerator = defineGenerator({
1193
1072
  output,
1194
1073
  group
1195
1074
  }),
1196
- fileTs: tsResolver.file({
1197
- name: node.operationId,
1198
- extname: ".ts",
1199
- tag: node.tags[0] ?? "default",
1200
- path: node.path,
1075
+ fileTs: resolveDependencyOperationFile({
1076
+ cache: ctx.cache,
1077
+ node,
1078
+ resolver: tsResolver,
1201
1079
  root,
1202
1080
  output: pluginTs.options?.output ?? output,
1203
1081
  group: pluginTs.options?.group