@kubb/plugin-react-query 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,250 +1,10 @@
1
1
  import "./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
7
  import fs from "node:fs";
8
- //#region ../../internals/utils/src/casing.ts
9
- /**
10
- * Shared implementation for camelCase and PascalCase conversion.
11
- * Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
12
- * and capitalizes each word according to `pascal`.
13
- *
14
- * When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
15
- */
16
- function toCamelOrPascal(text, pascal) {
17
- 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) => {
18
- if (word.length > 1 && word === word.toUpperCase()) return word;
19
- return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
20
- }).join("").replace(/[^a-zA-Z0-9]/g, "");
21
- }
22
- /**
23
- * Converts `text` to camelCase.
24
- *
25
- * @example Word boundaries
26
- * `camelCase('hello-world') // 'helloWorld'`
27
- *
28
- * @example With a prefix
29
- * `camelCase('tag', { prefix: 'create' }) // 'createTag'`
30
- */
31
- function camelCase(text, { prefix = "", suffix = "" } = {}) {
32
- return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
33
- }
34
- /**
35
- * Uppercases only the first character of `text`, leaving the rest untouched.
36
- * Unlike {@link pascalCase} it never re-splits word boundaries or strips characters.
37
- *
38
- * @example
39
- * `capitalize('getPetById') // 'GetPetById'`
40
- */
41
- function capitalize(text) {
42
- return `${text.charAt(0).toUpperCase()}${text.slice(1)}`;
43
- }
44
- //#endregion
45
- //#region ../../internals/utils/src/reserved.ts
46
- /**
47
- * JavaScript and Java reserved words.
48
- * @link https://github.com/jonschlinkert/reserved/blob/master/index.js
49
- */
50
- const reservedWords = /* @__PURE__ */ new Set([
51
- "abstract",
52
- "arguments",
53
- "boolean",
54
- "break",
55
- "byte",
56
- "case",
57
- "catch",
58
- "char",
59
- "class",
60
- "const",
61
- "continue",
62
- "debugger",
63
- "default",
64
- "delete",
65
- "do",
66
- "double",
67
- "else",
68
- "enum",
69
- "eval",
70
- "export",
71
- "extends",
72
- "false",
73
- "final",
74
- "finally",
75
- "float",
76
- "for",
77
- "function",
78
- "goto",
79
- "if",
80
- "implements",
81
- "import",
82
- "in",
83
- "instanceof",
84
- "int",
85
- "interface",
86
- "let",
87
- "long",
88
- "native",
89
- "new",
90
- "null",
91
- "package",
92
- "private",
93
- "protected",
94
- "public",
95
- "return",
96
- "short",
97
- "static",
98
- "super",
99
- "switch",
100
- "synchronized",
101
- "this",
102
- "throw",
103
- "throws",
104
- "transient",
105
- "true",
106
- "try",
107
- "typeof",
108
- "var",
109
- "void",
110
- "volatile",
111
- "while",
112
- "with",
113
- "yield",
114
- "Array",
115
- "Date",
116
- "hasOwnProperty",
117
- "Infinity",
118
- "isFinite",
119
- "isNaN",
120
- "isPrototypeOf",
121
- "length",
122
- "Math",
123
- "name",
124
- "NaN",
125
- "Number",
126
- "Object",
127
- "prototype",
128
- "String",
129
- "toString",
130
- "undefined",
131
- "valueOf"
132
- ]);
133
- /**
134
- * Returns `true` when `name` is a syntactically valid JavaScript variable name.
135
- *
136
- * @example
137
- * ```ts
138
- * isValidVarName('status') // true
139
- * isValidVarName('class') // false (reserved word)
140
- * isValidVarName('42foo') // false (starts with digit)
141
- * ```
142
- */
143
- function isValidVarName(name) {
144
- if (!name || reservedWords.has(name)) return false;
145
- return isIdentifier(name);
146
- }
147
- /**
148
- * Returns `true` when `name` is syntactically a valid identifier, ignoring reserved words.
149
- *
150
- * Reserved words and globals (`class`, `name`, `Date`, …) are valid as bare object-literal keys
151
- * even though they are not valid variable names, so use this (not {@link isValidVarName}) when
152
- * deciding whether an object key needs quoting.
153
- *
154
- * @example
155
- * ```ts
156
- * isIdentifier('name') // true
157
- * isIdentifier('x-total')// false
158
- * ```
159
- */
160
- function isIdentifier(name) {
161
- return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
162
- }
163
- //#endregion
164
- //#region ../../internals/utils/src/strings.ts
165
- /**
166
- * Renders a dotted path or string array as an optional-chaining accessor expression rooted at
167
- * `accessor`. Returns `null` for an empty path.
168
- *
169
- * @example
170
- * ```ts
171
- * getNestedAccessor('pagination.next.id', 'lastPage')
172
- * // "lastPage?.['pagination']?.['next']?.['id']"
173
- * ```
174
- */
175
- function getNestedAccessor(param, accessor) {
176
- const parts = Array.isArray(param) ? param : param.split(".");
177
- if (parts.length === 0 || parts.length === 1 && parts[0] === "") return null;
178
- return `${accessor}?.['${`${parts.join("']?.['")}']`}`;
179
- }
180
- //#endregion
181
- //#region ../../internals/utils/src/url.ts
182
- /**
183
- * Keeps the OpenAPI parameter name as-is when it is already a valid JS identifier, and
184
- * camelCases it only enough to become one otherwise (for example a hyphenated path segment).
185
- */
186
- function transformParam(raw) {
187
- return isValidVarName(raw) ? raw : camelCase(raw);
188
- }
189
- /**
190
- * Helpers for OpenAPI/Swagger paths.
191
- */
192
- var Url = class Url {
193
- /**
194
- * Converts an OpenAPI/Swagger path to Express-style colon syntax.
195
- *
196
- * @example
197
- * Url.toPath('/pet/{petId}') // '/pet/:petId'
198
- */
199
- static toPath(path) {
200
- return path.replace(/\{([^}]+)\}/g, ":$1");
201
- }
202
- /**
203
- * Rewrites OpenAPI placeholder names while keeping the `{...}` braces, so the generated `url`
204
- * literal aligns with the grouped `path` request option that the runtime client interpolates by
205
- * key.
206
- *
207
- * @example
208
- * Url.toSafeTemplate('/user/{monetary-account-id}') // '/user/{monetaryAccountId}'
209
- */
210
- static toSafeTemplate(path) {
211
- return path.replace(/\{([^}]+)\}/g, (_, name) => `{${transformParam(name)}}`);
212
- }
213
- /**
214
- * Converts an OpenAPI/Swagger path to a TypeScript template literal string.
215
- * `prefix` is prepended inside the literal, and `replacer` transforms each parameter name.
216
- *
217
- * @example
218
- * Url.toTemplateString('/pet/{petId}') // '`/pet/${petId}`'
219
- *
220
- * @example
221
- * Url.toTemplateString('/pet/{petId}', { prefix: 'https://api' }) // '`https://api/pet/${petId}`'
222
- */
223
- static toTemplateString(path, { prefix, replacer } = {}) {
224
- const result = path.split(/\{([^}]+)\}/).map((part, i) => {
225
- if (i % 2 === 0) return part;
226
- const param = transformParam(part);
227
- return `\${${replacer ? replacer(param) : param}}`;
228
- }).join("");
229
- return `\`${prefix ?? ""}${result}\``;
230
- }
231
- /**
232
- * Converts an OpenAPI/Swagger path to a template literal that reads each parameter off the
233
- * grouped `path` request option, e.g. `/pet/{petId}` becomes `` `/pet/${path.petId}` ``. Parameter
234
- * names match the generated `path` type, and `prefix` is prepended inside the literal. Shared by
235
- * the client and cypress generators that pass a grouped `path` object.
236
- *
237
- * @example
238
- * Url.toGroupedTemplateString('/pet/{petId}') // '`/pet/${path.petId}`'
239
- */
240
- static toGroupedTemplateString(path, { prefix } = {}) {
241
- return Url.toTemplateString(path, {
242
- prefix,
243
- replacer: (name) => `path.${name}`
244
- });
245
- }
246
- };
247
- //#endregion
248
8
  //#region ../../internals/shared/src/params.ts
249
9
  /**
250
10
  * Drops parameters that share the same name, keeping the first.
@@ -283,11 +43,30 @@ function operationFileEntry(node, name, extname = ".ts") {
283
43
  path: node.path
284
44
  };
285
45
  }
46
+ /**
47
+ * Resolves a dependency plugin's generated file for `node.operationId`, cached in `cache` (the
48
+ * current node's `ctx.cache`) under the resolver's own plugin name. Several dependents reading the
49
+ * same dependency for the same operation in one pass (a query plugin's several hook generators, the
50
+ * MCP handler, ...) share one computed name and path instead of each calling `resolver.file` again.
51
+ *
52
+ * @example Cache `plugin-ts`'s file for the current operation
53
+ * ```ts
54
+ * const fileTs = resolveDependencyOperationFile({ cache: ctx.cache, node, resolver: tsResolver, root, output })
55
+ * ```
56
+ */
57
+ function resolveDependencyOperationFile(options) {
58
+ const { cache, node, resolver, root, output, group } = options;
59
+ return cache.ensureItem(`${resolver.pluginName}:operationFile`, () => resolver.file({
60
+ ...operationFileEntry(node, node.operationId),
61
+ root,
62
+ output,
63
+ group: group ?? void 0
64
+ }));
65
+ }
286
66
  function getOperationLink(node, link) {
287
67
  if (!link) return null;
288
68
  if (typeof link === "function") return link(node) ?? null;
289
- if (link === "urlPath") return node.path ? `{@link ${Url.toPath(node.path)}}` : null;
290
- return node.path ? `{@link ${node.path.replaceAll("{", ":").replaceAll("}", "")}}` : null;
69
+ return node.path ? `{@link ${Url.toPath(node.path)}}` : null;
291
70
  }
292
71
  /**
293
72
  * Derives the shared `ContentTypeInfo` shape from a list of content types, tracking whether several
@@ -377,16 +156,24 @@ function buildOperationComments(node, options = {}) {
377
156
  if (!splitLines) return filteredComments;
378
157
  return filteredComments.flatMap((text) => text.split(/\r?\n/).map((line) => line.trim())).filter((comment) => Boolean(comment));
379
158
  }
159
+ const operationParameterGroupsByNode = /* @__PURE__ */ new WeakMap();
160
+ /**
161
+ * Groups an operation's parameters by location (`path`/`query`/`header`/`cookie`), deduping each
162
+ * group by name. Every plugin generator visiting the same `OperationNode` shares one AST instance
163
+ * (see `KubbDriver`), so the result is cached per node to avoid re-filtering and re-deduping the
164
+ * same parameters once per plugin.
165
+ */
380
166
  function getOperationParameters(node) {
381
- return {
382
- path: dedupeParams(node.parameters.filter((param) => param.in === "path").map((param) => isValidVarName(param.name) ? param : {
383
- ...param,
384
- name: camelCase(param.name)
385
- })),
167
+ const cached = operationParameterGroupsByNode.get(node);
168
+ if (cached) return cached;
169
+ const groups = {
170
+ path: dedupeParams(node.parameters.filter((param) => param.in === "path")),
386
171
  query: dedupeParams(node.parameters.filter((param) => param.in === "query")),
387
172
  header: dedupeParams(node.parameters.filter((param) => param.in === "header")),
388
173
  cookie: dedupeParams(node.parameters.filter((param) => param.in === "cookie"))
389
174
  };
175
+ operationParameterGroupsByNode.set(node, groups);
176
+ return groups;
390
177
  }
391
178
  function getStatusCodeNumber(statusCode) {
392
179
  const code = Number(statusCode);
@@ -451,6 +238,60 @@ function resolveOperationTypeNames(node, resolver, options = {}) {
451
238
  return result;
452
239
  }
453
240
  //#endregion
241
+ //#region ../../internals/utils/src/casing.ts
242
+ /**
243
+ * Shared implementation for camelCase and PascalCase conversion.
244
+ * Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
245
+ * and capitalizes each word according to `pascal`.
246
+ *
247
+ * When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
248
+ */
249
+ function toCamelOrPascal(text, pascal) {
250
+ 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) => {
251
+ if (word.length > 1 && word === word.toUpperCase()) return word;
252
+ return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
253
+ }).join("").replace(/[^a-zA-Z0-9]/g, "");
254
+ }
255
+ /**
256
+ * Converts `text` to camelCase.
257
+ *
258
+ * @example Word boundaries
259
+ * `camelCase('hello-world') // 'helloWorld'`
260
+ *
261
+ * @example With a prefix
262
+ * `camelCase('tag', { prefix: 'create' }) // 'createTag'`
263
+ */
264
+ function camelCase(text, { prefix = "", suffix = "" } = {}) {
265
+ return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
266
+ }
267
+ /**
268
+ * Uppercases only the first character of `text`, leaving the rest untouched.
269
+ * Unlike {@link pascalCase} it never re-splits word boundaries or strips characters.
270
+ *
271
+ * @example
272
+ * `capitalize('getPetById') // 'GetPetById'`
273
+ */
274
+ function capitalize(text) {
275
+ return `${text.charAt(0).toUpperCase()}${text.slice(1)}`;
276
+ }
277
+ //#endregion
278
+ //#region ../../internals/utils/src/strings.ts
279
+ /**
280
+ * Renders a dotted path or string array as an optional-chaining accessor expression rooted at
281
+ * `accessor`. Returns `null` for an empty path.
282
+ *
283
+ * @example
284
+ * ```ts
285
+ * getNestedAccessor('pagination.next.id', 'lastPage')
286
+ * // "lastPage?.['pagination']?.['next']?.['id']"
287
+ * ```
288
+ */
289
+ function getNestedAccessor(param, accessor) {
290
+ const parts = Array.isArray(param) ? param : param.split(".");
291
+ if (parts.length === 0 || parts.length === 1 && parts[0] === "") return null;
292
+ return `${accessor}?.['${`${parts.join("']?.['")}']`}`;
293
+ }
294
+ //#endregion
454
295
  //#region ../../internals/shared/src/group.ts
455
296
  /**
456
297
  * Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
@@ -570,23 +411,30 @@ function resolveContractClient(options) {
570
411
  * (plugin-fetch or plugin-axios). Returns `null` when no contract plugin is in play (the inline
571
412
  * path). The plugin injects `.kubb/client.ts` at the global output root, the same path consumers
572
413
  * read `RequestConfig` / `ResponseErrorConfig` from.
414
+ *
415
+ * The result is cached in the current node's `cache` (`ctx.cache`) under the client plugin's name,
416
+ * so several dependents reading the same client plugin for one operation in a single pass
417
+ * (react-query's query/mutation/infinite generators, vue-query, swr, the MCP handler, ...) share
418
+ * one computed result instead of each re-deriving the name and path.
573
419
  */
574
420
  function resolveClientOperation(options) {
575
- const { clientPlugin, driver, node, root, output } = options;
421
+ const { clientPlugin, driver, node, root, output, cache } = options;
576
422
  if (!clientPlugin) return null;
577
- const resolver = driver.getResolver(clientPlugin.pluginName);
578
- const plugin = driver.getPlugin(clientPlugin.pluginName);
579
- const file = resolver.file({
580
- ...operationFileEntry(node, node.operationId),
581
- root,
582
- output: plugin?.options?.output ?? output,
583
- group: plugin?.options?.group ?? void 0
423
+ return cache.ensureItem(`${clientPlugin.pluginName}:clientOperation`, () => {
424
+ const resolver = driver.getResolver(clientPlugin.pluginName);
425
+ const plugin = driver.getPlugin(clientPlugin.pluginName);
426
+ const file = resolver.file({
427
+ ...operationFileEntry(node, node.operationId),
428
+ root,
429
+ output: plugin?.options?.output ?? output,
430
+ group: plugin?.options?.group ?? void 0
431
+ });
432
+ return {
433
+ name: resolver.name(node.operationId),
434
+ path: file.path,
435
+ clientPath: path.resolve(root, ".kubb/client.ts")
436
+ };
584
437
  });
585
- return {
586
- name: resolver.name(node.operationId),
587
- path: file.path,
588
- clientPath: path.resolve(root, ".kubb/client.ts")
589
- };
590
438
  }
591
439
  //#endregion
592
440
  //#region ../../internals/tanstack-query/src/utils.ts
@@ -657,14 +505,16 @@ function buildGroupedRequestParam(node, options) {
657
505
  */
658
506
  function buildQueryOptionsParams(node, options) {
659
507
  const { resolver, memberTypeWrapper } = options;
660
- return createFunctionParameters({ params: [buildGroupedRequestParam(node, {
508
+ const groupedParam = buildGroupedRequestParam(node, {
661
509
  resolver,
662
510
  memberTypeWrapper
663
- }), createFunctionParameter({
511
+ });
512
+ const configParam = createFunctionParameter({
664
513
  name: "config",
665
514
  type: `Partial<Omit<RequestConfig, 'path' | 'query' | 'body' | 'headers' | 'url'>>`,
666
515
  default: "{}"
667
- })].filter((param) => param !== null) });
516
+ });
517
+ return createFunctionParameters({ params: [groupedParam, configParam].filter((param) => param !== null) });
668
518
  }
669
519
  /**
670
520
  * Builds the call to a client `<op>` function inside a query/mutation hook body. The function takes
@@ -762,6 +612,19 @@ function resolveMutationConfig(mutation, options) {
762
612
  };
763
613
  }
764
614
  /**
615
+ * Classifies an operation as a query or a mutation from the resolved `query` / `mutation` method lists.
616
+ * `query: false` still marks the operation as a query so the query-family generators keep matching it,
617
+ * and a method already claimed by `query` never counts as a mutation.
618
+ */
619
+ function classifyOperation(node, { query, mutation }) {
620
+ const isQuery = query === false || !!query && query.methods.some((method) => node.method.toLowerCase() === method.toLowerCase());
621
+ const queryMethods = new Set(query ? query.methods : []);
622
+ return {
623
+ isQuery,
624
+ isMutation: mutation !== false && !isQuery && (mutation ? mutation.methods : []).some((method) => !queryMethods.has(method) && node.method.toLowerCase() === method.toLowerCase())
625
+ };
626
+ }
627
+ /**
765
628
  * Applies the shared infinite-query defaults during plugin setup: a falsy value disables infinite
766
629
  * queries, and an object merges over `queryParam: 'id'` and `initialPageParam: 0` with the cursor
767
630
  * paths cleared.
@@ -800,6 +663,24 @@ function resolveOperationOverrides(node, override) {
800
663
  return override.find((ov) => matchesPattern(node, ov))?.options ?? {};
801
664
  }
802
665
  /**
666
+ * The request groups a query key is built from, matching `buildQueryKeyParams`. Headers are
667
+ * deliberately absent: they do not identify a cache entry, so the key factory takes no parameter
668
+ * for an operation that only carries headers.
669
+ */
670
+ const queryKeyGroupOrder = [
671
+ "path",
672
+ "query",
673
+ "body"
674
+ ];
675
+ /**
676
+ * Whether the generated query key factory declares a parameter, so a call site passes an argument
677
+ * only when one is accepted.
678
+ */
679
+ function hasQueryKeyParams(node) {
680
+ const groups = getRequestGroups(node);
681
+ return queryKeyGroupOrder.some((key) => groups[key]);
682
+ }
683
+ /**
803
684
  * Build QueryKey params as the grouped `{ path, query, body }` object (NO headers, NO config),
804
685
  * typed from the operation's `Options` minus `url`. The query key transformer reads the
805
686
  * grouped `path`/`query`/`body` bindings.
@@ -808,11 +689,7 @@ function buildQueryKeyParams(node, options) {
808
689
  const { resolver } = options;
809
690
  const groupedParam = buildGroupedRequestParam(node, {
810
691
  resolver,
811
- keys: [
812
- "path",
813
- "query",
814
- "body"
815
- ]
692
+ keys: queryKeyGroupOrder
816
693
  });
817
694
  return createFunctionParameters({ params: groupedParam ? [groupedParam] : [] });
818
695
  }
@@ -829,11 +706,7 @@ function InfiniteQueryOptions({ name, clientName, initialPageParam, cursorParam,
829
706
  });
830
707
  const groupedKeyParam = buildGroupedRequestParam(node, {
831
708
  resolver: tsResolver,
832
- keys: [
833
- "path",
834
- "query",
835
- "body"
836
- ],
709
+ keys: queryKeyGroupOrder,
837
710
  memberTypeWrapper
838
711
  });
839
712
  const queryKeyParamsNode = createFunctionParameters({ params: groupedKeyParam ? [groupedKeyParam] : [] });
@@ -1131,19 +1004,6 @@ function buildResolvedRequestParams(node) {
1131
1004
  if (names.length === 0) return null;
1132
1005
  return `{ ${names.map((name) => `${name}: typeof ${name} === 'function' ? ${name}() : ${name}`).join(", ")} }`;
1133
1006
  }
1134
- /**
1135
- * Classifies an operation as a query or a mutation from the resolved `query` / `mutation` method lists.
1136
- * `query: false` still marks the operation as a query so the query-family generators keep matching it,
1137
- * and a method already claimed by `query` never counts as a mutation.
1138
- */
1139
- function classifyOperation(node, { query, mutation }) {
1140
- const isQuery = query === false || !!query && query.methods.some((method) => node.method.toLowerCase() === method.toLowerCase());
1141
- const queryMethods = new Set(query ? query.methods : []);
1142
- return {
1143
- isQuery,
1144
- isMutation: mutation !== false && !isQuery && (mutation ? mutation.methods : []).some((method) => !queryMethods.has(method) && node.method.toLowerCase() === method.toLowerCase())
1145
- };
1146
- }
1147
1007
  //#endregion
1148
1008
  //#region src/generators/hookOptionsGenerator.tsx
1149
1009
  /**
@@ -1318,10 +1178,11 @@ function buildInfiniteQueryParamsNode(node, options) {
1318
1178
  }`,
1319
1179
  default: "{}"
1320
1180
  });
1321
- return createFunctionParameters({ params: [buildGroupedRequestParam(node, {
1181
+ const groupedParam = buildGroupedRequestParam(node, {
1322
1182
  resolver,
1323
1183
  memberTypeWrapper: maybeValueOrGetter
1324
- }), optionsParam].filter((param) => param !== null) });
1184
+ });
1185
+ return createFunctionParameters({ params: [groupedParam, optionsParam].filter((param) => param !== null) });
1325
1186
  }
1326
1187
  function InfiniteQuery({ name, queryKeyTypeName, queryOptionsName, queryKeyName, node, tsResolver, initialPageParam, queryParam, customOptions, suspense }) {
1327
1188
  const { TData: responseType, TError: errorType } = buildResponseTypes(node, tsResolver);
@@ -1341,7 +1202,7 @@ function InfiniteQuery({ name, queryKeyTypeName, queryOptionsName, queryKeyName,
1341
1202
  `TPageParam = ${pageParamType}`
1342
1203
  ];
1343
1204
  const resolvedParams = buildResolvedRequestParams(node);
1344
- const queryKeyArgs = resolvedParams ? "resolvedParams" : "";
1205
+ const queryKeyArgs = resolvedParams && hasQueryKeyParams(node) ? "resolvedParams" : "";
1345
1206
  const queryOptionsArgs = resolvedParams ? "resolvedParams, config" : "config";
1346
1207
  const paramsNode = buildInfiniteQueryParamsNode(node, {
1347
1208
  resolver: tsResolver,
@@ -1434,15 +1295,16 @@ function resolveMutationRequestType(node, resolver) {
1434
1295
  function buildMutationParamsNode(node, options) {
1435
1296
  const { resolver } = options;
1436
1297
  const { TData, TError } = buildResponseTypes(node, resolver);
1298
+ const generics = [
1299
+ TData,
1300
+ TError,
1301
+ resolveMutationRequestType(node, resolver),
1302
+ "TContext"
1303
+ ].join(", ");
1437
1304
  return createFunctionParameters({ params: [createFunctionParameter({
1438
1305
  name: "options",
1439
1306
  type: `{
1440
- mutation?: UseMutationOptions<${[
1441
- TData,
1442
- TError,
1443
- resolveMutationRequestType(node, resolver),
1444
- "TContext"
1445
- ].join(", ")}> & { client?: QueryClient },
1307
+ mutation?: UseMutationOptions<${generics}> & { client?: QueryClient },
1446
1308
  client?: ${buildRequestConfigType(node)},
1447
1309
  }`,
1448
1310
  default: "{}"
@@ -1500,10 +1362,11 @@ function buildQueryParamsNode(node, options) {
1500
1362
  }`,
1501
1363
  default: "{}"
1502
1364
  });
1503
- return createFunctionParameters({ params: [buildGroupedRequestParam(node, {
1365
+ const groupedParam = buildGroupedRequestParam(node, {
1504
1366
  resolver,
1505
1367
  memberTypeWrapper: maybeValueOrGetter
1506
- }), optionsParam].filter((param) => param !== null) });
1368
+ });
1369
+ return createFunctionParameters({ params: [groupedParam, optionsParam].filter((param) => param !== null) });
1507
1370
  }
1508
1371
  function Query({ name, queryKeyTypeName, queryOptionsName, queryKeyName, node, tsResolver, customOptions, suspense }) {
1509
1372
  const { TData, TError } = buildResponseTypes(node, tsResolver);
@@ -1528,7 +1391,7 @@ function Query({ name, queryKeyTypeName, queryOptionsName, queryKeyName, node, t
1528
1391
  `TQueryKey extends QueryKey = ${queryKeyTypeName}`
1529
1392
  ];
1530
1393
  const resolvedParams = buildResolvedRequestParams(node);
1531
- const queryKeyArgs = resolvedParams ? "resolvedParams" : "";
1394
+ const queryKeyArgs = resolvedParams && hasQueryKeyParams(node) ? "resolvedParams" : "";
1532
1395
  const queryOptionsArgs = resolvedParams ? "resolvedParams, config" : "config";
1533
1396
  const paramsNode = buildQueryParamsNode(node, {
1534
1397
  resolver: tsResolver,
@@ -1568,14 +1431,11 @@ function Query({ name, queryKeyTypeName, queryOptionsName, queryKeyName, node, t
1568
1431
  //#region src/components/QueryOptions.tsx
1569
1432
  const declarationPrinter = functionPrinter({ mode: "declaration" });
1570
1433
  const callPrinter = functionPrinter({ mode: "call" });
1571
- function getQueryOptionsParams(node, options) {
1572
- return buildQueryOptionsParams(node, options);
1573
- }
1574
1434
  function QueryOptions({ name, clientName, node, tsResolver, queryKeyName }) {
1575
1435
  const { TData, TError } = buildResponseTypes(node, tsResolver);
1576
1436
  const queryKeyParamsNode = buildQueryKeyParams(node, { resolver: tsResolver });
1577
1437
  const queryKeyParamsCall = callPrinter.print(queryKeyParamsNode) ?? "";
1578
- const paramsNode = getQueryOptionsParams(node, { resolver: tsResolver });
1438
+ const paramsNode = buildQueryOptionsParams(node, { resolver: tsResolver });
1579
1439
  const paramsSignature = declarationPrinter.print(paramsNode) ?? "";
1580
1440
  const queryFnBody = `const { data } = await ${buildClientCall(node, {
1581
1441
  clientName,
@@ -1606,36 +1466,44 @@ function QueryOptions({ name, clientName, node, tsResolver, queryKeyName }) {
1606
1466
  //#region src/generators/infiniteQueryGenerator.tsx
1607
1467
  /**
1608
1468
  * Built-in generator for `useInfiniteQuery` hooks. Enabled when
1609
- * `pluginReactQuery({ infinite: { ... } })`. Emits one `useFooInfiniteQuery`
1610
- * hook per query operation, wiring the configured `nextParam` /
1611
- * `previousParam` paths into TanStack Query's cursor-based pagination.
1469
+ * `pluginReactQuery({ infinite: { ... }, hooks: true })`. Emits one
1470
+ * `useFooInfiniteQuery` hook per query operation, wiring the configured
1471
+ * `nextParam` / `previousParam` paths into TanStack Query's cursor-based
1472
+ * pagination.
1612
1473
  */
1613
1474
  const infiniteQueryGenerator = defineGenerator({
1614
1475
  name: "react-infinite-query",
1615
1476
  renderer: jsxRenderer,
1477
+ match(node, ctx) {
1478
+ const operationNode = node;
1479
+ if (!ast.isHttpOperationNode(operationNode)) return false;
1480
+ const { query, mutation, infinite, hooks } = ctx.options;
1481
+ const { isQuery, isMutation } = classifyOperation(operationNode, {
1482
+ query,
1483
+ mutation
1484
+ });
1485
+ const infiniteOptions = infinite && typeof infinite === "object" ? infinite : null;
1486
+ if (!isQuery || isMutation || !infiniteOptions || !hooks) return false;
1487
+ const queryParamKeys = getOperationParameters(operationNode).query.map((p) => p.name);
1488
+ return infiniteOptions.queryParam ? queryParamKeys.includes(infiniteOptions.queryParam) || queryParamKeys.includes(`${infiniteOptions.queryParam}?`) : false;
1489
+ },
1616
1490
  operation(node, ctx) {
1617
1491
  if (!ast.isHttpOperationNode(node)) return null;
1618
1492
  const { config, driver, resolver, root } = ctx;
1619
- const { output, query, mutation, infinite, client, group, customOptions, hooks } = ctx.options;
1493
+ const { output, query, infinite, client, group, customOptions } = ctx.options;
1620
1494
  const pluginTs = driver.getPlugin(pluginTsName);
1621
1495
  if (!pluginTs) return null;
1622
1496
  const tsResolver = driver.getResolver(pluginTsName);
1623
- const { isQuery, isMutation } = classifyOperation(node, {
1624
- query,
1625
- mutation
1626
- });
1627
1497
  const infiniteOptions = infinite && typeof infinite === "object" ? infinite : null;
1628
- if (!isQuery || isMutation || !infiniteOptions) return null;
1629
- const normalizeKey = (key) => key.replace(/\?$/, "");
1630
- const queryParamKeys = getOperationParameters(node).query.map((p) => p.name);
1631
- if (!(infiniteOptions.queryParam ? queryParamKeys.some((k) => normalizeKey(k) === infiniteOptions.queryParam) : false)) return null;
1498
+ if (!infiniteOptions) return null;
1632
1499
  const importPath = query ? query.importPath : "@tanstack/react-query";
1633
1500
  const contractOp = resolveClientOperation({
1634
1501
  clientPlugin: { pluginName: client.pluginName },
1635
1502
  driver,
1636
1503
  node,
1637
1504
  root,
1638
- output
1505
+ output,
1506
+ cache: ctx.cache
1639
1507
  });
1640
1508
  if (!contractOp) return null;
1641
1509
  const queryName = resolver.infiniteQuery.name(node);
@@ -1649,15 +1517,20 @@ const infiniteQueryGenerator = defineGenerator({
1649
1517
  output,
1650
1518
  group: group ?? void 0
1651
1519
  }),
1652
- fileTs: tsResolver.file({
1653
- ...operationFileEntry(node, node.operationId),
1520
+ fileTs: resolveDependencyOperationFile({
1521
+ cache: ctx.cache,
1522
+ node,
1523
+ resolver: tsResolver,
1654
1524
  root,
1655
1525
  output: pluginTs.options?.output ?? output,
1656
- group: pluginTs.options?.group ?? void 0
1526
+ group: pluginTs.options?.group
1657
1527
  })
1658
1528
  };
1659
- const rawQueryParams = getOperationParameters(node).query;
1660
- const queryParamsTypeName = rawQueryParams.length > 0 && tsResolver.param.query(node, rawQueryParams[0]) !== tsResolver.param.name(node, rawQueryParams[0]) ? tsResolver.param.query(node, rawQueryParams[0]) : null;
1529
+ const { queryParamsTypeName } = resolvePageParamType(node, {
1530
+ resolver: tsResolver,
1531
+ initialPageParam: infiniteOptions.initialPageParam,
1532
+ queryParam: infiniteOptions.queryParam
1533
+ });
1661
1534
  const importedTypeNames = [
1662
1535
  tsResolver.response.options(node),
1663
1536
  queryParamsTypeName,
@@ -1738,33 +1611,31 @@ const infiniteQueryGenerator = defineGenerator({
1738
1611
  initialPageParam: infiniteOptions.initialPageParam,
1739
1612
  queryParam: infiniteOptions.queryParam
1740
1613
  }),
1741
- hooks && /* @__PURE__ */ jsxs(Fragment, { children: [
1742
- /* @__PURE__ */ jsx(File.Import, {
1743
- name: ["useInfiniteQuery"],
1744
- path: importPath
1745
- }),
1746
- /* @__PURE__ */ jsx(File.Import, {
1747
- name: [
1748
- "QueryKey",
1749
- "QueryClient",
1750
- "InfiniteQueryObserverOptions",
1751
- "UseInfiniteQueryResult"
1752
- ],
1753
- path: importPath,
1754
- isTypeOnly: true
1755
- }),
1756
- /* @__PURE__ */ jsx(InfiniteQuery, {
1757
- name: queryName,
1758
- queryOptionsName,
1759
- queryKeyName,
1760
- queryKeyTypeName,
1761
- node,
1762
- tsResolver,
1763
- initialPageParam: infiniteOptions.initialPageParam,
1764
- queryParam: infiniteOptions.queryParam,
1765
- customOptions
1766
- })
1767
- ] })
1614
+ /* @__PURE__ */ jsx(File.Import, {
1615
+ name: ["useInfiniteQuery"],
1616
+ path: importPath
1617
+ }),
1618
+ /* @__PURE__ */ jsx(File.Import, {
1619
+ name: [
1620
+ "QueryKey",
1621
+ "QueryClient",
1622
+ "InfiniteQueryObserverOptions",
1623
+ "UseInfiniteQueryResult"
1624
+ ],
1625
+ path: importPath,
1626
+ isTypeOnly: true
1627
+ }),
1628
+ /* @__PURE__ */ jsx(InfiniteQuery, {
1629
+ name: queryName,
1630
+ queryOptionsName,
1631
+ queryKeyName,
1632
+ queryKeyTypeName,
1633
+ node,
1634
+ tsResolver,
1635
+ initialPageParam: infiniteOptions.initialPageParam,
1636
+ queryParam: infiniteOptions.queryParam,
1637
+ customOptions
1638
+ })
1768
1639
  ]
1769
1640
  });
1770
1641
  }
@@ -1779,25 +1650,30 @@ const infiniteQueryGenerator = defineGenerator({
1779
1650
  const mutationGenerator = defineGenerator({
1780
1651
  name: "react-query-mutation",
1781
1652
  renderer: jsxRenderer,
1653
+ match(node, ctx) {
1654
+ const operationNode = node;
1655
+ if (!ast.isHttpOperationNode(operationNode)) return false;
1656
+ const { query, mutation } = ctx.options;
1657
+ return classifyOperation(operationNode, {
1658
+ query,
1659
+ mutation
1660
+ }).isMutation;
1661
+ },
1782
1662
  operation(node, ctx) {
1783
1663
  if (!ast.isHttpOperationNode(node)) return null;
1784
1664
  const { config, driver, resolver, root } = ctx;
1785
- const { output, query, mutation, client, group, customOptions, hooks } = ctx.options;
1665
+ const { output, mutation, client, group, customOptions, hooks } = ctx.options;
1786
1666
  const pluginTs = driver.getPlugin(pluginTsName);
1787
1667
  if (!pluginTs) return null;
1788
1668
  const tsResolver = driver.getResolver(pluginTsName);
1789
- const { isMutation } = classifyOperation(node, {
1790
- query,
1791
- mutation
1792
- });
1793
- if (!isMutation) return null;
1794
1669
  const importPath = mutation ? mutation.importPath : "@tanstack/react-query";
1795
1670
  const contractOp = resolveClientOperation({
1796
1671
  clientPlugin: { pluginName: client.pluginName },
1797
1672
  driver,
1798
1673
  node,
1799
1674
  root,
1800
- output
1675
+ output,
1676
+ cache: ctx.cache
1801
1677
  });
1802
1678
  if (!contractOp) return null;
1803
1679
  const mutationHookName = resolver.mutation.name(node);
@@ -1811,11 +1687,13 @@ const mutationGenerator = defineGenerator({
1811
1687
  output,
1812
1688
  group: group ?? void 0
1813
1689
  }),
1814
- fileTs: tsResolver.file({
1815
- ...operationFileEntry(node, node.operationId),
1690
+ fileTs: resolveDependencyOperationFile({
1691
+ cache: ctx.cache,
1692
+ node,
1693
+ resolver: tsResolver,
1816
1694
  root,
1817
1695
  output: pluginTs.options?.output ?? output,
1818
- group: pluginTs.options?.group ?? void 0
1696
+ group: pluginTs.options?.group
1819
1697
  })
1820
1698
  };
1821
1699
  const importedTypeNames = [tsResolver.response.options(node), ...resolveOperationTypeNames(node, tsResolver, {
@@ -1919,25 +1797,31 @@ const mutationGenerator = defineGenerator({
1919
1797
  const queryGenerator = defineGenerator({
1920
1798
  name: "react-query",
1921
1799
  renderer: jsxRenderer,
1800
+ match(node, ctx) {
1801
+ const operationNode = node;
1802
+ if (!ast.isHttpOperationNode(operationNode)) return false;
1803
+ const { query, mutation } = ctx.options;
1804
+ const { isQuery, isMutation } = classifyOperation(operationNode, {
1805
+ query,
1806
+ mutation
1807
+ });
1808
+ return isQuery && !isMutation;
1809
+ },
1922
1810
  operation(node, ctx) {
1923
1811
  if (!ast.isHttpOperationNode(node)) return null;
1924
1812
  const { config, driver, resolver, root } = ctx;
1925
- const { output, query, mutation, client, group, customOptions, hooks } = ctx.options;
1813
+ const { output, query, client, group, customOptions, hooks } = ctx.options;
1926
1814
  const pluginTs = driver.getPlugin(pluginTsName);
1927
1815
  if (!pluginTs) return null;
1928
1816
  const tsResolver = driver.getResolver(pluginTsName);
1929
- const { isQuery, isMutation } = classifyOperation(node, {
1930
- query,
1931
- mutation
1932
- });
1933
- if (!isQuery || isMutation) return null;
1934
1817
  const importPath = query ? query.importPath : "@tanstack/react-query";
1935
1818
  const contractOp = resolveClientOperation({
1936
1819
  clientPlugin: { pluginName: client.pluginName },
1937
1820
  driver,
1938
1821
  node,
1939
1822
  root,
1940
- output
1823
+ output,
1824
+ cache: ctx.cache
1941
1825
  });
1942
1826
  if (!contractOp) return null;
1943
1827
  const queryName = resolver.query.name(node);
@@ -1951,11 +1835,13 @@ const queryGenerator = defineGenerator({
1951
1835
  output,
1952
1836
  group: group ?? void 0
1953
1837
  }),
1954
- fileTs: tsResolver.file({
1955
- ...operationFileEntry(node, node.operationId),
1838
+ fileTs: resolveDependencyOperationFile({
1839
+ cache: ctx.cache,
1840
+ node,
1841
+ resolver: tsResolver,
1956
1842
  root,
1957
1843
  output: pluginTs.options?.output ?? output,
1958
- group: pluginTs.options?.group ?? void 0
1844
+ group: pluginTs.options?.group
1959
1845
  })
1960
1846
  };
1961
1847
  const importedTypeNames = [tsResolver.response.options(node), ...resolveOperationTypeNames(node, tsResolver, {
@@ -2056,38 +1942,44 @@ const queryGenerator = defineGenerator({
2056
1942
  //#endregion
2057
1943
  //#region src/generators/suspenseInfiniteQueryGenerator.tsx
2058
1944
  /**
2059
- * Built-in generator for `useSuspenseInfiniteQuery` hooks. Enabled when both
2060
- * `suspense` and `infinite` are configured. Combines suspense semantics with
2061
- * cursor-based pagination. Handlers throw promises while loading and pull
2062
- * additional pages on demand.
1945
+ * Built-in generator for `useSuspenseInfiniteQuery` hooks. Enabled when
1946
+ * `suspense`, `infinite`, and `hooks` are all configured. Combines suspense
1947
+ * semantics with cursor-based pagination. Handlers throw promises while
1948
+ * loading and pull additional pages on demand.
2063
1949
  */
2064
1950
  const suspenseInfiniteQueryGenerator = defineGenerator({
2065
1951
  name: "react-suspense-infinite-query",
2066
1952
  renderer: jsxRenderer,
1953
+ match(node, ctx) {
1954
+ const operationNode = node;
1955
+ if (!ast.isHttpOperationNode(operationNode)) return false;
1956
+ const { query, mutation, infinite, suspense, hooks } = ctx.options;
1957
+ const { isQuery, isMutation } = classifyOperation(operationNode, {
1958
+ query,
1959
+ mutation
1960
+ });
1961
+ const infiniteOptions = infinite && typeof infinite === "object" ? infinite : null;
1962
+ if (!isQuery || isMutation || !suspense || !infiniteOptions || !hooks) return false;
1963
+ const queryParamKeys = getOperationParameters(operationNode).query.map((p) => p.name);
1964
+ return infiniteOptions.queryParam ? queryParamKeys.includes(infiniteOptions.queryParam) || queryParamKeys.includes(`${infiniteOptions.queryParam}?`) : false;
1965
+ },
2067
1966
  operation(node, ctx) {
2068
1967
  if (!ast.isHttpOperationNode(node)) return null;
2069
1968
  const { config, driver, resolver, root } = ctx;
2070
- const { output, query, mutation, infinite, suspense, client, group, customOptions, hooks } = ctx.options;
1969
+ const { output, query, infinite, client, group, customOptions } = ctx.options;
2071
1970
  const pluginTs = driver.getPlugin(pluginTsName);
2072
1971
  if (!pluginTs) return null;
2073
1972
  const tsResolver = driver.getResolver(pluginTsName);
2074
- const { isQuery, isMutation } = classifyOperation(node, {
2075
- query,
2076
- mutation
2077
- });
2078
- const isSuspense = !!suspense;
2079
1973
  const infiniteOptions = infinite && typeof infinite === "object" ? infinite : null;
2080
- if (!isQuery || isMutation || !isSuspense || !infiniteOptions) return null;
2081
- const normalizeKey = (key) => key.replace(/\?$/, "");
2082
- const queryParamKeys = getOperationParameters(node).query.map((p) => p.name);
2083
- if (!(infiniteOptions.queryParam ? queryParamKeys.some((k) => normalizeKey(k) === infiniteOptions.queryParam) : false)) return null;
1974
+ if (!infiniteOptions) return null;
2084
1975
  const importPath = query ? query.importPath : "@tanstack/react-query";
2085
1976
  const contractOp = resolveClientOperation({
2086
1977
  clientPlugin: { pluginName: client.pluginName },
2087
1978
  driver,
2088
1979
  node,
2089
1980
  root,
2090
- output
1981
+ output,
1982
+ cache: ctx.cache
2091
1983
  });
2092
1984
  if (!contractOp) return null;
2093
1985
  const queryName = resolver.suspenseInfiniteQuery.name(node);
@@ -2101,11 +1993,13 @@ const suspenseInfiniteQueryGenerator = defineGenerator({
2101
1993
  output,
2102
1994
  group: group ?? void 0
2103
1995
  }),
2104
- fileTs: tsResolver.file({
2105
- ...operationFileEntry(node, node.operationId),
1996
+ fileTs: resolveDependencyOperationFile({
1997
+ cache: ctx.cache,
1998
+ node,
1999
+ resolver: tsResolver,
2106
2000
  root,
2107
2001
  output: pluginTs.options?.output ?? output,
2108
- group: pluginTs.options?.group ?? void 0
2002
+ group: pluginTs.options?.group
2109
2003
  })
2110
2004
  };
2111
2005
  const rawQueryParams = getOperationParameters(node).query;
@@ -2189,34 +2083,32 @@ const suspenseInfiniteQueryGenerator = defineGenerator({
2189
2083
  initialPageParam: infiniteOptions.initialPageParam,
2190
2084
  queryParam: infiniteOptions.queryParam
2191
2085
  }),
2192
- hooks && /* @__PURE__ */ jsxs(Fragment, { children: [
2193
- /* @__PURE__ */ jsx(File.Import, {
2194
- name: ["useSuspenseInfiniteQuery"],
2195
- path: importPath
2196
- }),
2197
- /* @__PURE__ */ jsx(File.Import, {
2198
- name: [
2199
- "QueryKey",
2200
- "QueryClient",
2201
- "UseSuspenseInfiniteQueryOptions",
2202
- "UseSuspenseInfiniteQueryResult"
2203
- ],
2204
- path: importPath,
2205
- isTypeOnly: true
2206
- }),
2207
- /* @__PURE__ */ jsx(InfiniteQuery, {
2208
- name: queryName,
2209
- queryOptionsName,
2210
- queryKeyName,
2211
- queryKeyTypeName,
2212
- node,
2213
- tsResolver,
2214
- initialPageParam: infiniteOptions.initialPageParam,
2215
- queryParam: infiniteOptions.queryParam,
2216
- customOptions,
2217
- suspense: true
2218
- })
2219
- ] })
2086
+ /* @__PURE__ */ jsx(File.Import, {
2087
+ name: ["useSuspenseInfiniteQuery"],
2088
+ path: importPath
2089
+ }),
2090
+ /* @__PURE__ */ jsx(File.Import, {
2091
+ name: [
2092
+ "QueryKey",
2093
+ "QueryClient",
2094
+ "UseSuspenseInfiniteQueryOptions",
2095
+ "UseSuspenseInfiniteQueryResult"
2096
+ ],
2097
+ path: importPath,
2098
+ isTypeOnly: true
2099
+ }),
2100
+ /* @__PURE__ */ jsx(InfiniteQuery, {
2101
+ name: queryName,
2102
+ queryOptionsName,
2103
+ queryKeyName,
2104
+ queryKeyTypeName,
2105
+ node,
2106
+ tsResolver,
2107
+ initialPageParam: infiniteOptions.initialPageParam,
2108
+ queryParam: infiniteOptions.queryParam,
2109
+ customOptions,
2110
+ suspense: true
2111
+ })
2220
2112
  ]
2221
2113
  });
2222
2114
  }
@@ -2225,32 +2117,39 @@ const suspenseInfiniteQueryGenerator = defineGenerator({
2225
2117
  //#region src/generators/suspenseQueryGenerator.tsx
2226
2118
  /**
2227
2119
  * Built-in generator for `useSuspenseQuery` hooks. Enabled when
2228
- * `pluginReactQuery({ suspense: {} })`. Emits one `useFooSuspenseQuery` hook
2229
- * per query operation. Suspense queries throw promises while loading and
2230
- * require a `<Suspense>` boundary in the React tree. TanStack Query v5+ only.
2120
+ * `pluginReactQuery({ suspense: {}, hooks: true })`. Emits one
2121
+ * `useFooSuspenseQuery` hook per query operation. Suspense queries throw
2122
+ * promises while loading and require a `<Suspense>` boundary in the React
2123
+ * tree. TanStack Query v5+ only.
2231
2124
  */
2232
2125
  const suspenseQueryGenerator = defineGenerator({
2233
2126
  name: "react-suspense-query",
2234
2127
  renderer: jsxRenderer,
2128
+ match(node, ctx) {
2129
+ const operationNode = node;
2130
+ if (!ast.isHttpOperationNode(operationNode)) return false;
2131
+ const { query, mutation, suspense, hooks } = ctx.options;
2132
+ const { isQuery, isMutation } = classifyOperation(operationNode, {
2133
+ query,
2134
+ mutation
2135
+ });
2136
+ return isQuery && !isMutation && !!suspense && hooks;
2137
+ },
2235
2138
  operation(node, ctx) {
2236
2139
  if (!ast.isHttpOperationNode(node)) return null;
2237
2140
  const { config, driver, resolver, root } = ctx;
2238
- const { output, query, mutation, suspense, client, group, customOptions, hooks } = ctx.options;
2141
+ const { output, query, client, group, customOptions } = ctx.options;
2239
2142
  const pluginTs = driver.getPlugin(pluginTsName);
2240
2143
  if (!pluginTs) return null;
2241
2144
  const tsResolver = driver.getResolver(pluginTsName);
2242
- const { isQuery, isMutation } = classifyOperation(node, {
2243
- query,
2244
- mutation
2245
- });
2246
- if (!isQuery || isMutation || !!!suspense) return null;
2247
2145
  const importPath = query ? query.importPath : "@tanstack/react-query";
2248
2146
  const contractOp = resolveClientOperation({
2249
2147
  clientPlugin: { pluginName: client.pluginName },
2250
2148
  driver,
2251
2149
  node,
2252
2150
  root,
2253
- output
2151
+ output,
2152
+ cache: ctx.cache
2254
2153
  });
2255
2154
  if (!contractOp) return null;
2256
2155
  const queryName = resolver.suspenseQuery.name(node);
@@ -2264,11 +2163,13 @@ const suspenseQueryGenerator = defineGenerator({
2264
2163
  output,
2265
2164
  group: group ?? void 0
2266
2165
  }),
2267
- fileTs: tsResolver.file({
2268
- ...operationFileEntry(node, node.operationId),
2166
+ fileTs: resolveDependencyOperationFile({
2167
+ cache: ctx.cache,
2168
+ node,
2169
+ resolver: tsResolver,
2269
2170
  root,
2270
2171
  output: pluginTs.options?.output ?? output,
2271
- group: pluginTs.options?.group ?? void 0
2172
+ group: pluginTs.options?.group
2272
2173
  })
2273
2174
  };
2274
2175
  const importedTypeNames = [tsResolver.response.options(node), ...resolveOperationTypeNames(node, tsResolver, {
@@ -2337,32 +2238,30 @@ const suspenseQueryGenerator = defineGenerator({
2337
2238
  node,
2338
2239
  tsResolver
2339
2240
  }),
2340
- suspense && hooks && /* @__PURE__ */ jsxs(Fragment, { children: [
2341
- /* @__PURE__ */ jsx(File.Import, {
2342
- name: ["useSuspenseQuery"],
2343
- path: importPath
2344
- }),
2345
- /* @__PURE__ */ jsx(File.Import, {
2346
- name: [
2347
- "QueryKey",
2348
- "QueryClient",
2349
- "UseSuspenseQueryOptions",
2350
- "UseSuspenseQueryResult"
2351
- ],
2352
- path: importPath,
2353
- isTypeOnly: true
2354
- }),
2355
- /* @__PURE__ */ jsx(Query, {
2356
- name: queryName,
2357
- queryOptionsName,
2358
- queryKeyName,
2359
- queryKeyTypeName,
2360
- node,
2361
- tsResolver,
2362
- customOptions,
2363
- suspense: true
2364
- })
2365
- ] })
2241
+ /* @__PURE__ */ jsx(File.Import, {
2242
+ name: ["useSuspenseQuery"],
2243
+ path: importPath
2244
+ }),
2245
+ /* @__PURE__ */ jsx(File.Import, {
2246
+ name: [
2247
+ "QueryKey",
2248
+ "QueryClient",
2249
+ "UseSuspenseQueryOptions",
2250
+ "UseSuspenseQueryResult"
2251
+ ],
2252
+ path: importPath,
2253
+ isTypeOnly: true
2254
+ }),
2255
+ /* @__PURE__ */ jsx(Query, {
2256
+ name: queryName,
2257
+ queryOptionsName,
2258
+ queryKeyName,
2259
+ queryKeyTypeName,
2260
+ node,
2261
+ tsResolver,
2262
+ customOptions,
2263
+ suspense: true
2264
+ })
2366
2265
  ]
2367
2266
  });
2368
2267
  }
@@ -2448,7 +2347,7 @@ const pluginReactQuery = definePlugin((options) => {
2448
2347
  const { output = {
2449
2348
  path: "hooks",
2450
2349
  barrel: { type: "named" }
2451
- }, group, exclude = [], include, override = [], suspense = {}, infinite = false, mutation = {}, query = {}, mutationKey = mutationKeyTransformer, queryKey = queryKeyTransformer, customOptions, hooks = false, client, resolver: userResolver, macros: userMacros } = options;
2350
+ }, group, exclude = [], include, override = [], suspense = false, infinite = false, mutation = {}, query = {}, mutationKey = mutationKeyTransformer, queryKey = queryKeyTransformer, customOptions, hooks = false, client, resolver: userResolver, macros: userMacros } = options;
2452
2351
  const selectedGenerators = [
2453
2352
  queryGenerator,
2454
2353
  suspenseQueryGenerator,