vite-plugin-mock-dev-server 1.0.7 → 1.1.1
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 +49 -3
- package/README.zh-CN.md +46 -0
- package/dist/index.cjs +103 -61
- package/dist/index.d.ts +26 -4
- package/dist/index.js +101 -59
- package/package.json +12 -10
package/README.md
CHANGED
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
- ⚙️ Support Enabled/Disabled any one of api mock
|
|
38
38
|
- 🔥 HMR
|
|
39
39
|
- ⚖️ Use `server.proxy`
|
|
40
|
-
- 🍕 Support `viteConfig.define` in mock file
|
|
41
|
-
- ⚓️ Support `resolve.alias`
|
|
40
|
+
- 🍕 Support `viteConfig.define` in mock file.
|
|
41
|
+
- ⚓️ Support `resolve.alias` in mock file.
|
|
42
42
|
- 📤 Support `multipart` content-type,mock upload file.
|
|
43
43
|
- 🌈 Support `vite preview` mode
|
|
44
44
|
- 🗂 Support building small independent deployable mock services.
|
|
@@ -333,6 +333,19 @@ export default defineMock({
|
|
|
333
333
|
'Content-Type': 'application/json'
|
|
334
334
|
},
|
|
335
335
|
|
|
336
|
+
/**
|
|
337
|
+
* response cookies
|
|
338
|
+
* @type Record<string, string | { value: string, option: CookieOption }>
|
|
339
|
+
* @see https://github.com/pillarjs/cookies#cookiessetname--values--options
|
|
340
|
+
*/
|
|
341
|
+
cookies: {
|
|
342
|
+
'your-cookie': 'your cookie value',
|
|
343
|
+
'cookie&option': {
|
|
344
|
+
value: 'cookie value',
|
|
345
|
+
option: { path: '/', httpOnly: true }
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
|
|
336
349
|
/**
|
|
337
350
|
* Response Body
|
|
338
351
|
* Support `string/number/array/object`
|
|
@@ -340,7 +353,7 @@ export default defineMock({
|
|
|
340
353
|
*
|
|
341
354
|
* @type string | number | array | object
|
|
342
355
|
*
|
|
343
|
-
* @type (request: { headers, query, body, params }) => any | Promise<any>
|
|
356
|
+
* @type (request: { headers, query, body, params, refererQuery, getCookie }) => any | Promise<any>
|
|
344
357
|
*/
|
|
345
358
|
body: {},
|
|
346
359
|
|
|
@@ -358,6 +371,39 @@ export default defineMock({
|
|
|
358
371
|
|
|
359
372
|
```
|
|
360
373
|
|
|
374
|
+
### Request/Response Enhance
|
|
375
|
+
|
|
376
|
+
When defining methods using `headers`, `body`, and `response`, the plugin adds new content to the `request` and `response` parameters.
|
|
377
|
+
|
|
378
|
+
**In Request:**
|
|
379
|
+
|
|
380
|
+
The original type of `request` is [`Connect.IncomingMessage`](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/connect/index.d.ts). The plugin adds data such as `query`, `params`, `body`, `refererQuery`, and the `getCookie(name)` method for obtaining cookie information on this basis.
|
|
381
|
+
|
|
382
|
+
```ts
|
|
383
|
+
type Request = Connect.IncomingMessage & {
|
|
384
|
+
query: object
|
|
385
|
+
params: object
|
|
386
|
+
body: any
|
|
387
|
+
refererQuery: object
|
|
388
|
+
getCookie: (name: string, option?: Cookies.GetOption) => string | undefined
|
|
389
|
+
}
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
**In Response:**
|
|
393
|
+
|
|
394
|
+
The original type of `response` is `http.ServerResponse<http.IncomingMessage>`. The plugin adds `setCookie(name, value)` method for configuration cookie on this basis.
|
|
395
|
+
|
|
396
|
+
```ts
|
|
397
|
+
type Response = http.ServerResponse<http.IncomingMessage> & {
|
|
398
|
+
setCookie: (
|
|
399
|
+
name: string,
|
|
400
|
+
value?: string | null,
|
|
401
|
+
option?: Cookies.SetOption,
|
|
402
|
+
) => void
|
|
403
|
+
}
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
|
|
361
407
|
> Tips:
|
|
362
408
|
>
|
|
363
409
|
> If you write mock files using json/json5,
|
package/README.zh-CN.md
CHANGED
|
@@ -326,6 +326,19 @@ export default defineMock({
|
|
|
326
326
|
'Content-Type': 'application/json'
|
|
327
327
|
},
|
|
328
328
|
|
|
329
|
+
/**
|
|
330
|
+
* 响应体 cookies
|
|
331
|
+
* @type Record<string, string | { value: string, option: CookieOption }>
|
|
332
|
+
* @see https://github.com/pillarjs/cookies#cookiessetname--values--options
|
|
333
|
+
*/
|
|
334
|
+
cookies: {
|
|
335
|
+
'your-cookie': 'your cookie value',
|
|
336
|
+
'cookie&option': {
|
|
337
|
+
value: 'cookie value',
|
|
338
|
+
option: { path: '/', httpOnly: true }
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
|
|
329
342
|
/**
|
|
330
343
|
* 响应体数据
|
|
331
344
|
* 定义返回的响应体数据内容。
|
|
@@ -358,6 +371,39 @@ export default defineMock({
|
|
|
358
371
|
|
|
359
372
|
```
|
|
360
373
|
|
|
374
|
+
### Request/Response 增强
|
|
375
|
+
|
|
376
|
+
当你配置 `headers`, `body`, and `response` 的函数形式时, 插件在参数 `request` 和 `response` 添加了新的内容用于帮助获取必要的数据.
|
|
377
|
+
|
|
378
|
+
**Request:**
|
|
379
|
+
|
|
380
|
+
`request`的原始数据类型是[`Connect.IncomingMessage`](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/connect/index.d.ts). 插件在此基础上,增加了 `query`, `params`, `body`, `refererQuery`,以及 `getCookie(name)` 方法用于获取cookie信息。
|
|
381
|
+
|
|
382
|
+
```ts
|
|
383
|
+
type Request = Connect.IncomingMessage & {
|
|
384
|
+
query: object
|
|
385
|
+
params: object
|
|
386
|
+
body: any
|
|
387
|
+
refererQuery: object
|
|
388
|
+
getCookie: (name: string, option?: Cookies.GetOption) => string | undefined
|
|
389
|
+
}
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
**Response:**
|
|
393
|
+
|
|
394
|
+
`response` 的原始数据类型是`http.ServerResponse<http.IncomingMessage>`. 插件在此基础上增加了 `setCookie(name, value)` 方法用于设置cookie
|
|
395
|
+
|
|
396
|
+
```ts
|
|
397
|
+
type Response = http.ServerResponse<http.IncomingMessage> & {
|
|
398
|
+
setCookie: (
|
|
399
|
+
name: string,
|
|
400
|
+
value?: string | null,
|
|
401
|
+
option?: Cookies.SetOption,
|
|
402
|
+
) => void
|
|
403
|
+
}
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
|
|
361
407
|
> 注意:
|
|
362
408
|
>
|
|
363
409
|
> 如果使用 json/json5 编写 mock文件,则不支持使用 `response` 方法,以及不支持使用其他字段的函数形式。
|
package/dist/index.cjs
CHANGED
|
@@ -39,7 +39,7 @@ __export(src_exports, {
|
|
|
39
39
|
});
|
|
40
40
|
module.exports = __toCommonJS(src_exports);
|
|
41
41
|
|
|
42
|
-
// node_modules/.pnpm/tsup@6.7.0_typescript@5.0.
|
|
42
|
+
// node_modules/.pnpm/tsup@6.7.0_typescript@5.0.4/node_modules/tsup/assets/cjs_shims.js
|
|
43
43
|
var getImportMetaUrl = () => typeof document === "undefined" ? new URL("file:" + __filename).href : document.currentScript && document.currentScript.src || new URL("main.js", document.baseURI).href;
|
|
44
44
|
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
45
45
|
|
|
@@ -54,7 +54,7 @@ var import_vite = require("vite");
|
|
|
54
54
|
|
|
55
55
|
// package.json
|
|
56
56
|
var name = "vite-plugin-mock-dev-server";
|
|
57
|
-
var version = "1.
|
|
57
|
+
var version = "1.1.1";
|
|
58
58
|
|
|
59
59
|
// src/esbuildPlugin.ts
|
|
60
60
|
var import_promises = __toESM(require("fs/promises"), 1);
|
|
@@ -137,6 +137,7 @@ var import_node_fs = __toESM(require("fs"), 1);
|
|
|
137
137
|
var import_node_path2 = __toESM(require("path"), 1);
|
|
138
138
|
var import_node_url = require("url");
|
|
139
139
|
var import_debug = __toESM(require("debug"), 1);
|
|
140
|
+
var import_path_to_regexp = require("path-to-regexp");
|
|
140
141
|
var import_picocolors = __toESM(require("picocolors"), 1);
|
|
141
142
|
var isArray = (val) => Array.isArray(val);
|
|
142
143
|
var isFunction = (val) => typeof val === "function";
|
|
@@ -184,6 +185,12 @@ var ensureProxies = (serverProxy = {}) => {
|
|
|
184
185
|
}).filter(Boolean);
|
|
185
186
|
return proxies;
|
|
186
187
|
};
|
|
188
|
+
function parseParams(pattern, url) {
|
|
189
|
+
const urlMatch = (0, import_path_to_regexp.match)(pattern, { decode: decodeURIComponent })(url) || {
|
|
190
|
+
params: {}
|
|
191
|
+
};
|
|
192
|
+
return urlMatch.params || {};
|
|
193
|
+
}
|
|
187
194
|
|
|
188
195
|
// src/build.ts
|
|
189
196
|
async function generateMockServer(ctx, config, options) {
|
|
@@ -226,6 +233,7 @@ async function generateMockServer(ctx, config, options) {
|
|
|
226
233
|
filename: import_node_path3.default.join(outputDir, "index.js"),
|
|
227
234
|
source: generatorServerEntryCode(
|
|
228
235
|
[...prefix, ...proxies],
|
|
236
|
+
options.cookiesOptions,
|
|
229
237
|
options.build.serverPort
|
|
230
238
|
)
|
|
231
239
|
},
|
|
@@ -287,7 +295,7 @@ function generatePackageJson(pkg, mockDeps) {
|
|
|
287
295
|
});
|
|
288
296
|
return JSON.stringify(mockPkg, null, 2);
|
|
289
297
|
}
|
|
290
|
-
function generatorServerEntryCode(proxies, port = 8080) {
|
|
298
|
+
function generatorServerEntryCode(proxies, cookiesOptions = {}, port = 8080) {
|
|
291
299
|
return `import connect from 'connect';
|
|
292
300
|
import corsMiddleware from 'cors';
|
|
293
301
|
import { baseMiddleware } from 'vite-plugin-mock-dev-server';
|
|
@@ -298,6 +306,7 @@ app.use(corsMiddleware());
|
|
|
298
306
|
app.use(baseMiddleware({ mockData }, {
|
|
299
307
|
formidableOptions: { multiples: true },
|
|
300
308
|
proxies: ${JSON.stringify(proxies)}
|
|
309
|
+
cookiesOptions: ${JSON.stringify(cookiesOptions)}
|
|
301
310
|
}));
|
|
302
311
|
app.listen(${port});
|
|
303
312
|
|
|
@@ -355,8 +364,9 @@ async function buildMockEntry(inputFile, define, alias) {
|
|
|
355
364
|
|
|
356
365
|
// src/baseMiddleware.ts
|
|
357
366
|
var import_node_url2 = require("url");
|
|
367
|
+
var import_cookies = __toESM(require("cookies"), 1);
|
|
358
368
|
var import_http_status = __toESM(require("http-status"), 1);
|
|
359
|
-
var
|
|
369
|
+
var import_path_to_regexp2 = require("path-to-regexp");
|
|
360
370
|
var import_picocolors2 = __toESM(require("picocolors"), 1);
|
|
361
371
|
|
|
362
372
|
// src/parseReqBody.ts
|
|
@@ -414,9 +424,9 @@ function equalObj(left, right) {
|
|
|
414
424
|
}
|
|
415
425
|
|
|
416
426
|
// src/baseMiddleware.ts
|
|
417
|
-
function baseMiddleware(mockLoader, { formidableOptions = {}, proxies }) {
|
|
427
|
+
function baseMiddleware(mockLoader, { formidableOptions = {}, proxies, cookiesOptions }) {
|
|
418
428
|
return async function(req, res, next) {
|
|
419
|
-
const
|
|
429
|
+
const startTime = Date.now();
|
|
420
430
|
const { query, pathname } = (0, import_node_url2.parse)(req.url, true);
|
|
421
431
|
const { query: refererQuery } = (0, import_node_url2.parse)(req.headers.referer || "", true);
|
|
422
432
|
if (!pathname || proxies.length === 0 || !proxies.some((context) => doesProxyContextMatchUrl(context, req.url))) {
|
|
@@ -424,71 +434,35 @@ function baseMiddleware(mockLoader, { formidableOptions = {}, proxies }) {
|
|
|
424
434
|
}
|
|
425
435
|
const mockData = mockLoader.mockData;
|
|
426
436
|
const mockUrl = Object.keys(mockData).find((key) => {
|
|
427
|
-
return (0,
|
|
437
|
+
return (0, import_path_to_regexp2.pathToRegexp)(key).test(pathname);
|
|
428
438
|
});
|
|
429
439
|
if (!mockUrl) {
|
|
430
440
|
return next();
|
|
431
441
|
}
|
|
432
|
-
const mockList = mockData[mockUrl];
|
|
433
442
|
const reqBody = await parseReqBody(req, formidableOptions);
|
|
434
|
-
const
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
if (hasMock && mock.validator) {
|
|
442
|
-
const urlMatch2 = (0, import_path_to_regexp.match)(mock.url, { decode: decodeURIComponent })(
|
|
443
|
-
pathname
|
|
444
|
-
) || { params: {} };
|
|
445
|
-
const params2 = urlMatch2.params || {};
|
|
446
|
-
const request2 = {
|
|
447
|
-
query,
|
|
448
|
-
refererQuery,
|
|
449
|
-
params: params2,
|
|
450
|
-
body: reqBody,
|
|
451
|
-
headers: req.headers
|
|
452
|
-
};
|
|
453
|
-
if (isFunction(mock.validator)) {
|
|
454
|
-
return mock.validator(request2);
|
|
455
|
-
} else {
|
|
456
|
-
return validate(request2, mock.validator);
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
return hasMock;
|
|
443
|
+
const cookies = new import_cookies.default(req, res, cookiesOptions);
|
|
444
|
+
const method = req.method.toUpperCase();
|
|
445
|
+
const currentMock = fineMock(mockData[mockUrl], pathname, method, {
|
|
446
|
+
query,
|
|
447
|
+
refererQuery,
|
|
448
|
+
body: reqBody,
|
|
449
|
+
headers: req.headers
|
|
460
450
|
});
|
|
461
451
|
if (!currentMock)
|
|
462
452
|
return next();
|
|
463
453
|
debug("middleware: ", method, pathname);
|
|
464
|
-
if (currentMock.delay && currentMock.delay > 0) {
|
|
465
|
-
await sleep(currentMock.delay);
|
|
466
|
-
}
|
|
467
|
-
res.statusCode = currentMock.status || 200;
|
|
468
|
-
res.statusMessage = currentMock.statusText || getHTTPStatusText(res.statusCode);
|
|
469
|
-
const urlMatch = (0, import_path_to_regexp.match)(currentMock.url, { decode: decodeURIComponent })(
|
|
470
|
-
pathname
|
|
471
|
-
) || { params: {} };
|
|
472
|
-
const params = urlMatch.params || {};
|
|
473
454
|
const request = req;
|
|
455
|
+
const response = res;
|
|
474
456
|
request.body = reqBody;
|
|
475
457
|
request.query = query;
|
|
476
458
|
request.refererQuery = refererQuery;
|
|
477
|
-
request.params =
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
Object.keys(headers).forEach((key) => {
|
|
485
|
-
res.setHeader(key, headers[key]);
|
|
486
|
-
});
|
|
487
|
-
} catch (e) {
|
|
488
|
-
log.error(`${import_picocolors2.default.red("[headers error]")} ${req.url}
|
|
489
|
-
`, e);
|
|
490
|
-
}
|
|
491
|
-
}
|
|
459
|
+
request.params = parseParams(currentMock.url, pathname);
|
|
460
|
+
request.getCookie = cookies.get.bind(cookies);
|
|
461
|
+
response.setCookie = cookies.set.bind(cookies);
|
|
462
|
+
await provideHeaders(request, response, currentMock.headers);
|
|
463
|
+
await provideCookies(request, response, currentMock.cookies);
|
|
464
|
+
res.statusCode = currentMock.status || 200;
|
|
465
|
+
res.statusMessage = currentMock.statusText || getHTTPStatusText(res.statusCode);
|
|
492
466
|
if (currentMock.body) {
|
|
493
467
|
try {
|
|
494
468
|
let body;
|
|
@@ -497,6 +471,7 @@ function baseMiddleware(mockLoader, { formidableOptions = {}, proxies }) {
|
|
|
497
471
|
} else {
|
|
498
472
|
body = currentMock.body;
|
|
499
473
|
}
|
|
474
|
+
await realDelay(startTime, currentMock.delay);
|
|
500
475
|
res.end(JSON.stringify(body));
|
|
501
476
|
} catch (e) {
|
|
502
477
|
log.error(`${import_picocolors2.default.red("[body error]")} ${req.url}
|
|
@@ -509,7 +484,8 @@ function baseMiddleware(mockLoader, { formidableOptions = {}, proxies }) {
|
|
|
509
484
|
}
|
|
510
485
|
if (currentMock.response) {
|
|
511
486
|
try {
|
|
512
|
-
await
|
|
487
|
+
await realDelay(startTime, currentMock.delay);
|
|
488
|
+
await currentMock.response(request, response, next);
|
|
513
489
|
} catch (e) {
|
|
514
490
|
log.error(`${import_picocolors2.default.red("[response error]")} ${req.url}
|
|
515
491
|
`, e);
|
|
@@ -522,6 +498,69 @@ function baseMiddleware(mockLoader, { formidableOptions = {}, proxies }) {
|
|
|
522
498
|
res.end("");
|
|
523
499
|
};
|
|
524
500
|
}
|
|
501
|
+
function fineMock(mockList, pathname, method, request) {
|
|
502
|
+
return mockList.find((mock) => {
|
|
503
|
+
if (!pathname || !mock || !mock.url)
|
|
504
|
+
return false;
|
|
505
|
+
const methods = mock.method ? isArray(mock.method) ? mock.method : [mock.method] : ["GET", "POST"];
|
|
506
|
+
if (!methods.includes(method))
|
|
507
|
+
return false;
|
|
508
|
+
const hasMock = (0, import_path_to_regexp2.pathToRegexp)(mock.url).test(pathname);
|
|
509
|
+
if (hasMock && mock.validator) {
|
|
510
|
+
const params = parseParams(mock.url, pathname);
|
|
511
|
+
const extraRequest = { params, ...request };
|
|
512
|
+
if (isFunction(mock.validator)) {
|
|
513
|
+
return mock.validator(extraRequest);
|
|
514
|
+
} else {
|
|
515
|
+
return validate(extraRequest, mock.validator);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
return hasMock;
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
async function provideHeaders(req, res, headersOption) {
|
|
522
|
+
res.setHeader("Content-Type", "application/json");
|
|
523
|
+
res.setHeader("Cache-Control", "no-cache,max-age=0");
|
|
524
|
+
res.setHeader("X-Mock", "generate by vite:plugin-mock-dev-server");
|
|
525
|
+
if (!headersOption)
|
|
526
|
+
return;
|
|
527
|
+
try {
|
|
528
|
+
const headers = isFunction(headersOption) ? await headersOption(req) : headersOption;
|
|
529
|
+
Object.keys(headers).forEach((key) => {
|
|
530
|
+
res.setHeader(key, headers[key]);
|
|
531
|
+
});
|
|
532
|
+
} catch (e) {
|
|
533
|
+
log.error(`${import_picocolors2.default.red("[headers error]")} ${req.url}
|
|
534
|
+
`, e);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
async function provideCookies(req, res, cookiesOption) {
|
|
538
|
+
if (!cookiesOption)
|
|
539
|
+
return;
|
|
540
|
+
try {
|
|
541
|
+
const cookies = isFunction(cookiesOption) ? await cookiesOption(req) : cookiesOption;
|
|
542
|
+
Object.keys(cookies).forEach((key) => {
|
|
543
|
+
const optional = cookies[key];
|
|
544
|
+
if (typeof optional === "string") {
|
|
545
|
+
res.setCookie(key, optional);
|
|
546
|
+
} else {
|
|
547
|
+
const { value, options } = optional;
|
|
548
|
+
res.setCookie(key, value, options);
|
|
549
|
+
}
|
|
550
|
+
});
|
|
551
|
+
} catch (e) {
|
|
552
|
+
log.error(`${import_picocolors2.default.red("[cookies error]")} ${req.url}
|
|
553
|
+
`, e);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
async function realDelay(startTime, delay) {
|
|
557
|
+
if (!delay || delay <= 0)
|
|
558
|
+
return;
|
|
559
|
+
const diff = Date.now() - startTime;
|
|
560
|
+
const realDelay2 = delay - diff;
|
|
561
|
+
if (realDelay2 > 0)
|
|
562
|
+
await sleep(realDelay2);
|
|
563
|
+
}
|
|
525
564
|
function doesProxyContextMatchUrl(context, url) {
|
|
526
565
|
return context.startsWith("^") && new RegExp(context).test(url) || url.startsWith(context);
|
|
527
566
|
}
|
|
@@ -825,7 +864,8 @@ async function mockServerMiddleware(config, options, httpServer, ws) {
|
|
|
825
864
|
const prefix = ensureArray(options.prefix);
|
|
826
865
|
return baseMiddleware(loader, {
|
|
827
866
|
formidableOptions: options.formidableOptions,
|
|
828
|
-
proxies: [...prefix, ...proxies]
|
|
867
|
+
proxies: [...prefix, ...proxies],
|
|
868
|
+
cookiesOptions: options.cookiesOptions
|
|
829
869
|
});
|
|
830
870
|
}
|
|
831
871
|
|
|
@@ -836,13 +876,15 @@ function mockDevServerPlugin({
|
|
|
836
876
|
exclude = ["**/node_modules/**", "**/.vscode/**", "**/.git/**"],
|
|
837
877
|
reload = false,
|
|
838
878
|
formidableOptions = {},
|
|
839
|
-
build: build3 = false
|
|
879
|
+
build: build3 = false,
|
|
880
|
+
cookiesOptions = {}
|
|
840
881
|
} = {}) {
|
|
841
882
|
const pluginOptions = {
|
|
842
883
|
prefix,
|
|
843
884
|
include,
|
|
844
885
|
exclude,
|
|
845
886
|
reload,
|
|
887
|
+
cookiesOptions,
|
|
846
888
|
formidableOptions: {
|
|
847
889
|
multiples: true,
|
|
848
890
|
...formidableOptions
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Connect, Plugin, ResolvedConfig } from 'vite';
|
|
2
2
|
import http from 'node:http';
|
|
3
|
+
import Cookies from 'cookies';
|
|
3
4
|
import formidable from 'formidable';
|
|
4
5
|
import EventEmitter from 'node:events';
|
|
5
6
|
import chokidar from 'chokidar';
|
|
@@ -35,6 +36,11 @@ interface MockServerPluginOptions {
|
|
|
35
36
|
* @see https://github.com/node-formidable/formidable#options
|
|
36
37
|
*/
|
|
37
38
|
formidableOptions?: formidable.Options;
|
|
39
|
+
/**
|
|
40
|
+
* cookies options
|
|
41
|
+
* @see https://github.com/pillarjs/cookies#new-cookiesrequest-response--options
|
|
42
|
+
*/
|
|
43
|
+
cookiesOptions?: Cookies.Option;
|
|
38
44
|
/**
|
|
39
45
|
* 当需要构建一个小型mock服务时,可配置此项
|
|
40
46
|
*
|
|
@@ -81,9 +87,20 @@ interface ExtraRequest {
|
|
|
81
87
|
*/
|
|
82
88
|
headers: Headers;
|
|
83
89
|
}
|
|
84
|
-
type MockRequest = ExtraRequest &
|
|
90
|
+
type MockRequest = Connect.IncomingMessage & ExtraRequest & {
|
|
91
|
+
getCookie: (name: string, option?: Cookies.GetOption) => string | undefined;
|
|
92
|
+
};
|
|
93
|
+
type MockResponse = http.ServerResponse<http.IncomingMessage> & {
|
|
94
|
+
setCookie: (name: string, value?: string | null, option?: Cookies.SetOption) => void;
|
|
95
|
+
};
|
|
85
96
|
type ResponseBodyFn = (request: MockRequest) => ResponseBody | Promise<ResponseBody>;
|
|
86
97
|
type ResponseHeaderFn = (request: MockRequest) => Headers | Promise<Headers>;
|
|
98
|
+
type CookieValue = string | {
|
|
99
|
+
value: string;
|
|
100
|
+
options?: Cookies.SetOption;
|
|
101
|
+
};
|
|
102
|
+
type ResponseCookies = Record<string, CookieValue>;
|
|
103
|
+
type ResponseCookiesFn = (request: MockRequest) => ResponseCookies | Promise<ResponseCookies>;
|
|
87
104
|
interface MockOptionsItem {
|
|
88
105
|
/**
|
|
89
106
|
* 需要做mock的接口地址,
|
|
@@ -127,6 +144,10 @@ interface MockOptionsItem {
|
|
|
127
144
|
* @default 0
|
|
128
145
|
*/
|
|
129
146
|
delay?: number;
|
|
147
|
+
/**
|
|
148
|
+
* 设置响应体 cookies
|
|
149
|
+
*/
|
|
150
|
+
cookies?: ResponseCookies | ResponseCookiesFn;
|
|
130
151
|
/**
|
|
131
152
|
* 配置响应体数据内容
|
|
132
153
|
*
|
|
@@ -140,7 +161,7 @@ interface MockOptionsItem {
|
|
|
140
161
|
*
|
|
141
162
|
* 在 req 中,还可以拿到 query、params、body等已解析的请求信息
|
|
142
163
|
*/
|
|
143
|
-
response?: (req: MockRequest, res:
|
|
164
|
+
response?: (req: MockRequest, res: MockResponse, next: Connect.NextFunction) => void | Promise<void>;
|
|
144
165
|
/**
|
|
145
166
|
* 请求验证器
|
|
146
167
|
*
|
|
@@ -153,7 +174,7 @@ interface MockOptionsItem {
|
|
|
153
174
|
type MockOptions = MockOptionsItem[];
|
|
154
175
|
type FormidableFile = formidable.File | formidable.File[];
|
|
155
176
|
|
|
156
|
-
declare function mockDevServerPlugin({ prefix, include, exclude, reload, formidableOptions, build, }?: MockServerPluginOptions): Plugin[];
|
|
177
|
+
declare function mockDevServerPlugin({ prefix, include, exclude, reload, formidableOptions, build, cookiesOptions, }?: MockServerPluginOptions): Plugin[];
|
|
157
178
|
|
|
158
179
|
/**
|
|
159
180
|
* mock config helper
|
|
@@ -222,9 +243,10 @@ declare class MockLoader extends EventEmitter {
|
|
|
222
243
|
|
|
223
244
|
interface BaseMiddlewareOptions {
|
|
224
245
|
formidableOptions: MockServerPluginOptions['formidableOptions'];
|
|
246
|
+
cookiesOptions: MockServerPluginOptions['cookiesOptions'];
|
|
225
247
|
proxies: string[];
|
|
226
248
|
}
|
|
227
|
-
declare function baseMiddleware(mockLoader: MockLoader, { formidableOptions, proxies }: BaseMiddlewareOptions): Connect.NextHandleFunction;
|
|
249
|
+
declare function baseMiddleware(mockLoader: MockLoader, { formidableOptions, proxies, cookiesOptions }: BaseMiddlewareOptions): Connect.NextHandleFunction;
|
|
228
250
|
|
|
229
251
|
declare function transformMockData(mockList: Map<string, MockOptionsItem | MockOptions> | (MockOptionsItem | MockOptions)[]): Record<string, MockOptions>;
|
|
230
252
|
|
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import { createFilter, normalizePath } from "vite";
|
|
|
9
9
|
|
|
10
10
|
// package.json
|
|
11
11
|
var name = "vite-plugin-mock-dev-server";
|
|
12
|
-
var version = "1.
|
|
12
|
+
var version = "1.1.1";
|
|
13
13
|
|
|
14
14
|
// src/esbuildPlugin.ts
|
|
15
15
|
import fsp from "fs/promises";
|
|
@@ -92,6 +92,7 @@ import fs from "fs";
|
|
|
92
92
|
import path2 from "path";
|
|
93
93
|
import { fileURLToPath } from "url";
|
|
94
94
|
import Debug from "debug";
|
|
95
|
+
import { match } from "path-to-regexp";
|
|
95
96
|
import colors from "picocolors";
|
|
96
97
|
var isArray = (val) => Array.isArray(val);
|
|
97
98
|
var isFunction = (val) => typeof val === "function";
|
|
@@ -139,6 +140,12 @@ var ensureProxies = (serverProxy = {}) => {
|
|
|
139
140
|
}).filter(Boolean);
|
|
140
141
|
return proxies;
|
|
141
142
|
};
|
|
143
|
+
function parseParams(pattern, url) {
|
|
144
|
+
const urlMatch = match(pattern, { decode: decodeURIComponent })(url) || {
|
|
145
|
+
params: {}
|
|
146
|
+
};
|
|
147
|
+
return urlMatch.params || {};
|
|
148
|
+
}
|
|
142
149
|
|
|
143
150
|
// src/build.ts
|
|
144
151
|
async function generateMockServer(ctx, config, options) {
|
|
@@ -181,6 +188,7 @@ async function generateMockServer(ctx, config, options) {
|
|
|
181
188
|
filename: path3.join(outputDir, "index.js"),
|
|
182
189
|
source: generatorServerEntryCode(
|
|
183
190
|
[...prefix, ...proxies],
|
|
191
|
+
options.cookiesOptions,
|
|
184
192
|
options.build.serverPort
|
|
185
193
|
)
|
|
186
194
|
},
|
|
@@ -242,7 +250,7 @@ function generatePackageJson(pkg, mockDeps) {
|
|
|
242
250
|
});
|
|
243
251
|
return JSON.stringify(mockPkg, null, 2);
|
|
244
252
|
}
|
|
245
|
-
function generatorServerEntryCode(proxies, port = 8080) {
|
|
253
|
+
function generatorServerEntryCode(proxies, cookiesOptions = {}, port = 8080) {
|
|
246
254
|
return `import connect from 'connect';
|
|
247
255
|
import corsMiddleware from 'cors';
|
|
248
256
|
import { baseMiddleware } from 'vite-plugin-mock-dev-server';
|
|
@@ -253,6 +261,7 @@ app.use(corsMiddleware());
|
|
|
253
261
|
app.use(baseMiddleware({ mockData }, {
|
|
254
262
|
formidableOptions: { multiples: true },
|
|
255
263
|
proxies: ${JSON.stringify(proxies)}
|
|
264
|
+
cookiesOptions: ${JSON.stringify(cookiesOptions)}
|
|
256
265
|
}));
|
|
257
266
|
app.listen(${port});
|
|
258
267
|
|
|
@@ -310,8 +319,9 @@ async function buildMockEntry(inputFile, define, alias) {
|
|
|
310
319
|
|
|
311
320
|
// src/baseMiddleware.ts
|
|
312
321
|
import { parse as urlParse } from "url";
|
|
322
|
+
import Cookies from "cookies";
|
|
313
323
|
import HTTP_STATUS from "http-status";
|
|
314
|
-
import {
|
|
324
|
+
import { pathToRegexp } from "path-to-regexp";
|
|
315
325
|
import colors2 from "picocolors";
|
|
316
326
|
|
|
317
327
|
// src/parseReqBody.ts
|
|
@@ -369,9 +379,9 @@ function equalObj(left, right) {
|
|
|
369
379
|
}
|
|
370
380
|
|
|
371
381
|
// src/baseMiddleware.ts
|
|
372
|
-
function baseMiddleware(mockLoader, { formidableOptions = {}, proxies }) {
|
|
382
|
+
function baseMiddleware(mockLoader, { formidableOptions = {}, proxies, cookiesOptions }) {
|
|
373
383
|
return async function(req, res, next) {
|
|
374
|
-
const
|
|
384
|
+
const startTime = Date.now();
|
|
375
385
|
const { query, pathname } = urlParse(req.url, true);
|
|
376
386
|
const { query: refererQuery } = urlParse(req.headers.referer || "", true);
|
|
377
387
|
if (!pathname || proxies.length === 0 || !proxies.some((context) => doesProxyContextMatchUrl(context, req.url))) {
|
|
@@ -384,66 +394,30 @@ function baseMiddleware(mockLoader, { formidableOptions = {}, proxies }) {
|
|
|
384
394
|
if (!mockUrl) {
|
|
385
395
|
return next();
|
|
386
396
|
}
|
|
387
|
-
const mockList = mockData[mockUrl];
|
|
388
397
|
const reqBody = await parseReqBody(req, formidableOptions);
|
|
389
|
-
const
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
if (hasMock && mock.validator) {
|
|
397
|
-
const urlMatch2 = match(mock.url, { decode: decodeURIComponent })(
|
|
398
|
-
pathname
|
|
399
|
-
) || { params: {} };
|
|
400
|
-
const params2 = urlMatch2.params || {};
|
|
401
|
-
const request2 = {
|
|
402
|
-
query,
|
|
403
|
-
refererQuery,
|
|
404
|
-
params: params2,
|
|
405
|
-
body: reqBody,
|
|
406
|
-
headers: req.headers
|
|
407
|
-
};
|
|
408
|
-
if (isFunction(mock.validator)) {
|
|
409
|
-
return mock.validator(request2);
|
|
410
|
-
} else {
|
|
411
|
-
return validate(request2, mock.validator);
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
return hasMock;
|
|
398
|
+
const cookies = new Cookies(req, res, cookiesOptions);
|
|
399
|
+
const method = req.method.toUpperCase();
|
|
400
|
+
const currentMock = fineMock(mockData[mockUrl], pathname, method, {
|
|
401
|
+
query,
|
|
402
|
+
refererQuery,
|
|
403
|
+
body: reqBody,
|
|
404
|
+
headers: req.headers
|
|
415
405
|
});
|
|
416
406
|
if (!currentMock)
|
|
417
407
|
return next();
|
|
418
408
|
debug("middleware: ", method, pathname);
|
|
419
|
-
if (currentMock.delay && currentMock.delay > 0) {
|
|
420
|
-
await sleep(currentMock.delay);
|
|
421
|
-
}
|
|
422
|
-
res.statusCode = currentMock.status || 200;
|
|
423
|
-
res.statusMessage = currentMock.statusText || getHTTPStatusText(res.statusCode);
|
|
424
|
-
const urlMatch = match(currentMock.url, { decode: decodeURIComponent })(
|
|
425
|
-
pathname
|
|
426
|
-
) || { params: {} };
|
|
427
|
-
const params = urlMatch.params || {};
|
|
428
409
|
const request = req;
|
|
410
|
+
const response = res;
|
|
429
411
|
request.body = reqBody;
|
|
430
412
|
request.query = query;
|
|
431
413
|
request.refererQuery = refererQuery;
|
|
432
|
-
request.params =
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
Object.keys(headers).forEach((key) => {
|
|
440
|
-
res.setHeader(key, headers[key]);
|
|
441
|
-
});
|
|
442
|
-
} catch (e) {
|
|
443
|
-
log.error(`${colors2.red("[headers error]")} ${req.url}
|
|
444
|
-
`, e);
|
|
445
|
-
}
|
|
446
|
-
}
|
|
414
|
+
request.params = parseParams(currentMock.url, pathname);
|
|
415
|
+
request.getCookie = cookies.get.bind(cookies);
|
|
416
|
+
response.setCookie = cookies.set.bind(cookies);
|
|
417
|
+
await provideHeaders(request, response, currentMock.headers);
|
|
418
|
+
await provideCookies(request, response, currentMock.cookies);
|
|
419
|
+
res.statusCode = currentMock.status || 200;
|
|
420
|
+
res.statusMessage = currentMock.statusText || getHTTPStatusText(res.statusCode);
|
|
447
421
|
if (currentMock.body) {
|
|
448
422
|
try {
|
|
449
423
|
let body;
|
|
@@ -452,6 +426,7 @@ function baseMiddleware(mockLoader, { formidableOptions = {}, proxies }) {
|
|
|
452
426
|
} else {
|
|
453
427
|
body = currentMock.body;
|
|
454
428
|
}
|
|
429
|
+
await realDelay(startTime, currentMock.delay);
|
|
455
430
|
res.end(JSON.stringify(body));
|
|
456
431
|
} catch (e) {
|
|
457
432
|
log.error(`${colors2.red("[body error]")} ${req.url}
|
|
@@ -464,7 +439,8 @@ function baseMiddleware(mockLoader, { formidableOptions = {}, proxies }) {
|
|
|
464
439
|
}
|
|
465
440
|
if (currentMock.response) {
|
|
466
441
|
try {
|
|
467
|
-
await
|
|
442
|
+
await realDelay(startTime, currentMock.delay);
|
|
443
|
+
await currentMock.response(request, response, next);
|
|
468
444
|
} catch (e) {
|
|
469
445
|
log.error(`${colors2.red("[response error]")} ${req.url}
|
|
470
446
|
`, e);
|
|
@@ -477,6 +453,69 @@ function baseMiddleware(mockLoader, { formidableOptions = {}, proxies }) {
|
|
|
477
453
|
res.end("");
|
|
478
454
|
};
|
|
479
455
|
}
|
|
456
|
+
function fineMock(mockList, pathname, method, request) {
|
|
457
|
+
return mockList.find((mock) => {
|
|
458
|
+
if (!pathname || !mock || !mock.url)
|
|
459
|
+
return false;
|
|
460
|
+
const methods = mock.method ? isArray(mock.method) ? mock.method : [mock.method] : ["GET", "POST"];
|
|
461
|
+
if (!methods.includes(method))
|
|
462
|
+
return false;
|
|
463
|
+
const hasMock = pathToRegexp(mock.url).test(pathname);
|
|
464
|
+
if (hasMock && mock.validator) {
|
|
465
|
+
const params = parseParams(mock.url, pathname);
|
|
466
|
+
const extraRequest = { params, ...request };
|
|
467
|
+
if (isFunction(mock.validator)) {
|
|
468
|
+
return mock.validator(extraRequest);
|
|
469
|
+
} else {
|
|
470
|
+
return validate(extraRequest, mock.validator);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
return hasMock;
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
async function provideHeaders(req, res, headersOption) {
|
|
477
|
+
res.setHeader("Content-Type", "application/json");
|
|
478
|
+
res.setHeader("Cache-Control", "no-cache,max-age=0");
|
|
479
|
+
res.setHeader("X-Mock", "generate by vite:plugin-mock-dev-server");
|
|
480
|
+
if (!headersOption)
|
|
481
|
+
return;
|
|
482
|
+
try {
|
|
483
|
+
const headers = isFunction(headersOption) ? await headersOption(req) : headersOption;
|
|
484
|
+
Object.keys(headers).forEach((key) => {
|
|
485
|
+
res.setHeader(key, headers[key]);
|
|
486
|
+
});
|
|
487
|
+
} catch (e) {
|
|
488
|
+
log.error(`${colors2.red("[headers error]")} ${req.url}
|
|
489
|
+
`, e);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
async function provideCookies(req, res, cookiesOption) {
|
|
493
|
+
if (!cookiesOption)
|
|
494
|
+
return;
|
|
495
|
+
try {
|
|
496
|
+
const cookies = isFunction(cookiesOption) ? await cookiesOption(req) : cookiesOption;
|
|
497
|
+
Object.keys(cookies).forEach((key) => {
|
|
498
|
+
const optional = cookies[key];
|
|
499
|
+
if (typeof optional === "string") {
|
|
500
|
+
res.setCookie(key, optional);
|
|
501
|
+
} else {
|
|
502
|
+
const { value, options } = optional;
|
|
503
|
+
res.setCookie(key, value, options);
|
|
504
|
+
}
|
|
505
|
+
});
|
|
506
|
+
} catch (e) {
|
|
507
|
+
log.error(`${colors2.red("[cookies error]")} ${req.url}
|
|
508
|
+
`, e);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
async function realDelay(startTime, delay) {
|
|
512
|
+
if (!delay || delay <= 0)
|
|
513
|
+
return;
|
|
514
|
+
const diff = Date.now() - startTime;
|
|
515
|
+
const realDelay2 = delay - diff;
|
|
516
|
+
if (realDelay2 > 0)
|
|
517
|
+
await sleep(realDelay2);
|
|
518
|
+
}
|
|
480
519
|
function doesProxyContextMatchUrl(context, url) {
|
|
481
520
|
return context.startsWith("^") && new RegExp(context).test(url) || url.startsWith(context);
|
|
482
521
|
}
|
|
@@ -780,7 +819,8 @@ async function mockServerMiddleware(config, options, httpServer, ws) {
|
|
|
780
819
|
const prefix = ensureArray(options.prefix);
|
|
781
820
|
return baseMiddleware(loader, {
|
|
782
821
|
formidableOptions: options.formidableOptions,
|
|
783
|
-
proxies: [...prefix, ...proxies]
|
|
822
|
+
proxies: [...prefix, ...proxies],
|
|
823
|
+
cookiesOptions: options.cookiesOptions
|
|
784
824
|
});
|
|
785
825
|
}
|
|
786
826
|
|
|
@@ -791,13 +831,15 @@ function mockDevServerPlugin({
|
|
|
791
831
|
exclude = ["**/node_modules/**", "**/.vscode/**", "**/.git/**"],
|
|
792
832
|
reload = false,
|
|
793
833
|
formidableOptions = {},
|
|
794
|
-
build: build3 = false
|
|
834
|
+
build: build3 = false,
|
|
835
|
+
cookiesOptions = {}
|
|
795
836
|
} = {}) {
|
|
796
837
|
const pluginOptions = {
|
|
797
838
|
prefix,
|
|
798
839
|
include,
|
|
799
840
|
exclude,
|
|
800
841
|
reload,
|
|
842
|
+
cookiesOptions,
|
|
801
843
|
formidableOptions: {
|
|
802
844
|
multiples: true,
|
|
803
845
|
...formidableOptions
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vite-plugin-mock-dev-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"vite",
|
|
6
6
|
"plugin",
|
|
@@ -32,12 +32,13 @@
|
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"chokidar": "^3.5.3",
|
|
34
34
|
"co-body": "^6.1.0",
|
|
35
|
+
"cookies": "^0.8.0",
|
|
35
36
|
"debug": "^4.3.4",
|
|
36
|
-
"esbuild": "^0.17.
|
|
37
|
+
"esbuild": "^0.17.17",
|
|
37
38
|
"fast-glob": "^3.2.12",
|
|
38
39
|
"formidable": "^2.1.1",
|
|
39
40
|
"http-status": "^1.6.2",
|
|
40
|
-
"is-core-module": "^2.
|
|
41
|
+
"is-core-module": "^2.12.0",
|
|
41
42
|
"json5": "^2.2.3",
|
|
42
43
|
"path-to-regexp": "^6.2.1",
|
|
43
44
|
"picocolors": "^1.0.0"
|
|
@@ -46,19 +47,20 @@
|
|
|
46
47
|
"@pengzhanbo/eslint-config-ts": "^0.3.4",
|
|
47
48
|
"@pengzhanbo/prettier-config": "^0.3.4",
|
|
48
49
|
"@types/co-body": "^6.1.0",
|
|
50
|
+
"@types/cookies": "^0.7.7",
|
|
49
51
|
"@types/debug": "^4.1.7",
|
|
50
52
|
"@types/formidable": "^2.0.5",
|
|
51
53
|
"@types/is-core-module": "^2.2.0",
|
|
52
|
-
"@types/node": "^18.15.
|
|
53
|
-
"bumpp": "^9.
|
|
54
|
+
"@types/node": "^18.15.12",
|
|
55
|
+
"bumpp": "^9.1.0",
|
|
54
56
|
"conventional-changelog-cli": "^2.2.2",
|
|
55
|
-
"eslint": "^8.
|
|
57
|
+
"eslint": "^8.38.0",
|
|
56
58
|
"mockjs": "^1.1.0",
|
|
57
|
-
"prettier": "^2.8.
|
|
59
|
+
"prettier": "^2.8.7",
|
|
58
60
|
"tsup": "^6.7.0",
|
|
59
|
-
"typescript": "^5.0.
|
|
60
|
-
"vite": "^4.
|
|
61
|
-
"vitepress": "1.0.0-alpha.
|
|
61
|
+
"typescript": "^5.0.4",
|
|
62
|
+
"vite": "^4.3.0",
|
|
63
|
+
"vitepress": "1.0.0-alpha.73",
|
|
62
64
|
"vue": "^3.2.47"
|
|
63
65
|
},
|
|
64
66
|
"peerDependencies": {
|