rspack-plugin-mock 0.1.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.
@@ -0,0 +1,1089 @@
1
+ // src/core/requestRecovery.ts
2
+ import { Buffer } from "buffer";
3
+ var requestCollectCache = /* @__PURE__ */ new WeakMap();
4
+ function collectRequest(req) {
5
+ const chunks = [];
6
+ req.addListener("data", (chunk) => {
7
+ chunks.push(Buffer.from(chunk));
8
+ });
9
+ req.addListener("end", () => {
10
+ if (chunks.length)
11
+ requestCollectCache.set(req, Buffer.concat(chunks));
12
+ });
13
+ }
14
+ function rewriteRequest(proxyReq, req) {
15
+ const buffer = requestCollectCache.get(req);
16
+ if (buffer) {
17
+ requestCollectCache.delete(req);
18
+ if (!proxyReq.headersSent)
19
+ proxyReq.setHeader("Content-Length", buffer.byteLength);
20
+ if (!proxyReq.writableEnded)
21
+ proxyReq.write(buffer);
22
+ }
23
+ }
24
+
25
+ // src/core/mockMiddleware.ts
26
+ import process3 from "process";
27
+ import { isBoolean as isBoolean2, toArray as toArray2 } from "@pengzhanbo/utils";
28
+ import cors from "cors";
29
+ import { pathToRegexp as pathToRegexp3 } from "path-to-regexp";
30
+
31
+ // src/core/resolvePluginOptions.ts
32
+ import process from "process";
33
+ function resolvePluginOptions({
34
+ prefix = [],
35
+ // wsPrefix = [],
36
+ cwd,
37
+ include = ["mock/**/*.mock.{js,ts,cjs,mjs,json,json5}"],
38
+ exclude = ["**/node_modules/**", "**/.vscode/**", "**/.git/**"],
39
+ // reload = false,
40
+ log = "info",
41
+ cors: cors2 = true,
42
+ formidableOptions = {},
43
+ // build = false,
44
+ cookiesOptions = {},
45
+ bodyParserOptions = {},
46
+ priority = {}
47
+ } = {}, context) {
48
+ const pluginOptions = {
49
+ prefix,
50
+ // wsPrefix,
51
+ cwd: cwd || context || process.cwd(),
52
+ include,
53
+ exclude,
54
+ // reload,
55
+ cors: cors2,
56
+ cookiesOptions,
57
+ log,
58
+ formidableOptions: {
59
+ multiples: true,
60
+ ...formidableOptions
61
+ },
62
+ bodyParserOptions,
63
+ priority
64
+ // build: build
65
+ // ? Object.assign(
66
+ // {
67
+ // serverPort: 8080,
68
+ // dist: 'mockServer',
69
+ // log: 'error',
70
+ // },
71
+ // typeof build === 'object' ? build : {},
72
+ // )
73
+ // : false,
74
+ };
75
+ return pluginOptions;
76
+ }
77
+
78
+ // src/core/mockCompiler.ts
79
+ import EventEmitter from "events";
80
+ import fs3, { promises as fsp2 } from "fs";
81
+ import process2 from "process";
82
+ import path4 from "path";
83
+ import fastGlob from "fast-glob";
84
+ import chokidar from "chokidar";
85
+ import { createFilter } from "@rollup/pluginutils";
86
+ import * as rspackCore from "@rspack/core";
87
+ import { Volume, createFsFromVolume } from "memfs";
88
+
89
+ // src/core/utils.ts
90
+ import fs from "fs";
91
+ import path from "path";
92
+ import { parse as queryParse } from "querystring";
93
+ import { URL as URL2, fileURLToPath } from "url";
94
+ import os from "os";
95
+ import Debug from "debug";
96
+ import { match } from "path-to-regexp";
97
+ function isStream(stream) {
98
+ return stream !== null && typeof stream === "object" && typeof stream.pipe === "function";
99
+ }
100
+ function isReadableStream(stream) {
101
+ return isStream(stream) && stream.readable !== false && typeof stream._read === "function" && typeof stream._readableState === "object";
102
+ }
103
+ function getDirname(importMetaUrl) {
104
+ return path.dirname(fileURLToPath(importMetaUrl));
105
+ }
106
+ var debug = Debug("rspack:mock-server");
107
+ function lookupFile(dir, formats, options) {
108
+ for (const format of formats) {
109
+ const fullPath = path.join(dir, format);
110
+ if (fs.existsSync(fullPath) && fs.statSync(fullPath).isFile()) {
111
+ const result = options?.pathOnly ? fullPath : fs.readFileSync(fullPath, "utf-8");
112
+ if (!options?.predicate || options.predicate(result))
113
+ return result;
114
+ }
115
+ }
116
+ const parentDir = path.dirname(dir);
117
+ if (parentDir !== dir && (!options?.rootDir || parentDir.startsWith(options?.rootDir))) {
118
+ return lookupFile(parentDir, formats, options);
119
+ }
120
+ }
121
+ function doesProxyContextMatchUrl(context, url, req) {
122
+ if (typeof context === "function") {
123
+ return context(url, req);
124
+ }
125
+ return context[0] === "^" && new RegExp(context).test(url) || url.startsWith(context);
126
+ }
127
+ function parseParams(pattern, url) {
128
+ const urlMatch = match(pattern, { decode: decodeURIComponent })(url) || {
129
+ params: {}
130
+ };
131
+ return urlMatch.params || {};
132
+ }
133
+ function urlParse(input) {
134
+ const url = new URL2(input, "http://example.com");
135
+ const pathname = decodeURIComponent(url.pathname);
136
+ const query = queryParse(url.search.replace(/^\?/, ""));
137
+ return { pathname, query };
138
+ }
139
+ var windowsSlashRE = /\\/g;
140
+ var isWindows = os.platform() === "win32";
141
+ function slash(p) {
142
+ return p.replace(windowsSlashRE, "/");
143
+ }
144
+ function normalizePath(id) {
145
+ return path.posix.normalize(isWindows ? slash(id) : id);
146
+ }
147
+
148
+ // src/core/loadFromCode.ts
149
+ import path2 from "path";
150
+ import fs2, { promises as fsp } from "fs";
151
+ async function loadFromCode({
152
+ filepath,
153
+ code,
154
+ isESM,
155
+ cwd
156
+ }) {
157
+ filepath = path2.resolve(cwd, filepath);
158
+ const fileBase = `${filepath}.timestamp-${Date.now()}`;
159
+ const ext = isESM ? ".mjs" : ".cjs";
160
+ const fileNameTmp = `${fileBase}${ext}`;
161
+ await fsp.writeFile(fileNameTmp, code, "utf8");
162
+ try {
163
+ const result = await import(fileNameTmp);
164
+ return result.default || result;
165
+ } finally {
166
+ try {
167
+ fs2.unlinkSync(fileNameTmp);
168
+ } catch {
169
+ }
170
+ }
171
+ }
172
+
173
+ // src/core/transform.ts
174
+ import {
175
+ isEmptyObject,
176
+ isFunction,
177
+ isObject as isObject2,
178
+ sortBy,
179
+ toArray
180
+ } from "@pengzhanbo/utils";
181
+
182
+ // src/core/validator.ts
183
+ import { isArray, isObject } from "@pengzhanbo/utils";
184
+ function validate(request, validator) {
185
+ return isObjectSubset(request.headers, validator.headers) && isObjectSubset(request.body, validator.body) && isObjectSubset(request.params, validator.params) && isObjectSubset(request.query, validator.query) && isObjectSubset(request.refererQuery, validator.refererQuery);
186
+ }
187
+ function isObjectSubset(source, target) {
188
+ if (!target)
189
+ return true;
190
+ for (const key in target) {
191
+ if (!isIncluded(source[key], target[key]))
192
+ return false;
193
+ }
194
+ return true;
195
+ }
196
+ function isIncluded(source, target) {
197
+ if (isArray(source) && isArray(target)) {
198
+ const seen = /* @__PURE__ */ new Set();
199
+ return target.every(
200
+ (ti) => source.some((si, i) => {
201
+ if (seen.has(i))
202
+ return false;
203
+ const included = isIncluded(si, ti);
204
+ if (included)
205
+ seen.add(i);
206
+ return included;
207
+ })
208
+ );
209
+ }
210
+ if (isObject(source) && isObject(target))
211
+ return isObjectSubset(source, target);
212
+ return Object.is(source, target);
213
+ }
214
+
215
+ // src/core/transform.ts
216
+ function transformRawData(rawData) {
217
+ return rawData.filter((item) => item[0]).map(([raw, __filepath__]) => {
218
+ let mockConfig;
219
+ if (raw.default) {
220
+ if (Array.isArray(raw.default)) {
221
+ mockConfig = raw.default.map((item) => ({ ...item, __filepath__ }));
222
+ } else {
223
+ mockConfig = { ...raw.default, __filepath__ };
224
+ }
225
+ } else if ("url" in raw) {
226
+ mockConfig = { ...raw, __filepath__ };
227
+ } else {
228
+ mockConfig = [];
229
+ Object.keys(raw || {}).forEach((key) => {
230
+ if (Array.isArray(raw[key])) {
231
+ mockConfig.push(...raw[key].map((item) => ({ ...item, __filepath__ })));
232
+ } else {
233
+ mockConfig.push({ ...raw[key], __filepath__ });
234
+ }
235
+ });
236
+ }
237
+ return mockConfig;
238
+ });
239
+ }
240
+ function transformMockData(mockList) {
241
+ const list = [];
242
+ for (const [, handle] of mockList.entries()) {
243
+ if (handle)
244
+ list.push(...toArray(handle));
245
+ }
246
+ const mocks = {};
247
+ list.filter((mock) => isObject2(mock) && mock.enabled !== false && mock.url).forEach((mock) => {
248
+ const { pathname, query } = urlParse(mock.url);
249
+ const list2 = mocks[pathname] ??= [];
250
+ const current = { ...mock, url: pathname };
251
+ if (current.ws !== true) {
252
+ const validator = current.validator;
253
+ if (!isEmptyObject(query)) {
254
+ if (isFunction(validator)) {
255
+ current.validator = function(request) {
256
+ return isObjectSubset(request.query, query) && validator(request);
257
+ };
258
+ } else if (validator) {
259
+ current.validator = { ...validator };
260
+ current.validator.query = current.validator.query ? { ...query, ...current.validator.query } : query;
261
+ } else {
262
+ current.validator = { query };
263
+ }
264
+ }
265
+ }
266
+ list2.push(current);
267
+ });
268
+ Object.keys(mocks).forEach((key) => {
269
+ mocks[key] = sortByValidator(mocks[key]);
270
+ });
271
+ return mocks;
272
+ }
273
+ function sortByValidator(mocks) {
274
+ return sortBy(mocks, (item) => {
275
+ if (item.ws === true)
276
+ return 0;
277
+ const { validator } = item;
278
+ if (!validator || isEmptyObject(validator))
279
+ return 2;
280
+ if (isFunction(validator))
281
+ return 0;
282
+ const count = Object.keys(validator).reduce(
283
+ (prev, key) => prev + keysCount(validator[key]),
284
+ 0
285
+ );
286
+ return 1 / count;
287
+ });
288
+ }
289
+ function keysCount(obj) {
290
+ if (!obj)
291
+ return 0;
292
+ return Object.keys(obj).length;
293
+ }
294
+
295
+ // src/core/resolveRspackOptions.ts
296
+ import path3 from "path";
297
+ var _dirname = getDirname(import.meta.url);
298
+ function resolveRspackOptions({
299
+ cwd,
300
+ isEsm,
301
+ entryFile,
302
+ outputFile,
303
+ plugins,
304
+ alias,
305
+ watch = false
306
+ }) {
307
+ const targets = ["node >= 18.0.0"];
308
+ return {
309
+ mode: "production",
310
+ context: cwd,
311
+ entry: entryFile,
312
+ watch,
313
+ target: "node18.0",
314
+ externalsType: isEsm ? "module" : "commonjs2",
315
+ externals: /^[^./].*/,
316
+ resolve: {
317
+ alias,
318
+ extensions: [".js", ".ts", ".cjs", ".mjs", ".json5", ".json"]
319
+ },
320
+ plugins,
321
+ output: {
322
+ library: { type: !isEsm ? "commonjs2" : "module" },
323
+ filename: outputFile,
324
+ path: "/"
325
+ },
326
+ experiments: { outputModule: isEsm },
327
+ module: {
328
+ rules: [
329
+ {
330
+ test: /\.json5?$/,
331
+ loader: path3.join(_dirname, "json5-loader.cjs"),
332
+ type: "javascript/auto"
333
+ },
334
+ {
335
+ test: /\.[cm]?js$/,
336
+ use: [
337
+ {
338
+ loader: "builtin:swc-loader",
339
+ options: {
340
+ jsc: { parser: { syntax: "ecmascript" } },
341
+ env: { targets }
342
+ }
343
+ }
344
+ ]
345
+ },
346
+ {
347
+ test: /\.[cm]?ts$/,
348
+ use: [
349
+ {
350
+ loader: "builtin:swc-loader",
351
+ options: {
352
+ jsc: { parser: { syntax: "typescript" } },
353
+ env: { targets }
354
+ }
355
+ }
356
+ ]
357
+ }
358
+ ]
359
+ }
360
+ };
361
+ }
362
+
363
+ // src/core/mockCompiler.ts
364
+ var vfs = createFsFromVolume(new Volume());
365
+ function createMockCompiler(options) {
366
+ return new MockCompiler(options);
367
+ }
368
+ var MockCompiler = class extends EventEmitter {
369
+ constructor(options) {
370
+ super();
371
+ this.options = options;
372
+ this.cwd = options.cwd || process2.cwd();
373
+ const { include, exclude } = this.options;
374
+ this.fileFilter = createFilter(include, exclude, {
375
+ resolve: false
376
+ });
377
+ try {
378
+ const pkg = lookupFile(this.cwd, ["package.json"]);
379
+ this.moduleType = !!pkg && JSON.parse(pkg).type === "module" ? "esm" : "cjs";
380
+ } catch {
381
+ }
382
+ this.entryFile = path4.resolve(process2.cwd(), "node_modules/.cache/mock-server/mock-server.ts");
383
+ this.outputFile = "mock.bundle.js";
384
+ }
385
+ cwd;
386
+ mockWatcher;
387
+ moduleType = "cjs";
388
+ entryFile;
389
+ outputFile;
390
+ _mockData = {};
391
+ fileFilter;
392
+ compiler;
393
+ get mockData() {
394
+ return this._mockData;
395
+ }
396
+ async run() {
397
+ await this.updateMockEntry();
398
+ this.watchMockFiles();
399
+ this.createCompiler(async (err, stats) => {
400
+ const name = "[rspack:mock]";
401
+ if (err) {
402
+ const error = stats?.compilation.getLogger(name).error || ((...args) => console.error(name, ...args));
403
+ error(err.stack || err);
404
+ if ("details" in err) {
405
+ error(err.details);
406
+ }
407
+ return;
408
+ }
409
+ if (stats?.hasErrors()) {
410
+ stats.compilation.getLogger(name).error(stats.toString({ colors: true }));
411
+ return;
412
+ }
413
+ const content = vfs.readFileSync(`/${this.outputFile}`, "utf-8");
414
+ try {
415
+ const result = await loadFromCode({
416
+ filepath: this.outputFile,
417
+ code: content,
418
+ isESM: this.moduleType === "esm",
419
+ cwd: this.cwd
420
+ });
421
+ this._mockData = transformMockData(transformRawData(result));
422
+ this.emit("update");
423
+ } catch (e) {
424
+ console.error("[rspack:mock-server]", e);
425
+ }
426
+ });
427
+ }
428
+ close() {
429
+ this.mockWatcher.close();
430
+ this.compiler?.close(() => {
431
+ });
432
+ this.emit("close");
433
+ }
434
+ updateAlias(alias) {
435
+ this.options.alias = {
436
+ ...this.options.alias,
437
+ ...alias
438
+ };
439
+ }
440
+ async updateMockEntry() {
441
+ const files = await this.getMockFiles();
442
+ await this.resolveEntryFile(files);
443
+ }
444
+ async getMockFiles() {
445
+ const { include } = this.options;
446
+ const files = await fastGlob(include, { cwd: this.cwd });
447
+ return files.filter(this.fileFilter);
448
+ }
449
+ watchMockFiles() {
450
+ const { include } = this.options;
451
+ const [firstGlob, ...otherGlob] = include;
452
+ const watcher = this.mockWatcher = chokidar.watch(firstGlob, {
453
+ ignoreInitial: true,
454
+ cwd: this.cwd
455
+ });
456
+ if (otherGlob.length > 0)
457
+ otherGlob.forEach((glob) => watcher.add(glob));
458
+ watcher.on("add", () => {
459
+ this.updateMockEntry();
460
+ });
461
+ watcher.on("unlink", async () => {
462
+ this.updateMockEntry();
463
+ });
464
+ }
465
+ async resolveEntryFile(fileList) {
466
+ const importers = [];
467
+ const exporters = [];
468
+ for (const [index, filepath] of fileList.entries()) {
469
+ const file = normalizePath(path4.join(this.cwd, filepath));
470
+ importers.push(`import * as m${index} from '${file}'`);
471
+ exporters.push(`[m${index}, '${filepath}']`);
472
+ }
473
+ const code = `${importers.join("\n")}
474
+
475
+ export default [
476
+ ${exporters.join(",\n ")}
477
+ ]`;
478
+ const dirname = path4.dirname(this.entryFile);
479
+ if (!fs3.existsSync(dirname)) {
480
+ await fsp2.mkdir(dirname, { recursive: true });
481
+ }
482
+ await fsp2.writeFile(this.entryFile, code, "utf8");
483
+ }
484
+ createCompiler(callback) {
485
+ const options = resolveRspackOptions({
486
+ isEsm: this.moduleType === "esm",
487
+ cwd: this.cwd,
488
+ plugins: this.options.plugins,
489
+ entryFile: this.entryFile,
490
+ outputFile: this.outputFile,
491
+ alias: this.options.alias,
492
+ watch: true
493
+ });
494
+ this.compiler = rspackCore.rspack(options, callback);
495
+ if (this.compiler)
496
+ this.compiler.outputFileSystem = vfs;
497
+ }
498
+ };
499
+
500
+ // src/core/baseMiddleware.ts
501
+ import { Buffer as Buffer2 } from "buffer";
502
+ import {
503
+ isArray as isArray3,
504
+ isEmptyObject as isEmptyObject3,
505
+ isFunction as isFunction2,
506
+ random,
507
+ sleep,
508
+ timestamp
509
+ } from "@pengzhanbo/utils";
510
+ import Cookies from "cookies";
511
+ import HTTP_STATUS from "http-status";
512
+ import * as mime from "mime-types";
513
+ import { pathToRegexp as pathToRegexp2 } from "path-to-regexp";
514
+ import colors from "picocolors";
515
+
516
+ // src/core/matchingWeight.ts
517
+ import {
518
+ isArray as isArray2,
519
+ isEmptyObject as isEmptyObject2,
520
+ isString,
521
+ sortBy as sortBy2,
522
+ uniq
523
+ } from "@pengzhanbo/utils";
524
+ import { parse, pathToRegexp } from "path-to-regexp";
525
+ var tokensCache = {};
526
+ function getTokens(rule) {
527
+ if (tokensCache[rule])
528
+ return tokensCache[rule];
529
+ const { tokens: tks } = parse(rule);
530
+ const tokens = [];
531
+ for (const tk of tks) {
532
+ if (!isString(tk)) {
533
+ tokens.push(tk);
534
+ } else {
535
+ const hasPrefix = tk[0] === "/";
536
+ const subTks = hasPrefix ? tk.slice(1).split("/") : tk.split("/");
537
+ tokens.push(
538
+ `${hasPrefix ? "/" : ""}${subTks[0]}`,
539
+ ...subTks.slice(1).map((t) => `/${t}`)
540
+ );
541
+ }
542
+ }
543
+ tokensCache[rule] = tokens;
544
+ return tokens;
545
+ }
546
+ function getHighest(rules) {
547
+ let weights = rules.map((rule) => getTokens(rule).length);
548
+ weights = weights.length === 0 ? [1] : weights;
549
+ return Math.max(...weights) + 2;
550
+ }
551
+ function sortFn(rule) {
552
+ const tokens = getTokens(rule);
553
+ let w = 0;
554
+ for (let i = 0; i < tokens.length; i++) {
555
+ const token = tokens[i];
556
+ if (!isString(token))
557
+ w += 10 ** (i + 1);
558
+ w += 10 ** (i + 1);
559
+ }
560
+ return w;
561
+ }
562
+ function preSort(rules) {
563
+ let matched = [];
564
+ const preMatch = [];
565
+ for (const rule of rules) {
566
+ const tokens = getTokens(rule);
567
+ const len = tokens.filter((token) => typeof token !== "string").length;
568
+ if (!preMatch[len])
569
+ preMatch[len] = [];
570
+ preMatch[len].push(rule);
571
+ }
572
+ for (const match2 of preMatch.filter((v) => v && v.length > 0))
573
+ matched = [...matched, ...sortBy2(match2, sortFn).reverse()];
574
+ return matched;
575
+ }
576
+ function defaultPriority(rules) {
577
+ const highest = getHighest(rules);
578
+ return sortBy2(rules, (rule) => {
579
+ const tokens = getTokens(rule);
580
+ const dym = tokens.filter((token) => typeof token !== "string");
581
+ if (dym.length === 0)
582
+ return 0;
583
+ let weight = dym.length;
584
+ let exp = 0;
585
+ for (let i = 0; i < tokens.length; i++) {
586
+ const token = tokens[i];
587
+ const isDynamic = !isString(token);
588
+ const {
589
+ pattern = "",
590
+ modifier,
591
+ prefix,
592
+ name
593
+ } = isDynamic ? token : {};
594
+ const isGlob = pattern && pattern.includes(".*");
595
+ const isSlash = prefix === "/";
596
+ const isNamed = isString(name);
597
+ exp += isDynamic && isSlash ? 1 : 0;
598
+ if (i === tokens.length - 1 && isGlob) {
599
+ weight += 5 * 10 ** (tokens.length === 1 ? highest + 1 : highest);
600
+ } else {
601
+ if (isGlob) {
602
+ weight += 3 * 10 ** (highest - 1);
603
+ } else if (pattern) {
604
+ if (isSlash) {
605
+ weight += (isNamed ? 2 : 1) * 10 ** (exp + 1);
606
+ } else {
607
+ weight -= 1 * 10 ** exp;
608
+ }
609
+ }
610
+ }
611
+ if (modifier === "+")
612
+ weight += 1 * 10 ** (highest - 1);
613
+ if (modifier === "*")
614
+ weight += 1 * 10 ** (highest - 1) + 1;
615
+ if (modifier === "?")
616
+ weight += 1 * 10 ** (exp + (isSlash ? 1 : 0));
617
+ }
618
+ return weight;
619
+ });
620
+ }
621
+ function matchingWeight(rules, url, priority) {
622
+ let matched = defaultPriority(
623
+ preSort(rules.filter((rule) => pathToRegexp(rule).test(url)))
624
+ );
625
+ const { global = [], special = {} } = priority;
626
+ if (global.length === 0 && isEmptyObject2(special) || matched.length === 0)
627
+ return matched;
628
+ const [statics, dynamics] = twoPartMatch(matched);
629
+ const globalMatch = global.filter((rule) => dynamics.includes(rule));
630
+ if (globalMatch.length > 0) {
631
+ matched = uniq([...statics, ...globalMatch, ...dynamics]);
632
+ }
633
+ if (isEmptyObject2(special))
634
+ return matched;
635
+ const specialRule = Object.keys(special).filter(
636
+ (rule) => matched.includes(rule)
637
+ )[0];
638
+ if (!specialRule)
639
+ return matched;
640
+ const options = special[specialRule];
641
+ const { rules: lowerRules, when } = isArray2(options) ? { rules: options, when: [] } : options;
642
+ if (lowerRules.includes(matched[0])) {
643
+ if (when.length === 0 || when.some((path5) => pathToRegexp(path5).test(url))) {
644
+ matched = uniq([specialRule, ...matched]);
645
+ }
646
+ }
647
+ return matched;
648
+ }
649
+ function twoPartMatch(rules) {
650
+ const statics = [];
651
+ const dynamics = [];
652
+ for (const rule of rules) {
653
+ const tokens = getTokens(rule);
654
+ const dym = tokens.filter((token) => typeof token !== "string");
655
+ if (dym.length > 0)
656
+ dynamics.push(rule);
657
+ else statics.push(rule);
658
+ }
659
+ return [statics, dynamics];
660
+ }
661
+
662
+ // src/core/parseReqBody.ts
663
+ import bodyParser from "co-body";
664
+ import formidable from "formidable";
665
+ async function parseReqBody(req, formidableOptions, bodyParserOptions = {}) {
666
+ const method = req.method.toUpperCase();
667
+ if (["GET", "DELETE", "HEAD"].includes(method))
668
+ return void 0;
669
+ const type = req.headers["content-type"]?.toLocaleLowerCase() || "";
670
+ const { limit, formLimit, jsonLimit, textLimit, ...rest } = bodyParserOptions;
671
+ try {
672
+ if (type.startsWith("application/json")) {
673
+ return await bodyParser.json(req, {
674
+ limit: jsonLimit || limit,
675
+ ...rest
676
+ });
677
+ }
678
+ if (type.startsWith("application/x-www-form-urlencoded")) {
679
+ return await bodyParser.form(req, {
680
+ limit: formLimit || limit,
681
+ ...rest
682
+ });
683
+ }
684
+ if (type.startsWith("text/plain")) {
685
+ return await bodyParser.text(req, {
686
+ limit: textLimit || limit,
687
+ ...rest
688
+ });
689
+ }
690
+ if (type.startsWith("multipart/form-data"))
691
+ return await parseMultipart(req, formidableOptions);
692
+ } catch (e) {
693
+ console.error(e);
694
+ }
695
+ return void 0;
696
+ }
697
+ async function parseMultipart(req, options) {
698
+ const form = formidable(options);
699
+ return new Promise((resolve, reject) => {
700
+ form.parse(req, (error, fields, files) => {
701
+ if (error) {
702
+ reject(error);
703
+ return;
704
+ }
705
+ resolve({ ...fields, ...files });
706
+ });
707
+ });
708
+ }
709
+
710
+ // src/core/baseMiddleware.ts
711
+ function baseMiddleware(compiler, {
712
+ formidableOptions = {},
713
+ bodyParserOptions = {},
714
+ proxies,
715
+ cookiesOptions,
716
+ logger,
717
+ priority = {}
718
+ }) {
719
+ return async function(req, res, next) {
720
+ const startTime = timestamp();
721
+ const { query, pathname } = urlParse(req.url);
722
+ if (!pathname || proxies.length === 0 || !proxies.some((context) => doesProxyContextMatchUrl(context, req.url, req))) {
723
+ return next();
724
+ }
725
+ const mockData = compiler.mockData;
726
+ const mockUrls = matchingWeight(Object.keys(mockData), pathname, priority);
727
+ if (mockUrls.length === 0) {
728
+ return next();
729
+ }
730
+ collectRequest(req);
731
+ const { query: refererQuery } = urlParse(req.headers.referer || "");
732
+ const reqBody = await parseReqBody(req, formidableOptions, bodyParserOptions);
733
+ const cookies = new Cookies(req, res, cookiesOptions);
734
+ const getCookie = cookies.get.bind(cookies);
735
+ const method = req.method.toUpperCase();
736
+ let mock;
737
+ let _mockUrl;
738
+ for (const mockUrl of mockUrls) {
739
+ mock = fineMock(mockData[mockUrl], logger, {
740
+ pathname,
741
+ method,
742
+ request: {
743
+ query,
744
+ refererQuery,
745
+ body: reqBody,
746
+ headers: req.headers,
747
+ getCookie
748
+ }
749
+ });
750
+ if (mock) {
751
+ _mockUrl = mockUrl;
752
+ break;
753
+ }
754
+ }
755
+ if (!mock) {
756
+ const matched = mockUrls.map(
757
+ (m) => m === _mockUrl ? colors.underline(colors.bold(m)) : colors.dim(m)
758
+ ).join(", ");
759
+ logger.warn(
760
+ `${colors.green(
761
+ pathname
762
+ )} matches ${matched} , but mock data is not found.`
763
+ );
764
+ return next();
765
+ }
766
+ const request = req;
767
+ const response = res;
768
+ request.body = reqBody;
769
+ request.query = query;
770
+ request.refererQuery = refererQuery;
771
+ request.params = parseParams(mock.url, pathname);
772
+ request.getCookie = getCookie;
773
+ response.setCookie = cookies.set.bind(cookies);
774
+ const {
775
+ body,
776
+ delay,
777
+ type = "json",
778
+ response: responseFn,
779
+ status = 200,
780
+ statusText,
781
+ log: logLevel,
782
+ __filepath__: filepath
783
+ } = mock;
784
+ responseStatus(response, status, statusText);
785
+ await provideHeaders(request, response, mock, logger);
786
+ await provideCookies(request, response, mock, logger);
787
+ logger.info(requestLog(request, filepath), logLevel);
788
+ logger.debug(
789
+ `${colors.magenta("DEBUG")} ${colors.underline(
790
+ pathname
791
+ )} matches: [ ${mockUrls.map(
792
+ (m) => m === _mockUrl ? colors.underline(colors.bold(m)) : colors.dim(m)
793
+ ).join(", ")} ]
794
+ `
795
+ );
796
+ if (body) {
797
+ try {
798
+ const content = isFunction2(body) ? await body(request) : body;
799
+ await realDelay(startTime, delay);
800
+ sendData(response, content, type);
801
+ } catch (e) {
802
+ logger.error(
803
+ `${colors.red(
804
+ `mock error at ${pathname}`
805
+ )}
806
+ ${e}
807
+ at body (${colors.underline(filepath)})`,
808
+ logLevel
809
+ );
810
+ responseStatus(response, 500);
811
+ res.end("");
812
+ }
813
+ return;
814
+ }
815
+ if (responseFn) {
816
+ try {
817
+ await realDelay(startTime, delay);
818
+ await responseFn(request, response, next);
819
+ } catch (e) {
820
+ logger.error(
821
+ `${colors.red(
822
+ `mock error at ${pathname}`
823
+ )}
824
+ ${e}
825
+ at response (${colors.underline(filepath)})`,
826
+ logLevel
827
+ );
828
+ responseStatus(response, 500);
829
+ res.end("");
830
+ }
831
+ return;
832
+ }
833
+ res.end("");
834
+ };
835
+ }
836
+ function fineMock(mockList, logger, {
837
+ pathname,
838
+ method,
839
+ request
840
+ }) {
841
+ return mockList.find((mock) => {
842
+ if (!pathname || !mock || !mock.url || mock.ws === true)
843
+ return false;
844
+ const methods = mock.method ? isArray3(mock.method) ? mock.method : [mock.method] : ["GET", "POST"];
845
+ if (!methods.includes(method))
846
+ return false;
847
+ const hasMock = pathToRegexp2(mock.url).test(pathname);
848
+ if (hasMock && mock.validator) {
849
+ const params = parseParams(mock.url, pathname);
850
+ if (isFunction2(mock.validator)) {
851
+ return mock.validator({ params, ...request });
852
+ } else {
853
+ try {
854
+ return validate({ params, ...request }, mock.validator);
855
+ } catch (e) {
856
+ const file = mock.__filepath__;
857
+ logger.error(
858
+ `${colors.red(
859
+ `mock error at ${pathname}`
860
+ )}
861
+ ${e}
862
+ at validator (${colors.underline(file)})`,
863
+ mock.log
864
+ );
865
+ return false;
866
+ }
867
+ }
868
+ }
869
+ return hasMock;
870
+ });
871
+ }
872
+ function responseStatus(response, status = 200, statusText) {
873
+ response.statusCode = status;
874
+ response.statusMessage = statusText || getHTTPStatusText(status);
875
+ }
876
+ async function provideHeaders(req, res, mock, logger) {
877
+ const { headers, type = "json" } = mock;
878
+ const filepath = mock.__filepath__;
879
+ const contentType2 = mime.contentType(type) || mime.contentType(mime.lookup(type) || "");
880
+ if (contentType2)
881
+ res.setHeader("Content-Type", contentType2);
882
+ res.setHeader("Cache-Control", "no-cache,max-age=0");
883
+ res.setHeader("X-Mock-Power-By", "vite-plugin-mock-dev-server");
884
+ if (filepath)
885
+ res.setHeader("X-File-Path", filepath);
886
+ if (!headers)
887
+ return;
888
+ try {
889
+ const raw = isFunction2(headers) ? await headers(req) : headers;
890
+ Object.keys(raw).forEach((key) => {
891
+ res.setHeader(key, raw[key]);
892
+ });
893
+ } catch (e) {
894
+ logger.error(
895
+ `${colors.red(
896
+ `mock error at ${req.url.split("?")[0]}`
897
+ )}
898
+ ${e}
899
+ at headers (${colors.underline(filepath)})`,
900
+ mock.log
901
+ );
902
+ }
903
+ }
904
+ async function provideCookies(req, res, mock, logger) {
905
+ const { cookies } = mock;
906
+ const filepath = mock.__filepath__;
907
+ if (!cookies)
908
+ return;
909
+ try {
910
+ const raw = isFunction2(cookies) ? await cookies(req) : cookies;
911
+ Object.keys(raw).forEach((key) => {
912
+ const cookie = raw[key];
913
+ if (isArray3(cookie)) {
914
+ const [value, options] = cookie;
915
+ res.setCookie(key, value, options);
916
+ } else {
917
+ res.setCookie(key, cookie);
918
+ }
919
+ });
920
+ } catch (e) {
921
+ logger.error(
922
+ `${colors.red(
923
+ `mock error at ${req.url.split("?")[0]}`
924
+ )}
925
+ ${e}
926
+ at cookies (${colors.underline(filepath)})`,
927
+ mock.log
928
+ );
929
+ }
930
+ }
931
+ function sendData(res, raw, type) {
932
+ if (isReadableStream(raw)) {
933
+ raw.pipe(res);
934
+ } else if (Buffer2.isBuffer(raw)) {
935
+ res.end(type === "text" || type === "json" ? raw.toString("utf-8") : raw);
936
+ } else {
937
+ const content = typeof raw === "string" ? raw : JSON.stringify(raw);
938
+ res.end(type === "buffer" ? Buffer2.from(content) : content);
939
+ }
940
+ }
941
+ async function realDelay(startTime, delay) {
942
+ if (!delay || typeof delay === "number" && delay <= 0 || isArray3(delay) && delay.length !== 2) {
943
+ return;
944
+ }
945
+ let realDelay2 = 0;
946
+ if (isArray3(delay)) {
947
+ const [min, max] = delay;
948
+ realDelay2 = random(min, max);
949
+ } else {
950
+ realDelay2 = delay - (timestamp() - startTime);
951
+ }
952
+ if (realDelay2 > 0)
953
+ await sleep(realDelay2);
954
+ }
955
+ function getHTTPStatusText(status) {
956
+ return HTTP_STATUS[status] || "Unknown";
957
+ }
958
+ function requestLog(request, filepath) {
959
+ const { url, method, query, params, body } = request;
960
+ let { pathname } = new URL(url, "http://example.com");
961
+ pathname = colors.green(decodeURIComponent(pathname));
962
+ const format = (prefix, data) => {
963
+ return !data || isEmptyObject3(data) ? "" : ` ${colors.gray(`${prefix}:`)}${JSON.stringify(data)}`;
964
+ };
965
+ const ms = colors.magenta(colors.bold(method));
966
+ const qs = format("query", query);
967
+ const ps = format("params", params);
968
+ const bs = format("body", body);
969
+ const file = ` ${colors.dim(colors.underline(`(${filepath})`))}`;
970
+ return `${ms} ${pathname}${qs}${ps}${bs}${file}`;
971
+ }
972
+
973
+ // src/core/logger.ts
974
+ import { isBoolean } from "@pengzhanbo/utils";
975
+ import colors2 from "picocolors";
976
+ var logLevels = {
977
+ silent: 0,
978
+ error: 1,
979
+ warn: 2,
980
+ info: 3,
981
+ debug: 4
982
+ };
983
+ function createLogger(prefix, defaultLevel = "info") {
984
+ prefix = `[${prefix}]`;
985
+ function output(type, msg, level) {
986
+ level = isBoolean(level) ? level ? defaultLevel : "error" : level;
987
+ const thresh = logLevels[level];
988
+ if (thresh >= logLevels[type]) {
989
+ const method = type === "info" || type === "debug" ? "log" : type;
990
+ const tag = type === "debug" ? colors2.magenta(colors2.bold(prefix)) : type === "info" ? colors2.cyan(colors2.bold(prefix)) : type === "warn" ? colors2.yellow(colors2.bold(prefix)) : colors2.red(colors2.bold(prefix));
991
+ const format = `${colors2.dim(
992
+ (/* @__PURE__ */ new Date()).toLocaleTimeString()
993
+ )} ${tag} ${msg}`;
994
+ console[method](format);
995
+ }
996
+ }
997
+ const logger = {
998
+ debug(msg, level = defaultLevel) {
999
+ output("debug", msg, level);
1000
+ },
1001
+ info(msg, level = defaultLevel) {
1002
+ output("info", msg, level);
1003
+ },
1004
+ warn(msg, level = defaultLevel) {
1005
+ output("warn", msg, level);
1006
+ },
1007
+ error(msg, level = defaultLevel) {
1008
+ output("error", msg, level);
1009
+ }
1010
+ };
1011
+ return logger;
1012
+ }
1013
+
1014
+ // src/core/mockMiddleware.ts
1015
+ function createManuallyMockMiddleware({ alias, proxies, context = process3.cwd(), plugins }, pluginOptions) {
1016
+ const options = resolvePluginOptions(pluginOptions, context);
1017
+ const logger = createLogger(
1018
+ "rspack:mock",
1019
+ isBoolean2(options.log) ? options.log ? "info" : "error" : options.log
1020
+ );
1021
+ const compiler = createMockCompiler({
1022
+ alias,
1023
+ plugins,
1024
+ cwd: options.cwd,
1025
+ include: toArray2(options.include),
1026
+ exclude: toArray2(options.exclude)
1027
+ });
1028
+ function mockMiddleware(middlewares) {
1029
+ middlewares.unshift(baseMiddleware(compiler, {
1030
+ formidableOptions: options.formidableOptions,
1031
+ proxies,
1032
+ cookiesOptions: options.cookiesOptions,
1033
+ bodyParserOptions: options.bodyParserOptions,
1034
+ priority: options.priority,
1035
+ logger
1036
+ }));
1037
+ const corsMiddleware = createCorsMiddleware(compiler, proxies, options);
1038
+ if (corsMiddleware) {
1039
+ middlewares.unshift(corsMiddleware);
1040
+ }
1041
+ return middlewares;
1042
+ }
1043
+ return {
1044
+ mockMiddleware,
1045
+ run: () => compiler.run(),
1046
+ close: () => compiler.close(),
1047
+ updateAlias: compiler.updateAlias.bind(compiler)
1048
+ };
1049
+ }
1050
+ function createMockMiddleware(middlewareOptions, pluginOptions) {
1051
+ const { mockMiddleware, run, close } = createManuallyMockMiddleware(
1052
+ middlewareOptions,
1053
+ pluginOptions
1054
+ );
1055
+ run();
1056
+ process3.on("exit", () => close());
1057
+ return mockMiddleware;
1058
+ }
1059
+ function createCorsMiddleware(compiler, proxies, options) {
1060
+ let corsOptions = {};
1061
+ const enabled = options.cors !== false;
1062
+ if (enabled) {
1063
+ corsOptions = {
1064
+ ...corsOptions,
1065
+ ...typeof options.cors === "boolean" ? {} : options.cors
1066
+ };
1067
+ }
1068
+ return !enabled ? void 0 : function(req, res, next) {
1069
+ const { pathname } = urlParse(req.url);
1070
+ if (!pathname || proxies.length === 0 || !proxies.some(
1071
+ (context) => doesProxyContextMatchUrl(context, req.url, req)
1072
+ )) {
1073
+ return next();
1074
+ }
1075
+ const mockData = compiler.mockData;
1076
+ const mockUrl = Object.keys(mockData).find(
1077
+ (key) => pathToRegexp3(key).test(pathname)
1078
+ );
1079
+ if (!mockUrl)
1080
+ return next();
1081
+ cors(corsOptions)(req, res, next);
1082
+ };
1083
+ }
1084
+
1085
+ export {
1086
+ rewriteRequest,
1087
+ createManuallyMockMiddleware,
1088
+ createMockMiddleware
1089
+ };