@kubb/plugin-react-query 5.0.0-beta.103 → 5.0.0-beta.105

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
@@ -43,6 +43,26 @@ function operationFileEntry(node, name, extname = ".ts") {
43
43
  path: node.path
44
44
  };
45
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.getOrSet(`${resolver.pluginName}:operationFile`, () => resolver.file({
60
+ ...operationFileEntry(node, node.operationId),
61
+ root,
62
+ output,
63
+ group: group ?? void 0
64
+ }));
65
+ }
46
66
  function getOperationLink(node, link) {
47
67
  if (!link) return null;
48
68
  if (typeof link === "function") return link(node) ?? null;
@@ -136,13 +156,24 @@ function buildOperationComments(node, options = {}) {
136
156
  if (!splitLines) return filteredComments;
137
157
  return filteredComments.flatMap((text) => text.split(/\r?\n/).map((line) => line.trim())).filter((comment) => Boolean(comment));
138
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
+ */
139
166
  function getOperationParameters(node) {
140
- return {
167
+ const cached = operationParameterGroupsByNode.get(node);
168
+ if (cached) return cached;
169
+ const groups = {
141
170
  path: dedupeParams(node.parameters.filter((param) => param.in === "path")),
142
171
  query: dedupeParams(node.parameters.filter((param) => param.in === "query")),
143
172
  header: dedupeParams(node.parameters.filter((param) => param.in === "header")),
144
173
  cookie: dedupeParams(node.parameters.filter((param) => param.in === "cookie"))
145
174
  };
175
+ operationParameterGroupsByNode.set(node, groups);
176
+ return groups;
146
177
  }
147
178
  function getStatusCodeNumber(statusCode) {
148
179
  const code = Number(statusCode);
@@ -380,23 +411,30 @@ function resolveContractClient(options) {
380
411
  * (plugin-fetch or plugin-axios). Returns `null` when no contract plugin is in play (the inline
381
412
  * path). The plugin injects `.kubb/client.ts` at the global output root, the same path consumers
382
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.
383
419
  */
384
420
  function resolveClientOperation(options) {
385
- const { clientPlugin, driver, node, root, output } = options;
421
+ const { clientPlugin, driver, node, root, output, cache } = options;
386
422
  if (!clientPlugin) return null;
387
- const resolver = driver.getResolver(clientPlugin.pluginName);
388
- const plugin = driver.getPlugin(clientPlugin.pluginName);
389
- const file = resolver.file({
390
- ...operationFileEntry(node, node.operationId),
391
- root,
392
- output: plugin?.options?.output ?? output,
393
- group: plugin?.options?.group ?? void 0
423
+ return cache.getOrSet(`${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
+ };
394
437
  });
395
- return {
396
- name: resolver.name(node.operationId),
397
- path: file.path,
398
- clientPath: path.resolve(root, ".kubb/client.ts")
399
- };
400
438
  }
401
439
  //#endregion
402
440
  //#region ../../internals/tanstack-query/src/utils.ts
@@ -1421,29 +1459,36 @@ function QueryOptions({ name, clientName, node, tsResolver, queryKeyName }) {
1421
1459
  const infiniteQueryGenerator = defineGenerator({
1422
1460
  name: "react-infinite-query",
1423
1461
  renderer: jsxRenderer,
1462
+ match(node, ctx) {
1463
+ const operationNode = node;
1464
+ if (!ast.isHttpOperationNode(operationNode)) return false;
1465
+ const { query, mutation, infinite, hooks } = ctx.options;
1466
+ const { isQuery, isMutation } = classifyOperation(operationNode, {
1467
+ query,
1468
+ mutation
1469
+ });
1470
+ const infiniteOptions = infinite && typeof infinite === "object" ? infinite : null;
1471
+ if (!isQuery || isMutation || !infiniteOptions || !hooks) return false;
1472
+ const queryParamKeys = getOperationParameters(operationNode).query.map((p) => p.name);
1473
+ return infiniteOptions.queryParam ? queryParamKeys.includes(infiniteOptions.queryParam) || queryParamKeys.includes(`${infiniteOptions.queryParam}?`) : false;
1474
+ },
1424
1475
  operation(node, ctx) {
1425
1476
  if (!ast.isHttpOperationNode(node)) return null;
1426
1477
  const { config, driver, resolver, root } = ctx;
1427
- const { output, query, mutation, infinite, client, group, customOptions, hooks } = ctx.options;
1478
+ const { output, query, infinite, client, group, customOptions } = ctx.options;
1428
1479
  const pluginTs = driver.getPlugin(pluginTsName);
1429
1480
  if (!pluginTs) return null;
1430
1481
  const tsResolver = driver.getResolver(pluginTsName);
1431
- const { isQuery, isMutation } = classifyOperation(node, {
1432
- query,
1433
- mutation
1434
- });
1435
1482
  const infiniteOptions = infinite && typeof infinite === "object" ? infinite : null;
1436
- if (!isQuery || isMutation || !infiniteOptions || !hooks) return null;
1437
- const normalizeKey = (key) => key.replace(/\?$/, "");
1438
- const queryParamKeys = getOperationParameters(node).query.map((p) => p.name);
1439
- if (!(infiniteOptions.queryParam ? queryParamKeys.some((k) => normalizeKey(k) === infiniteOptions.queryParam) : false)) return null;
1483
+ if (!infiniteOptions) return null;
1440
1484
  const importPath = query ? query.importPath : "@tanstack/react-query";
1441
1485
  const contractOp = resolveClientOperation({
1442
1486
  clientPlugin: { pluginName: client.pluginName },
1443
1487
  driver,
1444
1488
  node,
1445
1489
  root,
1446
- output
1490
+ output,
1491
+ cache: ctx.cache
1447
1492
  });
1448
1493
  if (!contractOp) return null;
1449
1494
  const queryName = resolver.infiniteQuery.name(node);
@@ -1457,11 +1502,13 @@ const infiniteQueryGenerator = defineGenerator({
1457
1502
  output,
1458
1503
  group: group ?? void 0
1459
1504
  }),
1460
- fileTs: tsResolver.file({
1461
- ...operationFileEntry(node, node.operationId),
1505
+ fileTs: resolveDependencyOperationFile({
1506
+ cache: ctx.cache,
1507
+ node,
1508
+ resolver: tsResolver,
1462
1509
  root,
1463
1510
  output: pluginTs.options?.output ?? output,
1464
- group: pluginTs.options?.group ?? void 0
1511
+ group: pluginTs.options?.group
1465
1512
  })
1466
1513
  };
1467
1514
  const { queryParamsTypeName } = resolvePageParamType(node, {
@@ -1588,25 +1635,30 @@ const infiniteQueryGenerator = defineGenerator({
1588
1635
  const mutationGenerator = defineGenerator({
1589
1636
  name: "react-query-mutation",
1590
1637
  renderer: jsxRenderer,
1638
+ match(node, ctx) {
1639
+ const operationNode = node;
1640
+ if (!ast.isHttpOperationNode(operationNode)) return false;
1641
+ const { query, mutation } = ctx.options;
1642
+ return classifyOperation(operationNode, {
1643
+ query,
1644
+ mutation
1645
+ }).isMutation;
1646
+ },
1591
1647
  operation(node, ctx) {
1592
1648
  if (!ast.isHttpOperationNode(node)) return null;
1593
1649
  const { config, driver, resolver, root } = ctx;
1594
- const { output, query, mutation, client, group, customOptions, hooks } = ctx.options;
1650
+ const { output, mutation, client, group, customOptions, hooks } = ctx.options;
1595
1651
  const pluginTs = driver.getPlugin(pluginTsName);
1596
1652
  if (!pluginTs) return null;
1597
1653
  const tsResolver = driver.getResolver(pluginTsName);
1598
- const { isMutation } = classifyOperation(node, {
1599
- query,
1600
- mutation
1601
- });
1602
- if (!isMutation) return null;
1603
1654
  const importPath = mutation ? mutation.importPath : "@tanstack/react-query";
1604
1655
  const contractOp = resolveClientOperation({
1605
1656
  clientPlugin: { pluginName: client.pluginName },
1606
1657
  driver,
1607
1658
  node,
1608
1659
  root,
1609
- output
1660
+ output,
1661
+ cache: ctx.cache
1610
1662
  });
1611
1663
  if (!contractOp) return null;
1612
1664
  const mutationHookName = resolver.mutation.name(node);
@@ -1620,11 +1672,13 @@ const mutationGenerator = defineGenerator({
1620
1672
  output,
1621
1673
  group: group ?? void 0
1622
1674
  }),
1623
- fileTs: tsResolver.file({
1624
- ...operationFileEntry(node, node.operationId),
1675
+ fileTs: resolveDependencyOperationFile({
1676
+ cache: ctx.cache,
1677
+ node,
1678
+ resolver: tsResolver,
1625
1679
  root,
1626
1680
  output: pluginTs.options?.output ?? output,
1627
- group: pluginTs.options?.group ?? void 0
1681
+ group: pluginTs.options?.group
1628
1682
  })
1629
1683
  };
1630
1684
  const importedTypeNames = [tsResolver.response.options(node), ...resolveOperationTypeNames(node, tsResolver, {
@@ -1728,25 +1782,31 @@ const mutationGenerator = defineGenerator({
1728
1782
  const queryGenerator = defineGenerator({
1729
1783
  name: "react-query",
1730
1784
  renderer: jsxRenderer,
1785
+ match(node, ctx) {
1786
+ const operationNode = node;
1787
+ if (!ast.isHttpOperationNode(operationNode)) return false;
1788
+ const { query, mutation } = ctx.options;
1789
+ const { isQuery, isMutation } = classifyOperation(operationNode, {
1790
+ query,
1791
+ mutation
1792
+ });
1793
+ return isQuery && !isMutation;
1794
+ },
1731
1795
  operation(node, ctx) {
1732
1796
  if (!ast.isHttpOperationNode(node)) return null;
1733
1797
  const { config, driver, resolver, root } = ctx;
1734
- const { output, query, mutation, client, group, customOptions, hooks } = ctx.options;
1798
+ const { output, query, client, group, customOptions, hooks } = ctx.options;
1735
1799
  const pluginTs = driver.getPlugin(pluginTsName);
1736
1800
  if (!pluginTs) return null;
1737
1801
  const tsResolver = driver.getResolver(pluginTsName);
1738
- const { isQuery, isMutation } = classifyOperation(node, {
1739
- query,
1740
- mutation
1741
- });
1742
- if (!isQuery || isMutation) return null;
1743
1802
  const importPath = query ? query.importPath : "@tanstack/react-query";
1744
1803
  const contractOp = resolveClientOperation({
1745
1804
  clientPlugin: { pluginName: client.pluginName },
1746
1805
  driver,
1747
1806
  node,
1748
1807
  root,
1749
- output
1808
+ output,
1809
+ cache: ctx.cache
1750
1810
  });
1751
1811
  if (!contractOp) return null;
1752
1812
  const queryName = resolver.query.name(node);
@@ -1760,11 +1820,13 @@ const queryGenerator = defineGenerator({
1760
1820
  output,
1761
1821
  group: group ?? void 0
1762
1822
  }),
1763
- fileTs: tsResolver.file({
1764
- ...operationFileEntry(node, node.operationId),
1823
+ fileTs: resolveDependencyOperationFile({
1824
+ cache: ctx.cache,
1825
+ node,
1826
+ resolver: tsResolver,
1765
1827
  root,
1766
1828
  output: pluginTs.options?.output ?? output,
1767
- group: pluginTs.options?.group ?? void 0
1829
+ group: pluginTs.options?.group
1768
1830
  })
1769
1831
  };
1770
1832
  const importedTypeNames = [tsResolver.response.options(node), ...resolveOperationTypeNames(node, tsResolver, {
@@ -1873,30 +1935,36 @@ const queryGenerator = defineGenerator({
1873
1935
  const suspenseInfiniteQueryGenerator = defineGenerator({
1874
1936
  name: "react-suspense-infinite-query",
1875
1937
  renderer: jsxRenderer,
1938
+ match(node, ctx) {
1939
+ const operationNode = node;
1940
+ if (!ast.isHttpOperationNode(operationNode)) return false;
1941
+ const { query, mutation, infinite, suspense, hooks } = ctx.options;
1942
+ const { isQuery, isMutation } = classifyOperation(operationNode, {
1943
+ query,
1944
+ mutation
1945
+ });
1946
+ const infiniteOptions = infinite && typeof infinite === "object" ? infinite : null;
1947
+ if (!isQuery || isMutation || !suspense || !infiniteOptions || !hooks) return false;
1948
+ const queryParamKeys = getOperationParameters(operationNode).query.map((p) => p.name);
1949
+ return infiniteOptions.queryParam ? queryParamKeys.includes(infiniteOptions.queryParam) || queryParamKeys.includes(`${infiniteOptions.queryParam}?`) : false;
1950
+ },
1876
1951
  operation(node, ctx) {
1877
1952
  if (!ast.isHttpOperationNode(node)) return null;
1878
1953
  const { config, driver, resolver, root } = ctx;
1879
- const { output, query, mutation, infinite, suspense, client, group, customOptions, hooks } = ctx.options;
1954
+ const { output, query, infinite, client, group, customOptions } = ctx.options;
1880
1955
  const pluginTs = driver.getPlugin(pluginTsName);
1881
1956
  if (!pluginTs) return null;
1882
1957
  const tsResolver = driver.getResolver(pluginTsName);
1883
- const { isQuery, isMutation } = classifyOperation(node, {
1884
- query,
1885
- mutation
1886
- });
1887
- const isSuspense = !!suspense;
1888
1958
  const infiniteOptions = infinite && typeof infinite === "object" ? infinite : null;
1889
- if (!isQuery || isMutation || !isSuspense || !infiniteOptions || !hooks) return null;
1890
- const normalizeKey = (key) => key.replace(/\?$/, "");
1891
- const queryParamKeys = getOperationParameters(node).query.map((p) => p.name);
1892
- if (!(infiniteOptions.queryParam ? queryParamKeys.some((k) => normalizeKey(k) === infiniteOptions.queryParam) : false)) return null;
1959
+ if (!infiniteOptions) return null;
1893
1960
  const importPath = query ? query.importPath : "@tanstack/react-query";
1894
1961
  const contractOp = resolveClientOperation({
1895
1962
  clientPlugin: { pluginName: client.pluginName },
1896
1963
  driver,
1897
1964
  node,
1898
1965
  root,
1899
- output
1966
+ output,
1967
+ cache: ctx.cache
1900
1968
  });
1901
1969
  if (!contractOp) return null;
1902
1970
  const queryName = resolver.suspenseInfiniteQuery.name(node);
@@ -1910,11 +1978,13 @@ const suspenseInfiniteQueryGenerator = defineGenerator({
1910
1978
  output,
1911
1979
  group: group ?? void 0
1912
1980
  }),
1913
- fileTs: tsResolver.file({
1914
- ...operationFileEntry(node, node.operationId),
1981
+ fileTs: resolveDependencyOperationFile({
1982
+ cache: ctx.cache,
1983
+ node,
1984
+ resolver: tsResolver,
1915
1985
  root,
1916
1986
  output: pluginTs.options?.output ?? output,
1917
- group: pluginTs.options?.group ?? void 0
1987
+ group: pluginTs.options?.group
1918
1988
  })
1919
1989
  };
1920
1990
  const rawQueryParams = getOperationParameters(node).query;
@@ -2040,25 +2110,31 @@ const suspenseInfiniteQueryGenerator = defineGenerator({
2040
2110
  const suspenseQueryGenerator = defineGenerator({
2041
2111
  name: "react-suspense-query",
2042
2112
  renderer: jsxRenderer,
2113
+ match(node, ctx) {
2114
+ const operationNode = node;
2115
+ if (!ast.isHttpOperationNode(operationNode)) return false;
2116
+ const { query, mutation, suspense, hooks } = ctx.options;
2117
+ const { isQuery, isMutation } = classifyOperation(operationNode, {
2118
+ query,
2119
+ mutation
2120
+ });
2121
+ return isQuery && !isMutation && !!suspense && hooks;
2122
+ },
2043
2123
  operation(node, ctx) {
2044
2124
  if (!ast.isHttpOperationNode(node)) return null;
2045
2125
  const { config, driver, resolver, root } = ctx;
2046
- const { output, query, mutation, suspense, client, group, customOptions, hooks } = ctx.options;
2126
+ const { output, query, client, group, customOptions } = ctx.options;
2047
2127
  const pluginTs = driver.getPlugin(pluginTsName);
2048
2128
  if (!pluginTs) return null;
2049
2129
  const tsResolver = driver.getResolver(pluginTsName);
2050
- const { isQuery, isMutation } = classifyOperation(node, {
2051
- query,
2052
- mutation
2053
- });
2054
- if (!isQuery || isMutation || !!!suspense || !hooks) return null;
2055
2130
  const importPath = query ? query.importPath : "@tanstack/react-query";
2056
2131
  const contractOp = resolveClientOperation({
2057
2132
  clientPlugin: { pluginName: client.pluginName },
2058
2133
  driver,
2059
2134
  node,
2060
2135
  root,
2061
- output
2136
+ output,
2137
+ cache: ctx.cache
2062
2138
  });
2063
2139
  if (!contractOp) return null;
2064
2140
  const queryName = resolver.suspenseQuery.name(node);
@@ -2072,11 +2148,13 @@ const suspenseQueryGenerator = defineGenerator({
2072
2148
  output,
2073
2149
  group: group ?? void 0
2074
2150
  }),
2075
- fileTs: tsResolver.file({
2076
- ...operationFileEntry(node, node.operationId),
2151
+ fileTs: resolveDependencyOperationFile({
2152
+ cache: ctx.cache,
2153
+ node,
2154
+ resolver: tsResolver,
2077
2155
  root,
2078
2156
  output: pluginTs.options?.output ?? output,
2079
- group: pluginTs.options?.group ?? void 0
2157
+ group: pluginTs.options?.group
2080
2158
  })
2081
2159
  };
2082
2160
  const importedTypeNames = [tsResolver.response.options(node), ...resolveOperationTypeNames(node, tsResolver, {