rspack-plugin-mock 0.1.0 → 0.3.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/README.md +221 -19
- package/README.zh-CN.md +888 -0
- package/dist/{chunk-G53QRHGV.cjs → chunk-I54ZNZWL.cjs} +942 -607
- package/dist/chunk-OYBMX3GQ.cjs +97 -0
- package/dist/chunk-P5FOCSCE.js +97 -0
- package/dist/{chunk-5MGZAMDI.js → chunk-YSJVV4SH.js} +1104 -769
- package/dist/helper.d.cts +2 -2
- package/dist/helper.d.ts +2 -2
- package/dist/index.cjs +17 -3
- package/dist/index.d.cts +71 -4
- package/dist/index.d.ts +71 -4
- package/dist/index.js +21 -7
- package/dist/rsbuild.cjs +172 -93
- package/dist/rsbuild.d.cts +1 -1
- package/dist/rsbuild.d.ts +1 -1
- package/dist/rsbuild.js +173 -94
- package/dist/rspack-BB-Jtq4f.d.cts +32 -0
- package/dist/rspack-h3uerEgg.d.ts +32 -0
- package/dist/rspack.cjs +3 -3
- package/dist/rspack.d.cts +2 -2
- package/dist/rspack.d.ts +2 -2
- package/dist/rspack.js +4 -4
- package/dist/{types-DhT3pRJ3.d.cts → types-C770q3L0.d.cts} +4 -1
- package/dist/{types-DhT3pRJ3.d.ts → types-C770q3L0.d.ts} +4 -1
- package/package.json +11 -9
- package/dist/chunk-4BGDHRTO.cjs +0 -68
- package/dist/chunk-B56QNVSS.js +0 -68
- package/dist/rspack-BI-Ifj4a.d.cts +0 -28
- package/dist/rspack-Db7drzDm.d.ts +0 -28
|
@@ -1,91 +1,3 @@
|
|
|
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
1
|
// src/core/utils.ts
|
|
90
2
|
import fs from "fs";
|
|
91
3
|
import path from "path";
|
|
@@ -94,6 +6,9 @@ import { URL as URL2, fileURLToPath } from "url";
|
|
|
94
6
|
import os from "os";
|
|
95
7
|
import Debug from "debug";
|
|
96
8
|
import { match } from "path-to-regexp";
|
|
9
|
+
import { Volume, createFsFromVolume } from "memfs";
|
|
10
|
+
var packageDir = getDirname(import.meta.url);
|
|
11
|
+
var vfs = createFsFromVolume(new Volume());
|
|
97
12
|
function isStream(stream) {
|
|
98
13
|
return stream !== null && typeof stream === "object" && typeof stream.pipe === "function";
|
|
99
14
|
}
|
|
@@ -103,7 +18,7 @@ function isReadableStream(stream) {
|
|
|
103
18
|
function getDirname(importMetaUrl) {
|
|
104
19
|
return path.dirname(fileURLToPath(importMetaUrl));
|
|
105
20
|
}
|
|
106
|
-
var debug = Debug("rspack:mock
|
|
21
|
+
var debug = Debug("rspack:mock");
|
|
107
22
|
function lookupFile(dir, formats, options) {
|
|
108
23
|
for (const format of formats) {
|
|
109
24
|
const fullPath = path.join(dir, format);
|
|
@@ -144,468 +59,150 @@ function slash(p) {
|
|
|
144
59
|
function normalizePath(id) {
|
|
145
60
|
return path.posix.normalize(isWindows ? slash(id) : id);
|
|
146
61
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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 {
|
|
62
|
+
function waitingFor(onSuccess, maxRetry = 5) {
|
|
63
|
+
return function wait(getter, retry = 0) {
|
|
64
|
+
const value = getter();
|
|
65
|
+
if (value) {
|
|
66
|
+
onSuccess(value);
|
|
67
|
+
} else if (retry < maxRetry) {
|
|
68
|
+
setTimeout(() => wait(getter, retry + 1), 100);
|
|
169
69
|
}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// src/core/requestRecovery.ts
|
|
74
|
+
import { Buffer } from "buffer";
|
|
75
|
+
var requestCollectCache = /* @__PURE__ */ new WeakMap();
|
|
76
|
+
function collectRequest(req) {
|
|
77
|
+
const chunks = [];
|
|
78
|
+
req.addListener("data", (chunk) => {
|
|
79
|
+
chunks.push(Buffer.from(chunk));
|
|
80
|
+
});
|
|
81
|
+
req.addListener("end", () => {
|
|
82
|
+
if (chunks.length)
|
|
83
|
+
requestCollectCache.set(req, Buffer.concat(chunks));
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
function rewriteRequest(proxyReq, req) {
|
|
87
|
+
const buffer = requestCollectCache.get(req);
|
|
88
|
+
if (buffer) {
|
|
89
|
+
requestCollectCache.delete(req);
|
|
90
|
+
if (!proxyReq.headersSent)
|
|
91
|
+
proxyReq.setHeader("Content-Length", buffer.byteLength);
|
|
92
|
+
if (!proxyReq.writableEnded)
|
|
93
|
+
proxyReq.write(buffer);
|
|
170
94
|
}
|
|
171
95
|
}
|
|
172
96
|
|
|
173
|
-
// src/core/
|
|
97
|
+
// src/core/baseMiddleware.ts
|
|
98
|
+
import { Buffer as Buffer2 } from "buffer";
|
|
174
99
|
import {
|
|
175
|
-
|
|
100
|
+
isArray as isArray3,
|
|
101
|
+
isEmptyObject as isEmptyObject2,
|
|
176
102
|
isFunction,
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
103
|
+
random,
|
|
104
|
+
sleep,
|
|
105
|
+
timestamp
|
|
180
106
|
} from "@pengzhanbo/utils";
|
|
107
|
+
import Cookies from "cookies";
|
|
108
|
+
import HTTP_STATUS from "http-status";
|
|
109
|
+
import * as mime from "mime-types";
|
|
110
|
+
import { pathToRegexp as pathToRegexp2 } from "path-to-regexp";
|
|
111
|
+
import colors from "picocolors";
|
|
181
112
|
|
|
182
|
-
// src/core/
|
|
183
|
-
import {
|
|
184
|
-
|
|
185
|
-
|
|
113
|
+
// src/core/matchingWeight.ts
|
|
114
|
+
import {
|
|
115
|
+
isArray,
|
|
116
|
+
isEmptyObject,
|
|
117
|
+
isString,
|
|
118
|
+
sortBy,
|
|
119
|
+
uniq
|
|
120
|
+
} from "@pengzhanbo/utils";
|
|
121
|
+
import { parse, pathToRegexp } from "path-to-regexp";
|
|
122
|
+
var tokensCache = {};
|
|
123
|
+
function getTokens(rule) {
|
|
124
|
+
if (tokensCache[rule])
|
|
125
|
+
return tokensCache[rule];
|
|
126
|
+
const { tokens: tks } = parse(rule);
|
|
127
|
+
const tokens = [];
|
|
128
|
+
for (const tk of tks) {
|
|
129
|
+
if (!isString(tk)) {
|
|
130
|
+
tokens.push(tk);
|
|
131
|
+
} else {
|
|
132
|
+
const hasPrefix = tk[0] === "/";
|
|
133
|
+
const subTks = hasPrefix ? tk.slice(1).split("/") : tk.split("/");
|
|
134
|
+
tokens.push(
|
|
135
|
+
`${hasPrefix ? "/" : ""}${subTks[0]}`,
|
|
136
|
+
...subTks.slice(1).map((t) => `/${t}`)
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
tokensCache[rule] = tokens;
|
|
141
|
+
return tokens;
|
|
186
142
|
}
|
|
187
|
-
function
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
143
|
+
function getHighest(rules) {
|
|
144
|
+
let weights = rules.map((rule) => getTokens(rule).length);
|
|
145
|
+
weights = weights.length === 0 ? [1] : weights;
|
|
146
|
+
return Math.max(...weights) + 2;
|
|
147
|
+
}
|
|
148
|
+
function sortFn(rule) {
|
|
149
|
+
const tokens = getTokens(rule);
|
|
150
|
+
let w = 0;
|
|
151
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
152
|
+
const token = tokens[i];
|
|
153
|
+
if (!isString(token))
|
|
154
|
+
w += 10 ** (i + 1);
|
|
155
|
+
w += 10 ** (i + 1);
|
|
193
156
|
}
|
|
194
|
-
return
|
|
157
|
+
return w;
|
|
195
158
|
}
|
|
196
|
-
function
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
seen.add(i);
|
|
206
|
-
return included;
|
|
207
|
-
})
|
|
208
|
-
);
|
|
159
|
+
function preSort(rules) {
|
|
160
|
+
let matched = [];
|
|
161
|
+
const preMatch = [];
|
|
162
|
+
for (const rule of rules) {
|
|
163
|
+
const tokens = getTokens(rule);
|
|
164
|
+
const len = tokens.filter((token) => typeof token !== "string").length;
|
|
165
|
+
if (!preMatch[len])
|
|
166
|
+
preMatch[len] = [];
|
|
167
|
+
preMatch[len].push(rule);
|
|
209
168
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
return
|
|
169
|
+
for (const match2 of preMatch.filter((v) => v && v.length > 0))
|
|
170
|
+
matched = [...matched, ...sortBy(match2, sortFn).reverse()];
|
|
171
|
+
return matched;
|
|
213
172
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
if (
|
|
220
|
-
|
|
221
|
-
|
|
173
|
+
function defaultPriority(rules) {
|
|
174
|
+
const highest = getHighest(rules);
|
|
175
|
+
return sortBy(rules, (rule) => {
|
|
176
|
+
const tokens = getTokens(rule);
|
|
177
|
+
const dym = tokens.filter((token) => typeof token !== "string");
|
|
178
|
+
if (dym.length === 0)
|
|
179
|
+
return 0;
|
|
180
|
+
let weight = dym.length;
|
|
181
|
+
let exp = 0;
|
|
182
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
183
|
+
const token = tokens[i];
|
|
184
|
+
const isDynamic = !isString(token);
|
|
185
|
+
const {
|
|
186
|
+
pattern = "",
|
|
187
|
+
modifier,
|
|
188
|
+
prefix,
|
|
189
|
+
name
|
|
190
|
+
} = isDynamic ? token : {};
|
|
191
|
+
const isGlob = pattern && pattern.includes(".*");
|
|
192
|
+
const isSlash = prefix === "/";
|
|
193
|
+
const isNamed = isString(name);
|
|
194
|
+
exp += isDynamic && isSlash ? 1 : 0;
|
|
195
|
+
if (i === tokens.length - 1 && isGlob) {
|
|
196
|
+
weight += 5 * 10 ** (tokens.length === 1 ? highest + 1 : highest);
|
|
222
197
|
} else {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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
|
-
}
|
|
198
|
+
if (isGlob) {
|
|
199
|
+
weight += 3 * 10 ** (highest - 1);
|
|
200
|
+
} else if (pattern) {
|
|
201
|
+
if (isSlash) {
|
|
202
|
+
weight += (isNamed ? 2 : 1) * 10 ** (exp + 1);
|
|
203
|
+
} else {
|
|
204
|
+
weight -= 1 * 10 ** exp;
|
|
205
|
+
}
|
|
609
206
|
}
|
|
610
207
|
}
|
|
611
208
|
if (modifier === "+")
|
|
@@ -623,14 +220,14 @@ function matchingWeight(rules, url, priority) {
|
|
|
623
220
|
preSort(rules.filter((rule) => pathToRegexp(rule).test(url)))
|
|
624
221
|
);
|
|
625
222
|
const { global = [], special = {} } = priority;
|
|
626
|
-
if (global.length === 0 &&
|
|
223
|
+
if (global.length === 0 && isEmptyObject(special) || matched.length === 0)
|
|
627
224
|
return matched;
|
|
628
225
|
const [statics, dynamics] = twoPartMatch(matched);
|
|
629
226
|
const globalMatch = global.filter((rule) => dynamics.includes(rule));
|
|
630
227
|
if (globalMatch.length > 0) {
|
|
631
228
|
matched = uniq([...statics, ...globalMatch, ...dynamics]);
|
|
632
229
|
}
|
|
633
|
-
if (
|
|
230
|
+
if (isEmptyObject(special))
|
|
634
231
|
return matched;
|
|
635
232
|
const specialRule = Object.keys(special).filter(
|
|
636
233
|
(rule) => matched.includes(rule)
|
|
@@ -638,9 +235,9 @@ function matchingWeight(rules, url, priority) {
|
|
|
638
235
|
if (!specialRule)
|
|
639
236
|
return matched;
|
|
640
237
|
const options = special[specialRule];
|
|
641
|
-
const { rules: lowerRules, when } =
|
|
238
|
+
const { rules: lowerRules, when } = isArray(options) ? { rules: options, when: [] } : options;
|
|
642
239
|
if (lowerRules.includes(matched[0])) {
|
|
643
|
-
if (when.length === 0 || when.some((
|
|
240
|
+
if (when.length === 0 || when.some((path6) => pathToRegexp(path6).test(url))) {
|
|
644
241
|
matched = uniq([specialRule, ...matched]);
|
|
645
242
|
}
|
|
646
243
|
}
|
|
@@ -692,19 +289,52 @@ async function parseReqBody(req, formidableOptions, bodyParserOptions = {}) {
|
|
|
692
289
|
} catch (e) {
|
|
693
290
|
console.error(e);
|
|
694
291
|
}
|
|
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
|
-
});
|
|
292
|
+
return void 0;
|
|
293
|
+
}
|
|
294
|
+
async function parseMultipart(req, options) {
|
|
295
|
+
const form = formidable(options);
|
|
296
|
+
return new Promise((resolve, reject) => {
|
|
297
|
+
form.parse(req, (error, fields, files) => {
|
|
298
|
+
if (error) {
|
|
299
|
+
reject(error);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
resolve({ ...fields, ...files });
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// src/core/validator.ts
|
|
308
|
+
import { isArray as isArray2, isObject } from "@pengzhanbo/utils";
|
|
309
|
+
function validate(request, validator) {
|
|
310
|
+
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);
|
|
311
|
+
}
|
|
312
|
+
function isObjectSubset(source, target) {
|
|
313
|
+
if (!target)
|
|
314
|
+
return true;
|
|
315
|
+
for (const key in target) {
|
|
316
|
+
if (!isIncluded(source[key], target[key]))
|
|
317
|
+
return false;
|
|
318
|
+
}
|
|
319
|
+
return true;
|
|
320
|
+
}
|
|
321
|
+
function isIncluded(source, target) {
|
|
322
|
+
if (isArray2(source) && isArray2(target)) {
|
|
323
|
+
const seen = /* @__PURE__ */ new Set();
|
|
324
|
+
return target.every(
|
|
325
|
+
(ti) => source.some((si, i) => {
|
|
326
|
+
if (seen.has(i))
|
|
327
|
+
return false;
|
|
328
|
+
const included = isIncluded(si, ti);
|
|
329
|
+
if (included)
|
|
330
|
+
seen.add(i);
|
|
331
|
+
return included;
|
|
332
|
+
})
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
if (isObject(source) && isObject(target))
|
|
336
|
+
return isObjectSubset(source, target);
|
|
337
|
+
return Object.is(source, target);
|
|
708
338
|
}
|
|
709
339
|
|
|
710
340
|
// src/core/baseMiddleware.ts
|
|
@@ -795,7 +425,7 @@ function baseMiddleware(compiler, {
|
|
|
795
425
|
);
|
|
796
426
|
if (body) {
|
|
797
427
|
try {
|
|
798
|
-
const content =
|
|
428
|
+
const content = isFunction(body) ? await body(request) : body;
|
|
799
429
|
await realDelay(startTime, delay);
|
|
800
430
|
sendData(response, content, type);
|
|
801
431
|
} catch (e) {
|
|
@@ -833,257 +463,962 @@ ${e}
|
|
|
833
463
|
res.end("");
|
|
834
464
|
};
|
|
835
465
|
}
|
|
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 (
|
|
851
|
-
return mock.validator({ params, ...request });
|
|
466
|
+
function fineMock(mockList, logger, {
|
|
467
|
+
pathname,
|
|
468
|
+
method,
|
|
469
|
+
request
|
|
470
|
+
}) {
|
|
471
|
+
return mockList.find((mock) => {
|
|
472
|
+
if (!pathname || !mock || !mock.url || mock.ws === true)
|
|
473
|
+
return false;
|
|
474
|
+
const methods = mock.method ? isArray3(mock.method) ? mock.method : [mock.method] : ["GET", "POST"];
|
|
475
|
+
if (!methods.includes(method))
|
|
476
|
+
return false;
|
|
477
|
+
const hasMock = pathToRegexp2(mock.url).test(pathname);
|
|
478
|
+
if (hasMock && mock.validator) {
|
|
479
|
+
const params = parseParams(mock.url, pathname);
|
|
480
|
+
if (isFunction(mock.validator)) {
|
|
481
|
+
return mock.validator({ params, ...request });
|
|
482
|
+
} else {
|
|
483
|
+
try {
|
|
484
|
+
return validate({ params, ...request }, mock.validator);
|
|
485
|
+
} catch (e) {
|
|
486
|
+
const file = mock.__filepath__;
|
|
487
|
+
logger.error(
|
|
488
|
+
`${colors.red(
|
|
489
|
+
`mock error at ${pathname}`
|
|
490
|
+
)}
|
|
491
|
+
${e}
|
|
492
|
+
at validator (${colors.underline(file)})`,
|
|
493
|
+
mock.log
|
|
494
|
+
);
|
|
495
|
+
return false;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
return hasMock;
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
function responseStatus(response, status = 200, statusText) {
|
|
503
|
+
response.statusCode = status;
|
|
504
|
+
response.statusMessage = statusText || getHTTPStatusText(status);
|
|
505
|
+
}
|
|
506
|
+
async function provideHeaders(req, res, mock, logger) {
|
|
507
|
+
const { headers, type = "json" } = mock;
|
|
508
|
+
const filepath = mock.__filepath__;
|
|
509
|
+
const contentType2 = mime.contentType(type) || mime.contentType(mime.lookup(type) || "");
|
|
510
|
+
if (contentType2)
|
|
511
|
+
res.setHeader("Content-Type", contentType2);
|
|
512
|
+
res.setHeader("Cache-Control", "no-cache,max-age=0");
|
|
513
|
+
res.setHeader("X-Mock-Power-By", "vite-plugin-mock-dev-server");
|
|
514
|
+
if (filepath)
|
|
515
|
+
res.setHeader("X-File-Path", filepath);
|
|
516
|
+
if (!headers)
|
|
517
|
+
return;
|
|
518
|
+
try {
|
|
519
|
+
const raw = isFunction(headers) ? await headers(req) : headers;
|
|
520
|
+
Object.keys(raw).forEach((key) => {
|
|
521
|
+
res.setHeader(key, raw[key]);
|
|
522
|
+
});
|
|
523
|
+
} catch (e) {
|
|
524
|
+
logger.error(
|
|
525
|
+
`${colors.red(
|
|
526
|
+
`mock error at ${req.url.split("?")[0]}`
|
|
527
|
+
)}
|
|
528
|
+
${e}
|
|
529
|
+
at headers (${colors.underline(filepath)})`,
|
|
530
|
+
mock.log
|
|
531
|
+
);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
async function provideCookies(req, res, mock, logger) {
|
|
535
|
+
const { cookies } = mock;
|
|
536
|
+
const filepath = mock.__filepath__;
|
|
537
|
+
if (!cookies)
|
|
538
|
+
return;
|
|
539
|
+
try {
|
|
540
|
+
const raw = isFunction(cookies) ? await cookies(req) : cookies;
|
|
541
|
+
Object.keys(raw).forEach((key) => {
|
|
542
|
+
const cookie = raw[key];
|
|
543
|
+
if (isArray3(cookie)) {
|
|
544
|
+
const [value, options] = cookie;
|
|
545
|
+
res.setCookie(key, value, options);
|
|
546
|
+
} else {
|
|
547
|
+
res.setCookie(key, cookie);
|
|
548
|
+
}
|
|
549
|
+
});
|
|
550
|
+
} catch (e) {
|
|
551
|
+
logger.error(
|
|
552
|
+
`${colors.red(
|
|
553
|
+
`mock error at ${req.url.split("?")[0]}`
|
|
554
|
+
)}
|
|
555
|
+
${e}
|
|
556
|
+
at cookies (${colors.underline(filepath)})`,
|
|
557
|
+
mock.log
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
function sendData(res, raw, type) {
|
|
562
|
+
if (isReadableStream(raw)) {
|
|
563
|
+
raw.pipe(res);
|
|
564
|
+
} else if (Buffer2.isBuffer(raw)) {
|
|
565
|
+
res.end(type === "text" || type === "json" ? raw.toString("utf-8") : raw);
|
|
566
|
+
} else {
|
|
567
|
+
const content = typeof raw === "string" ? raw : JSON.stringify(raw);
|
|
568
|
+
res.end(type === "buffer" ? Buffer2.from(content) : content);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
async function realDelay(startTime, delay) {
|
|
572
|
+
if (!delay || typeof delay === "number" && delay <= 0 || isArray3(delay) && delay.length !== 2) {
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
let realDelay2 = 0;
|
|
576
|
+
if (isArray3(delay)) {
|
|
577
|
+
const [min, max] = delay;
|
|
578
|
+
realDelay2 = random(min, max);
|
|
579
|
+
} else {
|
|
580
|
+
realDelay2 = delay - (timestamp() - startTime);
|
|
581
|
+
}
|
|
582
|
+
if (realDelay2 > 0)
|
|
583
|
+
await sleep(realDelay2);
|
|
584
|
+
}
|
|
585
|
+
function getHTTPStatusText(status) {
|
|
586
|
+
return HTTP_STATUS[status] || "Unknown";
|
|
587
|
+
}
|
|
588
|
+
function requestLog(request, filepath) {
|
|
589
|
+
const { url, method, query, params, body } = request;
|
|
590
|
+
let { pathname } = new URL(url, "http://example.com");
|
|
591
|
+
pathname = colors.green(decodeURIComponent(pathname));
|
|
592
|
+
const format = (prefix, data) => {
|
|
593
|
+
return !data || isEmptyObject2(data) ? "" : ` ${colors.gray(`${prefix}:`)}${JSON.stringify(data)}`;
|
|
594
|
+
};
|
|
595
|
+
const ms = colors.magenta(colors.bold(method));
|
|
596
|
+
const qs = format("query", query);
|
|
597
|
+
const ps = format("params", params);
|
|
598
|
+
const bs = format("body", body);
|
|
599
|
+
const file = ` ${colors.dim(colors.underline(`(${filepath})`))}`;
|
|
600
|
+
return `${ms} ${pathname}${qs}${ps}${bs}${file}`;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// src/core/mockMiddleware.ts
|
|
604
|
+
import cors from "cors";
|
|
605
|
+
import { pathToRegexp as pathToRegexp3 } from "path-to-regexp";
|
|
606
|
+
function createMockMiddleware(compiler, options) {
|
|
607
|
+
function mockMiddleware(middlewares, reload) {
|
|
608
|
+
middlewares.unshift(baseMiddleware(compiler, options));
|
|
609
|
+
const corsMiddleware = createCorsMiddleware(compiler, options);
|
|
610
|
+
if (corsMiddleware) {
|
|
611
|
+
middlewares.unshift(corsMiddleware);
|
|
612
|
+
}
|
|
613
|
+
if (options.reload) {
|
|
614
|
+
compiler.on("update", () => reload?.());
|
|
615
|
+
}
|
|
616
|
+
return middlewares;
|
|
617
|
+
}
|
|
618
|
+
return mockMiddleware;
|
|
619
|
+
}
|
|
620
|
+
function createCorsMiddleware(compiler, options) {
|
|
621
|
+
let corsOptions = {};
|
|
622
|
+
const enabled = options.cors !== false;
|
|
623
|
+
if (enabled) {
|
|
624
|
+
corsOptions = {
|
|
625
|
+
...corsOptions,
|
|
626
|
+
...typeof options.cors === "boolean" ? {} : options.cors
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
const proxies = options.proxies;
|
|
630
|
+
return !enabled ? void 0 : function(req, res, next) {
|
|
631
|
+
const { pathname } = urlParse(req.url);
|
|
632
|
+
if (!pathname || proxies.length === 0 || !proxies.some(
|
|
633
|
+
(context) => doesProxyContextMatchUrl(context, req.url, req)
|
|
634
|
+
)) {
|
|
635
|
+
return next();
|
|
636
|
+
}
|
|
637
|
+
const mockData = compiler.mockData;
|
|
638
|
+
const mockUrl = Object.keys(mockData).find(
|
|
639
|
+
(key) => pathToRegexp3(key).test(pathname)
|
|
640
|
+
);
|
|
641
|
+
if (!mockUrl)
|
|
642
|
+
return next();
|
|
643
|
+
cors(corsOptions)(req, res, next);
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// src/core/logger.ts
|
|
648
|
+
import { isBoolean } from "@pengzhanbo/utils";
|
|
649
|
+
import colors2 from "picocolors";
|
|
650
|
+
var logLevels = {
|
|
651
|
+
silent: 0,
|
|
652
|
+
error: 1,
|
|
653
|
+
warn: 2,
|
|
654
|
+
info: 3,
|
|
655
|
+
debug: 4
|
|
656
|
+
};
|
|
657
|
+
function createLogger(prefix, defaultLevel = "info") {
|
|
658
|
+
prefix = `[${prefix}]`;
|
|
659
|
+
function output(type, msg, level) {
|
|
660
|
+
level = isBoolean(level) ? level ? defaultLevel : "error" : level;
|
|
661
|
+
const thresh = logLevels[level];
|
|
662
|
+
if (thresh >= logLevels[type]) {
|
|
663
|
+
const method = type === "info" || type === "debug" ? "log" : type;
|
|
664
|
+
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));
|
|
665
|
+
const format = `${colors2.dim(
|
|
666
|
+
(/* @__PURE__ */ new Date()).toLocaleTimeString()
|
|
667
|
+
)} ${tag} ${msg}`;
|
|
668
|
+
console[method](format);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
const logger = {
|
|
672
|
+
debug(msg, level = defaultLevel) {
|
|
673
|
+
output("debug", msg, level);
|
|
674
|
+
},
|
|
675
|
+
info(msg, level = defaultLevel) {
|
|
676
|
+
output("info", msg, level);
|
|
677
|
+
},
|
|
678
|
+
warn(msg, level = defaultLevel) {
|
|
679
|
+
output("warn", msg, level);
|
|
680
|
+
},
|
|
681
|
+
error(msg, level = defaultLevel) {
|
|
682
|
+
output("error", msg, level);
|
|
683
|
+
}
|
|
684
|
+
};
|
|
685
|
+
return logger;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// src/core/resolvePluginOptions.ts
|
|
689
|
+
import process from "process";
|
|
690
|
+
import { isBoolean as isBoolean2, toArray } from "@pengzhanbo/utils";
|
|
691
|
+
function resolvePluginOptions({
|
|
692
|
+
prefix = [],
|
|
693
|
+
wsPrefix = [],
|
|
694
|
+
cwd,
|
|
695
|
+
include = ["mock/**/*.mock.{js,ts,cjs,mjs,json,json5}"],
|
|
696
|
+
exclude = ["**/node_modules/**", "**/.vscode/**", "**/.git/**"],
|
|
697
|
+
reload = false,
|
|
698
|
+
log = "info",
|
|
699
|
+
cors: cors2 = true,
|
|
700
|
+
formidableOptions = {},
|
|
701
|
+
build = false,
|
|
702
|
+
cookiesOptions = {},
|
|
703
|
+
bodyParserOptions = {},
|
|
704
|
+
priority = {}
|
|
705
|
+
} = {}, { alias, context, plugins, proxies }) {
|
|
706
|
+
const logger = createLogger(
|
|
707
|
+
"rspack:mock",
|
|
708
|
+
isBoolean2(log) ? log ? "info" : "error" : log
|
|
709
|
+
);
|
|
710
|
+
return {
|
|
711
|
+
prefix,
|
|
712
|
+
wsPrefix,
|
|
713
|
+
cwd: cwd || context || process.cwd(),
|
|
714
|
+
include,
|
|
715
|
+
exclude,
|
|
716
|
+
reload,
|
|
717
|
+
cors: cors2,
|
|
718
|
+
cookiesOptions,
|
|
719
|
+
log,
|
|
720
|
+
formidableOptions: {
|
|
721
|
+
multiples: true,
|
|
722
|
+
...formidableOptions
|
|
723
|
+
},
|
|
724
|
+
bodyParserOptions,
|
|
725
|
+
priority,
|
|
726
|
+
build: build ? Object.assign(
|
|
727
|
+
{
|
|
728
|
+
serverPort: 8080,
|
|
729
|
+
dist: "mockServer",
|
|
730
|
+
log: "error"
|
|
731
|
+
},
|
|
732
|
+
typeof build === "object" ? build : {}
|
|
733
|
+
) : false,
|
|
734
|
+
alias,
|
|
735
|
+
plugins,
|
|
736
|
+
proxies,
|
|
737
|
+
wsProxies: toArray(wsPrefix),
|
|
738
|
+
logger
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// src/core/transform.ts
|
|
743
|
+
import {
|
|
744
|
+
isEmptyObject as isEmptyObject3,
|
|
745
|
+
isFunction as isFunction2,
|
|
746
|
+
isObject as isObject2,
|
|
747
|
+
sortBy as sortBy2,
|
|
748
|
+
toArray as toArray2
|
|
749
|
+
} from "@pengzhanbo/utils";
|
|
750
|
+
function transformRawData(rawData) {
|
|
751
|
+
return rawData.filter((item) => item[0]).map(([raw, __filepath__]) => {
|
|
752
|
+
let mockConfig;
|
|
753
|
+
if (raw.default) {
|
|
754
|
+
if (Array.isArray(raw.default)) {
|
|
755
|
+
mockConfig = raw.default.map((item) => ({ ...item, __filepath__ }));
|
|
852
756
|
} else {
|
|
853
|
-
|
|
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
|
-
}
|
|
757
|
+
mockConfig = { ...raw.default, __filepath__ };
|
|
867
758
|
}
|
|
759
|
+
} else if ("url" in raw) {
|
|
760
|
+
mockConfig = { ...raw, __filepath__ };
|
|
761
|
+
} else {
|
|
762
|
+
mockConfig = [];
|
|
763
|
+
Object.keys(raw || {}).forEach((key) => {
|
|
764
|
+
if (Array.isArray(raw[key])) {
|
|
765
|
+
mockConfig.push(...raw[key].map((item) => ({ ...item, __filepath__ })));
|
|
766
|
+
} else {
|
|
767
|
+
mockConfig.push({ ...raw[key], __filepath__ });
|
|
768
|
+
}
|
|
769
|
+
});
|
|
868
770
|
}
|
|
869
|
-
return
|
|
771
|
+
return mockConfig;
|
|
870
772
|
});
|
|
871
773
|
}
|
|
872
|
-
function
|
|
873
|
-
|
|
874
|
-
|
|
774
|
+
function transformMockData(mockList) {
|
|
775
|
+
const list = [];
|
|
776
|
+
for (const [, handle] of mockList.entries()) {
|
|
777
|
+
if (handle)
|
|
778
|
+
list.push(...toArray2(handle));
|
|
779
|
+
}
|
|
780
|
+
const mocks = {};
|
|
781
|
+
list.filter((mock) => isObject2(mock) && mock.enabled !== false && mock.url).forEach((mock) => {
|
|
782
|
+
const { pathname, query } = urlParse(mock.url);
|
|
783
|
+
const list2 = mocks[pathname] ??= [];
|
|
784
|
+
const current = { ...mock, url: pathname };
|
|
785
|
+
if (current.ws !== true) {
|
|
786
|
+
const validator = current.validator;
|
|
787
|
+
if (!isEmptyObject3(query)) {
|
|
788
|
+
if (isFunction2(validator)) {
|
|
789
|
+
current.validator = function(request) {
|
|
790
|
+
return isObjectSubset(request.query, query) && validator(request);
|
|
791
|
+
};
|
|
792
|
+
} else if (validator) {
|
|
793
|
+
current.validator = { ...validator };
|
|
794
|
+
current.validator.query = current.validator.query ? { ...query, ...current.validator.query } : query;
|
|
795
|
+
} else {
|
|
796
|
+
current.validator = { query };
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
list2.push(current);
|
|
801
|
+
});
|
|
802
|
+
Object.keys(mocks).forEach((key) => {
|
|
803
|
+
mocks[key] = sortByValidator(mocks[key]);
|
|
804
|
+
});
|
|
805
|
+
return mocks;
|
|
875
806
|
}
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
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
|
|
807
|
+
function sortByValidator(mocks) {
|
|
808
|
+
return sortBy2(mocks, (item) => {
|
|
809
|
+
if (item.ws === true)
|
|
810
|
+
return 0;
|
|
811
|
+
const { validator } = item;
|
|
812
|
+
if (!validator || isEmptyObject3(validator))
|
|
813
|
+
return 2;
|
|
814
|
+
if (isFunction2(validator))
|
|
815
|
+
return 0;
|
|
816
|
+
const count = Object.keys(validator).reduce(
|
|
817
|
+
(prev, key) => prev + keysCount(validator[key]),
|
|
818
|
+
0
|
|
901
819
|
);
|
|
902
|
-
|
|
820
|
+
return 1 / count;
|
|
821
|
+
});
|
|
903
822
|
}
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
823
|
+
function keysCount(obj) {
|
|
824
|
+
if (!obj)
|
|
825
|
+
return 0;
|
|
826
|
+
return Object.keys(obj).length;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
// src/core/build.ts
|
|
830
|
+
import fs2 from "fs";
|
|
831
|
+
import fsp from "fs/promises";
|
|
832
|
+
import path3 from "path";
|
|
833
|
+
import process2 from "process";
|
|
834
|
+
import fg from "fast-glob";
|
|
835
|
+
import { createFilter } from "@rollup/pluginutils";
|
|
836
|
+
import color2 from "picocolors";
|
|
837
|
+
import { toArray as toArray3 } from "@pengzhanbo/utils";
|
|
838
|
+
|
|
839
|
+
// src/core/createRspackCompiler.ts
|
|
840
|
+
import path2 from "path";
|
|
841
|
+
import * as rspackCore from "@rspack/core";
|
|
842
|
+
import color from "picocolors";
|
|
843
|
+
import isCore from "is-core-module";
|
|
844
|
+
function createCompiler(options, callback) {
|
|
845
|
+
const rspackOptions = resolveRspackOptions(options);
|
|
846
|
+
const isWatch = rspackOptions.watch === true;
|
|
847
|
+
async function handler(err, stats) {
|
|
848
|
+
const name = "[rspack:mock]";
|
|
849
|
+
const logError = stats?.compilation.getLogger(name).error || ((...args) => console.error(color.red(name), ...args));
|
|
850
|
+
if (err) {
|
|
851
|
+
logError(err.stack || err);
|
|
852
|
+
if ("details" in err) {
|
|
853
|
+
logError(err.details);
|
|
854
|
+
}
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
if (stats?.hasErrors()) {
|
|
858
|
+
const info = stats.toJson();
|
|
859
|
+
logError(info.errors);
|
|
860
|
+
}
|
|
861
|
+
const code = vfs.readFileSync("/output.js", "utf-8");
|
|
862
|
+
const externals = [];
|
|
863
|
+
if (!isWatch) {
|
|
864
|
+
const modules = stats?.toJson().modules || [];
|
|
865
|
+
const aliasList = Object.keys(options.alias || {}).map((key) => key.replace(/\$$/g, ""));
|
|
866
|
+
for (const { name: name2 } of modules) {
|
|
867
|
+
if (name2?.startsWith("external")) {
|
|
868
|
+
const packageName = normalizePackageName(name2);
|
|
869
|
+
if (!isCore(packageName) && !aliasList.includes(packageName))
|
|
870
|
+
externals.push(normalizePackageName(name2));
|
|
871
|
+
}
|
|
918
872
|
}
|
|
873
|
+
}
|
|
874
|
+
await callback({ code, externals });
|
|
875
|
+
}
|
|
876
|
+
const compiler = rspackCore.rspack(rspackOptions, isWatch ? handler : void 0);
|
|
877
|
+
if (compiler)
|
|
878
|
+
compiler.outputFileSystem = vfs;
|
|
879
|
+
if (!isWatch) {
|
|
880
|
+
compiler?.run(async (...args) => {
|
|
881
|
+
await handler(...args);
|
|
882
|
+
compiler.close(() => {
|
|
883
|
+
});
|
|
919
884
|
});
|
|
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
885
|
}
|
|
886
|
+
return compiler;
|
|
930
887
|
}
|
|
931
|
-
function
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
}
|
|
937
|
-
const content = typeof raw === "string" ? raw : JSON.stringify(raw);
|
|
938
|
-
res.end(type === "buffer" ? Buffer2.from(content) : content);
|
|
939
|
-
}
|
|
888
|
+
function transformWithRspack(options) {
|
|
889
|
+
return new Promise((resolve) => {
|
|
890
|
+
createCompiler({ ...options, watch: false }, (result) => {
|
|
891
|
+
resolve(result);
|
|
892
|
+
});
|
|
893
|
+
});
|
|
940
894
|
}
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
if (isArray3(delay)) {
|
|
947
|
-
const [min, max] = delay;
|
|
948
|
-
realDelay2 = random(min, max);
|
|
949
|
-
} else {
|
|
950
|
-
realDelay2 = delay - (timestamp() - startTime);
|
|
895
|
+
function normalizePackageName(name) {
|
|
896
|
+
const filepath = name.replace("external ", "").slice(1, -1);
|
|
897
|
+
const [scope, packageName] = filepath.split("/");
|
|
898
|
+
if (filepath[0] === "@") {
|
|
899
|
+
return `${scope}/${packageName}`;
|
|
951
900
|
}
|
|
952
|
-
|
|
953
|
-
await sleep(realDelay2);
|
|
901
|
+
return scope;
|
|
954
902
|
}
|
|
955
|
-
function
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
903
|
+
function resolveRspackOptions({
|
|
904
|
+
cwd,
|
|
905
|
+
isEsm = true,
|
|
906
|
+
entryFile,
|
|
907
|
+
plugins,
|
|
908
|
+
alias,
|
|
909
|
+
watch = false
|
|
910
|
+
}) {
|
|
911
|
+
const targets = ["node >= 18.0.0"];
|
|
912
|
+
return {
|
|
913
|
+
mode: "production",
|
|
914
|
+
context: cwd,
|
|
915
|
+
entry: entryFile,
|
|
916
|
+
watch,
|
|
917
|
+
target: "node18.0",
|
|
918
|
+
externalsType: isEsm ? "module" : "commonjs2",
|
|
919
|
+
externals: /^[^./].*/,
|
|
920
|
+
resolve: {
|
|
921
|
+
alias,
|
|
922
|
+
extensions: [".js", ".ts", ".cjs", ".mjs", ".json5", ".json"]
|
|
923
|
+
},
|
|
924
|
+
plugins,
|
|
925
|
+
output: {
|
|
926
|
+
library: { type: !isEsm ? "commonjs2" : "module" },
|
|
927
|
+
filename: "output.js",
|
|
928
|
+
path: "/"
|
|
929
|
+
},
|
|
930
|
+
experiments: { outputModule: isEsm },
|
|
931
|
+
optimization: { minimize: !watch },
|
|
932
|
+
module: {
|
|
933
|
+
rules: [
|
|
934
|
+
{
|
|
935
|
+
test: /\.json5?$/,
|
|
936
|
+
loader: path2.join(packageDir, "json5-loader.cjs"),
|
|
937
|
+
type: "javascript/auto"
|
|
938
|
+
},
|
|
939
|
+
{
|
|
940
|
+
test: /\.[cm]?js$/,
|
|
941
|
+
use: [
|
|
942
|
+
{
|
|
943
|
+
loader: "builtin:swc-loader",
|
|
944
|
+
options: {
|
|
945
|
+
jsc: { parser: { syntax: "ecmascript" } },
|
|
946
|
+
env: { targets }
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
]
|
|
950
|
+
},
|
|
951
|
+
{
|
|
952
|
+
test: /\.[cm]?ts$/,
|
|
953
|
+
use: [
|
|
954
|
+
{
|
|
955
|
+
loader: "builtin:swc-loader",
|
|
956
|
+
options: {
|
|
957
|
+
jsc: { parser: { syntax: "typescript" } },
|
|
958
|
+
env: { targets }
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
]
|
|
962
|
+
}
|
|
963
|
+
]
|
|
964
|
+
}
|
|
964
965
|
};
|
|
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
966
|
}
|
|
972
967
|
|
|
973
|
-
// src/core/
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
}
|
|
968
|
+
// src/core/build.ts
|
|
969
|
+
async function buildMockServer(options, outputDir) {
|
|
970
|
+
const entryFile = path3.resolve(process2.cwd(), "node_modules/.cache/mock-server/mock-server.ts");
|
|
971
|
+
const mockFileList = await getMockFileList(options);
|
|
972
|
+
await writeMockEntryFile(entryFile, mockFileList, options.cwd);
|
|
973
|
+
const { code, externals } = await transformWithRspack({
|
|
974
|
+
entryFile,
|
|
975
|
+
cwd: options.cwd,
|
|
976
|
+
plugins: options.plugins,
|
|
977
|
+
alias: options.alias
|
|
978
|
+
});
|
|
979
|
+
await fsp.unlink(entryFile);
|
|
980
|
+
const outputList = [
|
|
981
|
+
{ filename: "mock-data.js", source: code },
|
|
982
|
+
{ filename: "index.js", source: generatorServerEntryCode(options) },
|
|
983
|
+
{ filename: "package.json", source: generatePackageJson(options, externals) }
|
|
984
|
+
];
|
|
985
|
+
const dist = path3.resolve(outputDir, options.build.dist);
|
|
986
|
+
options.logger.info(
|
|
987
|
+
`${color2.green("\u2713")} generate mock server in ${color2.cyan(path3.relative(process2.cwd(), dist))}`
|
|
988
|
+
);
|
|
989
|
+
if (!fs2.existsSync(dist)) {
|
|
990
|
+
await fsp.mkdir(dist, { recursive: true });
|
|
996
991
|
}
|
|
997
|
-
const
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
info(
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
992
|
+
for (const { filename, source } of outputList) {
|
|
993
|
+
await fsp.writeFile(path3.join(dist, filename), source, "utf8");
|
|
994
|
+
const sourceSize = (source.length / 1024).toFixed(2);
|
|
995
|
+
const space = filename.length < 24 ? " ".repeat(24 - filename.length) : "";
|
|
996
|
+
options.logger.info(` ${color2.green(filename)}${space}${color2.bold(color2.dim(`${sourceSize} kB`))}`);
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
function generatePackageJson(options, externals) {
|
|
1000
|
+
const deps = getHostDependencies(options.cwd);
|
|
1001
|
+
const { name, version } = getPluginPackageInfo();
|
|
1002
|
+
const mockPkg = {
|
|
1003
|
+
name: "mock-server",
|
|
1004
|
+
type: "module",
|
|
1005
|
+
scripts: {
|
|
1006
|
+
start: "node index.js"
|
|
1006
1007
|
},
|
|
1007
|
-
|
|
1008
|
-
|
|
1008
|
+
dependencies: {
|
|
1009
|
+
connect: "^3.7.0",
|
|
1010
|
+
[name]: `^${version}`,
|
|
1011
|
+
cors: "^2.8.5"
|
|
1009
1012
|
}
|
|
1010
1013
|
};
|
|
1011
|
-
|
|
1014
|
+
externals.forEach((dep) => {
|
|
1015
|
+
mockPkg.dependencies[dep] = deps[dep] || "latest";
|
|
1016
|
+
});
|
|
1017
|
+
return JSON.stringify(mockPkg, null, 2);
|
|
1012
1018
|
}
|
|
1019
|
+
function generatorServerEntryCode({
|
|
1020
|
+
proxies,
|
|
1021
|
+
wsPrefix,
|
|
1022
|
+
cookiesOptions,
|
|
1023
|
+
bodyParserOptions,
|
|
1024
|
+
priority,
|
|
1025
|
+
build
|
|
1026
|
+
}) {
|
|
1027
|
+
const { serverPort, log } = build;
|
|
1028
|
+
return `import { createServer } from 'node:http';
|
|
1029
|
+
import connect from 'connect';
|
|
1030
|
+
import corsMiddleware from 'cors';
|
|
1031
|
+
import {
|
|
1032
|
+
baseMiddleware,
|
|
1033
|
+
createLogger,
|
|
1034
|
+
mockWebSocket,
|
|
1035
|
+
transformMockData,
|
|
1036
|
+
transformRawData
|
|
1037
|
+
} from 'rspack-plugin-mock';
|
|
1038
|
+
import rawData from './mock-data.js';
|
|
1013
1039
|
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
+
const app = connect();
|
|
1041
|
+
const server = createServer(app);
|
|
1042
|
+
const logger = createLogger('mock-server', '${log}');
|
|
1043
|
+
const proxies = ${JSON.stringify(proxies)};
|
|
1044
|
+
const wsProxies = ${JSON.stringify(toArray3(wsPrefix))};
|
|
1045
|
+
const cookiesOptions = ${JSON.stringify(cookiesOptions)};
|
|
1046
|
+
const bodyParserOptions = ${JSON.stringify(bodyParserOptions)};
|
|
1047
|
+
const priority = ${JSON.stringify(priority)};
|
|
1048
|
+
const data = { mockData: transformMockData(transformRawData(rawData)) };
|
|
1049
|
+
|
|
1050
|
+
mockWebSocket(data, server, { wsProxies, cookiesOptions, logger });
|
|
1051
|
+
|
|
1052
|
+
app.use(corsMiddleware());
|
|
1053
|
+
app.use(baseMiddleware(data, {
|
|
1054
|
+
formidableOptions: { multiples: true },
|
|
1055
|
+
proxies,
|
|
1056
|
+
priority,
|
|
1057
|
+
cookiesOptions,
|
|
1058
|
+
bodyParserOptions,
|
|
1059
|
+
logger,
|
|
1060
|
+
}));
|
|
1061
|
+
|
|
1062
|
+
server.listen(${serverPort});
|
|
1063
|
+
|
|
1064
|
+
console.log('listen: http://localhost:${serverPort}');
|
|
1065
|
+
`;
|
|
1066
|
+
}
|
|
1067
|
+
async function getMockFileList({ cwd, include, exclude }) {
|
|
1068
|
+
const filter = createFilter(include, exclude, { resolve: false });
|
|
1069
|
+
return await fg(include, { cwd }).then((files) => files.filter(filter));
|
|
1070
|
+
}
|
|
1071
|
+
async function writeMockEntryFile(entryFile, files, cwd) {
|
|
1072
|
+
const importers = [];
|
|
1073
|
+
const exporters = [];
|
|
1074
|
+
for (const [index, filepath] of files.entries()) {
|
|
1075
|
+
const file = normalizePath(path3.join(cwd, filepath));
|
|
1076
|
+
importers.push(`import * as m${index} from '${file}'`);
|
|
1077
|
+
exporters.push(`[m${index}, '${filepath}']`);
|
|
1078
|
+
}
|
|
1079
|
+
const code = `${importers.join("\n")}
|
|
1080
|
+
|
|
1081
|
+
export default [
|
|
1082
|
+
${exporters.join(",\n ")}
|
|
1083
|
+
]`;
|
|
1084
|
+
const dirname = path3.dirname(entryFile);
|
|
1085
|
+
if (!fs2.existsSync(dirname)) {
|
|
1086
|
+
await fsp.mkdir(dirname, { recursive: true });
|
|
1087
|
+
}
|
|
1088
|
+
await fsp.writeFile(entryFile, code, "utf8");
|
|
1089
|
+
}
|
|
1090
|
+
function getPluginPackageInfo() {
|
|
1091
|
+
let pkg = {};
|
|
1092
|
+
try {
|
|
1093
|
+
const filepath = path3.join(packageDir, "../package.json");
|
|
1094
|
+
if (fs2.existsSync(filepath)) {
|
|
1095
|
+
pkg = JSON.parse(fs2.readFileSync(filepath, "utf8"));
|
|
1040
1096
|
}
|
|
1041
|
-
|
|
1097
|
+
} catch {
|
|
1042
1098
|
}
|
|
1043
1099
|
return {
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
close: () => compiler.close(),
|
|
1047
|
-
updateAlias: compiler.updateAlias.bind(compiler)
|
|
1100
|
+
name: pkg.name || "rspack-plugin-mock",
|
|
1101
|
+
version: pkg.version || "latest"
|
|
1048
1102
|
};
|
|
1049
1103
|
}
|
|
1050
|
-
function
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1104
|
+
function getHostDependencies(context) {
|
|
1105
|
+
let pkg = {};
|
|
1106
|
+
try {
|
|
1107
|
+
const content = lookupFile(context, ["package.json"]);
|
|
1108
|
+
if (content)
|
|
1109
|
+
pkg = JSON.parse(content);
|
|
1110
|
+
} catch {
|
|
1111
|
+
}
|
|
1112
|
+
return { ...pkg.dependencies, ...pkg.devDependencies };
|
|
1058
1113
|
}
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1114
|
+
|
|
1115
|
+
// src/core/mockCompiler.ts
|
|
1116
|
+
import EventEmitter from "events";
|
|
1117
|
+
import process3 from "process";
|
|
1118
|
+
import path5 from "path";
|
|
1119
|
+
import fastGlob from "fast-glob";
|
|
1120
|
+
import chokidar from "chokidar";
|
|
1121
|
+
import { createFilter as createFilter2 } from "@rollup/pluginutils";
|
|
1122
|
+
import { toArray as toArray4 } from "@pengzhanbo/utils";
|
|
1123
|
+
|
|
1124
|
+
// src/core/loadFromCode.ts
|
|
1125
|
+
import path4 from "path";
|
|
1126
|
+
import fs3, { promises as fsp2 } from "fs";
|
|
1127
|
+
async function loadFromCode({
|
|
1128
|
+
filepath,
|
|
1129
|
+
code,
|
|
1130
|
+
isESM,
|
|
1131
|
+
cwd
|
|
1132
|
+
}) {
|
|
1133
|
+
filepath = path4.resolve(cwd, filepath);
|
|
1134
|
+
const fileBase = `${filepath}.timestamp-${Date.now()}`;
|
|
1135
|
+
const ext = isESM ? ".mjs" : ".cjs";
|
|
1136
|
+
const fileNameTmp = `${fileBase}${ext}`;
|
|
1137
|
+
await fsp2.writeFile(fileNameTmp, code, "utf8");
|
|
1138
|
+
try {
|
|
1139
|
+
const result = await import(fileNameTmp);
|
|
1140
|
+
return result.default || result;
|
|
1141
|
+
} finally {
|
|
1142
|
+
try {
|
|
1143
|
+
fs3.unlinkSync(fileNameTmp);
|
|
1144
|
+
} catch {
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
// src/core/mockCompiler.ts
|
|
1150
|
+
function createMockCompiler(options) {
|
|
1151
|
+
return new MockCompiler(options);
|
|
1152
|
+
}
|
|
1153
|
+
var MockCompiler = class extends EventEmitter {
|
|
1154
|
+
constructor(options) {
|
|
1155
|
+
super();
|
|
1156
|
+
this.options = options;
|
|
1157
|
+
this.cwd = options.cwd || process3.cwd();
|
|
1158
|
+
const { include, exclude } = this.options;
|
|
1159
|
+
this.fileFilter = createFilter2(include, exclude, { resolve: false });
|
|
1160
|
+
try {
|
|
1161
|
+
const pkg = lookupFile(this.cwd, ["package.json"]);
|
|
1162
|
+
this.moduleType = !!pkg && JSON.parse(pkg).type === "module" ? "esm" : "cjs";
|
|
1163
|
+
} catch {
|
|
1164
|
+
}
|
|
1165
|
+
this.entryFile = path5.resolve(process3.cwd(), "node_modules/.cache/mock-server/mock-server.ts");
|
|
1166
|
+
}
|
|
1167
|
+
cwd;
|
|
1168
|
+
mockWatcher;
|
|
1169
|
+
moduleType = "cjs";
|
|
1170
|
+
entryFile;
|
|
1171
|
+
_mockData = {};
|
|
1172
|
+
fileFilter;
|
|
1173
|
+
watchInfo;
|
|
1174
|
+
compiler;
|
|
1175
|
+
get mockData() {
|
|
1176
|
+
return this._mockData;
|
|
1177
|
+
}
|
|
1178
|
+
async run() {
|
|
1179
|
+
await this.updateMockEntry();
|
|
1180
|
+
this.watchMockFiles();
|
|
1181
|
+
const { plugins, alias } = this.options;
|
|
1182
|
+
const options = {
|
|
1183
|
+
isEsm: this.moduleType === "esm",
|
|
1184
|
+
cwd: this.cwd,
|
|
1185
|
+
plugins,
|
|
1186
|
+
entryFile: this.entryFile,
|
|
1187
|
+
alias,
|
|
1188
|
+
watch: true
|
|
1189
|
+
};
|
|
1190
|
+
this.compiler = createCompiler(options, async ({ code }) => {
|
|
1191
|
+
try {
|
|
1192
|
+
const result = await loadFromCode({
|
|
1193
|
+
filepath: "mock.bundle.js",
|
|
1194
|
+
code,
|
|
1195
|
+
isESM: this.moduleType === "esm",
|
|
1196
|
+
cwd: this.cwd
|
|
1197
|
+
});
|
|
1198
|
+
this._mockData = transformMockData(transformRawData(result));
|
|
1199
|
+
this.emit("update", this.watchInfo || {});
|
|
1200
|
+
} catch (e) {
|
|
1201
|
+
this.options.logger.error(e.stack || e.message);
|
|
1202
|
+
}
|
|
1203
|
+
});
|
|
1204
|
+
}
|
|
1205
|
+
close() {
|
|
1206
|
+
this.mockWatcher.close();
|
|
1207
|
+
this.compiler?.close(() => {
|
|
1208
|
+
});
|
|
1209
|
+
this.emit("close");
|
|
1210
|
+
}
|
|
1211
|
+
updateAlias(alias) {
|
|
1212
|
+
this.options.alias = {
|
|
1213
|
+
...this.options.alias,
|
|
1214
|
+
...alias
|
|
1066
1215
|
};
|
|
1067
1216
|
}
|
|
1068
|
-
|
|
1069
|
-
const
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1217
|
+
async updateMockEntry() {
|
|
1218
|
+
const files = await this.getMockFiles();
|
|
1219
|
+
await writeMockEntryFile(this.entryFile, files, this.cwd);
|
|
1220
|
+
}
|
|
1221
|
+
async getMockFiles() {
|
|
1222
|
+
const { include } = this.options;
|
|
1223
|
+
const files = await fastGlob(include, { cwd: this.cwd });
|
|
1224
|
+
return files.filter(this.fileFilter);
|
|
1225
|
+
}
|
|
1226
|
+
watchMockFiles() {
|
|
1227
|
+
const { include } = this.options;
|
|
1228
|
+
const [firstGlob, ...otherGlob] = toArray4(include);
|
|
1229
|
+
const watcher = this.mockWatcher = chokidar.watch(firstGlob, {
|
|
1230
|
+
ignoreInitial: true,
|
|
1231
|
+
cwd: this.cwd
|
|
1232
|
+
});
|
|
1233
|
+
if (otherGlob.length > 0)
|
|
1234
|
+
otherGlob.forEach((glob) => watcher.add(glob));
|
|
1235
|
+
watcher.on("add", (filepath) => {
|
|
1236
|
+
if (this.fileFilter(filepath)) {
|
|
1237
|
+
this.watchInfo = { filepath, type: "add" };
|
|
1238
|
+
this.updateMockEntry();
|
|
1239
|
+
}
|
|
1240
|
+
});
|
|
1241
|
+
watcher.on("change", (filepath) => {
|
|
1242
|
+
if (this.fileFilter(filepath)) {
|
|
1243
|
+
this.watchInfo = { filepath, type: "change" };
|
|
1244
|
+
}
|
|
1245
|
+
});
|
|
1246
|
+
watcher.on("unlink", async (filepath) => {
|
|
1247
|
+
this.watchInfo = { filepath, type: "unlink" };
|
|
1248
|
+
this.updateMockEntry();
|
|
1249
|
+
});
|
|
1250
|
+
}
|
|
1251
|
+
};
|
|
1252
|
+
|
|
1253
|
+
// src/core/mockWebsocket.ts
|
|
1254
|
+
import Cookies2 from "cookies";
|
|
1255
|
+
import { pathToRegexp as pathToRegexp4 } from "path-to-regexp";
|
|
1256
|
+
import colors3 from "picocolors";
|
|
1257
|
+
import { WebSocketServer } from "ws";
|
|
1258
|
+
function mockWebSocket(compiler, httpServer, {
|
|
1259
|
+
wsProxies: proxies,
|
|
1260
|
+
cookiesOptions,
|
|
1261
|
+
logger
|
|
1262
|
+
}) {
|
|
1263
|
+
const hmrMap = /* @__PURE__ */ new Map();
|
|
1264
|
+
const poolMap = /* @__PURE__ */ new Map();
|
|
1265
|
+
const wssContextMap = /* @__PURE__ */ new WeakMap();
|
|
1266
|
+
const getWssMap = (mockUrl) => {
|
|
1267
|
+
let wssMap = poolMap.get(mockUrl);
|
|
1268
|
+
if (!wssMap)
|
|
1269
|
+
poolMap.set(mockUrl, wssMap = /* @__PURE__ */ new Map());
|
|
1270
|
+
return wssMap;
|
|
1271
|
+
};
|
|
1272
|
+
const getWss = (wssMap, pathname) => {
|
|
1273
|
+
let wss = wssMap.get(pathname);
|
|
1274
|
+
if (!wss)
|
|
1275
|
+
wssMap.set(pathname, wss = new WebSocketServer({ noServer: true }));
|
|
1276
|
+
return wss;
|
|
1277
|
+
};
|
|
1278
|
+
const addHmr = (filepath, mockUrl) => {
|
|
1279
|
+
let urlList = hmrMap.get(filepath);
|
|
1280
|
+
if (!urlList)
|
|
1281
|
+
hmrMap.set(filepath, urlList = /* @__PURE__ */ new Set());
|
|
1282
|
+
urlList.add(mockUrl);
|
|
1283
|
+
};
|
|
1284
|
+
const setupWss = (wssMap, wss, mock, context, pathname, filepath) => {
|
|
1285
|
+
try {
|
|
1286
|
+
mock.setup?.(wss, context);
|
|
1287
|
+
wss.on("close", () => wssMap.delete(pathname));
|
|
1288
|
+
wss.on("error", (e) => {
|
|
1289
|
+
logger.error(
|
|
1290
|
+
`${colors3.red(
|
|
1291
|
+
`WebSocket mock error at ${wss.path}`
|
|
1292
|
+
)}
|
|
1293
|
+
${e}
|
|
1294
|
+
at setup (${filepath})`,
|
|
1295
|
+
mock.log
|
|
1296
|
+
);
|
|
1297
|
+
});
|
|
1298
|
+
} catch (e) {
|
|
1299
|
+
logger.error(
|
|
1300
|
+
`${colors3.red(
|
|
1301
|
+
`WebSocket mock error at ${wss.path}`
|
|
1302
|
+
)}
|
|
1303
|
+
${e}
|
|
1304
|
+
at setup (${filepath})`,
|
|
1305
|
+
mock.log
|
|
1306
|
+
);
|
|
1074
1307
|
}
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1308
|
+
};
|
|
1309
|
+
const emitConnection = (wss, ws, req, connectionList) => {
|
|
1310
|
+
wss.emit("connection", ws, req);
|
|
1311
|
+
ws.on("close", () => {
|
|
1312
|
+
const i = connectionList.findIndex((item) => item.ws === ws);
|
|
1313
|
+
if (i !== -1)
|
|
1314
|
+
connectionList.splice(i, 1);
|
|
1315
|
+
});
|
|
1316
|
+
};
|
|
1317
|
+
const restartWss = (wssMap, wss, mock, pathname, filepath) => {
|
|
1318
|
+
const { cleanupList, connectionList, context } = wssContextMap.get(wss);
|
|
1319
|
+
cleanupRunner(cleanupList);
|
|
1320
|
+
connectionList.forEach(({ ws }) => ws.removeAllListeners());
|
|
1321
|
+
wss.removeAllListeners();
|
|
1322
|
+
setupWss(wssMap, wss, mock, context, pathname, filepath);
|
|
1323
|
+
connectionList.forEach(
|
|
1324
|
+
({ ws, req }) => emitConnection(wss, ws, req, connectionList)
|
|
1078
1325
|
);
|
|
1079
|
-
if (!mockUrl)
|
|
1080
|
-
return next();
|
|
1081
|
-
cors(corsOptions)(req, res, next);
|
|
1082
1326
|
};
|
|
1327
|
+
compiler.on("update", ({ filepath }) => {
|
|
1328
|
+
if (!hmrMap.has(filepath))
|
|
1329
|
+
return;
|
|
1330
|
+
const mockUrlList = hmrMap.get(filepath);
|
|
1331
|
+
if (!mockUrlList)
|
|
1332
|
+
return;
|
|
1333
|
+
for (const mockUrl of mockUrlList.values()) {
|
|
1334
|
+
for (const mock of compiler.mockData[mockUrl]) {
|
|
1335
|
+
if (!mock.ws || mock.__filepath__ !== filepath)
|
|
1336
|
+
return;
|
|
1337
|
+
const wssMap = getWssMap(mockUrl);
|
|
1338
|
+
for (const [pathname, wss] of wssMap.entries())
|
|
1339
|
+
restartWss(wssMap, wss, mock, pathname, filepath);
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
});
|
|
1343
|
+
httpServer?.on("upgrade", (req, socket, head) => {
|
|
1344
|
+
const { pathname, query } = urlParse(req.url);
|
|
1345
|
+
if (!pathname || proxies.length === 0 || !proxies.some((context) => doesProxyContextMatchUrl(context, req.url, req))) {
|
|
1346
|
+
return;
|
|
1347
|
+
}
|
|
1348
|
+
const mockData = compiler.mockData;
|
|
1349
|
+
const mockUrl = Object.keys(mockData).find((key) => {
|
|
1350
|
+
return pathToRegexp4(key).test(pathname);
|
|
1351
|
+
});
|
|
1352
|
+
if (!mockUrl)
|
|
1353
|
+
return;
|
|
1354
|
+
const mock = mockData[mockUrl].find((mock2) => {
|
|
1355
|
+
return mock2.url && mock2.ws && pathToRegexp4(mock2.url).test(pathname);
|
|
1356
|
+
});
|
|
1357
|
+
if (!mock)
|
|
1358
|
+
return;
|
|
1359
|
+
const filepath = mock.__filepath__;
|
|
1360
|
+
addHmr(filepath, mockUrl);
|
|
1361
|
+
const wssMap = getWssMap(mockUrl);
|
|
1362
|
+
const wss = getWss(wssMap, pathname);
|
|
1363
|
+
let wssContext = wssContextMap.get(wss);
|
|
1364
|
+
if (!wssContext) {
|
|
1365
|
+
const cleanupList = [];
|
|
1366
|
+
const context = {
|
|
1367
|
+
onCleanup: (cleanup) => cleanupList.push(cleanup)
|
|
1368
|
+
};
|
|
1369
|
+
wssContext = { cleanupList, context, connectionList: [] };
|
|
1370
|
+
wssContextMap.set(wss, wssContext);
|
|
1371
|
+
setupWss(wssMap, wss, mock, context, pathname, filepath);
|
|
1372
|
+
}
|
|
1373
|
+
const request = req;
|
|
1374
|
+
const cookies = new Cookies2(req, req, cookiesOptions);
|
|
1375
|
+
const { query: refererQuery } = urlParse(req.headers.referer || "");
|
|
1376
|
+
request.query = query;
|
|
1377
|
+
request.refererQuery = refererQuery;
|
|
1378
|
+
request.params = parseParams(mockUrl, pathname);
|
|
1379
|
+
request.getCookie = cookies.get.bind(cookies);
|
|
1380
|
+
wss.handleUpgrade(request, socket, head, (ws) => {
|
|
1381
|
+
logger.info(
|
|
1382
|
+
`${colors3.magenta(colors3.bold("WebSocket"))} ${colors3.green(
|
|
1383
|
+
req.url
|
|
1384
|
+
)} connected ${colors3.dim(`(${filepath})`)}`,
|
|
1385
|
+
mock.log
|
|
1386
|
+
);
|
|
1387
|
+
wssContext.connectionList.push({ req: request, ws });
|
|
1388
|
+
emitConnection(wss, ws, request, wssContext.connectionList);
|
|
1389
|
+
});
|
|
1390
|
+
});
|
|
1391
|
+
httpServer?.on("close", () => {
|
|
1392
|
+
for (const wssMap of poolMap.values()) {
|
|
1393
|
+
for (const wss of wssMap.values()) {
|
|
1394
|
+
const wssContext = wssContextMap.get(wss);
|
|
1395
|
+
cleanupRunner(wssContext.cleanupList);
|
|
1396
|
+
wss.close();
|
|
1397
|
+
}
|
|
1398
|
+
wssMap.clear();
|
|
1399
|
+
}
|
|
1400
|
+
poolMap.clear();
|
|
1401
|
+
hmrMap.clear();
|
|
1402
|
+
});
|
|
1403
|
+
}
|
|
1404
|
+
function cleanupRunner(cleanupList) {
|
|
1405
|
+
let cleanup;
|
|
1406
|
+
while (cleanup = cleanupList.shift())
|
|
1407
|
+
cleanup?.();
|
|
1083
1408
|
}
|
|
1084
1409
|
|
|
1085
1410
|
export {
|
|
1411
|
+
waitingFor,
|
|
1086
1412
|
rewriteRequest,
|
|
1087
|
-
|
|
1088
|
-
createMockMiddleware
|
|
1413
|
+
baseMiddleware,
|
|
1414
|
+
createMockMiddleware,
|
|
1415
|
+
logLevels,
|
|
1416
|
+
createLogger,
|
|
1417
|
+
resolvePluginOptions,
|
|
1418
|
+
transformRawData,
|
|
1419
|
+
transformMockData,
|
|
1420
|
+
sortByValidator,
|
|
1421
|
+
buildMockServer,
|
|
1422
|
+
createMockCompiler,
|
|
1423
|
+
mockWebSocket
|
|
1089
1424
|
};
|