@kubb/plugin-msw 5.0.0-beta.64 → 5.0.0-beta.74
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/README.md +2 -2
- package/dist/index.cjs +639 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +92 -2
- package/dist/index.js +634 -7
- package/dist/index.js.map +1 -1
- package/package.json +9 -18
- package/dist/components-BZ9KakZO.cjs +0 -476
- package/dist/components-BZ9KakZO.cjs.map +0 -1
- package/dist/components-DNiDZfAv.js +0 -423
- package/dist/components-DNiDZfAv.js.map +0 -1
- package/dist/components.cjs +0 -5
- package/dist/components.d.ts +0 -53
- package/dist/components.js +0 -2
- package/dist/generators-CkGOweF4.cjs +0 -218
- package/dist/generators-CkGOweF4.cjs.map +0 -1
- package/dist/generators-U8mPM19f.js +0 -208
- package/dist/generators-U8mPM19f.js.map +0 -1
- package/dist/generators.cjs +0 -4
- package/dist/generators.d.ts +0 -23
- package/dist/generators.js +0 -2
- package/dist/types-Dxnur70I.d.ts +0 -99
- /package/dist/{chunk-C0LytTxp.js → rolldown-runtime-C0LytTxp.js} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -2,11 +2,39 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
|
|
6
|
-
const require_generators = require("./generators-CkGOweF4.cjs");
|
|
5
|
+
//#endregion
|
|
7
6
|
let _kubb_core = require("@kubb/core");
|
|
8
7
|
let _kubb_plugin_faker = require("@kubb/plugin-faker");
|
|
9
8
|
let _kubb_plugin_ts = require("@kubb/plugin-ts");
|
|
9
|
+
let _kubb_renderer_jsx = require("@kubb/renderer-jsx");
|
|
10
|
+
let _kubb_renderer_jsx_jsx_runtime = require("@kubb/renderer-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
|
|
10
38
|
//#region ../../internals/utils/src/fs.ts
|
|
11
39
|
/**
|
|
12
40
|
* Builds a nested file path from a dotted name. Splits on dots that precede a letter
|
|
@@ -27,9 +55,244 @@ let _kubb_plugin_ts = require("@kubb/plugin-ts");
|
|
|
27
55
|
* @example Suffix applied to the final segment only
|
|
28
56
|
* `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
|
|
29
57
|
*/
|
|
30
|
-
function toFilePath(name, caseLast =
|
|
58
|
+
function toFilePath(name, caseLast = camelCase) {
|
|
31
59
|
const parts = name.split(/\.(?=[a-zA-Z])/);
|
|
32
|
-
return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) :
|
|
60
|
+
return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
//#region ../../internals/utils/src/reserved.ts
|
|
64
|
+
/**
|
|
65
|
+
* JavaScript and Java reserved words.
|
|
66
|
+
* @link https://github.com/jonschlinkert/reserved/blob/master/index.js
|
|
67
|
+
*/
|
|
68
|
+
const reservedWords = new Set([
|
|
69
|
+
"abstract",
|
|
70
|
+
"arguments",
|
|
71
|
+
"boolean",
|
|
72
|
+
"break",
|
|
73
|
+
"byte",
|
|
74
|
+
"case",
|
|
75
|
+
"catch",
|
|
76
|
+
"char",
|
|
77
|
+
"class",
|
|
78
|
+
"const",
|
|
79
|
+
"continue",
|
|
80
|
+
"debugger",
|
|
81
|
+
"default",
|
|
82
|
+
"delete",
|
|
83
|
+
"do",
|
|
84
|
+
"double",
|
|
85
|
+
"else",
|
|
86
|
+
"enum",
|
|
87
|
+
"eval",
|
|
88
|
+
"export",
|
|
89
|
+
"extends",
|
|
90
|
+
"false",
|
|
91
|
+
"final",
|
|
92
|
+
"finally",
|
|
93
|
+
"float",
|
|
94
|
+
"for",
|
|
95
|
+
"function",
|
|
96
|
+
"goto",
|
|
97
|
+
"if",
|
|
98
|
+
"implements",
|
|
99
|
+
"import",
|
|
100
|
+
"in",
|
|
101
|
+
"instanceof",
|
|
102
|
+
"int",
|
|
103
|
+
"interface",
|
|
104
|
+
"let",
|
|
105
|
+
"long",
|
|
106
|
+
"native",
|
|
107
|
+
"new",
|
|
108
|
+
"null",
|
|
109
|
+
"package",
|
|
110
|
+
"private",
|
|
111
|
+
"protected",
|
|
112
|
+
"public",
|
|
113
|
+
"return",
|
|
114
|
+
"short",
|
|
115
|
+
"static",
|
|
116
|
+
"super",
|
|
117
|
+
"switch",
|
|
118
|
+
"synchronized",
|
|
119
|
+
"this",
|
|
120
|
+
"throw",
|
|
121
|
+
"throws",
|
|
122
|
+
"transient",
|
|
123
|
+
"true",
|
|
124
|
+
"try",
|
|
125
|
+
"typeof",
|
|
126
|
+
"var",
|
|
127
|
+
"void",
|
|
128
|
+
"volatile",
|
|
129
|
+
"while",
|
|
130
|
+
"with",
|
|
131
|
+
"yield",
|
|
132
|
+
"Array",
|
|
133
|
+
"Date",
|
|
134
|
+
"hasOwnProperty",
|
|
135
|
+
"Infinity",
|
|
136
|
+
"isFinite",
|
|
137
|
+
"isNaN",
|
|
138
|
+
"isPrototypeOf",
|
|
139
|
+
"length",
|
|
140
|
+
"Math",
|
|
141
|
+
"name",
|
|
142
|
+
"NaN",
|
|
143
|
+
"Number",
|
|
144
|
+
"Object",
|
|
145
|
+
"prototype",
|
|
146
|
+
"String",
|
|
147
|
+
"toString",
|
|
148
|
+
"undefined",
|
|
149
|
+
"valueOf"
|
|
150
|
+
]);
|
|
151
|
+
/**
|
|
152
|
+
* Returns `true` when `name` is a syntactically valid JavaScript variable name.
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* ```ts
|
|
156
|
+
* isValidVarName('status') // true
|
|
157
|
+
* isValidVarName('class') // false (reserved word)
|
|
158
|
+
* isValidVarName('42foo') // false (starts with digit)
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
161
|
+
function isValidVarName(name) {
|
|
162
|
+
if (!name || reservedWords.has(name)) return false;
|
|
163
|
+
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
|
|
164
|
+
}
|
|
165
|
+
//#endregion
|
|
166
|
+
//#region ../../internals/utils/src/url.ts
|
|
167
|
+
function transformParam(raw, casing) {
|
|
168
|
+
const param = isValidVarName(raw) ? raw : camelCase(raw);
|
|
169
|
+
return casing === "camelcase" ? camelCase(param) : param;
|
|
170
|
+
}
|
|
171
|
+
function toParamsObject(path, { replacer, casing } = {}) {
|
|
172
|
+
const params = {};
|
|
173
|
+
for (const match of path.matchAll(/\{([^}]+)\}/g)) {
|
|
174
|
+
const param = transformParam(match[1], casing);
|
|
175
|
+
const key = replacer ? replacer(param) : param;
|
|
176
|
+
params[key] = key;
|
|
177
|
+
}
|
|
178
|
+
return Object.keys(params).length > 0 ? params : null;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Helpers for OpenAPI/Swagger paths, plus a thin wrapper over the native `URL`.
|
|
182
|
+
*/
|
|
183
|
+
var Url = class Url {
|
|
184
|
+
/**
|
|
185
|
+
* Reports whether `url` is a parseable absolute URL. Delegates to the native `URL.canParse`.
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* Url.canParse('https://petstore.swagger.io/v2') // true
|
|
189
|
+
* Url.canParse('/pet/{petId}') // false
|
|
190
|
+
*/
|
|
191
|
+
static canParse(url, base) {
|
|
192
|
+
return URL.canParse(url, base);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Converts an OpenAPI/Swagger path to Express-style colon syntax.
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* Url.toPath('/pet/{petId}') // '/pet/:petId'
|
|
199
|
+
*/
|
|
200
|
+
static toPath(path) {
|
|
201
|
+
return path.replace(/\{([^}]+)\}/g, ":$1");
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Converts an OpenAPI/Swagger path to a TypeScript template literal string.
|
|
205
|
+
* `prefix` is prepended inside the literal, `replacer` transforms each parameter name,
|
|
206
|
+
* and `casing` controls parameter identifier casing.
|
|
207
|
+
*
|
|
208
|
+
* @example
|
|
209
|
+
* Url.toTemplateString('/pet/{petId}') // '`/pet/${petId}`'
|
|
210
|
+
*
|
|
211
|
+
* @example
|
|
212
|
+
* Url.toTemplateString('/pet/{petId}', { prefix: 'https://api' }) // '`https://api/pet/${petId}`'
|
|
213
|
+
*/
|
|
214
|
+
static toTemplateString(path, { prefix, replacer, casing } = {}) {
|
|
215
|
+
const result = path.split(/\{([^}]+)\}/).map((part, i) => {
|
|
216
|
+
if (i % 2 === 0) return part;
|
|
217
|
+
const param = transformParam(part, casing);
|
|
218
|
+
return `\${${replacer ? replacer(param) : param}}`;
|
|
219
|
+
}).join("");
|
|
220
|
+
return `\`${prefix ?? ""}${result}\``;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Converts an OpenAPI/Swagger path to a template literal that reads each parameter off the
|
|
224
|
+
* grouped `path` request option, e.g. `/pet/{petId}` becomes `` `/pet/${path.petId}` ``. Parameter
|
|
225
|
+
* names are camelCased to match the generated `path` type, and `prefix` is prepended inside the
|
|
226
|
+
* literal. Shared by the client and cypress generators that pass a grouped `path` object.
|
|
227
|
+
*
|
|
228
|
+
* @example
|
|
229
|
+
* Url.toGroupedTemplateString('/pet/{petId}') // '`/pet/${path.petId}`'
|
|
230
|
+
*/
|
|
231
|
+
static toGroupedTemplateString(path, { prefix } = {}) {
|
|
232
|
+
return Url.toTemplateString(path, {
|
|
233
|
+
prefix,
|
|
234
|
+
casing: "camelcase",
|
|
235
|
+
replacer: (name) => `path.${name}`
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Returns the path and its extracted params as a structured `URLObject`, or as a stringified
|
|
240
|
+
* expression when `stringify` is set.
|
|
241
|
+
*
|
|
242
|
+
* @example
|
|
243
|
+
* Url.toObject('/pet/{petId}')
|
|
244
|
+
* // { url: '/pet/:petId', params: { petId: 'petId' } }
|
|
245
|
+
*/
|
|
246
|
+
static toObject(path, { type = "path", replacer, stringify, casing } = {}) {
|
|
247
|
+
const object = {
|
|
248
|
+
url: type === "path" ? Url.toPath(path) : Url.toTemplateString(path, {
|
|
249
|
+
replacer,
|
|
250
|
+
casing
|
|
251
|
+
}),
|
|
252
|
+
params: toParamsObject(path, {
|
|
253
|
+
replacer,
|
|
254
|
+
casing
|
|
255
|
+
})
|
|
256
|
+
};
|
|
257
|
+
if (stringify) {
|
|
258
|
+
if (type === "template") return JSON.stringify(object).replaceAll("'", "").replaceAll(`"`, "");
|
|
259
|
+
if (object.params) return `{ url: '${object.url}', params: ${JSON.stringify(object.params).replaceAll("'", "").replaceAll(`"`, "")} }`;
|
|
260
|
+
return `{ url: '${object.url}' }`;
|
|
261
|
+
}
|
|
262
|
+
return object;
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
//#endregion
|
|
266
|
+
//#region ../../internals/shared/src/operation.ts
|
|
267
|
+
function getStatusCodeNumber(statusCode) {
|
|
268
|
+
const code = Number(statusCode);
|
|
269
|
+
return Number.isNaN(code) ? null : code;
|
|
270
|
+
}
|
|
271
|
+
function isSuccessStatusCode(statusCode) {
|
|
272
|
+
const code = getStatusCodeNumber(statusCode);
|
|
273
|
+
return code !== null && code >= 200 && code < 300;
|
|
274
|
+
}
|
|
275
|
+
function getSuccessResponses(responses) {
|
|
276
|
+
return responses.filter((response) => isSuccessStatusCode(response.statusCode));
|
|
277
|
+
}
|
|
278
|
+
function getOperationSuccessResponses(node) {
|
|
279
|
+
return getSuccessResponses(node.responses);
|
|
280
|
+
}
|
|
281
|
+
function getPrimarySuccessResponse(node) {
|
|
282
|
+
return getOperationSuccessResponses(node)[0] ?? null;
|
|
283
|
+
}
|
|
284
|
+
function resolveResponseTypes(node, resolver) {
|
|
285
|
+
const types = [];
|
|
286
|
+
for (const response of node.responses) {
|
|
287
|
+
if (response.statusCode === "default") {
|
|
288
|
+
types.push(["default", resolver.resolveResponseName(node)]);
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
const code = getStatusCodeNumber(response.statusCode);
|
|
292
|
+
if (code === null) continue;
|
|
293
|
+
types.push([code, isSuccessStatusCode(code) ? resolver.resolveResponseName(node) : resolver.resolveResponseStatusName(node, response.statusCode)]);
|
|
294
|
+
}
|
|
295
|
+
return types;
|
|
33
296
|
}
|
|
34
297
|
//#endregion
|
|
35
298
|
//#region ../../internals/shared/src/group.ts
|
|
@@ -55,7 +318,7 @@ function createGroupConfig(group) {
|
|
|
55
318
|
if (!group) return null;
|
|
56
319
|
const defaultName = (ctx) => {
|
|
57
320
|
if (group.type === "path") return `${ctx.group.split("/")[1]}`;
|
|
58
|
-
return
|
|
321
|
+
return camelCase(ctx.group);
|
|
59
322
|
};
|
|
60
323
|
return {
|
|
61
324
|
...group,
|
|
@@ -63,6 +326,372 @@ function createGroupConfig(group) {
|
|
|
63
326
|
};
|
|
64
327
|
}
|
|
65
328
|
//#endregion
|
|
329
|
+
//#region src/generators/handlersGenerator.ts
|
|
330
|
+
/**
|
|
331
|
+
* Aggregate generator enabled by `pluginMsw({ handlers: true })`. Emits a
|
|
332
|
+
* `handlers.ts` file that re-exports every generated handler grouped by HTTP
|
|
333
|
+
* method, ready to spread into `setupServer(...handlers)` or
|
|
334
|
+
* `setupWorker(...handlers)`.
|
|
335
|
+
*/
|
|
336
|
+
const handlersGenerator = (0, _kubb_core.defineGenerator)({
|
|
337
|
+
name: "plugin-msw",
|
|
338
|
+
operations(nodes, ctx) {
|
|
339
|
+
const { resolver, config, root } = ctx;
|
|
340
|
+
const { output, group } = ctx.options;
|
|
341
|
+
const handlersName = resolver.resolveHandlersName();
|
|
342
|
+
const file = resolver.resolveFile({
|
|
343
|
+
name: resolver.resolvePathName(handlersName, "file"),
|
|
344
|
+
extname: ".ts"
|
|
345
|
+
}, {
|
|
346
|
+
root,
|
|
347
|
+
output,
|
|
348
|
+
group: group ?? void 0
|
|
349
|
+
});
|
|
350
|
+
const imports = nodes.map((node) => {
|
|
351
|
+
const operationName = resolver.resolveHandlerName(node);
|
|
352
|
+
const operationFile = resolver.resolveFile({
|
|
353
|
+
name: resolver.resolveName(node.operationId),
|
|
354
|
+
extname: ".ts",
|
|
355
|
+
tag: node.tags[0] ?? "default",
|
|
356
|
+
path: node.path
|
|
357
|
+
}, {
|
|
358
|
+
root,
|
|
359
|
+
output,
|
|
360
|
+
group: group ?? void 0
|
|
361
|
+
});
|
|
362
|
+
return _kubb_core.ast.factory.createImport({
|
|
363
|
+
name: [operationName],
|
|
364
|
+
root: file.path,
|
|
365
|
+
path: operationFile.path
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
const handlers = nodes.map((node) => `${resolver.resolveHandlerName(node)}()`);
|
|
369
|
+
return [_kubb_core.ast.factory.createFile({
|
|
370
|
+
baseName: file.baseName,
|
|
371
|
+
path: file.path,
|
|
372
|
+
meta: file.meta,
|
|
373
|
+
banner: resolver.resolveBanner(ctx.meta, {
|
|
374
|
+
output,
|
|
375
|
+
config,
|
|
376
|
+
file: {
|
|
377
|
+
path: file.path,
|
|
378
|
+
baseName: file.baseName
|
|
379
|
+
}
|
|
380
|
+
}),
|
|
381
|
+
footer: resolver.resolveFooter(ctx.meta, {
|
|
382
|
+
output,
|
|
383
|
+
config,
|
|
384
|
+
file: {
|
|
385
|
+
path: file.path,
|
|
386
|
+
baseName: file.baseName
|
|
387
|
+
}
|
|
388
|
+
}),
|
|
389
|
+
imports,
|
|
390
|
+
sources: [_kubb_core.ast.factory.createSource({
|
|
391
|
+
name: handlersName,
|
|
392
|
+
isIndexable: true,
|
|
393
|
+
isExportable: true,
|
|
394
|
+
nodes: [_kubb_core.ast.factory.createText(`export const ${handlersName} = ${JSON.stringify(handlers).replaceAll("\"", "")} as const`)]
|
|
395
|
+
})]
|
|
396
|
+
})];
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
//#endregion
|
|
400
|
+
//#region src/utils.ts
|
|
401
|
+
/**
|
|
402
|
+
* Gets the content type from a response, defaulting to 'application/json' if a schema exists.
|
|
403
|
+
*/
|
|
404
|
+
function getContentType(response) {
|
|
405
|
+
if (!hasResponseSchema(response)) return null;
|
|
406
|
+
return getResponseContentType(response) ?? "application/json";
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Determines if a response has a schema that is not void or any.
|
|
410
|
+
*/
|
|
411
|
+
function hasResponseSchema(response) {
|
|
412
|
+
const schema = response?.content?.find((entry) => entry.schema)?.schema;
|
|
413
|
+
return !!schema && schema.type !== "void" && schema.type !== "any";
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Picks the content type used for the mocked response header. When a response declares multiple
|
|
417
|
+
* content types, JSON is preferred (the faker mock body is JSON), otherwise the first declared type.
|
|
418
|
+
*/
|
|
419
|
+
function getResponseContentType(response) {
|
|
420
|
+
const contents = response?.content ?? [];
|
|
421
|
+
const value = (contents.find((entry) => {
|
|
422
|
+
const baseType = entry.contentType?.split(";")[0]?.trim().toLowerCase();
|
|
423
|
+
return baseType === "application/json" || baseType?.endsWith("+json");
|
|
424
|
+
}) ?? contents[0])?.contentType;
|
|
425
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Converts an HTTP method to its lowercase MSW equivalent (e.g., 'POST' → 'post').
|
|
429
|
+
*/
|
|
430
|
+
function getMswMethod(node) {
|
|
431
|
+
return _kubb_core.ast.isHttpOperationNode(node) ? node.method.toLowerCase() : "";
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
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_core.ast.isHttpOperationNode(node) ? node.path.replaceAll("{", ":").replaceAll("}", "") : "";
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Resolves faker metadata for an MSW operation, including response name and file path.
|
|
441
|
+
*/
|
|
442
|
+
function resolveFakerMeta(node, options) {
|
|
443
|
+
const { root, fakerResolver, fakerOutput, fakerGroup } = options;
|
|
444
|
+
const tag = node.tags[0] ?? "default";
|
|
445
|
+
return {
|
|
446
|
+
name: fakerResolver.resolveResponseName(node),
|
|
447
|
+
file: fakerResolver.resolveFile({
|
|
448
|
+
name: node.operationId,
|
|
449
|
+
extname: ".ts",
|
|
450
|
+
tag,
|
|
451
|
+
path: node.path
|
|
452
|
+
}, {
|
|
453
|
+
root,
|
|
454
|
+
output: fakerOutput,
|
|
455
|
+
group: fakerGroup ?? void 0
|
|
456
|
+
})
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
//#endregion
|
|
460
|
+
//#region src/components/Mock.tsx
|
|
461
|
+
const declarationPrinter$2 = (0, _kubb_plugin_ts.functionPrinter)({ mode: "declaration" });
|
|
462
|
+
function Mock({ baseURL = "", name, typeName, requestTypeName, node }) {
|
|
463
|
+
const method = getMswMethod(node);
|
|
464
|
+
const successResponse = getPrimarySuccessResponse(node);
|
|
465
|
+
const statusCode = successResponse ? Number(successResponse.statusCode) : 200;
|
|
466
|
+
const contentType = getContentType(successResponse);
|
|
467
|
+
const url = Url.toPath(getMswUrl(node));
|
|
468
|
+
const headers = [contentType ? `'Content-Type': '${contentType}'` : null].filter(Boolean);
|
|
469
|
+
const dataType = hasResponseSchema(successResponse) ? typeName : "string | number | boolean | null | object";
|
|
470
|
+
const callbackType = requestTypeName ? `HttpResponseResolver<Record<string, string>, ${requestTypeName}, any>` : `((info: Parameters<Parameters<typeof http.${method}>[1]>[0]) => Response | Promise<Response>)`;
|
|
471
|
+
const params = declarationPrinter$2.print((0, _kubb_plugin_ts.createFunctionParameters)({ params: [(0, _kubb_plugin_ts.createFunctionParameter)({
|
|
472
|
+
name: "data",
|
|
473
|
+
type: `${dataType} | ${callbackType}`,
|
|
474
|
+
optional: true
|
|
475
|
+
})] }));
|
|
476
|
+
const httpCall = requestTypeName ? `http.${method}<Record<string, string>, ${requestTypeName}, any>` : `http.${method}`;
|
|
477
|
+
return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
|
|
478
|
+
name,
|
|
479
|
+
isIndexable: true,
|
|
480
|
+
isExportable: true,
|
|
481
|
+
children: /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.Function, {
|
|
482
|
+
name,
|
|
483
|
+
export: true,
|
|
484
|
+
params: params ?? "",
|
|
485
|
+
children: `return ${httpCall}(\`${baseURL}${url.replace(/([^/]):/g, "$1\\\\:")}\`, function handler(info) {
|
|
486
|
+
if(typeof data === 'function') return data(info)
|
|
487
|
+
|
|
488
|
+
return new Response(JSON.stringify(data), {
|
|
489
|
+
status: ${statusCode},
|
|
490
|
+
${headers.length ? ` headers: {
|
|
491
|
+
${headers.join(", \n")}
|
|
492
|
+
},` : ""}
|
|
493
|
+
})
|
|
494
|
+
})`
|
|
495
|
+
})
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
//#endregion
|
|
499
|
+
//#region src/components/MockWithFaker.tsx
|
|
500
|
+
const declarationPrinter$1 = (0, _kubb_plugin_ts.functionPrinter)({ mode: "declaration" });
|
|
501
|
+
function MockWithFaker({ baseURL = "", name, fakerName, typeName, requestTypeName, node }) {
|
|
502
|
+
const method = getMswMethod(node);
|
|
503
|
+
const successResponse = getPrimarySuccessResponse(node);
|
|
504
|
+
const statusCode = successResponse ? Number(successResponse.statusCode) : 200;
|
|
505
|
+
const contentType = getContentType(successResponse);
|
|
506
|
+
const url = Url.toPath(getMswUrl(node));
|
|
507
|
+
const headers = [contentType ? `'Content-Type': '${contentType}'` : null].filter(Boolean);
|
|
508
|
+
const callbackType = requestTypeName ? `HttpResponseResolver<Record<string, string>, ${requestTypeName}, any>` : `((info: Parameters<Parameters<typeof http.${method}>[1]>[0]) => Response | Promise<Response>)`;
|
|
509
|
+
const params = declarationPrinter$1.print((0, _kubb_plugin_ts.createFunctionParameters)({ params: [(0, _kubb_plugin_ts.createFunctionParameter)({
|
|
510
|
+
name: "data",
|
|
511
|
+
type: `${typeName} | ${callbackType}`,
|
|
512
|
+
optional: true
|
|
513
|
+
})] }));
|
|
514
|
+
const httpCall = requestTypeName ? `http.${method}<Record<string, string>, ${requestTypeName}, any>` : `http.${method}`;
|
|
515
|
+
return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
|
|
516
|
+
name,
|
|
517
|
+
isIndexable: true,
|
|
518
|
+
isExportable: true,
|
|
519
|
+
children: /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.Function, {
|
|
520
|
+
name,
|
|
521
|
+
export: true,
|
|
522
|
+
params: params ?? "",
|
|
523
|
+
children: `return ${httpCall}('${baseURL}${url.replace(/([^/]):/g, "$1\\\\:")}', function handler(info) {
|
|
524
|
+
if(typeof data === 'function') return data(info)
|
|
525
|
+
|
|
526
|
+
return new Response(JSON.stringify(data || ${fakerName}(data)), {
|
|
527
|
+
status: ${statusCode},
|
|
528
|
+
${headers.length ? ` headers: {
|
|
529
|
+
${headers.join(", \n")}
|
|
530
|
+
},` : ""}
|
|
531
|
+
})
|
|
532
|
+
})`
|
|
533
|
+
})
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
//#endregion
|
|
537
|
+
//#region src/components/Response.tsx
|
|
538
|
+
const declarationPrinter = (0, _kubb_plugin_ts.functionPrinter)({ mode: "declaration" });
|
|
539
|
+
function Response({ name, typeName, response }) {
|
|
540
|
+
const statusCode = Number(response.statusCode);
|
|
541
|
+
const contentType = getContentType(response);
|
|
542
|
+
const headers = [contentType ? `'Content-Type': '${contentType}'` : null].filter(Boolean);
|
|
543
|
+
const params = declarationPrinter.print((0, _kubb_plugin_ts.createFunctionParameters)({ params: [(0, _kubb_plugin_ts.createFunctionParameter)({
|
|
544
|
+
name: "data",
|
|
545
|
+
type: typeName,
|
|
546
|
+
optional: !hasResponseSchema(response)
|
|
547
|
+
})] }));
|
|
548
|
+
const responseName = `${name}Response${statusCode}`;
|
|
549
|
+
return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
|
|
550
|
+
name: responseName,
|
|
551
|
+
isIndexable: true,
|
|
552
|
+
isExportable: true,
|
|
553
|
+
children: /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.Function, {
|
|
554
|
+
name: responseName,
|
|
555
|
+
export: true,
|
|
556
|
+
params: params ?? "",
|
|
557
|
+
children: `
|
|
558
|
+
return new Response(JSON.stringify(data), {
|
|
559
|
+
status: ${statusCode},
|
|
560
|
+
${headers.length ? ` headers: {
|
|
561
|
+
${headers.join(", \n")}
|
|
562
|
+
},` : ""}
|
|
563
|
+
})`
|
|
564
|
+
})
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
//#endregion
|
|
568
|
+
//#region src/generators/mswGenerator.tsx
|
|
569
|
+
/**
|
|
570
|
+
* Built-in operation generator for `@kubb/plugin-msw`. Emits one MSW handler
|
|
571
|
+
* per OpenAPI operation. With `parser: 'faker'` the handler returns a value
|
|
572
|
+
* from `@kubb/plugin-faker`; with `parser: 'data'` it returns a typed empty
|
|
573
|
+
* payload for tests to fill in.
|
|
574
|
+
*/
|
|
575
|
+
const mswGenerator = (0, _kubb_core.defineGenerator)({
|
|
576
|
+
name: "msw",
|
|
577
|
+
renderer: _kubb_renderer_jsx.jsxRenderer,
|
|
578
|
+
operation(node, ctx) {
|
|
579
|
+
if (!_kubb_core.ast.isHttpOperationNode(node)) return null;
|
|
580
|
+
const { driver, resolver, config, root } = ctx;
|
|
581
|
+
const { output, parser, baseURL, group } = ctx.options;
|
|
582
|
+
const fileName = resolver.resolveName(node.operationId);
|
|
583
|
+
const mock = {
|
|
584
|
+
name: resolver.resolveHandlerName(node),
|
|
585
|
+
file: resolver.resolveFile({
|
|
586
|
+
name: fileName,
|
|
587
|
+
extname: ".ts",
|
|
588
|
+
tag: node.tags[0] ?? "default",
|
|
589
|
+
path: node.path
|
|
590
|
+
}, {
|
|
591
|
+
root,
|
|
592
|
+
output,
|
|
593
|
+
group: group ?? void 0
|
|
594
|
+
})
|
|
595
|
+
};
|
|
596
|
+
const fakerPlugin = parser === "faker" ? driver.getPlugin(_kubb_plugin_faker.pluginFakerName) : null;
|
|
597
|
+
const faker = parser === "faker" && fakerPlugin ? resolveFakerMeta(node, {
|
|
598
|
+
root,
|
|
599
|
+
fakerResolver: driver.getResolver(_kubb_plugin_faker.pluginFakerName),
|
|
600
|
+
fakerOutput: fakerPlugin.options?.output ?? output,
|
|
601
|
+
fakerGroup: fakerPlugin.options?.group ?? null
|
|
602
|
+
}) : null;
|
|
603
|
+
const pluginTs = driver.getPlugin(_kubb_plugin_ts.pluginTsName);
|
|
604
|
+
if (!pluginTs) return null;
|
|
605
|
+
const tsResolver = driver.getResolver(_kubb_plugin_ts.pluginTsName);
|
|
606
|
+
const type = {
|
|
607
|
+
file: tsResolver.resolveFile({
|
|
608
|
+
name: node.operationId,
|
|
609
|
+
extname: ".ts",
|
|
610
|
+
tag: node.tags[0] ?? "default",
|
|
611
|
+
path: node.path
|
|
612
|
+
}, {
|
|
613
|
+
root,
|
|
614
|
+
output: pluginTs.options?.output ?? output,
|
|
615
|
+
group: pluginTs.options?.group ?? void 0
|
|
616
|
+
}),
|
|
617
|
+
responseName: tsResolver.resolveResponseName(node)
|
|
618
|
+
};
|
|
619
|
+
const types = resolveResponseTypes(node, tsResolver);
|
|
620
|
+
const hasSuccessSchema = getOperationSuccessResponses(node).some((response) => !!response.content?.[0]?.schema);
|
|
621
|
+
const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : null;
|
|
622
|
+
return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx.File, {
|
|
623
|
+
baseName: mock.file.baseName,
|
|
624
|
+
path: mock.file.path,
|
|
625
|
+
meta: mock.file.meta,
|
|
626
|
+
banner: resolver.resolveBanner(ctx.meta, {
|
|
627
|
+
output,
|
|
628
|
+
config,
|
|
629
|
+
file: {
|
|
630
|
+
path: mock.file.path,
|
|
631
|
+
baseName: mock.file.baseName
|
|
632
|
+
}
|
|
633
|
+
}),
|
|
634
|
+
footer: resolver.resolveFooter(ctx.meta, {
|
|
635
|
+
output,
|
|
636
|
+
config,
|
|
637
|
+
file: {
|
|
638
|
+
path: mock.file.path,
|
|
639
|
+
baseName: mock.file.baseName
|
|
640
|
+
}
|
|
641
|
+
}),
|
|
642
|
+
children: [
|
|
643
|
+
/* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
|
|
644
|
+
name: ["http"],
|
|
645
|
+
path: "msw"
|
|
646
|
+
}),
|
|
647
|
+
/* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
|
|
648
|
+
name: ["HttpResponseResolver"],
|
|
649
|
+
isTypeOnly: true,
|
|
650
|
+
path: "msw"
|
|
651
|
+
}),
|
|
652
|
+
/* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
|
|
653
|
+
name: Array.from(new Set([
|
|
654
|
+
type.responseName,
|
|
655
|
+
...types.map((t) => t[1]),
|
|
656
|
+
...requestName ? [requestName] : []
|
|
657
|
+
])),
|
|
658
|
+
path: type.file.path,
|
|
659
|
+
root: mock.file.path,
|
|
660
|
+
isTypeOnly: true
|
|
661
|
+
}),
|
|
662
|
+
parser === "faker" && faker && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
|
|
663
|
+
name: [faker.name],
|
|
664
|
+
root: mock.file.path,
|
|
665
|
+
path: faker.file.path
|
|
666
|
+
}),
|
|
667
|
+
types.filter(([code]) => code !== "default").map(([code, typeName]) => {
|
|
668
|
+
const response = node.responses.find((item) => item.statusCode === String(code));
|
|
669
|
+
if (!response) return null;
|
|
670
|
+
return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(Response, {
|
|
671
|
+
typeName,
|
|
672
|
+
response,
|
|
673
|
+
name: mock.name
|
|
674
|
+
}, typeName);
|
|
675
|
+
}),
|
|
676
|
+
parser === "faker" && faker && hasSuccessSchema ? /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(MockWithFaker, {
|
|
677
|
+
name: mock.name,
|
|
678
|
+
typeName: type.responseName,
|
|
679
|
+
requestTypeName: requestName,
|
|
680
|
+
fakerName: faker.name,
|
|
681
|
+
node,
|
|
682
|
+
baseURL
|
|
683
|
+
}) : /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(Mock, {
|
|
684
|
+
name: mock.name,
|
|
685
|
+
typeName: type.responseName,
|
|
686
|
+
requestTypeName: requestName,
|
|
687
|
+
node,
|
|
688
|
+
baseURL
|
|
689
|
+
})
|
|
690
|
+
]
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
});
|
|
694
|
+
//#endregion
|
|
66
695
|
//#region src/resolvers/resolverMsw.ts
|
|
67
696
|
/**
|
|
68
697
|
* Default resolver used by `@kubb/plugin-msw`. Decides the names and file
|
|
@@ -80,10 +709,10 @@ const resolverMsw = (0, _kubb_core.defineResolver)(() => ({
|
|
|
80
709
|
name: "default",
|
|
81
710
|
pluginName: "plugin-msw",
|
|
82
711
|
default(name, type) {
|
|
83
|
-
return type === "file" ? toFilePath(name) :
|
|
712
|
+
return type === "file" ? toFilePath(name) : camelCase(name);
|
|
84
713
|
},
|
|
85
714
|
resolveName(name) {
|
|
86
|
-
return
|
|
715
|
+
return camelCase(name, { suffix: "handler" });
|
|
87
716
|
},
|
|
88
717
|
resolvePathName(name, type) {
|
|
89
718
|
return this.default(name, type);
|
|
@@ -132,7 +761,7 @@ const pluginMsw = (0, _kubb_core.definePlugin)((options) => {
|
|
|
132
761
|
const { output = {
|
|
133
762
|
path: "handlers",
|
|
134
763
|
barrel: { type: "named" }
|
|
135
|
-
}, group, exclude = [], include, override = [], handlers = false, parser = "data", baseURL, resolver: userResolver, macros: userMacros
|
|
764
|
+
}, group, exclude = [], include, override = [], handlers = false, parser = "data", baseURL, resolver: userResolver, macros: userMacros } = options;
|
|
136
765
|
const groupConfig = createGroupConfig(group);
|
|
137
766
|
return {
|
|
138
767
|
name: pluginMswName,
|
|
@@ -156,9 +785,8 @@ const pluginMsw = (0, _kubb_core.definePlugin)((options) => {
|
|
|
156
785
|
});
|
|
157
786
|
ctx.setResolver(resolver);
|
|
158
787
|
if (userMacros?.length) ctx.setMacros(userMacros);
|
|
159
|
-
ctx.addGenerator(
|
|
160
|
-
if (handlers) ctx.addGenerator(
|
|
161
|
-
for (const gen of userGenerators) ctx.addGenerator(gen);
|
|
788
|
+
ctx.addGenerator(mswGenerator);
|
|
789
|
+
if (handlers) ctx.addGenerator(handlersGenerator);
|
|
162
790
|
} }
|
|
163
791
|
};
|
|
164
792
|
});
|