vite-plugin-mock-dev-server 1.0.7 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.2/node_modules/tsup/assets/cjs_shims.js
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.0.7";
57
+ var version = "1.1.0";
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 import_path_to_regexp = require("path-to-regexp");
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 method = req.method.toUpperCase();
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, import_path_to_regexp.pathToRegexp)(key).test(pathname);
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 currentMock = mockList.find((mock) => {
435
- if (!pathname || !mock || !mock.url)
436
- return false;
437
- const methods = mock.method ? isArray(mock.method) ? mock.method : [mock.method] : ["GET", "POST"];
438
- if (!methods.includes(req.method.toUpperCase()))
439
- return false;
440
- const hasMock = (0, import_path_to_regexp.pathToRegexp)(mock.url).test(pathname);
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 = params;
478
- res.setHeader("Content-Type", "application/json");
479
- res.setHeader("Cache-Control", "no-cache,max-age=0");
480
- res.setHeader("X-Mock", "generate by vite:plugin-mock-dev-server");
481
- if (currentMock.headers) {
482
- try {
483
- const headers = isFunction(currentMock.headers) ? await currentMock.headers(request) : currentMock.headers;
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,12 @@ function baseMiddleware(mockLoader, { formidableOptions = {}, proxies }) {
509
484
  }
510
485
  if (currentMock.response) {
511
486
  try {
512
- await currentMock.response(request, res, next);
487
+ const end = response.end.bind(response);
488
+ response.end = (...args) => {
489
+ realDelay(startTime, currentMock.delay).then(() => end(...args));
490
+ return response;
491
+ };
492
+ await currentMock.response(request, response, next);
513
493
  } catch (e) {
514
494
  log.error(`${import_picocolors2.default.red("[response error]")} ${req.url}
515
495
  `, e);
@@ -522,6 +502,69 @@ function baseMiddleware(mockLoader, { formidableOptions = {}, proxies }) {
522
502
  res.end("");
523
503
  };
524
504
  }
505
+ function fineMock(mockList, pathname, method, request) {
506
+ return mockList.find((mock) => {
507
+ if (!pathname || !mock || !mock.url)
508
+ return false;
509
+ const methods = mock.method ? isArray(mock.method) ? mock.method : [mock.method] : ["GET", "POST"];
510
+ if (!methods.includes(method))
511
+ return false;
512
+ const hasMock = (0, import_path_to_regexp2.pathToRegexp)(mock.url).test(pathname);
513
+ if (hasMock && mock.validator) {
514
+ const params = parseParams(mock.url, pathname);
515
+ const extraRequest = { params, ...request };
516
+ if (isFunction(mock.validator)) {
517
+ return mock.validator(extraRequest);
518
+ } else {
519
+ return validate(extraRequest, mock.validator);
520
+ }
521
+ }
522
+ return hasMock;
523
+ });
524
+ }
525
+ async function provideHeaders(req, res, headersOption) {
526
+ res.setHeader("Content-Type", "application/json");
527
+ res.setHeader("Cache-Control", "no-cache,max-age=0");
528
+ res.setHeader("X-Mock", "generate by vite:plugin-mock-dev-server");
529
+ if (!headersOption)
530
+ return;
531
+ try {
532
+ const headers = isFunction(headersOption) ? await headersOption(req) : headersOption;
533
+ Object.keys(headers).forEach((key) => {
534
+ res.setHeader(key, headers[key]);
535
+ });
536
+ } catch (e) {
537
+ log.error(`${import_picocolors2.default.red("[headers error]")} ${req.url}
538
+ `, e);
539
+ }
540
+ }
541
+ async function provideCookies(req, res, cookiesOption) {
542
+ if (!cookiesOption)
543
+ return;
544
+ try {
545
+ const cookies = isFunction(cookiesOption) ? await cookiesOption(req) : cookiesOption;
546
+ Object.keys(cookies).forEach((key) => {
547
+ const optional = cookies[key];
548
+ if (typeof optional === "string") {
549
+ res.setCookie(key, optional);
550
+ } else {
551
+ const { value, options } = optional;
552
+ res.setCookie(key, value, options);
553
+ }
554
+ });
555
+ } catch (e) {
556
+ log.error(`${import_picocolors2.default.red("[cookies error]")} ${req.url}
557
+ `, e);
558
+ }
559
+ }
560
+ async function realDelay(startTime, delay) {
561
+ if (!delay || delay <= 0)
562
+ return;
563
+ const diff = Date.now() - startTime;
564
+ const realDelay2 = delay - diff;
565
+ if (realDelay2 > 0)
566
+ await sleep(realDelay2);
567
+ }
525
568
  function doesProxyContextMatchUrl(context, url) {
526
569
  return context.startsWith("^") && new RegExp(context).test(url) || url.startsWith(context);
527
570
  }
@@ -825,7 +868,8 @@ async function mockServerMiddleware(config, options, httpServer, ws) {
825
868
  const prefix = ensureArray(options.prefix);
826
869
  return baseMiddleware(loader, {
827
870
  formidableOptions: options.formidableOptions,
828
- proxies: [...prefix, ...proxies]
871
+ proxies: [...prefix, ...proxies],
872
+ cookiesOptions: options.cookiesOptions
829
873
  });
830
874
  }
831
875
 
@@ -836,13 +880,15 @@ function mockDevServerPlugin({
836
880
  exclude = ["**/node_modules/**", "**/.vscode/**", "**/.git/**"],
837
881
  reload = false,
838
882
  formidableOptions = {},
839
- build: build3 = false
883
+ build: build3 = false,
884
+ cookiesOptions = {}
840
885
  } = {}) {
841
886
  const pluginOptions = {
842
887
  prefix,
843
888
  include,
844
889
  exclude,
845
890
  reload,
891
+ cookiesOptions,
846
892
  formidableOptions: {
847
893
  multiples: true,
848
894
  ...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 & http.IncomingMessage;
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: http.ServerResponse<http.IncomingMessage>, next: Connect.NextFunction) => void | Promise<void>;
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.0.7";
12
+ var version = "1.1.0";
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 { match, pathToRegexp } from "path-to-regexp";
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 method = req.method.toUpperCase();
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 currentMock = mockList.find((mock) => {
390
- if (!pathname || !mock || !mock.url)
391
- return false;
392
- const methods = mock.method ? isArray(mock.method) ? mock.method : [mock.method] : ["GET", "POST"];
393
- if (!methods.includes(req.method.toUpperCase()))
394
- return false;
395
- const hasMock = pathToRegexp(mock.url).test(pathname);
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 = params;
433
- res.setHeader("Content-Type", "application/json");
434
- res.setHeader("Cache-Control", "no-cache,max-age=0");
435
- res.setHeader("X-Mock", "generate by vite:plugin-mock-dev-server");
436
- if (currentMock.headers) {
437
- try {
438
- const headers = isFunction(currentMock.headers) ? await currentMock.headers(request) : currentMock.headers;
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,12 @@ function baseMiddleware(mockLoader, { formidableOptions = {}, proxies }) {
464
439
  }
465
440
  if (currentMock.response) {
466
441
  try {
467
- await currentMock.response(request, res, next);
442
+ const end = response.end.bind(response);
443
+ response.end = (...args) => {
444
+ realDelay(startTime, currentMock.delay).then(() => end(...args));
445
+ return response;
446
+ };
447
+ await currentMock.response(request, response, next);
468
448
  } catch (e) {
469
449
  log.error(`${colors2.red("[response error]")} ${req.url}
470
450
  `, e);
@@ -477,6 +457,69 @@ function baseMiddleware(mockLoader, { formidableOptions = {}, proxies }) {
477
457
  res.end("");
478
458
  };
479
459
  }
460
+ function fineMock(mockList, pathname, method, request) {
461
+ return mockList.find((mock) => {
462
+ if (!pathname || !mock || !mock.url)
463
+ return false;
464
+ const methods = mock.method ? isArray(mock.method) ? mock.method : [mock.method] : ["GET", "POST"];
465
+ if (!methods.includes(method))
466
+ return false;
467
+ const hasMock = pathToRegexp(mock.url).test(pathname);
468
+ if (hasMock && mock.validator) {
469
+ const params = parseParams(mock.url, pathname);
470
+ const extraRequest = { params, ...request };
471
+ if (isFunction(mock.validator)) {
472
+ return mock.validator(extraRequest);
473
+ } else {
474
+ return validate(extraRequest, mock.validator);
475
+ }
476
+ }
477
+ return hasMock;
478
+ });
479
+ }
480
+ async function provideHeaders(req, res, headersOption) {
481
+ res.setHeader("Content-Type", "application/json");
482
+ res.setHeader("Cache-Control", "no-cache,max-age=0");
483
+ res.setHeader("X-Mock", "generate by vite:plugin-mock-dev-server");
484
+ if (!headersOption)
485
+ return;
486
+ try {
487
+ const headers = isFunction(headersOption) ? await headersOption(req) : headersOption;
488
+ Object.keys(headers).forEach((key) => {
489
+ res.setHeader(key, headers[key]);
490
+ });
491
+ } catch (e) {
492
+ log.error(`${colors2.red("[headers error]")} ${req.url}
493
+ `, e);
494
+ }
495
+ }
496
+ async function provideCookies(req, res, cookiesOption) {
497
+ if (!cookiesOption)
498
+ return;
499
+ try {
500
+ const cookies = isFunction(cookiesOption) ? await cookiesOption(req) : cookiesOption;
501
+ Object.keys(cookies).forEach((key) => {
502
+ const optional = cookies[key];
503
+ if (typeof optional === "string") {
504
+ res.setCookie(key, optional);
505
+ } else {
506
+ const { value, options } = optional;
507
+ res.setCookie(key, value, options);
508
+ }
509
+ });
510
+ } catch (e) {
511
+ log.error(`${colors2.red("[cookies error]")} ${req.url}
512
+ `, e);
513
+ }
514
+ }
515
+ async function realDelay(startTime, delay) {
516
+ if (!delay || delay <= 0)
517
+ return;
518
+ const diff = Date.now() - startTime;
519
+ const realDelay2 = delay - diff;
520
+ if (realDelay2 > 0)
521
+ await sleep(realDelay2);
522
+ }
480
523
  function doesProxyContextMatchUrl(context, url) {
481
524
  return context.startsWith("^") && new RegExp(context).test(url) || url.startsWith(context);
482
525
  }
@@ -780,7 +823,8 @@ async function mockServerMiddleware(config, options, httpServer, ws) {
780
823
  const prefix = ensureArray(options.prefix);
781
824
  return baseMiddleware(loader, {
782
825
  formidableOptions: options.formidableOptions,
783
- proxies: [...prefix, ...proxies]
826
+ proxies: [...prefix, ...proxies],
827
+ cookiesOptions: options.cookiesOptions
784
828
  });
785
829
  }
786
830
 
@@ -791,13 +835,15 @@ function mockDevServerPlugin({
791
835
  exclude = ["**/node_modules/**", "**/.vscode/**", "**/.git/**"],
792
836
  reload = false,
793
837
  formidableOptions = {},
794
- build: build3 = false
838
+ build: build3 = false,
839
+ cookiesOptions = {}
795
840
  } = {}) {
796
841
  const pluginOptions = {
797
842
  prefix,
798
843
  include,
799
844
  exclude,
800
845
  reload,
846
+ cookiesOptions,
801
847
  formidableOptions: {
802
848
  multiples: true,
803
849
  ...formidableOptions
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-mock-dev-server",
3
- "version": "1.0.7",
3
+ "version": "1.1.0",
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.12",
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.11.0",
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.5",
53
- "bumpp": "^9.0.0",
54
+ "@types/node": "^18.15.12",
55
+ "bumpp": "^9.1.0",
54
56
  "conventional-changelog-cli": "^2.2.2",
55
- "eslint": "^8.36.0",
57
+ "eslint": "^8.38.0",
56
58
  "mockjs": "^1.1.0",
57
- "prettier": "^2.8.6",
59
+ "prettier": "^2.8.7",
58
60
  "tsup": "^6.7.0",
59
- "typescript": "^5.0.2",
60
- "vite": "^4.2.1",
61
- "vitepress": "1.0.0-alpha.61",
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": {