@kubb/plugin-swr 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.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,19 @@ 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
+ /**
701
564
  * Build QueryKey params as the grouped `{ path, query, body }` object (NO headers, NO config),
702
565
  * typed from the operation's `Options` minus `url`. The query key transformer reads the
703
566
  * grouped `path`/`query`/`body` bindings.
@@ -844,10 +707,12 @@ function buildMutationParamsNode(node, options) {
844
707
  const { mutationKeyTypeName, mutationArgTypeName, resolver } = options;
845
708
  const responseName = resolver.response.response(node);
846
709
  const errorNames = resolveErrorNames(node, resolver);
710
+ const TData = responseName;
711
+ const TError = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`;
847
712
  return createFunctionParameters({ params: [createFunctionParameter({
848
713
  name: "options",
849
714
  type: `{
850
- mutation?: SWRMutationConfiguration<${responseName}, ${`ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`}, ${mutationKeyTypeName} | null, ${mutationArgTypeName}> & { throwOnError?: boolean },
715
+ mutation?: SWRMutationConfiguration<${TData}, ${TError}, ${mutationKeyTypeName} | null, ${mutationArgTypeName}> & { throwOnError?: boolean },
851
716
  client?: ${buildRequestConfigType(node)},
852
717
  shouldFetch?: boolean,
853
718
  }`,
@@ -942,17 +807,20 @@ function buildQueryParamsNode(node, options) {
942
807
  const { resolver } = options;
943
808
  const responseName = resolver.response.response(node);
944
809
  const errorNames = resolveErrorNames(node, resolver);
810
+ const TData = responseName;
811
+ const TError = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`;
945
812
  const optionsParam = createFunctionParameter({
946
813
  name: "options",
947
814
  type: `{
948
- query?: SWRConfiguration<${[responseName, `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`].join(", ")}>,
815
+ query?: SWRConfiguration<${[TData, TError].join(", ")}>,
949
816
  client?: ${buildClientOptionType()},
950
817
  shouldFetch?: boolean,
951
818
  immutable?: boolean
952
819
  }`,
953
820
  default: "{}"
954
821
  });
955
- return createFunctionParameters({ params: [buildGroupedRequestParam(node, { resolver }), optionsParam].filter((param) => param !== null) });
822
+ const groupedParam = buildGroupedRequestParam(node, { resolver });
823
+ return createFunctionParameters({ params: [groupedParam, optionsParam].filter((param) => param !== null) });
956
824
  }
957
825
  function Query({ name, queryKeyTypeName, queryOptionsName, queryKeyName, node, tsResolver }) {
958
826
  const responseName = tsResolver.response.response(node);
@@ -1044,16 +912,19 @@ const mutationGenerator = defineGenerator({
1044
912
  const pluginTs = driver.getPlugin(pluginTsName);
1045
913
  if (!pluginTs) return null;
1046
914
  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;
915
+ const { isMutation } = classifyOperation(node, {
916
+ query,
917
+ mutation
918
+ });
919
+ if (!isMutation) return null;
1050
920
  const importPath = mutation ? mutation.importPath : "swr/mutation";
1051
921
  const contractOp = resolveClientOperation({
1052
922
  clientPlugin: { pluginName: client.pluginName },
1053
923
  driver,
1054
924
  node,
1055
925
  root,
1056
- output
926
+ output,
927
+ cache: ctx.cache
1057
928
  });
1058
929
  if (!contractOp) return null;
1059
930
  const mutationHookName = resolver.mutation.name(node);
@@ -1070,11 +941,10 @@ const mutationGenerator = defineGenerator({
1070
941
  output,
1071
942
  group
1072
943
  }),
1073
- fileTs: tsResolver.file({
1074
- name: node.operationId,
1075
- extname: ".ts",
1076
- tag: node.tags[0] ?? "default",
1077
- path: node.path,
944
+ fileTs: resolveDependencyOperationFile({
945
+ cache: ctx.cache,
946
+ node,
947
+ resolver: tsResolver,
1078
948
  root,
1079
949
  output: pluginTs.options?.output ?? output,
1080
950
  group: pluginTs.options?.group
@@ -1165,18 +1035,23 @@ const queryGenerator = defineGenerator({
1165
1035
  operation(node, ctx) {
1166
1036
  if (!ast.isHttpOperationNode(node)) return null;
1167
1037
  const { config, driver, resolver, root } = ctx;
1168
- const { output, query, client, group } = ctx.options;
1038
+ const { output, query, mutation, client, group } = ctx.options;
1169
1039
  const pluginTs = driver.getPlugin(pluginTsName);
1170
1040
  if (!pluginTs) return null;
1171
1041
  const tsResolver = driver.getResolver(pluginTsName);
1172
- if (!(query === false || !!query && query.methods.some((method) => node.method.toLowerCase() === method.toLowerCase()))) return null;
1042
+ const { isQuery } = classifyOperation(node, {
1043
+ query,
1044
+ mutation
1045
+ });
1046
+ if (!isQuery) return null;
1173
1047
  const importPath = query ? query.importPath : "swr";
1174
1048
  const contractOp = resolveClientOperation({
1175
1049
  clientPlugin: { pluginName: client.pluginName },
1176
1050
  driver,
1177
1051
  node,
1178
1052
  root,
1179
- output
1053
+ output,
1054
+ cache: ctx.cache
1180
1055
  });
1181
1056
  if (!contractOp) return null;
1182
1057
  const queryName = resolver.query.name(node);
@@ -1193,11 +1068,10 @@ const queryGenerator = defineGenerator({
1193
1068
  output,
1194
1069
  group
1195
1070
  }),
1196
- fileTs: tsResolver.file({
1197
- name: node.operationId,
1198
- extname: ".ts",
1199
- tag: node.tags[0] ?? "default",
1200
- path: node.path,
1071
+ fileTs: resolveDependencyOperationFile({
1072
+ cache: ctx.cache,
1073
+ node,
1074
+ resolver: tsResolver,
1201
1075
  root,
1202
1076
  output: pluginTs.options?.output ?? output,
1203
1077
  group: pluginTs.options?.group