@kubb/plugin-swr 5.4.0 → 5.4.3

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,26 +1,8 @@
1
- import { t as __name } from "./rolldown-runtime-C0LytTxp.js";
2
- import { Resolver, Url, addGenerator, ast, createResolver, defineExtension, defineGenerator, setMacros, setResolver, useConfig } from "kubb/kit";
1
+ import { Diagnostics, Resolver, Url, addGenerator, ast, createResolver, defineExtension, defineGenerator, setMacros, setResolver, useConfig } from "kubb/kit";
3
2
  import path from "node:path";
4
3
  import { createFunctionParameter, createFunctionParameters, createObjectBindingPattern, createTypeLiteral, functionPrinter, pluginTsName } from "@kubb/plugin-ts";
5
4
  import { File, Function, Type, jsxRenderer } from "kubb/jsx";
6
5
  import { Fragment, jsx, jsxs } from "kubb/jsx/jsx-runtime";
7
- //#region ../../internals/shared/src/params.ts
8
- /**
9
- * Drops parameters that share the same name, keeping the first.
10
- *
11
- * A malformed spec can declare the same parameter name twice within one `in` location. Both would
12
- * resolve to the same output property, so emitting both would yield an object type with a duplicate
13
- * member, which TypeScript rejects. This is a defensive guard against that case, not a casing guard:
14
- * parameter names flow through unchanged, so no two distinct names ever collide here anymore.
15
- */
16
- function dedupeParams(params) {
17
- const seen = /* @__PURE__ */ new Set();
18
- return params.filter((param) => {
19
- if (seen.has(param.name)) return false;
20
- seen.add(param.name);
21
- return true;
22
- });
23
- }
24
6
  //#endregion
25
7
  //#region ../../internals/shared/src/operation.ts
26
8
  /**
@@ -91,12 +73,12 @@ function getContentTypeInfo(node) {
91
73
  function getResponseContentTypeInfo(node) {
92
74
  return buildContentTypeInfo(getPrimarySuccessResponse(node)?.content?.map((e) => e.contentType) ?? []);
93
75
  }
76
+ const requestConfigBaseType = `Partial<Omit<RequestConfig, 'path' | 'query' | 'body' | 'headers' | 'url'>>`;
94
77
  function buildRequestConfigType(node) {
95
78
  const request = getContentTypeInfo(node);
96
79
  const response = getResponseContentTypeInfo(node);
97
- const configType = `Partial<Omit<RequestConfig, 'path' | 'query' | 'body' | 'headers' | 'url'>>`;
98
80
  const members = [request.isMultipleContentTypes ? `request?: ${request.contentTypeUnion}` : null, response.isMultipleContentTypes ? `response?: ${response.contentTypeUnion}` : null].filter(Boolean);
99
- return members.length ? `${configType} & { contentType?: { ${members.join("; ")} } }` : configType;
81
+ return members.length ? `${requestConfigBaseType} & { contentType?: { ${members.join("; ")} } }` : requestConfigBaseType;
100
82
  }
101
83
  /**
102
84
  * Builds the `client?:` option type shared by the generated query hooks (`useQuery`,
@@ -105,7 +87,7 @@ function buildRequestConfigType(node) {
105
87
  * content type for.
106
88
  */
107
89
  function buildClientOptionType() {
108
- return `Partial<Omit<RequestConfig, 'path' | 'query' | 'body' | 'headers' | 'url'>>`;
90
+ return requestConfigBaseType;
109
91
  }
110
92
  /**
111
93
  * Which of the grouped request options an operation carries.
@@ -165,11 +147,19 @@ const operationParameterGroupsByNode = /* @__PURE__ */ new WeakMap();
165
147
  function getOperationParameters(node) {
166
148
  const cached = operationParameterGroupsByNode.get(node);
167
149
  if (cached) return cached;
150
+ const group = (location) => {
151
+ const seen = /* @__PURE__ */ new Set();
152
+ return node.parameters.filter((param) => {
153
+ if (param.in !== location || seen.has(param.name)) return false;
154
+ seen.add(param.name);
155
+ return true;
156
+ });
157
+ };
168
158
  const groups = {
169
- path: dedupeParams(node.parameters.filter((param) => param.in === "path")),
170
- query: dedupeParams(node.parameters.filter((param) => param.in === "query")),
171
- header: dedupeParams(node.parameters.filter((param) => param.in === "header")),
172
- cookie: dedupeParams(node.parameters.filter((param) => param.in === "cookie"))
159
+ path: group("path"),
160
+ query: group("query"),
161
+ header: group("header"),
162
+ cookie: group("cookie")
173
163
  };
174
164
  operationParameterGroupsByNode.set(node, groups);
175
165
  return groups;
@@ -308,11 +298,11 @@ function createGroupConfig(group) {
308
298
  //#region kubb-extension.json
309
299
  var compatibility = {
310
300
  "kubb": ">=5.8.3",
311
- "node": ">=22"
301
+ "node": ">=22.12"
312
302
  };
313
303
  //#endregion
314
304
  //#region package.json
315
- var version = "5.4.0";
305
+ var version = "5.4.3";
316
306
  //#endregion
317
307
  //#region ../../internals/client/src/resolveClient.ts
318
308
  /**
@@ -356,7 +346,7 @@ const contractPlugins = [
356
346
  * @example
357
347
  * ```ts
358
348
  * resolveClient({ client: undefined, pluginNames: ['plugin-ts'] })
359
- * // { kind: 'error', message: 'No client plugin is registered…' }
349
+ * // { kind: 'error', code: 'KUBB_MISSING_DEPENDENCY', message: 'No client plugin is registered.', help: 'Add …' }
360
350
  * ```
361
351
  */
362
352
  function resolveClient(options) {
@@ -366,7 +356,9 @@ function resolveClient(options) {
366
356
  const pluginName = selectorToPlugin[client];
367
357
  if (!has(pluginName)) return {
368
358
  kind: "error",
369
- message: `\`client: '${client}'\` is set but \`@kubb/plugin-${client}\` is not registered in \`plugins\`. Add it, or drop \`client\` to use a different client plugin.`
359
+ code: Diagnostics.code.missingDependency,
360
+ message: `\`client: '${client}'\` is set but \`@kubb/plugin-${client}\` is not registered in \`plugins\`.`,
361
+ help: `Add \`@kubb/plugin-${client}\` to \`plugins\`, or drop \`client\` to use a different client plugin.`
370
362
  };
371
363
  return {
372
364
  kind: "contract",
@@ -375,7 +367,9 @@ function resolveClient(options) {
375
367
  }
376
368
  if (client !== void 0) return {
377
369
  kind: "error",
378
- message: `Option \`client\` is ${typeof client === "string" ? `'${client}'` : String(client)}, but it must be one of 'fetch', 'axios', 'client'. Drop \`client\` to use the registered client plugin.`
370
+ code: Diagnostics.code.invalidPluginOptions,
371
+ message: `Option \`client\` is ${typeof client === "string" ? `'${client}'` : String(client)}, but it must be one of 'fetch', 'axios', 'client'.`,
372
+ help: "Set `client` to one of those values, or drop it to use the registered client plugin."
379
373
  };
380
374
  const registered = contractPlugins.filter(has);
381
375
  if (registered.length === 1) return {
@@ -384,25 +378,40 @@ function resolveClient(options) {
384
378
  };
385
379
  if (registered.length > 1) return {
386
380
  kind: "error",
387
- message: `Multiple client plugins are registered (${registered.map((name) => `\`@kubb/${name}\``).join(", ")}). Set \`client: 'fetch' | 'axios' | 'client'\` to choose which client the hooks call, or register a single client plugin.`
381
+ code: Diagnostics.code.invalidPluginOptions,
382
+ message: `Multiple client plugins are registered (${registered.map((name) => `\`@kubb/${name}\``).join(", ")}) and \`client\` is not set.`,
383
+ help: "Set `client: 'fetch' | 'axios' | 'client'` to choose which client the hooks call, or register a single client plugin."
388
384
  };
389
385
  return {
390
386
  kind: "error",
391
- message: "No client plugin is registered. Add `@kubb/plugin-axios`, `@kubb/plugin-fetch`, or `@kubb/plugin-client` to `plugins` so the generated code has an HTTP client to call."
387
+ code: Diagnostics.code.missingDependency,
388
+ message: "No client plugin is registered.",
389
+ help: "Add `@kubb/plugin-fetch`, `@kubb/plugin-axios`, or `@kubb/plugin-client` to `plugins` so the generated code has an HTTP client to call."
392
390
  };
393
391
  }
394
392
  /**
395
- * Resolves the contract client during a consumer plugin's setup hook. Extracts the plugin names
396
- * from the raw `plugins` config, applies {@link resolveClient}, and throws the diagnostic on a
397
- * misconfiguration so every consumer fails fast with the same message.
393
+ * Resolves the contract client during a consumer plugin's setup hook.
394
+ * It extracts the plugin names from the raw `plugins` config and applies {@link resolveClient}.
395
+ * On a misconfiguration it throws a config diagnostic tagged with `plugin`, so every consumer
396
+ * fails fast with the same message.
398
397
  */
399
398
  function resolveContractClient(options) {
400
- const { client, plugins = [] } = options;
399
+ const { plugin, client, plugins = [] } = options;
401
400
  const resolved = resolveClient({
402
401
  client,
403
402
  pluginNames: plugins.map((plugin) => plugin.name).filter((name) => Boolean(name))
404
403
  });
405
- if (resolved.kind === "error") throw new Error(resolved.message);
404
+ if (resolved.kind === "error") {
405
+ const { code, message, help } = resolved;
406
+ throw new Diagnostics.Error({
407
+ code,
408
+ severity: "error",
409
+ message,
410
+ help,
411
+ location: { kind: "config" },
412
+ plugin
413
+ });
414
+ }
406
415
  return resolved;
407
416
  }
408
417
  //#endregion
@@ -466,6 +475,95 @@ function resolveClientOperation(options) {
466
475
  });
467
476
  }
468
477
  //#endregion
478
+ //#region ../../internals/tanstack-query/src/components/HookFile.tsx
479
+ /**
480
+ * Resolves what every TanStack hook file shares: its file, the `@kubb/plugin-ts` file and type names, banner and footer.
481
+ */
482
+ function resolveHookFile(node, ctx, options) {
483
+ const { name, output, group, contractOp, keyTypeName, extraTypeNames = [] } = options;
484
+ const { config, root, resolver } = ctx;
485
+ const pluginTs = ctx.requirePlugin(pluginTsName);
486
+ const tsResolver = ctx.driver.getResolver(pluginTsName);
487
+ const file = resolver.file({
488
+ ...operationFileEntry(node, name),
489
+ root,
490
+ output,
491
+ group: group ?? void 0
492
+ });
493
+ const fileTs = resolveDependencyOperationFile({
494
+ cache: ctx.cache,
495
+ node,
496
+ resolver: tsResolver,
497
+ root,
498
+ output: pluginTs.options?.output ?? output,
499
+ group: pluginTs.options?.group
500
+ });
501
+ const importedTypeNames = [
502
+ tsResolver.response.options(node),
503
+ ...extraTypeNames,
504
+ ...resolveOperationTypeNames(node, tsResolver, {
505
+ exclude: keyTypeName ? [keyTypeName] : void 0,
506
+ order: "body-response-first",
507
+ includeParams: false
508
+ })
509
+ ].filter((typeName) => Boolean(typeName));
510
+ const fileMeta = {
511
+ path: file.path,
512
+ baseName: file.baseName
513
+ };
514
+ return {
515
+ file,
516
+ fileTs,
517
+ tsResolver,
518
+ contractOp,
519
+ importedTypeNames,
520
+ banner: resolver.default.banner(ctx.meta, {
521
+ output,
522
+ config,
523
+ file: fileMeta
524
+ }),
525
+ footer: resolver.default.footer(ctx.meta, {
526
+ output,
527
+ config,
528
+ file: fileMeta
529
+ })
530
+ };
531
+ }
532
+ /**
533
+ * The `<File>` shell shared by the TanStack hook generators; renders `children` after the imports.
534
+ */
535
+ function HookFile({ hook, imports, children }) {
536
+ const { file, fileTs, contractOp, importedTypeNames, banner, footer } = hook;
537
+ return /* @__PURE__ */ jsxs(File, {
538
+ baseName: file.baseName,
539
+ path: file.path,
540
+ meta: file.meta,
541
+ banner,
542
+ footer,
543
+ children: [
544
+ /* @__PURE__ */ jsx(File.Import, {
545
+ name: [contractOp.name],
546
+ root: file.path,
547
+ path: contractOp.path
548
+ }),
549
+ /* @__PURE__ */ jsx(File.Import, {
550
+ name: ["RequestConfig", "ResponseErrorConfig"],
551
+ root: contractOp.clientPathIsFile ? file.path : void 0,
552
+ path: contractOp.clientPath,
553
+ isTypeOnly: true
554
+ }),
555
+ imports,
556
+ fileTs && importedTypeNames.length > 0 && /* @__PURE__ */ jsx(File.Import, {
557
+ name: Array.from(new Set(importedTypeNames)),
558
+ root: file.path,
559
+ path: fileTs.path,
560
+ isTypeOnly: true
561
+ }),
562
+ children
563
+ ]
564
+ });
565
+ }
566
+ //#endregion
469
567
  //#region ../../internals/tanstack-query/src/utils.ts
470
568
  /**
471
569
  * The grouped request options, ordered for both the destructured signature and the
@@ -588,6 +686,19 @@ function buildCallResultBody(call, options = {}) {
588
686
  return `return ${call}.unwrap()`;
589
687
  }
590
688
  /**
689
+ * Builds the `TData` / `TError` type expressions shared by every generated hook, joining the resolved
690
+ * success responses into `TData` and wrapping the error responses in `ResponseErrorConfig<...>`.
691
+ */
692
+ function buildResponseTypes(node, resolver) {
693
+ const successNames = resolveSuccessNames(node, resolver);
694
+ const responseName = successNames.length > 0 ? successNames.join(" | ") : resolver.response.response(node);
695
+ const errorNames = resolveErrorNames(node, resolver);
696
+ return {
697
+ TData: responseName,
698
+ TError: `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`
699
+ };
700
+ }
701
+ /**
591
702
  * Applies the shared query defaults during plugin setup: `false` disables query generation, and an
592
703
  * object merges over `methods: ['GET', 'QUERY']` and the plugin's runtime `importPath`. QUERY
593
704
  * (RFC 10008) is a safe, idempotent read that carries its input in the body, so it is a query too.
@@ -647,13 +758,30 @@ const queryKeyGroupOrder = [
647
758
  * grouped `path`/`query`/`body` bindings.
648
759
  */
649
760
  function buildQueryKeyParams(node, options) {
650
- const { resolver } = options;
761
+ const { resolver, memberTypeWrapper } = options;
651
762
  const groupedParam = buildGroupedRequestParam(node, {
652
763
  resolver,
653
- keys: queryKeyGroupOrder
764
+ keys: queryKeyGroupOrder,
765
+ memberTypeWrapper
654
766
  });
655
767
  return createFunctionParameters({ params: groupedParam ? [groupedParam] : [] });
656
768
  }
769
+ /**
770
+ * Builds the hook parameter list: the grouped request param (omitted when `withRequest` is `false`) then `options` typed as `optionsType`.
771
+ */
772
+ function buildHookParams(node, options) {
773
+ const { resolver, optionsType, memberTypeWrapper, withRequest = true } = options;
774
+ const optionsParam = createFunctionParameter({
775
+ name: "options",
776
+ type: optionsType,
777
+ default: "{}"
778
+ });
779
+ const groupedParam = withRequest ? buildGroupedRequestParam(node, {
780
+ resolver,
781
+ memberTypeWrapper
782
+ }) : null;
783
+ return createFunctionParameters({ params: [groupedParam, optionsParam].filter((param) => param !== null) });
784
+ }
657
785
  //#endregion
658
786
  //#region ../../internals/tanstack-query/src/components/MutationKey.tsx
659
787
  const declarationPrinter$4 = functionPrinter({ mode: "declaration" });
@@ -661,7 +789,7 @@ const mutationKeyTransformer = ({ node }) => {
661
789
  if (!node.path) return [];
662
790
  return [`{ url: '${Url.toPath(node.path)}' }`];
663
791
  };
664
- function MutationKey$1({ name, node, transformer }) {
792
+ function MutationKey({ name, node, transformer, typeName }) {
665
793
  const paramsNode = createFunctionParameters({ params: [] });
666
794
  const paramsSignature = declarationPrinter$4.print(paramsNode) ?? "";
667
795
  const keys = (transformer ?? mutationKeyTransformer)({
@@ -669,7 +797,7 @@ function MutationKey$1({ name, node, transformer }) {
669
797
  casing: "camelcase",
670
798
  variant: "mutation"
671
799
  });
672
- return /* @__PURE__ */ jsx(File.Source, {
800
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(File.Source, {
673
801
  name,
674
802
  isExportable: true,
675
803
  isIndexable: true,
@@ -680,9 +808,18 @@ function MutationKey$1({ name, node, transformer }) {
680
808
  singleLine: true,
681
809
  children: `[${keys.join(", ")}] as const`
682
810
  })
683
- });
811
+ }), typeName && /* @__PURE__ */ jsx(File.Source, {
812
+ name: typeName,
813
+ isExportable: true,
814
+ isIndexable: true,
815
+ isTypeOnly: true,
816
+ children: /* @__PURE__ */ jsx(Type, {
817
+ export: true,
818
+ name: typeName,
819
+ children: `ReturnType<typeof ${name}>`
820
+ })
821
+ })] });
684
822
  }
685
- __name(MutationKey$1, "MutationKey");
686
823
  //#endregion
687
824
  //#region ../../internals/tanstack-query/src/resolver.ts
688
825
  /**
@@ -751,8 +888,11 @@ const queryKeyTransformer = ({ node, variant }) => {
751
888
  if (node.requestBody?.content?.[0]?.schema) result.push("...(body !== undefined ? [body] : [])");
752
889
  return result;
753
890
  };
754
- function QueryKey({ name, node, tsResolver, typeName, transformer, variant = "query" }) {
755
- const paramsNode = buildQueryKeyParams(node, { resolver: tsResolver });
891
+ function QueryKey({ name, node, tsResolver, typeName, transformer, variant = "query", memberTypeWrapper, exportType }) {
892
+ const paramsNode = buildQueryKeyParams(node, {
893
+ resolver: tsResolver,
894
+ memberTypeWrapper
895
+ });
756
896
  const paramsSignature = declarationPrinter$3.print(paramsNode) ?? "";
757
897
  const keys = (transformer ?? queryKeyTransformer)({
758
898
  node,
@@ -772,9 +912,12 @@ function QueryKey({ name, node, tsResolver, typeName, transformer, variant = "qu
772
912
  })
773
913
  }), /* @__PURE__ */ jsx(File.Source, {
774
914
  name: typeName,
915
+ isExportable: exportType,
916
+ isIndexable: exportType,
775
917
  isTypeOnly: true,
776
918
  children: /* @__PURE__ */ jsx(Type, {
777
919
  name: typeName,
920
+ export: exportType,
778
921
  children: `ReturnType<typeof ${name}>`
779
922
  })
780
923
  })] });
@@ -785,11 +928,7 @@ const declarationPrinter$2 = functionPrinter({ mode: "declaration" });
785
928
  const callPrinter$1 = functionPrinter({ mode: "call" });
786
929
  function buildMutationParamsNode(node, options) {
787
930
  const { mutationKeyTypeName, mutationArgTypeName, resolver } = options;
788
- const successNames = resolveSuccessNames(node, resolver);
789
- const responseName = successNames.length > 0 ? successNames.join(" | ") : resolver.response.response(node);
790
- const errorNames = resolveErrorNames(node, resolver);
791
- const TData = responseName;
792
- const TError = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`;
931
+ const { TData, TError } = buildResponseTypes(node, resolver);
793
932
  return createFunctionParameters({ params: [createFunctionParameter({
794
933
  name: "options",
795
934
  type: `{
@@ -801,11 +940,7 @@ function buildMutationParamsNode(node, options) {
801
940
  })] });
802
941
  }
803
942
  function Mutation({ name, clientName, mutationKeyName, mutationKeyTypeName, mutationArgTypeName, node, tsResolver, returnType = "full" }) {
804
- const successNames = resolveSuccessNames(node, tsResolver);
805
- const responseName = successNames.length > 0 ? successNames.join(" | ") : tsResolver.response.response(node);
806
- const errorNames = resolveErrorNames(node, tsResolver);
807
- const TData = responseName;
808
- const TError = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`;
943
+ const { TData, TError } = buildResponseTypes(node, tsResolver);
809
944
  const groupedParam = buildGroupedRequestParam(node, { resolver: tsResolver });
810
945
  const hasMutationParams = groupedParam !== null;
811
946
  const groupedParamsNode = createFunctionParameters({ params: groupedParam ? [groupedParam] : [] });
@@ -862,62 +997,99 @@ function Mutation({ name, clientName, mutationKeyName, mutationKeyTypeName, muta
862
997
  })] });
863
998
  }
864
999
  //#endregion
865
- //#region src/components/MutationKey.tsx
866
- function MutationKey({ name, typeName, node, transformer }) {
867
- return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(MutationKey$1, {
868
- name,
869
- node,
870
- transformer
871
- }), /* @__PURE__ */ jsx(File.Source, {
872
- name: typeName,
873
- isExportable: true,
874
- isIndexable: true,
875
- isTypeOnly: true,
876
- children: /* @__PURE__ */ jsx(Type, {
877
- export: true,
878
- name: typeName,
879
- children: `ReturnType<typeof ${name}>`
880
- })
881
- })] });
882
- }
1000
+ //#region src/generators/mutationGenerator.tsx
1001
+ /**
1002
+ * Built-in generator for `useSWRMutation` hooks. Emits one `useFooMutation` hook
1003
+ * per POST/PUT/DELETE operation (configurable via `mutation.methods`) plus
1004
+ * the matching `fooMutationKey` helper.
1005
+ */
1006
+ const mutationGenerator = defineGenerator({
1007
+ name: "swr-mutation",
1008
+ renderer: jsxRenderer,
1009
+ operation(node, ctx) {
1010
+ if (!ast.isHttpOperationNode(node)) return null;
1011
+ const { driver, resolver, root } = ctx;
1012
+ const { output, query, mutation, client, group } = ctx.options;
1013
+ const { isMutation } = classifyOperation(node, {
1014
+ query,
1015
+ mutation
1016
+ });
1017
+ if (!isMutation) return null;
1018
+ const importPath = mutation ? mutation.importPath : "swr/mutation";
1019
+ const contractOp = resolveClientOperation({
1020
+ clientPlugin: { pluginName: client.pluginName },
1021
+ driver,
1022
+ node,
1023
+ root,
1024
+ output,
1025
+ cache: ctx.cache
1026
+ });
1027
+ if (!contractOp) return null;
1028
+ const mutationHookName = resolver.mutation.name(node);
1029
+ const mutationKeyName = resolver.mutation.keyName(node);
1030
+ const mutationKeyTypeName = resolver.mutation.keyTypeName(node);
1031
+ const mutationArgTypeName = resolver.mutation.argTypeName(node);
1032
+ const hook = resolveHookFile(node, ctx, {
1033
+ name: mutationHookName,
1034
+ output,
1035
+ group,
1036
+ contractOp
1037
+ });
1038
+ return /* @__PURE__ */ jsxs(HookFile, {
1039
+ hook,
1040
+ children: [/* @__PURE__ */ jsx(MutationKey, {
1041
+ name: mutationKeyName,
1042
+ typeName: mutationKeyTypeName,
1043
+ node,
1044
+ transformer: ctx.options.mutationKey
1045
+ }), mutation && /* @__PURE__ */ jsxs(Fragment, { children: [
1046
+ /* @__PURE__ */ jsx(File.Import, {
1047
+ name: "useSWRMutation",
1048
+ path: importPath
1049
+ }),
1050
+ /* @__PURE__ */ jsx(File.Import, {
1051
+ name: ["SWRMutationConfiguration"],
1052
+ path: importPath,
1053
+ isTypeOnly: true
1054
+ }),
1055
+ /* @__PURE__ */ jsx(Mutation, {
1056
+ name: mutationHookName,
1057
+ clientName: contractOp.name,
1058
+ mutationKeyName,
1059
+ mutationKeyTypeName,
1060
+ mutationArgTypeName,
1061
+ node,
1062
+ tsResolver: hook.tsResolver,
1063
+ returnType: contractOp.returnType
1064
+ })
1065
+ ] })]
1066
+ });
1067
+ }
1068
+ });
883
1069
  //#endregion
884
1070
  //#region src/components/Query.tsx
885
1071
  const declarationPrinter$1 = functionPrinter({ mode: "declaration" });
886
1072
  const callPrinter = functionPrinter({ mode: "call" });
887
- function buildQueryParamsNode(node, options) {
888
- const { resolver } = options;
889
- const successNames = resolveSuccessNames(node, resolver);
890
- const responseName = successNames.length > 0 ? successNames.join(" | ") : resolver.response.response(node);
891
- const errorNames = resolveErrorNames(node, resolver);
892
- const TData = responseName;
893
- const TError = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`;
894
- const optionsParam = createFunctionParameter({
895
- name: "options",
896
- type: `{
897
- query?: SWRConfiguration<${[TData, TError].join(", ")}>,
898
- client?: ${buildClientOptionType()},
899
- shouldFetch?: boolean,
900
- immutable?: boolean
901
- }`,
902
- default: "{}"
903
- });
904
- const groupedParam = buildGroupedRequestParam(node, { resolver });
905
- return createFunctionParameters({ params: [groupedParam, optionsParam].filter((param) => param !== null) });
906
- }
907
1073
  function Query({ name, queryKeyTypeName, queryOptionsName, queryKeyName, node, tsResolver }) {
908
- const successNames = resolveSuccessNames(node, tsResolver);
909
- const responseName = successNames.length > 0 ? successNames.join(" | ") : tsResolver.response.response(node);
910
- const errorNames = resolveErrorNames(node, tsResolver);
1074
+ const { TData, TError } = buildResponseTypes(node, tsResolver);
911
1075
  const generics = [
912
- responseName,
913
- `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`,
1076
+ TData,
1077
+ TError,
914
1078
  `${queryKeyTypeName} | null`
915
1079
  ];
916
1080
  const queryKeyParamsNode = buildQueryKeyParams(node, { resolver: tsResolver });
917
1081
  const queryKeyParamsCall = callPrinter.print(queryKeyParamsNode) ?? "";
918
1082
  const queryOptionsParamsNode = buildQueryOptionsParams(node, { resolver: tsResolver });
919
1083
  const queryOptionsParamsCall = callPrinter.print(queryOptionsParamsNode) ?? "";
920
- const paramsNode = buildQueryParamsNode(node, { resolver: tsResolver });
1084
+ const paramsNode = buildHookParams(node, {
1085
+ resolver: tsResolver,
1086
+ optionsType: `{
1087
+ query?: SWRConfiguration<${[TData, TError].join(", ")}>,
1088
+ client?: ${buildClientOptionType()},
1089
+ shouldFetch?: boolean,
1090
+ immutable?: boolean
1091
+ }`
1092
+ });
921
1093
  const paramsSignature = declarationPrinter$1.print(paramsNode) ?? "";
922
1094
  return /* @__PURE__ */ jsx(File.Source, {
923
1095
  name,
@@ -978,134 +1150,6 @@ function QueryOptions({ name, clientName, node, tsResolver, returnType = "full"
978
1150
  });
979
1151
  }
980
1152
  //#endregion
981
- //#region src/generators/mutationGenerator.tsx
982
- /**
983
- * Built-in generator for `useSWRMutation` hooks. Emits one `useFooMutation` hook
984
- * per POST/PUT/DELETE operation (configurable via `mutation.methods`) plus
985
- * the matching `fooMutationKey` helper.
986
- */
987
- const mutationGenerator = defineGenerator({
988
- name: "swr-mutation",
989
- renderer: jsxRenderer,
990
- operation(node, ctx) {
991
- if (!ast.isHttpOperationNode(node)) return null;
992
- const { config, driver, resolver, root } = ctx;
993
- const { output, query, mutation, client, group } = ctx.options;
994
- const pluginTs = ctx.requirePlugin(pluginTsName);
995
- const tsResolver = driver.getResolver(pluginTsName);
996
- const { isMutation } = classifyOperation(node, {
997
- query,
998
- mutation
999
- });
1000
- if (!isMutation) return null;
1001
- const importPath = mutation ? mutation.importPath : "swr/mutation";
1002
- const contractOp = resolveClientOperation({
1003
- clientPlugin: { pluginName: client.pluginName },
1004
- driver,
1005
- node,
1006
- root,
1007
- output,
1008
- cache: ctx.cache
1009
- });
1010
- if (!contractOp) return null;
1011
- const mutationHookName = resolver.mutation.name(node);
1012
- const mutationKeyName = resolver.mutation.keyName(node);
1013
- const mutationKeyTypeName = resolver.mutation.keyTypeName(node);
1014
- const mutationArgTypeName = resolver.mutation.argTypeName(node);
1015
- const meta = {
1016
- file: resolver.file({
1017
- name: mutationHookName,
1018
- extname: ".ts",
1019
- tag: node.tags[0] ?? "default",
1020
- path: node.path,
1021
- root,
1022
- output,
1023
- group
1024
- }),
1025
- fileTs: resolveDependencyOperationFile({
1026
- cache: ctx.cache,
1027
- node,
1028
- resolver: tsResolver,
1029
- root,
1030
- output: pluginTs.options?.output ?? output,
1031
- group: pluginTs.options?.group
1032
- })
1033
- };
1034
- const importedTypeNames = [tsResolver.response.options(node), ...resolveOperationTypeNames(node, tsResolver, {
1035
- order: "body-response-first",
1036
- includeParams: false
1037
- })].filter((name) => Boolean(name));
1038
- const calledClientName = contractOp.name;
1039
- return /* @__PURE__ */ jsxs(File, {
1040
- baseName: meta.file.baseName,
1041
- path: meta.file.path,
1042
- meta: meta.file.meta,
1043
- banner: resolver.default.banner(ctx.meta, {
1044
- output,
1045
- config,
1046
- file: {
1047
- path: meta.file.path,
1048
- baseName: meta.file.baseName
1049
- }
1050
- }),
1051
- footer: resolver.default.footer(ctx.meta, {
1052
- output,
1053
- config,
1054
- file: {
1055
- path: meta.file.path,
1056
- baseName: meta.file.baseName
1057
- }
1058
- }),
1059
- children: [
1060
- /* @__PURE__ */ jsx(File.Import, {
1061
- name: [contractOp.name],
1062
- root: meta.file.path,
1063
- path: contractOp.path
1064
- }),
1065
- /* @__PURE__ */ jsx(File.Import, {
1066
- name: ["RequestConfig", "ResponseErrorConfig"],
1067
- root: contractOp.clientPathIsFile ? meta.file.path : void 0,
1068
- path: contractOp.clientPath,
1069
- isTypeOnly: true
1070
- }),
1071
- meta.fileTs && importedTypeNames.length > 0 && /* @__PURE__ */ jsx(File.Import, {
1072
- name: Array.from(new Set(importedTypeNames)),
1073
- root: meta.file.path,
1074
- path: meta.fileTs.path,
1075
- isTypeOnly: true
1076
- }),
1077
- /* @__PURE__ */ jsx(MutationKey, {
1078
- name: mutationKeyName,
1079
- typeName: mutationKeyTypeName,
1080
- node,
1081
- transformer: ctx.options.mutationKey
1082
- }),
1083
- mutation && /* @__PURE__ */ jsxs(Fragment, { children: [
1084
- /* @__PURE__ */ jsx(File.Import, {
1085
- name: "useSWRMutation",
1086
- path: importPath
1087
- }),
1088
- /* @__PURE__ */ jsx(File.Import, {
1089
- name: ["SWRMutationConfiguration"],
1090
- path: importPath,
1091
- isTypeOnly: true
1092
- }),
1093
- /* @__PURE__ */ jsx(Mutation, {
1094
- name: mutationHookName,
1095
- clientName: calledClientName,
1096
- mutationKeyName,
1097
- mutationKeyTypeName,
1098
- mutationArgTypeName,
1099
- node,
1100
- tsResolver,
1101
- returnType: contractOp.returnType
1102
- })
1103
- ] })
1104
- ]
1105
- });
1106
- }
1107
- });
1108
- //#endregion
1109
1153
  //#region src/generators/queryGenerator.tsx
1110
1154
  /**
1111
1155
  * Built-in generator for `useSWR` hooks. Emits one `useFoo` hook per read operation plus the
@@ -1116,10 +1160,8 @@ const queryGenerator = defineGenerator({
1116
1160
  renderer: jsxRenderer,
1117
1161
  operation(node, ctx) {
1118
1162
  if (!ast.isHttpOperationNode(node)) return null;
1119
- const { config, driver, resolver, root } = ctx;
1163
+ const { driver, resolver, root } = ctx;
1120
1164
  const { output, query, mutation, client, group } = ctx.options;
1121
- const pluginTs = ctx.requirePlugin(pluginTsName);
1122
- const tsResolver = driver.getResolver(pluginTsName);
1123
1165
  const { isQuery } = classifyOperation(node, {
1124
1166
  query,
1125
1167
  mutation
@@ -1139,69 +1181,17 @@ const queryGenerator = defineGenerator({
1139
1181
  const queryOptionsName = resolver.query.optionsName(node);
1140
1182
  const queryKeyName = resolver.query.keyName(node);
1141
1183
  const queryKeyTypeName = resolver.query.keyTypeName(node);
1142
- const meta = {
1143
- file: resolver.file({
1144
- name: queryName,
1145
- extname: ".ts",
1146
- tag: node.tags[0] ?? "default",
1147
- path: node.path,
1148
- root,
1149
- output,
1150
- group
1151
- }),
1152
- fileTs: resolveDependencyOperationFile({
1153
- cache: ctx.cache,
1154
- node,
1155
- resolver: tsResolver,
1156
- root,
1157
- output: pluginTs.options?.output ?? output,
1158
- group: pluginTs.options?.group
1159
- })
1160
- };
1161
- const importedTypeNames = [tsResolver.response.options(node), ...resolveOperationTypeNames(node, tsResolver, {
1162
- exclude: [queryKeyTypeName],
1163
- order: "body-response-first",
1164
- includeParams: false
1165
- })].filter((name) => Boolean(name));
1166
- const calledClientName = contractOp.name;
1167
- return /* @__PURE__ */ jsxs(File, {
1168
- baseName: meta.file.baseName,
1169
- path: meta.file.path,
1170
- meta: meta.file.meta,
1171
- banner: resolver.default.banner(ctx.meta, {
1172
- output,
1173
- config,
1174
- file: {
1175
- path: meta.file.path,
1176
- baseName: meta.file.baseName
1177
- }
1178
- }),
1179
- footer: resolver.default.footer(ctx.meta, {
1180
- output,
1181
- config,
1182
- file: {
1183
- path: meta.file.path,
1184
- baseName: meta.file.baseName
1185
- }
1186
- }),
1184
+ const hook = resolveHookFile(node, ctx, {
1185
+ name: queryName,
1186
+ output,
1187
+ group,
1188
+ contractOp,
1189
+ keyTypeName: queryKeyTypeName
1190
+ });
1191
+ const { tsResolver } = hook;
1192
+ return /* @__PURE__ */ jsxs(HookFile, {
1193
+ hook,
1187
1194
  children: [
1188
- /* @__PURE__ */ jsx(File.Import, {
1189
- name: [contractOp.name],
1190
- root: meta.file.path,
1191
- path: contractOp.path
1192
- }),
1193
- /* @__PURE__ */ jsx(File.Import, {
1194
- name: ["RequestConfig", "ResponseErrorConfig"],
1195
- root: contractOp.clientPathIsFile ? meta.file.path : void 0,
1196
- path: contractOp.clientPath,
1197
- isTypeOnly: true
1198
- }),
1199
- meta.fileTs && importedTypeNames.length > 0 && /* @__PURE__ */ jsx(File.Import, {
1200
- name: Array.from(new Set(importedTypeNames)),
1201
- root: meta.file.path,
1202
- path: meta.fileTs.path,
1203
- isTypeOnly: true
1204
- }),
1205
1195
  /* @__PURE__ */ jsx(QueryKey, {
1206
1196
  name: queryKeyName,
1207
1197
  typeName: queryKeyTypeName,
@@ -1212,7 +1202,7 @@ const queryGenerator = defineGenerator({
1212
1202
  }),
1213
1203
  /* @__PURE__ */ jsx(QueryOptions, {
1214
1204
  name: queryOptionsName,
1215
- clientName: calledClientName,
1205
+ clientName: contractOp.name,
1216
1206
  node,
1217
1207
  tsResolver,
1218
1208
  returnType: contractOp.returnType
@@ -1319,6 +1309,7 @@ const pluginSwr = defineExtension({
1319
1309
  return {
1320
1310
  output,
1321
1311
  client: resolveContractClient({
1312
+ plugin: pluginSwrName,
1322
1313
  client,
1323
1314
  plugins: useConfig().plugins
1324
1315
  }),