@kubb/plugin-swr 5.0.0-alpha.9 → 5.0.0-beta.103

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,144 +1,1212 @@
1
- import "./chunk--u3MIqq1.js";
2
- import { a as MutationKey, i as QueryKey, o as camelCase, s as pascalCase } from "./components-DRDGvgXG.js";
3
- import { n as mutationGenerator, t as queryGenerator } from "./generators-CRSl6u2M.js";
1
+ import { t as __name } from "./rolldown-runtime-C0LytTxp.js";
2
+ import { Resolver, Url, ast, createResolver, defineGenerator, definePlugin } from "kubb/kit";
4
3
  import path from "node:path";
5
- import { createPlugin, getBarrelFiles, getMode } from "@kubb/core";
6
- import { pluginClientName } from "@kubb/plugin-client";
7
- import { source } from "@kubb/plugin-client/templates/clients/axios.source";
8
- import { source as source$1 } from "@kubb/plugin-client/templates/clients/fetch.source";
9
- import { source as source$2 } from "@kubb/plugin-client/templates/config.source";
10
- import { OperationGenerator, pluginOasName } from "@kubb/plugin-oas";
11
- import { pluginTsName } from "@kubb/plugin-ts";
12
- import { pluginZodName } from "@kubb/plugin-zod";
4
+ import { createFunctionParameter, createFunctionParameters, createObjectBindingPattern, createTypeLiteral, functionPrinter, pluginTsName } from "@kubb/plugin-ts";
5
+ import { File, Function, Type, jsxRenderer } from "kubb/jsx";
6
+ 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
+ //#endregion
25
+ //#region ../../internals/shared/src/operation.ts
26
+ /**
27
+ * Builds the `ResolverFileParams` every operation generator passes to
28
+ * `resolver.file`: a file named `name`, tagged by the operation's first
29
+ * tag (or `'default'`), at the operation's path. Centralizes the entry object
30
+ * that was repeated at dozens of call sites across the client and query plugins.
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * resolver.file(operationFileEntry(node, node.operationId), { root, output, group })
35
+ * ```
36
+ */
37
+ function operationFileEntry(node, name, extname = ".ts") {
38
+ return {
39
+ name,
40
+ extname,
41
+ tag: node.tags[0] ?? "default",
42
+ path: node.path
43
+ };
44
+ }
45
+ function getOperationLink(node, link) {
46
+ if (!link) return null;
47
+ if (typeof link === "function") return link(node) ?? null;
48
+ return node.path ? `{@link ${Url.toPath(node.path)}}` : null;
49
+ }
50
+ /**
51
+ * Derives the shared `ContentTypeInfo` shape from a list of content types, tracking whether several
52
+ * are present and the union, default, and form-data flags the client uses to pick one.
53
+ */
54
+ function buildContentTypeInfo(contentTypes) {
55
+ const isMultipleContentTypes = contentTypes.length > 1;
56
+ return {
57
+ contentTypes,
58
+ isMultipleContentTypes,
59
+ contentTypeUnion: isMultipleContentTypes ? contentTypes.map((ct) => JSON.stringify(ct)).join(" | ") : "",
60
+ defaultContentType: contentTypes[0] ?? "application/json",
61
+ hasFormData: contentTypes.some((ct) => ct === "multipart/form-data")
62
+ };
63
+ }
64
+ function getContentTypeInfo(node) {
65
+ return buildContentTypeInfo(node.requestBody?.content?.map((e) => e.contentType) ?? []);
66
+ }
67
+ /**
68
+ * The request-body counterpart for the primary success response: the content types it documents and
69
+ * whether several are present, so the client can let a caller pick which one to accept.
70
+ */
71
+ function getResponseContentTypeInfo(node) {
72
+ return buildContentTypeInfo(getPrimarySuccessResponse(node)?.content?.map((e) => e.contentType) ?? []);
73
+ }
74
+ function buildRequestConfigType(node) {
75
+ const request = getContentTypeInfo(node);
76
+ const response = getResponseContentTypeInfo(node);
77
+ const configType = `Partial<Omit<RequestConfig, 'path' | 'query' | 'body' | 'headers' | 'url'>>`;
78
+ const members = [request.isMultipleContentTypes ? `request?: ${request.contentTypeUnion}` : null, response.isMultipleContentTypes ? `response?: ${response.contentTypeUnion}` : null].filter(Boolean);
79
+ return members.length ? `${configType} & { contentType?: { ${members.join("; ")} } }` : configType;
80
+ }
81
+ /**
82
+ * Builds the `client?:` option type shared by the generated query hooks (`useQuery`,
83
+ * `useInfiniteQuery`, `useSWR`, ...). Unlike {@link buildRequestConfigType}, it never adds a
84
+ * `contentType?:` member: query hooks wrap GET operations, which carry no request body to select a
85
+ * content type for.
86
+ */
87
+ function buildClientOptionType() {
88
+ return `Partial<Omit<RequestConfig, 'path' | 'query' | 'body' | 'headers' | 'url'>>`;
89
+ }
90
+ /**
91
+ * Which of the grouped request options an operation carries.
92
+ */
93
+ function getRequestGroups(node) {
94
+ const { path, query, header } = getOperationParameters(node);
95
+ return {
96
+ path: path.length > 0,
97
+ query: query.length > 0,
98
+ body: Boolean(node.requestBody?.content?.[0]?.schema),
99
+ headers: header.length > 0
100
+ };
101
+ }
102
+ /**
103
+ * Resolves which grouped request options an operation carries together with whether each group
104
+ * holds a required member. The grouped parameter stays optional only when nothing inside it is
105
+ * required, matching the generated `RequestConfig` type.
106
+ */
107
+ function getRequestGroupOptionality(node) {
108
+ const groups = getRequestGroups(node);
109
+ const { path, query, header } = getOperationParameters(node);
110
+ const hasRequiredPath = path.some((param) => param.required);
111
+ const hasRequiredQuery = query.some((param) => param.required);
112
+ const hasRequiredHeader = header.some((param) => param.required);
113
+ return {
114
+ groups,
115
+ hasRequiredPath,
116
+ hasRequiredQuery,
117
+ hasRequiredHeader,
118
+ isOptional: !hasRequiredPath && !hasRequiredQuery && !hasRequiredHeader && !groups.body
119
+ };
120
+ }
121
+ function buildOperationComments(node, options = {}) {
122
+ const { link = "pathTemplate", linkPosition = "afterDeprecated", splitLines = false } = options;
123
+ const linkComment = getOperationLink(node, link);
124
+ const filteredComments = (linkPosition === "beforeDeprecated" ? [
125
+ node.description && `@description ${node.description}`,
126
+ node.summary && `@summary ${node.summary}`,
127
+ linkComment,
128
+ node.deprecated && "@deprecated"
129
+ ] : [
130
+ node.description && `@description ${node.description}`,
131
+ node.summary && `@summary ${node.summary}`,
132
+ node.deprecated && "@deprecated",
133
+ linkComment
134
+ ]).filter((comment) => Boolean(comment));
135
+ if (!splitLines) return filteredComments;
136
+ return filteredComments.flatMap((text) => text.split(/\r?\n/).map((line) => line.trim())).filter((comment) => Boolean(comment));
137
+ }
138
+ function getOperationParameters(node) {
139
+ return {
140
+ path: dedupeParams(node.parameters.filter((param) => param.in === "path")),
141
+ query: dedupeParams(node.parameters.filter((param) => param.in === "query")),
142
+ header: dedupeParams(node.parameters.filter((param) => param.in === "header")),
143
+ cookie: dedupeParams(node.parameters.filter((param) => param.in === "cookie"))
144
+ };
145
+ }
146
+ function getStatusCodeNumber(statusCode) {
147
+ const code = Number(statusCode);
148
+ return Number.isNaN(code) ? null : code;
149
+ }
150
+ function isSuccessStatusCode(statusCode) {
151
+ const code = getStatusCodeNumber(statusCode);
152
+ return code !== null && code >= 200 && code < 300;
153
+ }
154
+ function isErrorStatusCode(statusCode) {
155
+ const code = getStatusCodeNumber(statusCode);
156
+ return code !== null && code >= 400;
157
+ }
158
+ function getSuccessResponses(responses) {
159
+ return responses.filter((response) => isSuccessStatusCode(response.statusCode));
160
+ }
161
+ function getOperationSuccessResponses(node) {
162
+ return getSuccessResponses(node.responses);
163
+ }
164
+ function getPrimarySuccessResponse(node) {
165
+ return getOperationSuccessResponses(node)[0] ?? null;
166
+ }
167
+ function resolveErrorNames(node, resolver) {
168
+ return node.responses.filter((response) => isErrorStatusCode(response.statusCode)).map((response) => resolver.response.status(node, response.statusCode));
169
+ }
170
+ function resolveStatusCodeNames(node, resolver) {
171
+ return node.responses.map((response) => resolver.response.status(node, response.statusCode));
172
+ }
173
+ const typeNamesByResolver = /* @__PURE__ */ new WeakMap();
174
+ function resolveOperationTypeNames(node, resolver, options = {}) {
175
+ const cacheKey = `${node.operationId}\0${options.order ?? ""}\0${options.responseStatusNames ?? ""}\0${options.includeParams === false ? "noparams" : ""}\0${(options.exclude ?? []).join(",")}`;
176
+ let byResolver = typeNamesByResolver.get(resolver);
177
+ if (byResolver) {
178
+ const cached = byResolver.get(cacheKey);
179
+ if (cached) return cached;
180
+ } else {
181
+ byResolver = /* @__PURE__ */ new Map();
182
+ typeNamesByResolver.set(resolver, byResolver);
183
+ }
184
+ const { path, query, header } = getOperationParameters(node);
185
+ const responseStatusNames = options.responseStatusNames === "error" ? resolveErrorNames(node, resolver) : options.responseStatusNames === false ? [] : resolveStatusCodeNames(node, resolver);
186
+ const exclude = new Set(options.exclude ?? []);
187
+ const paramNames = options.includeParams === false ? [] : [
188
+ ...path.map((param) => resolver.param.path(node, param)),
189
+ ...query.map((param) => resolver.param.query(node, param)),
190
+ ...header.map((param) => resolver.param.headers(node, param))
191
+ ];
192
+ const bodyAndResponseNames = [node.requestBody?.content?.[0]?.schema ? resolver.response.body(node) : null, resolver.response.response(node)];
193
+ const result = (options.order === "body-response-first" ? [
194
+ ...bodyAndResponseNames,
195
+ ...paramNames,
196
+ ...responseStatusNames
197
+ ] : [
198
+ ...paramNames,
199
+ ...bodyAndResponseNames,
200
+ ...responseStatusNames
201
+ ]).filter((name) => Boolean(name) && !exclude.has(name));
202
+ byResolver.set(cacheKey, result);
203
+ return result;
204
+ }
205
+ //#endregion
206
+ //#region ../../internals/utils/src/casing.ts
207
+ /**
208
+ * Shared implementation for camelCase and PascalCase conversion.
209
+ * Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
210
+ * and capitalizes each word according to `pascal`.
211
+ *
212
+ * When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
213
+ */
214
+ function toCamelOrPascal(text, pascal) {
215
+ 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) => {
216
+ if (word.length > 1 && word === word.toUpperCase()) return word;
217
+ return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
218
+ }).join("").replace(/[^a-zA-Z0-9]/g, "");
219
+ }
220
+ /**
221
+ * Converts `text` to camelCase.
222
+ *
223
+ * @example Word boundaries
224
+ * `camelCase('hello-world') // 'helloWorld'`
225
+ *
226
+ * @example With a prefix
227
+ * `camelCase('tag', { prefix: 'create' }) // 'createTag'`
228
+ */
229
+ function camelCase(text, { prefix = "", suffix = "" } = {}) {
230
+ return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
231
+ }
232
+ /**
233
+ * Uppercases only the first character of `text`, leaving the rest untouched.
234
+ * Unlike {@link pascalCase} it never re-splits word boundaries or strips characters.
235
+ *
236
+ * @example
237
+ * `capitalize('getPetById') // 'GetPetById'`
238
+ */
239
+ function capitalize(text) {
240
+ return `${text.charAt(0).toUpperCase()}${text.slice(1)}`;
241
+ }
242
+ //#endregion
243
+ //#region ../../internals/shared/src/group.ts
244
+ /**
245
+ * Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
246
+ * shared default naming so every plugin groups output consistently:
247
+ *
248
+ * - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).
249
+ * - other groups use the camelCased group (`pet store` → `petStore`).
250
+ *
251
+ * A user-provided `group.name` always wins over the default namer, so callers stay in
252
+ * control of their output folders. Returns `null` when grouping is disabled, matching the
253
+ * per-plugin convention.
254
+ *
255
+ * @param group - The user-supplied group option, or `undefined` to disable grouping.
256
+ *
257
+ * @example
258
+ * ```ts
259
+ * createGroupConfig(group) // shared across every plugin
260
+ * ```
261
+ */
262
+ function createGroupConfig(group) {
263
+ if (!group) return null;
264
+ const defaultName = (ctx) => {
265
+ if (group.type === "path") return `${ctx.group.split("/")[1]}`;
266
+ return camelCase(ctx.group);
267
+ };
268
+ return {
269
+ ...group,
270
+ name: group.name ? group.name : defaultName
271
+ };
272
+ }
273
+ //#endregion
274
+ //#region ../../internals/client/src/resolveClient.ts
275
+ /**
276
+ * Resolves which client plugin a consumer (react-query, vue-query, swr, mcp) should call for an
277
+ * operation, shared so the selection rules and diagnostics stay in one place.
278
+ *
279
+ * Every client runtime lives in a dedicated client plugin, so a consumer always calls a registered
280
+ * contract client plugin (plugin-fetch or plugin-axios) and never emits its own inline client:
281
+ *
282
+ * - `contract` — a registered contract client plugin owns the `<op>` functions and the consumer
283
+ * imports and calls them.
284
+ * - `error` — no client plugin is registered, several are registered without a selector, or the
285
+ * requested one is missing.
286
+ *
287
+ * The `client` string selects explicitly (`'fetch'` / `'axios'`); when it is unset a lone registered
288
+ * contract client plugin is picked up automatically.
289
+ */
290
+ const pluginFetchName = "plugin-fetch";
291
+ const pluginAxiosName = "plugin-axios";
292
+ const selectorToPlugin = {
293
+ fetch: pluginFetchName,
294
+ axios: pluginAxiosName
295
+ };
296
+ const contractPlugins = [pluginFetchName, pluginAxiosName];
297
+ /**
298
+ * Applies the `client` resolution rules. See the module comment for the strategy shape.
299
+ *
300
+ * @example
301
+ * ```ts
302
+ * resolveClient({ client: 'fetch', pluginNames: ['plugin-ts', 'plugin-fetch'] })
303
+ * // { kind: 'contract', pluginName: 'plugin-fetch' }
304
+ * ```
305
+ *
306
+ * @example
307
+ * ```ts
308
+ * resolveClient({ client: undefined, pluginNames: ['plugin-ts'] })
309
+ * // { kind: 'error', message: 'No client plugin is registered…' }
310
+ * ```
311
+ */
312
+ function resolveClient(options) {
313
+ const { client, pluginNames } = options;
314
+ const has = (name) => pluginNames.includes(name);
315
+ if (client === "fetch" || client === "axios") {
316
+ const pluginName = selectorToPlugin[client];
317
+ if (!has(pluginName)) return {
318
+ kind: "error",
319
+ 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.`
320
+ };
321
+ return {
322
+ kind: "contract",
323
+ pluginName
324
+ };
325
+ }
326
+ const registered = contractPlugins.filter(has);
327
+ if (registered.length === 1) return {
328
+ kind: "contract",
329
+ pluginName: registered[0]
330
+ };
331
+ if (registered.length > 1) return {
332
+ kind: "error",
333
+ message: `Multiple client plugins are registered (${registered.map((name) => `\`@kubb/${name}\``).join(", ")}). Set \`client: 'fetch' | 'axios'\` to choose which client the hooks call, or register a single client plugin.`
334
+ };
335
+ return {
336
+ kind: "error",
337
+ message: "No client plugin is registered. Add `@kubb/plugin-axios` or `@kubb/plugin-fetch` to `plugins` so the generated code has an HTTP client to call."
338
+ };
339
+ }
340
+ /**
341
+ * Resolves the contract client during a consumer plugin's setup hook. Extracts the plugin names
342
+ * from the raw `plugins` config, applies {@link resolveClient}, and throws the diagnostic on a
343
+ * misconfiguration so every consumer fails fast with the same message.
344
+ */
345
+ function resolveContractClient(options) {
346
+ const { client, plugins = [] } = options;
347
+ const resolved = resolveClient({
348
+ client,
349
+ pluginNames: plugins.map((plugin) => plugin.name).filter((name) => Boolean(name))
350
+ });
351
+ if (resolved.kind === "error") throw new Error(resolved.message);
352
+ return resolved;
353
+ }
354
+ //#endregion
355
+ //#region ../../internals/client/src/resolveClientOperation.ts
356
+ /**
357
+ * Resolves the contract client `<op>` a consumer (query hook, MCP handler) imports, by looking up
358
+ * the registered contract client plugin's resolver and output. Works for any contract client plugin
359
+ * (plugin-fetch or plugin-axios). Returns `null` when no contract plugin is in play (the inline
360
+ * path). The plugin injects `.kubb/client.ts` at the global output root, the same path consumers
361
+ * read `RequestConfig` / `ResponseErrorConfig` from.
362
+ */
363
+ function resolveClientOperation(options) {
364
+ const { clientPlugin, driver, node, root, output } = options;
365
+ if (!clientPlugin) return null;
366
+ const resolver = driver.getResolver(clientPlugin.pluginName);
367
+ const plugin = driver.getPlugin(clientPlugin.pluginName);
368
+ const file = resolver.file({
369
+ ...operationFileEntry(node, node.operationId),
370
+ root,
371
+ output: plugin?.options?.output ?? output,
372
+ group: plugin?.options?.group ?? void 0
373
+ });
374
+ return {
375
+ name: resolver.name(node.operationId),
376
+ path: file.path,
377
+ clientPath: path.resolve(root, ".kubb/client.ts")
378
+ };
379
+ }
380
+ //#endregion
381
+ //#region ../../internals/tanstack-query/src/utils.ts
382
+ /**
383
+ * The grouped request options, ordered for both the destructured signature and the
384
+ * call passed to the underlying client.
385
+ */
386
+ const requestGroupOrder = [
387
+ "path",
388
+ "query",
389
+ "body",
390
+ "headers"
391
+ ];
392
+ /**
393
+ * Builds the grouped `{ path, query, body, headers }` parameter that mirrors the client
394
+ * function signature. Only the groups the operation carries are emitted, typed from the
395
+ * operation's `Options`. `keys` narrows the emitted groups, used by the query key which
396
+ * never carries `headers`.
397
+ *
398
+ * By default the whole group is typed as the single `Options` reference. When
399
+ * `memberTypeWrapper` is set, each group is emitted as its own member typed from the matching
400
+ * `Options['<group>']` slice and wrapped, used by vue-query to apply
401
+ * `MaybeRefOrGetter` per group.
402
+ */
403
+ function buildGroupedRequestParam(node, options) {
404
+ const { resolver, keys = requestGroupOrder, memberTypeWrapper } = options;
405
+ const { groups, hasRequiredPath, hasRequiredQuery, hasRequiredHeader } = getRequestGroupOptionality(node);
406
+ const names = keys.filter((key) => groups[key]);
407
+ if (names.length === 0) return null;
408
+ const requiredByGroup = {
409
+ path: hasRequiredPath,
410
+ query: hasRequiredQuery,
411
+ body: groups.body,
412
+ headers: hasRequiredHeader
413
+ };
414
+ const isOptional = names.every((name) => !requiredByGroup[name]);
415
+ const optionsName = resolver.response.options(node);
416
+ const omittedKeys = requestGroupOrder.filter((key) => !keys.includes(key));
417
+ const optionsType = omittedKeys.length > 0 ? `Omit<${optionsName}, ${omittedKeys.map((key) => `'${key}'`).join(" | ")}>` : optionsName;
418
+ if (memberTypeWrapper) {
419
+ const members = names.map((name) => ({
420
+ name,
421
+ type: memberTypeWrapper(`${optionsType}['${name}']`),
422
+ optional: !requiredByGroup[name]
423
+ }));
424
+ return createFunctionParameter({
425
+ name: createObjectBindingPattern({ elements: names.map((name) => ({ name })) }),
426
+ type: createTypeLiteral({ members }),
427
+ optional: false,
428
+ ...isOptional ? { default: "{}" } : {}
429
+ });
430
+ }
431
+ return createFunctionParameter({
432
+ name: createObjectBindingPattern({ elements: names.map((name) => ({ name })) }),
433
+ type: optionsType,
434
+ optional: false,
435
+ ...isOptional ? { default: "{}" } : {}
436
+ });
437
+ }
438
+ /**
439
+ * Builds the shared `({ path, query, body, headers }, config = {})` parameter list for a
440
+ * TanStack query-options function. The leading parameter mirrors the client signature, and the
441
+ * trailing `config` is a partial `RequestConfig` minus the grouped data-shape keys, which are passed
442
+ * explicitly. Framework plugins wrap the result when needed, for example vue-query applies `MaybeRefOrGetter`.
443
+ */
444
+ function buildQueryOptionsParams(node, options) {
445
+ const { resolver, memberTypeWrapper } = options;
446
+ return createFunctionParameters({ params: [buildGroupedRequestParam(node, {
447
+ resolver,
448
+ memberTypeWrapper
449
+ }), createFunctionParameter({
450
+ name: "config",
451
+ type: `Partial<Omit<RequestConfig, 'path' | 'query' | 'body' | 'headers' | 'url'>>`,
452
+ default: "{}"
453
+ })].filter((param) => param !== null) });
454
+ }
455
+ /**
456
+ * Builds the call to a client `<op>` function inside a query/mutation hook body. The function takes
457
+ * a single grouped options object, so the operation's request groups are passed as
458
+ * shorthand alongside the spread `config`, with `throwOnError: true` pinned last so a caller's config
459
+ * can't flip the query's error semantics. Queries thread the TanStack `signal`; mutations omit it.
460
+ *
461
+ * When `unwrapName` is set, each request group is passed as an explicit member unwrapped through it,
462
+ * used by vue-query to resolve refs and getters with `toValue(...)` at call time.
463
+ *
464
+ * @example
465
+ * ```ts
466
+ * buildClientCall(node, { clientName: 'getPetById', signal: true })
467
+ * // getPetById({ path, ...config, signal: config.signal ?? signal, throwOnError: true })
468
+ * ```
469
+ */
470
+ function buildClientCall(node, options) {
471
+ const { clientName, signal = false, unwrapName } = options;
472
+ const { groups } = getRequestGroupOptionality(node);
473
+ return `${clientName}({ ${[
474
+ "...config",
475
+ ...requestGroupOrder.filter((key) => groups[key]).map((name) => unwrapName ? `${name}: ${unwrapName(name)}` : name),
476
+ signal ? "signal: config.signal ?? signal" : null,
477
+ "throwOnError: true"
478
+ ].filter((part) => part !== null).join(", ")} })`;
479
+ }
480
+ /**
481
+ * Applies the shared query defaults during plugin setup: `false` disables query generation, and an
482
+ * object merges over `methods: ['GET']` and the plugin's runtime `importPath`.
483
+ */
484
+ function resolveQueryConfig(query, options) {
485
+ if (query === false) return false;
486
+ return {
487
+ importPath: options.importPath,
488
+ methods: ["GET"],
489
+ ...query
490
+ };
491
+ }
492
+ /**
493
+ * Applies the shared mutation defaults during plugin setup: `false` disables mutation generation,
494
+ * and an object merges over `methods: ['POST', 'PUT', 'PATCH', 'DELETE']` and the plugin's runtime
495
+ * `importPath`.
496
+ */
497
+ function resolveMutationConfig(mutation, options) {
498
+ if (mutation === false) return false;
499
+ return {
500
+ importPath: options.importPath,
501
+ methods: [
502
+ "POST",
503
+ "PUT",
504
+ "PATCH",
505
+ "DELETE"
506
+ ],
507
+ ...mutation
508
+ };
509
+ }
510
+ /**
511
+ * Classifies an operation as a query or a mutation from the resolved `query` / `mutation` method lists.
512
+ * `query: false` still marks the operation as a query so the query-family generators keep matching it,
513
+ * and a method already claimed by `query` never counts as a mutation.
514
+ */
515
+ function classifyOperation(node, { query, mutation }) {
516
+ const isQuery = query === false || !!query && query.methods.some((method) => node.method.toLowerCase() === method.toLowerCase());
517
+ const queryMethods = new Set(query ? query.methods : []);
518
+ return {
519
+ isQuery,
520
+ isMutation: mutation !== false && !isQuery && (mutation ? mutation.methods : []).some((method) => !queryMethods.has(method) && node.method.toLowerCase() === method.toLowerCase())
521
+ };
522
+ }
523
+ /**
524
+ * Build QueryKey params as the grouped `{ path, query, body }` object (NO headers, NO config),
525
+ * typed from the operation's `Options` minus `url`. The query key transformer reads the
526
+ * grouped `path`/`query`/`body` bindings.
527
+ */
528
+ function buildQueryKeyParams(node, options) {
529
+ const { resolver } = options;
530
+ const groupedParam = buildGroupedRequestParam(node, {
531
+ resolver,
532
+ keys: [
533
+ "path",
534
+ "query",
535
+ "body"
536
+ ]
537
+ });
538
+ return createFunctionParameters({ params: groupedParam ? [groupedParam] : [] });
539
+ }
540
+ //#endregion
541
+ //#region ../../internals/tanstack-query/src/components/MutationKey.tsx
542
+ const declarationPrinter$4 = functionPrinter({ mode: "declaration" });
543
+ const mutationKeyTransformer = ({ node }) => {
544
+ if (!node.path) return [];
545
+ return [`{ url: '${Url.toPath(node.path)}' }`];
546
+ };
547
+ function MutationKey$1({ name, node, transformer }) {
548
+ const paramsNode = createFunctionParameters({ params: [] });
549
+ const paramsSignature = declarationPrinter$4.print(paramsNode) ?? "";
550
+ const keys = (transformer ?? mutationKeyTransformer)({
551
+ node,
552
+ casing: "camelcase"
553
+ });
554
+ return /* @__PURE__ */ jsx(File.Source, {
555
+ name,
556
+ isExportable: true,
557
+ isIndexable: true,
558
+ children: /* @__PURE__ */ jsx(Function.Arrow, {
559
+ name,
560
+ export: true,
561
+ params: paramsSignature,
562
+ singleLine: true,
563
+ children: `[${keys.join(", ")}] as const`
564
+ })
565
+ });
566
+ }
567
+ __name(MutationKey$1, "MutationKey");
568
+ //#endregion
569
+ //#region ../../internals/tanstack-query/src/resolver.ts
570
+ /**
571
+ * Builds the shared query namespace for a variant. Spread the result into
572
+ * `createResolver` once per variant the plugin supports.
573
+ *
574
+ * @example
575
+ * ```ts
576
+ * createResolver<PluginReactQuery>({
577
+ * query: createQueryResolver(),
578
+ * suspenseQuery: createQueryResolver('Suspense'),
579
+ * })
580
+ * ```
581
+ */
582
+ function createQueryResolver(variant = "") {
583
+ return {
584
+ name(node) {
585
+ return `use${capitalize(this.name(node.operationId))}${variant}`;
586
+ },
587
+ optionsName(node) {
588
+ return `${this.name(node.operationId)}${variant}QueryOptions`;
589
+ },
590
+ keyName(node) {
591
+ return `${this.name(node.operationId)}${variant}QueryKey`;
592
+ },
593
+ keyTypeName(node) {
594
+ return `${capitalize(this.name(node.operationId))}${variant}QueryKey`;
595
+ },
596
+ clientName(node) {
597
+ return `${this.name(node.operationId)}${variant}`;
598
+ }
599
+ };
600
+ }
601
+ /**
602
+ * Builds the shared mutation namespace. Spread the result into
603
+ * `createResolver` and add plugin-specific methods (`optionsName`,
604
+ * `typeName`, `argTypeName`) next to it.
605
+ *
606
+ * @example
607
+ * ```ts
608
+ * createResolver<PluginSwr>({ mutation: { ...createMutationResolver(), argTypeName(node) {...} } })
609
+ * ```
610
+ */
611
+ function createMutationResolver() {
612
+ return {
613
+ name(node) {
614
+ return `use${capitalize(this.name(node.operationId))}`;
615
+ },
616
+ keyName(node) {
617
+ return `${this.name(node.operationId)}MutationKey`;
618
+ }
619
+ };
620
+ }
621
+ //#endregion
622
+ //#region ../../internals/tanstack-query/src/components/QueryKey.tsx
623
+ const declarationPrinter$3 = functionPrinter({ mode: "declaration" });
624
+ const queryKeyTransformer = ({ node }) => {
625
+ if (!node.path) return [];
626
+ const hasPathParams = getOperationParameters(node).path.length > 0;
627
+ const hasQueryParams = getOperationParameters(node).query.length > 0;
628
+ const hasRequestBody = !!node.requestBody?.content?.[0]?.schema;
629
+ return [
630
+ hasPathParams ? `{ url: '${Url.toPath(node.path)}', params: path }` : `{ url: '${Url.toPath(node.path)}' }`,
631
+ hasQueryParams ? "...(query ? [query] : [])" : null,
632
+ hasRequestBody ? "...(body ? [body] : [])" : null
633
+ ].filter(Boolean);
634
+ };
635
+ function QueryKey({ name, node, tsResolver, typeName, transformer }) {
636
+ const paramsNode = buildQueryKeyParams(node, { resolver: tsResolver });
637
+ const paramsSignature = declarationPrinter$3.print(paramsNode) ?? "";
638
+ const keys = (transformer ?? queryKeyTransformer)({
639
+ node,
640
+ casing: "camelcase"
641
+ });
642
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(File.Source, {
643
+ name,
644
+ isExportable: true,
645
+ isIndexable: true,
646
+ children: /* @__PURE__ */ jsx(Function.Arrow, {
647
+ name,
648
+ export: true,
649
+ params: paramsSignature,
650
+ singleLine: true,
651
+ children: `[${keys.join(", ")}] as const`
652
+ })
653
+ }), /* @__PURE__ */ jsx(File.Source, {
654
+ name: typeName,
655
+ isTypeOnly: true,
656
+ children: /* @__PURE__ */ jsx(Type, {
657
+ name: typeName,
658
+ children: `ReturnType<typeof ${name}>`
659
+ })
660
+ })] });
661
+ }
662
+ //#endregion
663
+ //#region src/components/Mutation.tsx
664
+ const declarationPrinter$2 = functionPrinter({ mode: "declaration" });
665
+ const callPrinter$1 = functionPrinter({ mode: "call" });
666
+ function buildMutationParamsNode(node, options) {
667
+ const { mutationKeyTypeName, mutationArgTypeName, resolver } = options;
668
+ const responseName = resolver.response.response(node);
669
+ const errorNames = resolveErrorNames(node, resolver);
670
+ return createFunctionParameters({ params: [createFunctionParameter({
671
+ name: "options",
672
+ type: `{
673
+ mutation?: SWRMutationConfiguration<${responseName}, ${`ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`}, ${mutationKeyTypeName} | null, ${mutationArgTypeName}> & { throwOnError?: boolean },
674
+ client?: ${buildRequestConfigType(node)},
675
+ shouldFetch?: boolean,
676
+ }`,
677
+ default: "{}"
678
+ })] });
679
+ }
680
+ function Mutation({ name, clientName, mutationKeyName, mutationKeyTypeName, mutationArgTypeName, node, tsResolver }) {
681
+ const responseName = tsResolver.response.response(node);
682
+ const errorNames = resolveErrorNames(node, tsResolver);
683
+ const TData = responseName;
684
+ const TError = `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`;
685
+ const groupedParam = buildGroupedRequestParam(node, { resolver: tsResolver });
686
+ const hasMutationParams = groupedParam !== null;
687
+ const groupedParamsNode = createFunctionParameters({ params: groupedParam ? [groupedParam] : [] });
688
+ const argTypeBody = hasMutationParams ? tsResolver.response.options(node) : "";
689
+ const argBindingStr = hasMutationParams ? callPrinter$1.print(groupedParamsNode) ?? "" : "";
690
+ const mutationFnBody = `const { data } = await ${buildClientCall(node, {
691
+ clientName,
692
+ signal: false
693
+ })}
694
+ return data`;
695
+ const generics = [
696
+ TData,
697
+ TError,
698
+ `${mutationKeyTypeName} | null`,
699
+ mutationArgTypeName
700
+ ];
701
+ const paramsNode = buildMutationParamsNode(node, {
702
+ mutationKeyTypeName,
703
+ mutationArgTypeName,
704
+ resolver: tsResolver
705
+ });
706
+ const paramsSignature = declarationPrinter$2.print(paramsNode) ?? "";
707
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(File.Source, {
708
+ name: mutationArgTypeName,
709
+ isExportable: true,
710
+ isIndexable: true,
711
+ isTypeOnly: true,
712
+ children: /* @__PURE__ */ jsx(Type, {
713
+ name: mutationArgTypeName,
714
+ export: true,
715
+ children: hasMutationParams ? argTypeBody : "never"
716
+ })
717
+ }), /* @__PURE__ */ jsx(File.Source, {
718
+ name,
719
+ isExportable: true,
720
+ isIndexable: true,
721
+ children: /* @__PURE__ */ jsx(Function, {
722
+ name,
723
+ export: true,
724
+ params: paramsSignature,
725
+ JSDoc: { comments: buildOperationComments(node) },
726
+ children: `
727
+ const { mutation: mutationOptions, client: config = {}, shouldFetch = true } = options ?? {}
728
+ const mutationKey = ${mutationKeyName}()
729
+
730
+ return useSWRMutation<${generics.join(", ")}>(
731
+ shouldFetch ? mutationKey : null,
732
+ async (_url${hasMutationParams ? `, { arg: ${argBindingStr} }` : ""}) => {
733
+ ${mutationFnBody}
734
+ },
735
+ mutationOptions
736
+ )
737
+ `
738
+ })
739
+ })] });
740
+ }
741
+ //#endregion
742
+ //#region src/components/MutationKey.tsx
743
+ function MutationKey({ name, typeName, node, transformer }) {
744
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(MutationKey$1, {
745
+ name,
746
+ node,
747
+ transformer
748
+ }), /* @__PURE__ */ jsx(File.Source, {
749
+ name: typeName,
750
+ isExportable: true,
751
+ isIndexable: true,
752
+ isTypeOnly: true,
753
+ children: /* @__PURE__ */ jsx(Type, {
754
+ export: true,
755
+ name: typeName,
756
+ children: `ReturnType<typeof ${name}>`
757
+ })
758
+ })] });
759
+ }
760
+ //#endregion
761
+ //#region src/components/Query.tsx
762
+ const declarationPrinter$1 = functionPrinter({ mode: "declaration" });
763
+ const callPrinter = functionPrinter({ mode: "call" });
764
+ function buildQueryParamsNode(node, options) {
765
+ const { resolver } = options;
766
+ const responseName = resolver.response.response(node);
767
+ const errorNames = resolveErrorNames(node, resolver);
768
+ const optionsParam = createFunctionParameter({
769
+ name: "options",
770
+ type: `{
771
+ query?: SWRConfiguration<${[responseName, `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`].join(", ")}>,
772
+ client?: ${buildClientOptionType()},
773
+ shouldFetch?: boolean,
774
+ immutable?: boolean
775
+ }`,
776
+ default: "{}"
777
+ });
778
+ return createFunctionParameters({ params: [buildGroupedRequestParam(node, { resolver }), optionsParam].filter((param) => param !== null) });
779
+ }
780
+ function Query({ name, queryKeyTypeName, queryOptionsName, queryKeyName, node, tsResolver }) {
781
+ const responseName = tsResolver.response.response(node);
782
+ const errorNames = resolveErrorNames(node, tsResolver);
783
+ const generics = [
784
+ responseName,
785
+ `ResponseErrorConfig<${errorNames.length > 0 ? errorNames.join(" | ") : "Error"}>`,
786
+ `${queryKeyTypeName} | null`
787
+ ];
788
+ const queryKeyParamsNode = buildQueryKeyParams(node, { resolver: tsResolver });
789
+ const queryKeyParamsCall = callPrinter.print(queryKeyParamsNode) ?? "";
790
+ const queryOptionsParamsNode = buildQueryOptionsParams(node, { resolver: tsResolver });
791
+ const queryOptionsParamsCall = callPrinter.print(queryOptionsParamsNode) ?? "";
792
+ const paramsNode = buildQueryParamsNode(node, { resolver: tsResolver });
793
+ const paramsSignature = declarationPrinter$1.print(paramsNode) ?? "";
794
+ return /* @__PURE__ */ jsx(File.Source, {
795
+ name,
796
+ isExportable: true,
797
+ isIndexable: true,
798
+ children: /* @__PURE__ */ jsx(Function, {
799
+ name,
800
+ export: true,
801
+ params: paramsSignature,
802
+ JSDoc: { comments: buildOperationComments(node) },
803
+ children: `
804
+ const { query: queryOptions, client: config = {}, shouldFetch = true, immutable } = options ?? {}
805
+
806
+ const queryKey = ${queryKeyName}(${queryKeyParamsCall})
807
+
808
+ return useSWR<${generics.join(", ")}>(
809
+ shouldFetch ? queryKey : null,
810
+ {
811
+ ...${queryOptionsName}(${queryOptionsParamsCall}),
812
+ ...(immutable ? {
813
+ revalidateIfStale: false,
814
+ revalidateOnFocus: false,
815
+ revalidateOnReconnect: false
816
+ } : { }),
817
+ ...queryOptions,
818
+ }
819
+ )
820
+ `
821
+ })
822
+ });
823
+ }
824
+ //#endregion
825
+ //#region src/components/QueryOptions.tsx
826
+ const declarationPrinter = functionPrinter({ mode: "declaration" });
827
+ function QueryOptions({ name, clientName, node, tsResolver }) {
828
+ const paramsNode = buildQueryOptionsParams(node, { resolver: tsResolver });
829
+ const paramsSignature = declarationPrinter.print(paramsNode) ?? "";
830
+ const fetcherBody = `const { data } = await ${buildClientCall(node, {
831
+ clientName,
832
+ signal: false
833
+ })}
834
+ return data`;
835
+ return /* @__PURE__ */ jsx(File.Source, {
836
+ name,
837
+ isExportable: true,
838
+ isIndexable: true,
839
+ children: /* @__PURE__ */ jsx(Function, {
840
+ name,
841
+ export: true,
842
+ params: paramsSignature,
843
+ children: `
844
+ return {
845
+ fetcher: async () => {
846
+ ${fetcherBody}
847
+ },
848
+ }
849
+ `
850
+ })
851
+ });
852
+ }
853
+ //#endregion
854
+ //#region src/generators/mutationGenerator.tsx
855
+ /**
856
+ * Built-in generator for `useSWRMutation` hooks. Emits one `useFooMutation` hook
857
+ * per POST/PUT/DELETE operation (configurable via `mutation.methods`) plus
858
+ * the matching `fooMutationKey` helper.
859
+ */
860
+ const mutationGenerator = defineGenerator({
861
+ name: "swr-mutation",
862
+ renderer: jsxRenderer,
863
+ operation(node, ctx) {
864
+ if (!ast.isHttpOperationNode(node)) return null;
865
+ const { config, driver, resolver, root } = ctx;
866
+ const { output, query, mutation, client, group } = ctx.options;
867
+ const pluginTs = driver.getPlugin(pluginTsName);
868
+ if (!pluginTs) return null;
869
+ const tsResolver = driver.getResolver(pluginTsName);
870
+ const { isMutation } = classifyOperation(node, {
871
+ query,
872
+ mutation
873
+ });
874
+ if (!isMutation) return null;
875
+ const importPath = mutation ? mutation.importPath : "swr/mutation";
876
+ const contractOp = resolveClientOperation({
877
+ clientPlugin: { pluginName: client.pluginName },
878
+ driver,
879
+ node,
880
+ root,
881
+ output
882
+ });
883
+ if (!contractOp) return null;
884
+ const mutationHookName = resolver.mutation.name(node);
885
+ const mutationKeyName = resolver.mutation.keyName(node);
886
+ const mutationKeyTypeName = resolver.mutation.keyTypeName(node);
887
+ const mutationArgTypeName = resolver.mutation.argTypeName(node);
888
+ const meta = {
889
+ file: resolver.file({
890
+ name: mutationHookName,
891
+ extname: ".ts",
892
+ tag: node.tags[0] ?? "default",
893
+ path: node.path,
894
+ root,
895
+ output,
896
+ group
897
+ }),
898
+ fileTs: tsResolver.file({
899
+ name: node.operationId,
900
+ extname: ".ts",
901
+ tag: node.tags[0] ?? "default",
902
+ path: node.path,
903
+ root,
904
+ output: pluginTs.options?.output ?? output,
905
+ group: pluginTs.options?.group
906
+ })
907
+ };
908
+ const importedTypeNames = [tsResolver.response.options(node), ...resolveOperationTypeNames(node, tsResolver, {
909
+ order: "body-response-first",
910
+ includeParams: false
911
+ })].filter((name) => Boolean(name));
912
+ const calledClientName = contractOp.name;
913
+ return /* @__PURE__ */ jsxs(File, {
914
+ baseName: meta.file.baseName,
915
+ path: meta.file.path,
916
+ meta: meta.file.meta,
917
+ banner: resolver.default.banner(ctx.meta, {
918
+ output,
919
+ config,
920
+ file: {
921
+ path: meta.file.path,
922
+ baseName: meta.file.baseName
923
+ }
924
+ }),
925
+ footer: resolver.default.footer(ctx.meta, {
926
+ output,
927
+ config,
928
+ file: {
929
+ path: meta.file.path,
930
+ baseName: meta.file.baseName
931
+ }
932
+ }),
933
+ children: [
934
+ /* @__PURE__ */ jsx(File.Import, {
935
+ name: [contractOp.name],
936
+ root: meta.file.path,
937
+ path: contractOp.path
938
+ }),
939
+ /* @__PURE__ */ jsx(File.Import, {
940
+ name: ["RequestConfig", "ResponseErrorConfig"],
941
+ root: meta.file.path,
942
+ path: contractOp.clientPath,
943
+ isTypeOnly: true
944
+ }),
945
+ meta.fileTs && importedTypeNames.length > 0 && /* @__PURE__ */ jsx(File.Import, {
946
+ name: Array.from(new Set(importedTypeNames)),
947
+ root: meta.file.path,
948
+ path: meta.fileTs.path,
949
+ isTypeOnly: true
950
+ }),
951
+ /* @__PURE__ */ jsx(MutationKey, {
952
+ name: mutationKeyName,
953
+ typeName: mutationKeyTypeName,
954
+ node,
955
+ transformer: ctx.options.mutationKey
956
+ }),
957
+ mutation && /* @__PURE__ */ jsxs(Fragment, { children: [
958
+ /* @__PURE__ */ jsx(File.Import, {
959
+ name: "useSWRMutation",
960
+ path: importPath
961
+ }),
962
+ /* @__PURE__ */ jsx(File.Import, {
963
+ name: ["SWRMutationConfiguration"],
964
+ path: importPath,
965
+ isTypeOnly: true
966
+ }),
967
+ /* @__PURE__ */ jsx(Mutation, {
968
+ name: mutationHookName,
969
+ clientName: calledClientName,
970
+ mutationKeyName,
971
+ mutationKeyTypeName,
972
+ mutationArgTypeName,
973
+ node,
974
+ tsResolver
975
+ })
976
+ ] })
977
+ ]
978
+ });
979
+ }
980
+ });
981
+ //#endregion
982
+ //#region src/generators/queryGenerator.tsx
983
+ /**
984
+ * Built-in generator for `useSWR` hooks. Emits one `useFoo` hook per read operation plus the
985
+ * matching `fooQueryKey` helper and `fooQueryOptions` factory.
986
+ */
987
+ const queryGenerator = defineGenerator({
988
+ name: "swr-query",
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 = driver.getPlugin(pluginTsName);
995
+ if (!pluginTs) return null;
996
+ const tsResolver = driver.getResolver(pluginTsName);
997
+ const { isQuery } = classifyOperation(node, {
998
+ query,
999
+ mutation
1000
+ });
1001
+ if (!isQuery) return null;
1002
+ const importPath = query ? query.importPath : "swr";
1003
+ const contractOp = resolveClientOperation({
1004
+ clientPlugin: { pluginName: client.pluginName },
1005
+ driver,
1006
+ node,
1007
+ root,
1008
+ output
1009
+ });
1010
+ if (!contractOp) return null;
1011
+ const queryName = resolver.query.name(node);
1012
+ const queryOptionsName = resolver.query.optionsName(node);
1013
+ const queryKeyName = resolver.query.keyName(node);
1014
+ const queryKeyTypeName = resolver.query.keyTypeName(node);
1015
+ const meta = {
1016
+ file: resolver.file({
1017
+ name: queryName,
1018
+ extname: ".ts",
1019
+ tag: node.tags[0] ?? "default",
1020
+ path: node.path,
1021
+ root,
1022
+ output,
1023
+ group
1024
+ }),
1025
+ fileTs: tsResolver.file({
1026
+ name: node.operationId,
1027
+ extname: ".ts",
1028
+ tag: node.tags[0] ?? "default",
1029
+ path: node.path,
1030
+ root,
1031
+ output: pluginTs.options?.output ?? output,
1032
+ group: pluginTs.options?.group
1033
+ })
1034
+ };
1035
+ const importedTypeNames = [tsResolver.response.options(node), ...resolveOperationTypeNames(node, tsResolver, {
1036
+ exclude: [queryKeyTypeName],
1037
+ order: "body-response-first",
1038
+ includeParams: false
1039
+ })].filter((name) => Boolean(name));
1040
+ const calledClientName = contractOp.name;
1041
+ return /* @__PURE__ */ jsxs(File, {
1042
+ baseName: meta.file.baseName,
1043
+ path: meta.file.path,
1044
+ meta: meta.file.meta,
1045
+ banner: resolver.default.banner(ctx.meta, {
1046
+ output,
1047
+ config,
1048
+ file: {
1049
+ path: meta.file.path,
1050
+ baseName: meta.file.baseName
1051
+ }
1052
+ }),
1053
+ footer: resolver.default.footer(ctx.meta, {
1054
+ output,
1055
+ config,
1056
+ file: {
1057
+ path: meta.file.path,
1058
+ baseName: meta.file.baseName
1059
+ }
1060
+ }),
1061
+ children: [
1062
+ /* @__PURE__ */ jsx(File.Import, {
1063
+ name: [contractOp.name],
1064
+ root: meta.file.path,
1065
+ path: contractOp.path
1066
+ }),
1067
+ /* @__PURE__ */ jsx(File.Import, {
1068
+ name: ["RequestConfig", "ResponseErrorConfig"],
1069
+ root: meta.file.path,
1070
+ path: contractOp.clientPath,
1071
+ isTypeOnly: true
1072
+ }),
1073
+ meta.fileTs && importedTypeNames.length > 0 && /* @__PURE__ */ jsx(File.Import, {
1074
+ name: Array.from(new Set(importedTypeNames)),
1075
+ root: meta.file.path,
1076
+ path: meta.fileTs.path,
1077
+ isTypeOnly: true
1078
+ }),
1079
+ /* @__PURE__ */ jsx(QueryKey, {
1080
+ name: queryKeyName,
1081
+ typeName: queryKeyTypeName,
1082
+ node,
1083
+ tsResolver,
1084
+ transformer: ctx.options.queryKey
1085
+ }),
1086
+ /* @__PURE__ */ jsx(QueryOptions, {
1087
+ name: queryOptionsName,
1088
+ clientName: calledClientName,
1089
+ node,
1090
+ tsResolver
1091
+ }),
1092
+ query && /* @__PURE__ */ jsxs(Fragment, { children: [
1093
+ /* @__PURE__ */ jsx(File.Import, {
1094
+ name: "useSWR",
1095
+ path: importPath
1096
+ }),
1097
+ /* @__PURE__ */ jsx(File.Import, {
1098
+ name: ["SWRConfiguration"],
1099
+ path: importPath,
1100
+ isTypeOnly: true
1101
+ }),
1102
+ /* @__PURE__ */ jsx(Query, {
1103
+ name: queryName,
1104
+ queryOptionsName,
1105
+ queryKeyName,
1106
+ queryKeyTypeName,
1107
+ node,
1108
+ tsResolver
1109
+ })
1110
+ ] })
1111
+ ]
1112
+ });
1113
+ }
1114
+ });
1115
+ //#endregion
1116
+ //#region src/resolvers/resolverSwr.ts
1117
+ /**
1118
+ * Default resolver used by `@kubb/plugin-swr`. Decides the names and file paths for every generated
1119
+ * SWR hook (`useFoo`, `useFooMutation`) and its companion helpers (`fooQueryKey`, `fooQueryOptions`,
1120
+ * `fooMutationKey`).
1121
+ *
1122
+ * Functions and files use the built-in camelCase casing; hooks get the `use` prefix.
1123
+ *
1124
+ * @example Resolve hook and helper names
1125
+ * ```ts
1126
+ * import { resolverSwr } from '@kubb/plugin-swr'
1127
+ *
1128
+ * resolverSwr.query.name(operationNode) // 'useGetPetById'
1129
+ * resolverSwr.query.keyName(operationNode) // 'getPetByIdQueryKey'
1130
+ * resolverSwr.mutation.name(operationNode) // 'useUpdatePet'
1131
+ * ```
1132
+ */
1133
+ const resolverSwr = createResolver({
1134
+ pluginName: "plugin-swr",
1135
+ query: createQueryResolver(),
1136
+ mutation: {
1137
+ ...createMutationResolver(),
1138
+ keyTypeName(node) {
1139
+ return `${capitalize(this.name(node.operationId))}MutationKey`;
1140
+ },
1141
+ argTypeName(node) {
1142
+ return `${capitalize(this.name(node.operationId))}MutationArg`;
1143
+ }
1144
+ }
1145
+ });
1146
+ //#endregion
13
1147
  //#region src/plugin.ts
1148
+ /**
1149
+ * Canonical plugin name for `@kubb/plugin-swr`. Used for driver lookups and cross-plugin
1150
+ * dependency references.
1151
+ */
14
1152
  const pluginSwrName = "plugin-swr";
15
- const pluginSwr = createPlugin((options) => {
1153
+ /**
1154
+ * Generates SWR hooks from OpenAPI operations. Read operations become `useFoo` hooks built on
1155
+ * `useSWR` and write operations become `useFoo` hooks built on `useSWRMutation`, wrapping the
1156
+ * client functions from `@kubb/plugin-axios` or `@kubb/plugin-fetch`.
1157
+ *
1158
+ * @example
1159
+ * ```ts
1160
+ * import { defineConfig } from 'kubb/config'
1161
+ * import { pluginTs } from '@kubb/plugin-ts'
1162
+ * import { pluginSwr } from '@kubb/plugin-swr'
1163
+ *
1164
+ * export default defineConfig({
1165
+ * input: './petStore.yaml',
1166
+ * output: { path: './src/gen' },
1167
+ * plugins: [
1168
+ * pluginTs(),
1169
+ * pluginSwr({ output: { path: './hooks' } }),
1170
+ * ],
1171
+ * })
1172
+ * ```
1173
+ */
1174
+ const pluginSwr = definePlugin((options) => {
16
1175
  const { output = {
17
1176
  path: "hooks",
18
- barrelType: "named"
19
- }, group, exclude = [], include, override = [], parser = "client", transformers = {}, query, mutation, client, paramsType = "inline", pathParamsType = paramsType === "object" ? "object" : options.pathParamsType || "inline", mutationKey = MutationKey.getTransformer, queryKey = QueryKey.getTransformer, generators = [queryGenerator, mutationGenerator].filter(Boolean), paramsCasing, contentType } = options;
20
- const clientName = client?.client ?? "axios";
21
- const clientImportPath = client?.importPath ?? (!client?.bundle ? `@kubb/plugin-client/clients/${clientName}` : void 0);
1177
+ barrel: { type: "named" }
1178
+ }, group, exclude = [], include, override = [], mutation = {}, query = {}, mutationKey = mutationKeyTransformer, queryKey = queryKeyTransformer, client, resolver: userResolver, macros: userMacros } = options;
1179
+ const selectedGenerators = [queryGenerator, mutationGenerator];
1180
+ const groupConfig = createGroupConfig(group) ?? void 0;
22
1181
  return {
23
1182
  name: pluginSwrName,
24
- options: {
25
- output,
26
- client: {
27
- bundle: client?.bundle,
28
- baseURL: client?.baseURL,
29
- client: clientName,
30
- clientType: client?.clientType ?? "function",
31
- importPath: clientImportPath,
32
- dataReturnType: client?.dataReturnType ?? "data",
33
- paramsCasing
34
- },
35
- queryKey,
36
- query: query === false ? false : {
37
- importPath: "swr",
38
- methods: ["get"],
39
- ...query
40
- },
41
- mutationKey,
42
- mutation: mutation === false ? false : {
43
- importPath: "swr/mutation",
44
- methods: [
45
- "post",
46
- "put",
47
- "delete",
48
- "patch"
49
- ],
50
- ...mutation
51
- },
52
- parser,
53
- paramsType,
54
- pathParamsType,
55
- paramsCasing,
56
- group
57
- },
58
- pre: [
59
- pluginOasName,
60
- pluginTsName,
61
- parser === "zod" ? pluginZodName : void 0
62
- ].filter(Boolean),
63
- resolvePath(baseName, pathMode, options) {
64
- const root = path.resolve(this.config.root, this.config.output.path);
65
- if ((pathMode ?? getMode(path.resolve(root, output.path))) === "single")
66
- /**
67
- * when output is a file then we will always append to the same file(output file), see fileManager.addOrAppend
68
- * Other plugins then need to call addOrAppend instead of just add from the fileManager class
69
- */
70
- return path.resolve(root, output.path);
71
- if (group && (options?.group?.path || options?.group?.tag)) {
72
- const groupName = group?.name ? group.name : (ctx) => {
73
- if (group?.type === "path") return `${ctx.group.split("/")[1]}`;
74
- return `${camelCase(ctx.group)}Controller`;
75
- };
76
- return path.resolve(root, output.path, groupName({ group: group.type === "path" ? options.group.path : options.group.tag }), baseName);
77
- }
78
- return path.resolve(root, output.path, baseName);
79
- },
80
- resolveName(name, type) {
81
- let resolvedName = camelCase(name);
82
- if (type === "file" || type === "function") resolvedName = camelCase(name, { isFile: type === "file" });
83
- if (type === "type") resolvedName = pascalCase(name);
84
- if (type) return transformers?.name?.(resolvedName, type) || resolvedName;
85
- return resolvedName;
86
- },
87
- async install() {
88
- const root = path.resolve(this.config.root, this.config.output.path);
89
- const mode = getMode(path.resolve(root, output.path));
90
- const oas = await this.getOas();
91
- const baseURL = await this.getBaseURL();
92
- if (baseURL) this.plugin.options.client.baseURL = baseURL;
93
- const hasClientPlugin = !!this.driver.getPluginByName(pluginClientName);
94
- if (this.plugin.options.client.bundle && !hasClientPlugin && !this.plugin.options.client.importPath) await this.upsertFile({
95
- baseName: "fetch.ts",
96
- path: path.resolve(root, ".kubb/fetch.ts"),
97
- sources: [{
98
- name: "fetch",
99
- value: this.plugin.options.client.client === "fetch" ? source$1 : source,
100
- isExportable: true,
101
- isIndexable: true
102
- }],
103
- imports: [],
104
- exports: []
105
- });
106
- if (!hasClientPlugin) await this.addFile({
107
- baseName: "config.ts",
108
- path: path.resolve(root, ".kubb/config.ts"),
109
- sources: [{
110
- name: "config",
111
- value: source$2,
112
- isExportable: false,
113
- isIndexable: false
114
- }],
115
- imports: [],
116
- exports: []
117
- });
118
- const files = await new OperationGenerator(this.plugin.options, {
119
- fabric: this.fabric,
120
- oas,
121
- driver: this.driver,
122
- events: this.events,
123
- plugin: this.plugin,
124
- contentType,
1183
+ options,
1184
+ dependencies: [pluginTsName],
1185
+ hooks: { "kubb:plugin:setup"(ctx) {
1186
+ const resolver = userResolver ? Resolver.merge(resolverSwr, userResolver) : resolverSwr;
1187
+ ctx.setOptions({
1188
+ output,
1189
+ client: resolveContractClient({
1190
+ client,
1191
+ plugins: ctx.config.plugins
1192
+ }),
1193
+ queryKey,
1194
+ query: resolveQueryConfig(query, { importPath: "swr" }),
1195
+ mutationKey,
1196
+ mutation: resolveMutationConfig(mutation, { importPath: "swr/mutation" }),
1197
+ group: groupConfig,
125
1198
  exclude,
126
1199
  include,
127
1200
  override,
128
- mode
129
- }).build(...generators);
130
- await this.upsertFile(...files);
131
- const barrelFiles = await getBarrelFiles(this.fabric.files, {
132
- type: output.barrelType ?? "named",
133
- root,
134
- output,
135
- meta: { pluginName: this.plugin.name }
1201
+ resolver
136
1202
  });
137
- await this.upsertFile(...barrelFiles);
138
- }
1203
+ ctx.setResolver(resolver);
1204
+ if (userMacros?.length) ctx.setMacros(userMacros);
1205
+ ctx.addGenerator(...selectedGenerators);
1206
+ } }
139
1207
  };
140
1208
  });
141
1209
  //#endregion
142
- export { pluginSwr, pluginSwrName };
1210
+ export { pluginSwr as default, pluginSwr, pluginSwrName, resolverSwr };
143
1211
 
144
1212
  //# sourceMappingURL=index.js.map