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