@kubb/plugin-msw 5.0.0-beta.98 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -8,264 +8,46 @@ let _kubb_plugin_faker = require("@kubb/plugin-faker");
8
8
  let _kubb_plugin_ts = require("@kubb/plugin-ts");
9
9
  let kubb_jsx = require("kubb/jsx");
10
10
  let kubb_jsx_jsx_runtime = require("kubb/jsx/jsx-runtime");
11
- //#region ../../internals/utils/src/casing.ts
12
- /**
13
- * Shared implementation for camelCase and PascalCase conversion.
14
- * Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
15
- * and capitalizes each word according to `pascal`.
16
- *
17
- * When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
18
- */
19
- function toCamelOrPascal(text, pascal) {
20
- 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) => {
21
- if (word.length > 1 && word === word.toUpperCase()) return word;
22
- return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
23
- }).join("").replace(/[^a-zA-Z0-9]/g, "");
24
- }
25
- /**
26
- * Converts `text` to camelCase.
27
- *
28
- * @example Word boundaries
29
- * `camelCase('hello-world') // 'helloWorld'`
30
- *
31
- * @example With a prefix
32
- * `camelCase('tag', { prefix: 'create' }) // 'createTag'`
33
- */
34
- function camelCase(text, { prefix = "", suffix = "" } = {}) {
35
- return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
36
- }
37
- //#endregion
38
- //#region ../../internals/utils/src/reserved.ts
39
- /**
40
- * JavaScript and Java reserved words.
41
- * @link https://github.com/jonschlinkert/reserved/blob/master/index.js
42
- */
43
- const reservedWords = /* @__PURE__ */ new Set([
44
- "abstract",
45
- "arguments",
46
- "boolean",
47
- "break",
48
- "byte",
49
- "case",
50
- "catch",
51
- "char",
52
- "class",
53
- "const",
54
- "continue",
55
- "debugger",
56
- "default",
57
- "delete",
58
- "do",
59
- "double",
60
- "else",
61
- "enum",
62
- "eval",
63
- "export",
64
- "extends",
65
- "false",
66
- "final",
67
- "finally",
68
- "float",
69
- "for",
70
- "function",
71
- "goto",
72
- "if",
73
- "implements",
74
- "import",
75
- "in",
76
- "instanceof",
77
- "int",
78
- "interface",
79
- "let",
80
- "long",
81
- "native",
82
- "new",
83
- "null",
84
- "package",
85
- "private",
86
- "protected",
87
- "public",
88
- "return",
89
- "short",
90
- "static",
91
- "super",
92
- "switch",
93
- "synchronized",
94
- "this",
95
- "throw",
96
- "throws",
97
- "transient",
98
- "true",
99
- "try",
100
- "typeof",
101
- "var",
102
- "void",
103
- "volatile",
104
- "while",
105
- "with",
106
- "yield",
107
- "Array",
108
- "Date",
109
- "hasOwnProperty",
110
- "Infinity",
111
- "isFinite",
112
- "isNaN",
113
- "isPrototypeOf",
114
- "length",
115
- "Math",
116
- "name",
117
- "NaN",
118
- "Number",
119
- "Object",
120
- "prototype",
121
- "String",
122
- "toString",
123
- "undefined",
124
- "valueOf"
125
- ]);
11
+ //#region ../../internals/shared/src/operation.ts
126
12
  /**
127
- * Returns `true` when `name` is a syntactically valid JavaScript variable name.
13
+ * Builds the `ResolverFileParams` every operation generator passes to
14
+ * `resolver.file`: a file named `name`, tagged by the operation's first
15
+ * tag (or `'default'`), at the operation's path. Centralizes the entry object
16
+ * that was repeated at dozens of call sites across the client and query plugins.
128
17
  *
129
18
  * @example
130
19
  * ```ts
131
- * isValidVarName('status') // true
132
- * isValidVarName('class') // false (reserved word)
133
- * isValidVarName('42foo') // false (starts with digit)
20
+ * resolver.file(operationFileEntry(node, node.operationId), { root, output, group })
134
21
  * ```
135
22
  */
136
- function isValidVarName(name) {
137
- if (!name || reservedWords.has(name)) return false;
138
- return isIdentifier(name);
23
+ function operationFileEntry(node, name, extname = ".ts") {
24
+ return {
25
+ name,
26
+ extname,
27
+ tag: node.tags[0] ?? "default",
28
+ path: node.path
29
+ };
139
30
  }
140
31
  /**
141
- * Returns `true` when `name` is syntactically a valid identifier, ignoring reserved words.
32
+ * Resolves a dependency plugin's generated file for `node.operationId`, cached in `cache` (the
33
+ * current node's `ctx.cache`) under the resolver's own plugin name. Several dependents reading the
34
+ * same dependency for the same operation in one pass (a query plugin's several hook generators, the
35
+ * MCP handler, ...) share one computed name and path instead of each calling `resolver.file` again.
142
36
  *
143
- * Reserved words and globals (`class`, `name`, `Date`, …) are valid as bare object-literal keys
144
- * even though they are not valid variable names, so use this (not {@link isValidVarName}) when
145
- * deciding whether an object key needs quoting.
146
- *
147
- * @example
37
+ * @example Cache `plugin-ts`'s file for the current operation
148
38
  * ```ts
149
- * isIdentifier('name') // true
150
- * isIdentifier('x-total')// false
39
+ * const fileTs = resolveDependencyOperationFile({ cache: ctx.cache, node, resolver: tsResolver, root, output })
151
40
  * ```
152
41
  */
153
- function isIdentifier(name) {
154
- return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
155
- }
156
- //#endregion
157
- //#region ../../internals/utils/src/url.ts
158
- function transformParam(raw, casing) {
159
- const param = isValidVarName(raw) ? raw : camelCase(raw);
160
- return casing === "camelcase" ? camelCase(param) : param;
161
- }
162
- function toParamsObject(path, { replacer, casing } = {}) {
163
- const params = {};
164
- for (const match of path.matchAll(/\{([^}]+)\}/g)) {
165
- const param = transformParam(match[1], casing);
166
- const key = replacer ? replacer(param) : param;
167
- params[key] = key;
168
- }
169
- return Object.keys(params).length > 0 ? params : null;
42
+ function resolveDependencyOperationFile(options) {
43
+ const { cache, node, resolver, root, output, group } = options;
44
+ return cache.ensureItem(`${resolver.pluginName}:operationFile`, () => resolver.file({
45
+ ...operationFileEntry(node, node.operationId),
46
+ root,
47
+ output,
48
+ group: group ?? void 0
49
+ }));
170
50
  }
171
- /**
172
- * Helpers for OpenAPI/Swagger paths, plus a thin wrapper over the native `URL`.
173
- */
174
- var Url = class Url {
175
- /**
176
- * Reports whether `url` is a parseable absolute URL. Delegates to the native `URL.canParse`.
177
- *
178
- * @example
179
- * Url.canParse('https://petstore.swagger.io/v2') // true
180
- * Url.canParse('/pet/{petId}') // false
181
- */
182
- static canParse(url, base) {
183
- return URL.canParse(url, base);
184
- }
185
- /**
186
- * Converts an OpenAPI/Swagger path to Express-style colon syntax.
187
- *
188
- * @example
189
- * Url.toPath('/pet/{petId}') // '/pet/:petId'
190
- */
191
- static toPath(path) {
192
- return path.replace(/\{([^}]+)\}/g, ":$1");
193
- }
194
- /**
195
- * Rewrites OpenAPI placeholder names while keeping the `{...}` braces, so the generated `url`
196
- * literal aligns with the grouped `path` request option that the runtime client interpolates by
197
- * key.
198
- *
199
- * @example
200
- * Url.toCasedTemplate('/projects/{project_id}', { casing: 'camelcase' }) // '/projects/{projectId}'
201
- */
202
- static toCasedTemplate(path, { casing } = {}) {
203
- return path.replace(/\{([^}]+)\}/g, (_, name) => `{${transformParam(name, casing)}}`);
204
- }
205
- /**
206
- * Converts an OpenAPI/Swagger path to a TypeScript template literal string.
207
- * `prefix` is prepended inside the literal, `replacer` transforms each parameter name,
208
- * and `casing` controls parameter identifier casing.
209
- *
210
- * @example
211
- * Url.toTemplateString('/pet/{petId}') // '`/pet/${petId}`'
212
- *
213
- * @example
214
- * Url.toTemplateString('/pet/{petId}', { prefix: 'https://api' }) // '`https://api/pet/${petId}`'
215
- */
216
- static toTemplateString(path, { prefix, replacer, casing } = {}) {
217
- const result = path.split(/\{([^}]+)\}/).map((part, i) => {
218
- if (i % 2 === 0) return part;
219
- const param = transformParam(part, casing);
220
- return `\${${replacer ? replacer(param) : param}}`;
221
- }).join("");
222
- return `\`${prefix ?? ""}${result}\``;
223
- }
224
- /**
225
- * Converts an OpenAPI/Swagger path to a template literal that reads each parameter off the
226
- * grouped `path` request option, e.g. `/pet/{petId}` becomes `` `/pet/${path.petId}` ``. Parameter
227
- * names are camelCased to match the generated `path` type, and `prefix` is prepended inside the
228
- * literal. Shared by the client and cypress generators that pass a grouped `path` object.
229
- *
230
- * @example
231
- * Url.toGroupedTemplateString('/pet/{petId}') // '`/pet/${path.petId}`'
232
- */
233
- static toGroupedTemplateString(path, { prefix } = {}) {
234
- return Url.toTemplateString(path, {
235
- prefix,
236
- casing: "camelcase",
237
- replacer: (name) => `path.${name}`
238
- });
239
- }
240
- /**
241
- * Returns the path and its extracted params as a structured `URLObject`, or as a stringified
242
- * expression when `stringify` is set.
243
- *
244
- * @example
245
- * Url.toObject('/pet/{petId}')
246
- * // { url: '/pet/:petId', params: { petId: 'petId' } }
247
- */
248
- static toObject(path, { type = "path", replacer, stringify, casing } = {}) {
249
- const object = {
250
- url: type === "path" ? Url.toPath(path) : Url.toTemplateString(path, {
251
- replacer,
252
- casing
253
- }),
254
- params: toParamsObject(path, {
255
- replacer,
256
- casing
257
- })
258
- };
259
- if (stringify) {
260
- if (type === "template") return JSON.stringify(object).replaceAll("'", "").replaceAll(`"`, "");
261
- if (object.params) return `{ url: '${object.url}', params: ${JSON.stringify(object.params).replaceAll("'", "").replaceAll(`"`, "")} }`;
262
- return `{ url: '${object.url}' }`;
263
- }
264
- return object;
265
- }
266
- };
267
- //#endregion
268
- //#region ../../internals/shared/src/operation.ts
269
51
  function getStatusCodeNumber(statusCode) {
270
52
  const code = Number(statusCode);
271
53
  return Number.isNaN(code) ? null : code;
@@ -297,6 +79,33 @@ function resolveResponseTypes(node, resolver) {
297
79
  return types;
298
80
  }
299
81
  //#endregion
82
+ //#region ../../internals/utils/src/casing.ts
83
+ /**
84
+ * Shared implementation for camelCase and PascalCase conversion.
85
+ * Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
86
+ * and capitalizes each word according to `pascal`.
87
+ *
88
+ * When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
89
+ */
90
+ function toCamelOrPascal(text, pascal) {
91
+ 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) => {
92
+ if (word.length > 1 && word === word.toUpperCase()) return word;
93
+ return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
94
+ }).join("").replace(/[^a-zA-Z0-9]/g, "");
95
+ }
96
+ /**
97
+ * Converts `text` to camelCase.
98
+ *
99
+ * @example Word boundaries
100
+ * `camelCase('hello-world') // 'helloWorld'`
101
+ *
102
+ * @example With a prefix
103
+ * `camelCase('tag', { prefix: 'create' }) // 'createTag'`
104
+ */
105
+ function camelCase(text, { prefix = "", suffix = "" } = {}) {
106
+ return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
107
+ }
108
+ //#endregion
300
109
  //#region ../../internals/shared/src/group.ts
301
110
  /**
302
111
  * Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
@@ -431,12 +240,6 @@ function getMswMethod(node) {
431
240
  return kubb_kit.ast.isHttpOperationNode(node) ? node.method.toLowerCase() : "";
432
241
  }
433
242
  /**
434
- * Converts an OpenAPI-style path to an Express/MSW-style path by replacing `{param}` with `:param`.
435
- */
436
- function getMswUrl(node) {
437
- return kubb_kit.ast.isHttpOperationNode(node) ? node.path.replaceAll("{", ":").replaceAll("}", "") : "";
438
- }
439
- /**
440
243
  * Resolves faker metadata for an MSW operation, including response name and file path.
441
244
  */
442
245
  function resolveFakerMeta(node, options) {
@@ -463,7 +266,7 @@ function Mock({ baseURL = "", name, fakerName, typeName, requestTypeName, node }
463
266
  const successResponse = getPrimarySuccessResponse(node);
464
267
  const statusCode = successResponse ? Number(successResponse.statusCode) : 200;
465
268
  const contentType = getContentType(successResponse);
466
- const url = Url.toPath(getMswUrl(node));
269
+ const url = kubb_kit.ast.isHttpOperationNode(node) ? kubb_kit.Url.toPath(node.path) : "";
467
270
  const headers = [contentType ? `'Content-Type': '${contentType}'` : null].filter(Boolean);
468
271
  const dataType = hasResponseSchema(successResponse) ? typeName : "string | number | boolean | null | object";
469
272
  const paramType = fakerName ? typeName : dataType;
@@ -477,6 +280,7 @@ function Mock({ baseURL = "", name, fakerName, typeName, requestTypeName, node }
477
280
  const requestUrl = `${baseURL}${url.replace(/([^/]):/g, "$1\\\\:")}`;
478
281
  const urlLiteral = fakerName ? `'${requestUrl}'` : `\`${requestUrl}\``;
479
282
  const responseBody = fakerName ? `JSON.stringify(data || ${fakerName}(data))` : "JSON.stringify(data)";
283
+ const headersBlock = headers.length ? `\n headers: {\n ${headers.join(", \n")}\n },` : "";
480
284
  return /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Source, {
481
285
  name,
482
286
  isIndexable: true,
@@ -489,10 +293,7 @@ function Mock({ baseURL = "", name, fakerName, typeName, requestTypeName, node }
489
293
  if(typeof data === 'function') return data(info)
490
294
 
491
295
  return new Response(${responseBody}, {
492
- status: ${statusCode},
493
- ${headers.length ? ` headers: {
494
- ${headers.join(", \n")}
495
- },` : ""}
296
+ status: ${statusCode},${headersBlock}
496
297
  })
497
298
  })`
498
299
  })
@@ -511,6 +312,7 @@ function Response({ name, typeName, response }) {
511
312
  optional: !hasResponseSchema(response)
512
313
  })] }));
513
314
  const responseName = `${name}Response${statusCode}`;
315
+ const headersBlock = headers.length ? `\n headers: {\n ${headers.join(", \n")}\n },` : "";
514
316
  return /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Source, {
515
317
  name: responseName,
516
318
  isIndexable: true,
@@ -521,10 +323,7 @@ function Response({ name, typeName, response }) {
521
323
  params: params ?? "",
522
324
  children: `
523
325
  return new Response(JSON.stringify(data), {
524
- status: ${statusCode},
525
- ${headers.length ? ` headers: {
526
- ${headers.join(", \n")}
527
- },` : ""}
326
+ status: ${statusCode},${headersBlock}
528
327
  })`
529
328
  })
530
329
  });
@@ -568,14 +367,13 @@ const mswGenerator = (0, kubb_kit.defineGenerator)({
568
367
  if (!pluginTs) return null;
569
368
  const tsResolver = driver.getResolver(_kubb_plugin_ts.pluginTsName);
570
369
  const type = {
571
- file: tsResolver.file({
572
- name: node.operationId,
573
- extname: ".ts",
574
- tag: node.tags[0] ?? "default",
575
- path: node.path,
370
+ file: resolveDependencyOperationFile({
371
+ cache: ctx.cache,
372
+ node,
373
+ resolver: tsResolver,
576
374
  root,
577
375
  output: pluginTs.options?.output ?? output,
578
- group: pluginTs.options?.group ?? void 0
376
+ group: pluginTs.options?.group
579
377
  }),
580
378
  responseName: tsResolver.response.response(node)
581
379
  };