vite-plugin-mock-dev-server 1.1.2 → 1.1.3

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
@@ -260,11 +260,12 @@ export default defineApiMock({
260
260
  export default defineMock({
261
261
  /**
262
262
  * Request address, supports the `/api/user/:id` format.
263
+ * The plugin matches the path through `path-to-regexp`.
264
+ * @see https://github.com/pillarjs/path-to-regexp
263
265
  */
264
266
  url: '/api/test',
265
267
  /**
266
268
  * Supported request methods of the interface.
267
- *
268
269
  * @type string | string[]
269
270
  * @default ['POST','GET']
270
271
  *
@@ -282,13 +283,11 @@ export default defineMock({
282
283
  enable: true,
283
284
  /**
284
285
  * Set interface response delay, unit: ms.
285
- *
286
286
  * @default 0
287
287
  */
288
288
  delay: 1000,
289
289
  /**
290
290
  * response status
291
- *
292
291
  * @default 200
293
292
  */
294
293
  status: 200,
@@ -309,8 +308,8 @@ export default defineMock({
309
308
  * then the validation method is to strictly compare whether the `value`
310
309
  * of each `key` in headers/body/query/params in the request interface is exactly equal.
311
310
  * If they are all equal, then the validation passes.
312
- *
313
311
  * @type ({ headers: object; body: object; query: object; params: object; refererQuery: object }) => boolean
312
+ *
314
313
  * If the validator passed in is a function,
315
314
  * then the data related to the requested interface will be provided as input parameters
316
315
  * for users to perform custom validation and return a boolean.
@@ -329,11 +328,8 @@ export default defineMock({
329
328
  refererQuery: {}
330
329
  },
331
330
  /**
332
- *
333
331
  * response headers
334
- *
335
332
  * @type Record<string, any>
336
- *
337
333
  * @type (({ query, body, params, headers }) => Record<string, any>)
338
334
  */
339
335
  headers: {
@@ -349,17 +345,28 @@ export default defineMock({
349
345
  'your-cookie': 'your cookie value',
350
346
  'cookie&option': ['cookie value', { path: '/', httpOnly: true }]
351
347
  },
348
+ /**
349
+ * Response body data type, optional values include `text, json, buffer`.
350
+ * And also support types included in `mime-db`.
351
+ * When the response body returns a file and you are not sure which type to use,
352
+ * you can pass the file name as the value. The plugin will internally search for matching
353
+ * `content-type` based on the file name suffix.
354
+ * However, if it is a TypeScript file such as `a.ts`, it may not be correctly matched
355
+ * as a JavaScript script. You need to modify `a.ts` to `a.js` as the value passed
356
+ * in order to recognize it correctly.
357
+ * @see https://github.com/jshttp/mime-db
358
+ * @default 'json'
359
+ */
360
+ type: 'json',
352
361
 
353
362
  /**
354
363
  * Response Body
355
- * Support `string/number/array/object`
364
+ * Support `string/number/array/object/buffer/ReadableStream`
356
365
  * You can also use libraries such as' mockjs' to generate data content
357
- *
358
- * @type string | number | array | object
359
- *
366
+ * @type string | number | array | object | ReadableStream | buffer
360
367
  * @type (request: { headers, query, body, params, refererQuery, getCookie }) => any | Promise<any>
361
368
  */
362
- body: {},
369
+ body: '',
363
370
 
364
371
  /**
365
372
  * If the mock requirement cannot be solved through `body` configuration,
@@ -408,37 +415,35 @@ type Response = http.ServerResponse<http.IncomingMessage> & {
408
415
  ```
409
416
 
410
417
 
411
- > Tips
418
+ > **Tips:**
412
419
  >
413
420
  > If you write mock files using json/json5,
414
421
  > the 'response' method is not supported,
415
422
  > as is the function form that uses other fields.
416
423
 
424
+ ## Example
425
+
417
426
  `mock/**/*.mock.{ts,js,mjs,cjs,json,json5}`
418
427
 
419
428
  See more examples: [example](/example/)
420
429
 
421
- #### Example 1:
422
- Match `/api/test`,And returns a response body content with empty data
430
+ **exp:** Match `/api/test`,And returns a response body content with empty data
423
431
  ```ts
424
432
  export default defineMock({
425
433
  url: '/api/test',
426
434
  })
427
435
  ```
428
436
 
429
- #### Example 2:
430
- Match `/api/test` ,And returns a static content data
437
+ **exp:** Match `/api/test` ,And returns a static content data
431
438
  ```ts
432
439
  export default defineMock({
433
440
  url: '/api/test',
434
- body: {
435
- a: 1
436
- }
441
+ body: { a: 1 },
437
442
  })
438
443
  ```
439
444
 
440
- #### Example 3:
441
- Only Support `GET` Method
445
+
446
+ **exp:** Only Support `GET` Method
442
447
  ```ts
443
448
  export default defineMock({
444
449
  url: '/api/test',
@@ -446,69 +451,56 @@ export default defineMock({
446
451
  })
447
452
  ```
448
453
 
449
- #### Example 4:
450
- In the response header, add a custom header
454
+ **exp:** In the response header, add a custom header and cookie
451
455
  ```ts
452
456
  export default defineMock({
453
457
  url: '/api/test',
454
- headers: {
455
- 'X-Custom': '12345678'
456
- }
458
+ headers: { 'X-Custom': '12345678' },
459
+ cookies: { 'my-cookie': '123456789' },
457
460
  })
458
461
  ```
459
462
  ```ts
460
463
  export default defineMock({
461
464
  url: '/api/test',
462
465
  headers({ query, body, params, headers }) {
463
- return {
464
- 'X-Custom': query.custom
465
- }
466
+ return { 'X-Custom': query.custom }
467
+ },
468
+ cookies() {
469
+ return { 'my-cookie': '123456789' }
466
470
  }
467
471
  })
468
472
  ```
469
473
 
470
- #### Example 5:
471
- Define multiple mock requests for the same url and match valid rules with validators
474
+
475
+ **exp:** Define multiple mock requests for the same url and match valid rules with validators
472
476
  ```ts
473
477
  export default defineMock([
474
478
  // Match /api/test?a=1
475
479
  {
476
480
  url: '/api/test',
477
481
  validator: {
478
- query: {
479
- a: 1
480
- }
482
+ query: { a: 1 },
481
483
  },
482
- body: {
483
- message: 'query.a == 1'
484
- }
484
+ body: { message: 'query.a == 1' },
485
485
  },
486
486
  // Match /api/test?a=2
487
487
  {
488
488
  url: '/api/test',
489
489
  validator: {
490
- query: {
491
- a: 2
492
- }
490
+ query: { a: 2 },
493
491
  },
494
- body: {
495
- message: 'query.a == 2'
496
- }
492
+ body: { message: 'query.a == 2' },
497
493
  },
498
494
  {
499
- /**
500
- * `?a=3` will resolve to `validator.query`
501
- */
495
+ // `?a=3` will resolve to `validator.query`
502
496
  url: '/api/test?a=3',
503
- body: {
504
- message: 'query.a == 3'
505
- }
497
+ body: { message: 'query.a == 3' }
506
498
  }
507
499
  ])
508
500
  ```
509
501
 
510
- #### Example 6:
511
- Response Delay
502
+
503
+ **exp:** Response Delay
512
504
  ```ts
513
505
  export default defineMock({
514
506
  url: '/api/test',
@@ -516,8 +508,8 @@ export default defineMock({
516
508
  })
517
509
  ```
518
510
 
519
- #### Example 7:
520
- The interface request failed
511
+
512
+ **exp:** The interface request failed
521
513
  ```ts
522
514
  export default defineMock({
523
515
  url: '/api/test',
@@ -526,23 +518,57 @@ export default defineMock({
526
518
  })
527
519
  ```
528
520
 
529
- #### Example 8:
530
- Dynamic route matching
521
+
522
+ **exp:** Dynamic route matching
531
523
  ```ts
532
524
  export default defineMock({
533
525
  url: '/api/user/:userId',
534
526
  body({ params }) {
535
- return {
536
- userId: params.userId,
537
- }
527
+ return { userId: params.userId }
538
528
  }
539
529
  })
540
530
  ```
541
531
 
542
532
  The `userId` in the route will be resolved into the `request.params` object.
543
533
 
544
- #### Example 9:
545
- Use `mockjs`:
534
+ **exp:** Use buffer to respond data
535
+ ```ts
536
+ // Since the default value of type is json,
537
+ // although buffer is used for body during transmission,
538
+ // the content-type is still json.
539
+ export default defineMock({
540
+ url: 'api/buffer',
541
+ body: Buffer.from(JSON.stringify({ a: 1 }))
542
+ })
543
+ ```
544
+ ```ts
545
+ // When the type is buffer, the content-type is application/octet-stream.
546
+ // The data passed in through body will be converted to a buffer.
547
+ export default defineMock({
548
+ url: 'api/buffer',
549
+ type: 'buffer',
550
+ // Convert using Buffer.from(body) for internal use
551
+ body: { a: 1 }
552
+ })
553
+ ```
554
+
555
+ **exp:** Response file type
556
+
557
+ Simulate file download, pass in the file reading stream.
558
+ ```ts
559
+ import { createReadStream } from 'node:fs'
560
+ export default defineMock({
561
+ url: '/api/download',
562
+ // When you are unsure of the type, you can pass in the file name for internal parsing by the plugin.
563
+ type: 'my-app.dmg',
564
+ body: createReadStream('./my-app.dmg')
565
+ })
566
+ ```
567
+ ```html
568
+ <a href="/api/download" download="my-app.dmg">Download File</a>
569
+ ```
570
+
571
+ **exp:** Use `mockjs`:
546
572
  ```ts
547
573
  import Mock from 'mockjs'
548
574
  export default defineMock({
@@ -556,8 +582,8 @@ export default defineMock({
556
582
  ```
557
583
  You need installed `mockjs`
558
584
 
559
- ### Example 10:
560
- Use `response` to customize the response
585
+
586
+ **exp:** Use `response` to customize the response
561
587
  ```ts
562
588
  export default defineMock({
563
589
  url: '/api/test',
@@ -576,11 +602,10 @@ export default defineMock({
576
602
  })
577
603
  ```
578
604
 
579
- ### Example 11:
580
- Use json / json5
605
+
606
+ **exp:** Use json / json5
581
607
  ```json
582
608
  {
583
- // Support comment
584
609
  "url": "/api/test",
585
610
  "body": {
586
611
  "a": 1
@@ -588,9 +613,9 @@ Use json / json5
588
613
  }
589
614
  ```
590
615
 
591
- ### Example 12:
592
616
 
593
- multipart, upload file.
617
+
618
+ **exp:** multipart, upload file.
594
619
 
595
620
  use [`formidable`](https://www.npmjs.com/package/formidable#readme) to supported.
596
621
  ``` html
package/README.zh-CN.md CHANGED
@@ -255,12 +255,13 @@ export default defineApiMock({
255
255
  ```ts
256
256
  export default defineMock({
257
257
  /**
258
- * 请求地址,支持 `/api/user/:id` 格式
258
+ * 请求地址,支持 `/api/user/:id` 格式
259
+ * 插件通过 `path-to-regexp` 匹配路径
260
+ * @see https://github.com/pillarjs/path-to-regexp
259
261
  */
260
262
  url: '/api/test',
261
263
  /**
262
264
  * 接口支持的请求方法
263
- *
264
265
  * @type string | string[]
265
266
  * @default ['POST','GET']
266
267
  *
@@ -268,23 +269,19 @@ export default defineMock({
268
269
  method: ['GET', 'POST'],
269
270
  /**
270
271
  * 是否启用当前 mock请求
271
- *
272
272
  * 在实际场景中,我们一般只需要某几个mock接口生效,
273
273
  * 而不是所以mock接口都启用。
274
274
  * 对当前不需要mock的接口,可设置为 false
275
- *
276
275
  * @default true
277
276
  */
278
277
  enable: true,
279
278
  /**
280
279
  * 设置接口响应延迟, 单位:ms
281
- *
282
280
  * @default 0
283
281
  */
284
282
  delay: 1000,
285
283
  /**
286
284
  * 响应状态码
287
- *
288
285
  * @default 200
289
286
  */
290
287
  status: 200,
@@ -320,11 +317,8 @@ export default defineMock({
320
317
  refererQuery: {}
321
318
  },
322
319
  /**
323
- *
324
320
  * 响应状态 headers
325
- *
326
321
  * @type Record<string, any>
327
- *
328
322
  * @type (({ query, body, params, headers }) => Record<string, any>)
329
323
  * 入参部分为 请求相关信息
330
324
  */
@@ -342,6 +336,18 @@ export default defineMock({
342
336
  'cookie&option': ['cookie value', { path: '/', httpOnly: true }]
343
337
  },
344
338
 
339
+ /**
340
+ * 响应体数据类型, 可选值包括 `text, json, buffer`,
341
+ * 还支持`mime-db`中的包含的类型。
342
+ * 当响应体返回的是一个文件,而你不确定应该使用哪个类型时,可以将文件名作为值传入,
343
+ * 插件内部会根据文件名后缀查找匹配的`content-type`。
344
+ * 但如果是 `typescript`文件如 `a.ts`,可能不会被正确匹配为 `javascript`脚本,
345
+ * 你需要将 `a.ts` 修改为 `a.js`作为值传入才能正确识别。
346
+ * @see https://github.com/jshttp/mime-db
347
+ * @default 'json'
348
+ */
349
+ type: 'json',
350
+
345
351
  /**
346
352
  * 响应体数据
347
353
  * 定义返回的响应体数据内容。
@@ -360,10 +366,8 @@ export default defineMock({
360
366
  * 如果通过 body 配置不能解决mock需求,
361
367
  * 那么可以通过 配置 response,暴露http server 的接口,
362
368
  * 实现完全可控的自定义配置
363
- *
364
369
  * 在 req参数中,已内置了 query、body、params 的解析,
365
- * 你可以直接使用它们
366
- *
370
+ * 你可以直接使用它们。
367
371
  * 别忘了,需要通过 `res.end()` 返回响应体数据,
368
372
  * 或者需要跳过mock,那么别忘了调用 `next()`
369
373
  */
@@ -407,35 +411,38 @@ type Response = http.ServerResponse<http.IncomingMessage> & {
407
411
  ```
408
412
 
409
413
 
410
- > 注意:
414
+ > **注意:**
411
415
  >
412
416
  > 如果使用 json/json5 编写 mock文件,则不支持使用 `response` 方法,以及不支持使用其他字段的函数形式。
413
417
 
418
+ ## Example
419
+
414
420
  `mock/**/*.mock.{ts,js,mjs,cjs,json,json5}`
415
421
 
416
422
  查看更多示例: [example](/example/)
417
423
 
418
- #### 示例1:
419
- 命中 `/api/test` 请求,并返回一个 数据为空的响应体内容
424
+ **exp:** 命中 `/api/test` 请求,并返回一个 数据为空的响应体内容
420
425
  ```ts
421
426
  export default defineMock({
422
427
  url: '/api/test',
423
428
  })
424
429
  ```
425
430
 
426
- #### 示例2:
427
- 命中 `/api/test` 请求,并返回一个固定内容数据
431
+ **exp:** 命中 `/api/test` 请求,并返回一个固定内容数据
428
432
  ```ts
429
433
  export default defineMock({
430
434
  url: '/api/test',
431
- body: {
432
- a: 1
433
- }
435
+ body: { a: 1 },
436
+ })
437
+ ```
438
+ ```ts
439
+ export default defineMock({
440
+ url: '/api/test',
441
+ body: () => ({ a: 1 })
434
442
  })
435
443
  ```
436
444
 
437
- #### 示例3:
438
- 限定只允许 `GET` 请求
445
+ **exp:** 限定只允许 `GET` 请求
439
446
  ```ts
440
447
  export default defineMock({
441
448
  url: '/api/test',
@@ -443,69 +450,56 @@ export default defineMock({
443
450
  })
444
451
  ```
445
452
 
446
- #### 示例4:
447
- 在返回的响应头中,添加自定义header
453
+
454
+ **exp:** 在返回的响应头中,添加自定义 header 和 cookie
448
455
  ```ts
449
456
  export default defineMock({
450
457
  url: '/api/test',
451
- headers: {
452
- 'X-Custom': '12345678'
453
- }
458
+ headers: { 'X-Custom': '12345678' },
459
+ cookies: { 'my-cookie': '123456789' },
454
460
  })
455
461
  ```
456
462
  ```ts
457
463
  export default defineMock({
458
464
  url: '/api/test',
459
465
  headers({ query, body, params, headers }) {
460
- return {
461
- 'X-Custom': query.custom
462
- }
466
+ return { 'X-Custom': query.custom }
467
+ },
468
+ cookies() {
469
+ return { 'my-cookie': '123456789' }
463
470
  }
464
471
  })
465
472
  ```
466
473
 
467
- #### 示例5:
468
- 定义多个相同url请求mock,并使用验证器匹配生效规则
474
+ **exp:** 定义多个相同url请求mock,并使用验证器匹配生效规则
469
475
  ```ts
470
476
  export default defineMock([
471
477
  // 命中 /api/test?a=1
472
478
  {
473
479
  url: '/api/test',
474
480
  validator: {
475
- query: {
476
- a: 1
477
- }
481
+ query: { a: 1 },
478
482
  },
479
- body: {
480
- message: 'query.a === 1'
481
- }
483
+ body: { message: 'query.a === 1' },
482
484
  },
483
485
  // 命中 /api/test?a=2
484
486
  {
485
487
  url: '/api/test',
486
488
  validator: {
487
- query: {
488
- a: 2
489
- }
489
+ query: { a: 2 },
490
490
  },
491
- body: {
492
- message: 'query.a === 2'
493
- }
491
+ body: { message: 'query.a === 2' },
494
492
  },
495
493
  {
496
- /**
497
- * `?a=3` 将会解析到 `validator.query`
498
- */
494
+ // `?a=3` 将会解析到 `validator.query`
499
495
  url: '/api/test?a=3',
500
- body: {
501
- message: 'query.a == 3'
502
- }
496
+ body: { message: 'query.a == 3' },
503
497
  }
504
498
  ])
505
499
  ```
506
500
 
507
- #### 示例6:
508
- 延迟接口响应:
501
+
502
+ **exp:** 延迟接口响应:
509
503
  ```ts
510
504
  export default defineMock({
511
505
  url: '/api/test',
@@ -513,33 +507,64 @@ export default defineMock({
513
507
  })
514
508
  ```
515
509
 
516
- #### 示例7:
517
- 使接口请求失败
510
+ **exp:** 使接口请求失败
518
511
  ```ts
519
512
  export default defineMock({
520
513
  url: '/api/test',
521
- status: 504,
514
+ status: 502,
522
515
  statusText: 'Bad Gateway'
523
516
  })
524
517
  ```
525
518
 
526
- #### 示例8:
527
- 动态路由匹配
519
+ **exp:** 动态路由匹配
528
520
  ```ts
529
521
  export default defineMock({
530
522
  url: '/api/user/:userId',
531
523
  body({ params }) {
532
- return {
533
- userId: params.userId,
534
- }
524
+ return { userId: params.userId }
535
525
  }
536
526
  })
537
527
  ```
538
528
 
539
529
  路由中的 `userId`将会解析到 `request.params` 对象中.
540
530
 
541
- #### 示例9:
542
- 使用 `mockjs` 生成响应数据:
531
+ **exp:** 使用 buffer 响应数据
532
+ ```ts
533
+ // 由于 type 默认值是 json,虽然在传输过程中body使用buffer,
534
+ // 但是 content-type 还是为 json
535
+ export default defineMock({
536
+ url: 'api/buffer',
537
+ body: Buffer.from(JSON.stringify({ a: 1 }))
538
+ })
539
+ ```
540
+ ```ts
541
+ // 当 type 为 buffer 时,content-type 为 application/octet-stream,
542
+ // body 传入的数据会被转为 buffer
543
+ export default defineMock({
544
+ url: 'api/buffer',
545
+ type: 'buffer',
546
+ // 内部使用 Buffer.from(body) 进行转换
547
+ body: { a: 1 }
548
+ })
549
+ ```
550
+
551
+ **exp:** 响应文件类型
552
+
553
+ 模拟文件下载,传入文件读取流
554
+ ```ts
555
+ import { createReadStream } from 'node:fs'
556
+ export default defineMock({
557
+ url: '/api/download',
558
+ // 当你不确定类型,可传入文件名由插件内部进行解析
559
+ type: 'my-app.dmg',
560
+ body: createReadStream('./my-app.dmg')
561
+ })
562
+ ```
563
+ ```html
564
+ <a href="/api/download" download="my-app.dmg">下载文件</a>
565
+ ```
566
+
567
+ **exp:** 使用 `mockjs` 生成响应数据:
543
568
  ```ts
544
569
  import Mock from 'mockjs'
545
570
  export default defineMock({
@@ -553,8 +578,8 @@ export default defineMock({
553
578
  ```
554
579
  请先安装 `mockjs`
555
580
 
556
- ### 示例10:
557
- 使用 `response` 自定义响应
581
+
582
+ **exp:** 使用 `response` 自定义响应
558
583
  ```ts
559
584
  export default defineMock({
560
585
  url: '/api/test',
@@ -573,11 +598,10 @@ export default defineMock({
573
598
  })
574
599
  ```
575
600
 
576
- ### 示例11:
577
- 使用 json / json5
601
+
602
+ **exp:** 使用 json / json5
578
603
  ```json
579
604
  {
580
- // 支持 comment
581
605
  "url": "/api/test",
582
606
  "body": {
583
607
  "a": 1
@@ -585,9 +609,8 @@ export default defineMock({
585
609
  }
586
610
  ```
587
611
 
588
- ### Example 12:
589
612
 
590
- multipart, 文件上传.
613
+ **exp:** multipart, 文件上传.
591
614
 
592
615
  通过 [`formidable`](https://www.npmjs.com/package/formidable#readme) 支持。
593
616
  ``` html
package/dist/index.cjs CHANGED
@@ -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.1.2";
57
+ var version = "1.1.3";
58
58
 
59
59
  // src/esbuildPlugin.ts
60
60
  var import_promises = __toESM(require("fs/promises"), 1);
@@ -141,6 +141,8 @@ var import_path_to_regexp = require("path-to-regexp");
141
141
  var import_picocolors = __toESM(require("picocolors"), 1);
142
142
  var isArray = (val) => Array.isArray(val);
143
143
  var isFunction = (val) => typeof val === "function";
144
+ var isStream = (stream) => stream !== null && typeof stream === "object" && typeof stream.pipe === "function";
145
+ var isReadableStream = (stream) => isStream(stream) && stream.readable !== false && typeof stream._read === "function" && typeof stream._readableState === "object";
144
146
  function sleep(timeout) {
145
147
  return new Promise((resolve) => setTimeout(resolve, timeout));
146
148
  }
@@ -180,8 +182,9 @@ function lookupFile(dir, formats, options) {
180
182
  }
181
183
  var ensureProxies = (serverProxy = {}) => {
182
184
  const proxies = Object.keys(serverProxy).map((key) => {
185
+ var _a, _b;
183
186
  const value = serverProxy[key];
184
- return typeof value === "string" ? key : value.ws === true ? "" : key;
187
+ return typeof value === "string" ? key : value.ws || ((_a = value.target) == null ? void 0 : _a.toString().startsWith("ws:")) || ((_b = value.target) == null ? void 0 : _b.toString().startsWith("wss:")) ? "" : key;
185
188
  }).filter(Boolean);
186
189
  return proxies;
187
190
  };
@@ -363,9 +366,11 @@ async function buildMockEntry(inputFile, define, alias) {
363
366
  }
364
367
 
365
368
  // src/baseMiddleware.ts
369
+ var import_node_buffer = require("buffer");
366
370
  var import_node_url2 = require("url");
367
371
  var import_cookies = __toESM(require("cookies"), 1);
368
372
  var import_http_status = __toESM(require("http-status"), 1);
373
+ var mime = __toESM(require("mime-types"), 1);
369
374
  var import_path_to_regexp2 = require("path-to-regexp");
370
375
  var import_picocolors2 = __toESM(require("picocolors"), 1);
371
376
 
@@ -428,7 +433,6 @@ function baseMiddleware(mockLoader, { formidableOptions = {}, proxies, cookiesOp
428
433
  return async function(req, res, next) {
429
434
  const startTime = Date.now();
430
435
  const { query, pathname } = (0, import_node_url2.parse)(req.url, true);
431
- const { query: refererQuery } = (0, import_node_url2.parse)(req.headers.referer || "", true);
432
436
  if (!pathname || proxies.length === 0 || !proxies.some((context) => doesProxyContextMatchUrl(context, req.url))) {
433
437
  return next();
434
438
  }
@@ -438,6 +442,7 @@ function baseMiddleware(mockLoader, { formidableOptions = {}, proxies, cookiesOp
438
442
  });
439
443
  if (!mockUrl)
440
444
  return next();
445
+ const { query: refererQuery } = (0, import_node_url2.parse)(req.headers.referer || "", true);
441
446
  const reqBody = await parseReqBody(req, formidableOptions);
442
447
  const method = req.method.toUpperCase();
443
448
  const mock = fineMock(mockData[mockUrl], pathname, method, {
@@ -458,15 +463,22 @@ function baseMiddleware(mockLoader, { formidableOptions = {}, proxies, cookiesOp
458
463
  request.params = parseParams(mock.url, pathname);
459
464
  request.getCookie = cookies.get.bind(cookies);
460
465
  response.setCookie = cookies.set.bind(cookies);
461
- responseStatus(response, mock.status, mock.statusText);
462
- await provideHeaders(request, response, mock.headers);
463
- await provideCookies(request, response, mock.cookies);
464
- const { body, delay, response: responseFn } = mock;
466
+ const {
467
+ body,
468
+ delay,
469
+ type = "json",
470
+ response: responseFn,
471
+ status = 200,
472
+ statusText
473
+ } = mock;
474
+ responseStatus(response, status, statusText);
475
+ await provideHeaders(request, response, mock);
476
+ await provideCookies(request, response, mock);
465
477
  if (body) {
466
478
  try {
467
- const result = isFunction(body) ? await body(request) : mock.body;
479
+ const content = isFunction(body) ? await body(request) : body;
468
480
  await realDelay(startTime, delay);
469
- res.end(JSON.stringify(result));
481
+ sendData(response, content, type);
470
482
  } catch (e) {
471
483
  log.error(`${import_picocolors2.default.red("[body error]")} ${req.url}
472
484
  `, e);
@@ -514,34 +526,35 @@ function responseStatus(response, status = 200, statusText) {
514
526
  response.statusCode = status;
515
527
  response.statusMessage = statusText || getHTTPStatusText(status);
516
528
  }
517
- async function provideHeaders(req, res, headersOption) {
518
- res.setHeader("Content-Type", "application/json");
529
+ async function provideHeaders(req, res, { headers, type = "json" }) {
530
+ const contentType2 = mime.contentType(type) || mime.contentType(mime.lookup(type) || "");
531
+ contentType2 && res.setHeader("Content-Type", contentType2);
519
532
  res.setHeader("Cache-Control", "no-cache,max-age=0");
520
533
  res.setHeader("X-Mock", "generate by vite:plugin-mock-dev-server");
521
- if (!headersOption)
534
+ if (!headers)
522
535
  return;
523
536
  try {
524
- const headers = isFunction(headersOption) ? await headersOption(req) : headersOption;
525
- Object.keys(headers).forEach((key) => {
526
- res.setHeader(key, headers[key]);
537
+ const raw = isFunction(headers) ? await headers(req) : headers;
538
+ Object.keys(raw).forEach((key) => {
539
+ res.setHeader(key, raw[key]);
527
540
  });
528
541
  } catch (e) {
529
542
  log.error(`${import_picocolors2.default.red("[headers error]")} ${req.url}
530
543
  `, e);
531
544
  }
532
545
  }
533
- async function provideCookies(req, res, cookiesOption) {
534
- if (!cookiesOption)
546
+ async function provideCookies(req, res, { cookies }) {
547
+ if (!cookies)
535
548
  return;
536
549
  try {
537
- const cookies = isFunction(cookiesOption) ? await cookiesOption(req) : cookiesOption;
538
- Object.keys(cookies).forEach((key) => {
539
- const optional = cookies[key];
540
- if (isArray(optional)) {
541
- const [value, options] = optional;
550
+ const raw = isFunction(cookies) ? await cookies(req) : cookies;
551
+ Object.keys(raw).forEach((key) => {
552
+ const cookie = raw[key];
553
+ if (isArray(cookie)) {
554
+ const [value, options] = cookie;
542
555
  res.setCookie(key, value, options);
543
556
  } else {
544
- res.setCookie(key, optional);
557
+ res.setCookie(key, cookie);
545
558
  }
546
559
  });
547
560
  } catch (e) {
@@ -549,6 +562,16 @@ async function provideCookies(req, res, cookiesOption) {
549
562
  `, e);
550
563
  }
551
564
  }
565
+ function sendData(res, raw, type) {
566
+ if (isReadableStream(raw)) {
567
+ raw.pipe(res);
568
+ } else if (import_node_buffer.Buffer.isBuffer(raw)) {
569
+ res.end(type === "text" || type === "json" ? raw.toString("utf-8") : raw);
570
+ } else {
571
+ const content = typeof raw === "string" ? raw : JSON.stringify(raw);
572
+ res.end(type === "buffer" ? import_node_buffer.Buffer.from(content) : content);
573
+ }
574
+ }
552
575
  async function realDelay(startTime, delay) {
553
576
  if (!delay || delay <= 0)
554
577
  return;
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 { Readable } from 'node:stream';
3
4
  import Cookies from 'cookies';
4
5
  import formidable from 'formidable';
5
6
  import EventEmitter from 'node:events';
@@ -79,7 +80,7 @@ interface ServerBuildOption {
79
80
  }
80
81
  type Method = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'TRACE' | 'OPTIONS';
81
82
  type Headers = http.IncomingHttpHeaders;
82
- type ResponseBody = Record<string, any> | any[] | string | number | null;
83
+ type ResponseBody = Record<string, any> | any[] | string | number | Readable | Buffer | null;
83
84
  interface ExtraRequest {
84
85
  /**
85
86
  * The query string located after `?` in the request address has been parsed into JSON.
@@ -218,6 +219,37 @@ interface MockOptionsItem {
218
219
  * ```
219
220
  */
220
221
  cookies?: ResponseCookies | ResponseCookiesFn;
222
+ /**
223
+ * Response body data type, optional values include `text, json, buffer`.
224
+ *
225
+ * And also support types included in `mime-db`.
226
+ * When the response body returns a file and you are not sure which type to use,
227
+ * you can pass the file name as the value. The plugin will internally search for matching
228
+ * `content-type` based on the file name suffix.
229
+ *
230
+ * However, if it is a TypeScript file such as `a.ts`, it may not be correctly matched
231
+ * as a JavaScript script. You need to modify `a.ts` to `a.js` as the value passed
232
+ * in order to recognize it correctly.
233
+ *
234
+ * 响应体数据类型, 可选值包括 `text, json, buffer`,
235
+ *
236
+ * 还支持`mime-db`中的包含的类型。
237
+ * 当响应体返回的是一个文件,而你不确定应该使用哪个类型时,可以将文件名作为值传入,
238
+ * 插件内部会根据文件名后缀查找匹配的`content-type`。
239
+ *
240
+ * 但如果是 `typescript`文件如 `a.ts`,可能不会被正确匹配为 `javascript`脚本,
241
+ * 你需要将 `a.ts` 修改为 `a.js`作为值传入才能正确识别。
242
+ * @see [mime-db](https://github.com/jshttp/mime-db)
243
+ * @default 'json'
244
+ * @example
245
+ * ```txt
246
+ * json
247
+ * buffer
248
+ * my-app.dmg
249
+ * music.mp4
250
+ * ```
251
+ */
252
+ type?: 'text' | 'json' | 'buffer' | string;
221
253
  /**
222
254
  * Configure response body data content
223
255
  *
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.1.2";
12
+ var version = "1.1.3";
13
13
 
14
14
  // src/esbuildPlugin.ts
15
15
  import fsp from "fs/promises";
@@ -96,6 +96,8 @@ import { match } from "path-to-regexp";
96
96
  import colors from "picocolors";
97
97
  var isArray = (val) => Array.isArray(val);
98
98
  var isFunction = (val) => typeof val === "function";
99
+ var isStream = (stream) => stream !== null && typeof stream === "object" && typeof stream.pipe === "function";
100
+ var isReadableStream = (stream) => isStream(stream) && stream.readable !== false && typeof stream._read === "function" && typeof stream._readableState === "object";
99
101
  function sleep(timeout) {
100
102
  return new Promise((resolve) => setTimeout(resolve, timeout));
101
103
  }
@@ -135,8 +137,9 @@ function lookupFile(dir, formats, options) {
135
137
  }
136
138
  var ensureProxies = (serverProxy = {}) => {
137
139
  const proxies = Object.keys(serverProxy).map((key) => {
140
+ var _a, _b;
138
141
  const value = serverProxy[key];
139
- return typeof value === "string" ? key : value.ws === true ? "" : key;
142
+ return typeof value === "string" ? key : value.ws || ((_a = value.target) == null ? void 0 : _a.toString().startsWith("ws:")) || ((_b = value.target) == null ? void 0 : _b.toString().startsWith("wss:")) ? "" : key;
140
143
  }).filter(Boolean);
141
144
  return proxies;
142
145
  };
@@ -318,9 +321,11 @@ async function buildMockEntry(inputFile, define, alias) {
318
321
  }
319
322
 
320
323
  // src/baseMiddleware.ts
324
+ import { Buffer } from "buffer";
321
325
  import { parse as urlParse } from "url";
322
326
  import Cookies from "cookies";
323
327
  import HTTP_STATUS from "http-status";
328
+ import * as mime from "mime-types";
324
329
  import { pathToRegexp } from "path-to-regexp";
325
330
  import colors2 from "picocolors";
326
331
 
@@ -383,7 +388,6 @@ function baseMiddleware(mockLoader, { formidableOptions = {}, proxies, cookiesOp
383
388
  return async function(req, res, next) {
384
389
  const startTime = Date.now();
385
390
  const { query, pathname } = urlParse(req.url, true);
386
- const { query: refererQuery } = urlParse(req.headers.referer || "", true);
387
391
  if (!pathname || proxies.length === 0 || !proxies.some((context) => doesProxyContextMatchUrl(context, req.url))) {
388
392
  return next();
389
393
  }
@@ -393,6 +397,7 @@ function baseMiddleware(mockLoader, { formidableOptions = {}, proxies, cookiesOp
393
397
  });
394
398
  if (!mockUrl)
395
399
  return next();
400
+ const { query: refererQuery } = urlParse(req.headers.referer || "", true);
396
401
  const reqBody = await parseReqBody(req, formidableOptions);
397
402
  const method = req.method.toUpperCase();
398
403
  const mock = fineMock(mockData[mockUrl], pathname, method, {
@@ -413,15 +418,22 @@ function baseMiddleware(mockLoader, { formidableOptions = {}, proxies, cookiesOp
413
418
  request.params = parseParams(mock.url, pathname);
414
419
  request.getCookie = cookies.get.bind(cookies);
415
420
  response.setCookie = cookies.set.bind(cookies);
416
- responseStatus(response, mock.status, mock.statusText);
417
- await provideHeaders(request, response, mock.headers);
418
- await provideCookies(request, response, mock.cookies);
419
- const { body, delay, response: responseFn } = mock;
421
+ const {
422
+ body,
423
+ delay,
424
+ type = "json",
425
+ response: responseFn,
426
+ status = 200,
427
+ statusText
428
+ } = mock;
429
+ responseStatus(response, status, statusText);
430
+ await provideHeaders(request, response, mock);
431
+ await provideCookies(request, response, mock);
420
432
  if (body) {
421
433
  try {
422
- const result = isFunction(body) ? await body(request) : mock.body;
434
+ const content = isFunction(body) ? await body(request) : body;
423
435
  await realDelay(startTime, delay);
424
- res.end(JSON.stringify(result));
436
+ sendData(response, content, type);
425
437
  } catch (e) {
426
438
  log.error(`${colors2.red("[body error]")} ${req.url}
427
439
  `, e);
@@ -469,34 +481,35 @@ function responseStatus(response, status = 200, statusText) {
469
481
  response.statusCode = status;
470
482
  response.statusMessage = statusText || getHTTPStatusText(status);
471
483
  }
472
- async function provideHeaders(req, res, headersOption) {
473
- res.setHeader("Content-Type", "application/json");
484
+ async function provideHeaders(req, res, { headers, type = "json" }) {
485
+ const contentType2 = mime.contentType(type) || mime.contentType(mime.lookup(type) || "");
486
+ contentType2 && res.setHeader("Content-Type", contentType2);
474
487
  res.setHeader("Cache-Control", "no-cache,max-age=0");
475
488
  res.setHeader("X-Mock", "generate by vite:plugin-mock-dev-server");
476
- if (!headersOption)
489
+ if (!headers)
477
490
  return;
478
491
  try {
479
- const headers = isFunction(headersOption) ? await headersOption(req) : headersOption;
480
- Object.keys(headers).forEach((key) => {
481
- res.setHeader(key, headers[key]);
492
+ const raw = isFunction(headers) ? await headers(req) : headers;
493
+ Object.keys(raw).forEach((key) => {
494
+ res.setHeader(key, raw[key]);
482
495
  });
483
496
  } catch (e) {
484
497
  log.error(`${colors2.red("[headers error]")} ${req.url}
485
498
  `, e);
486
499
  }
487
500
  }
488
- async function provideCookies(req, res, cookiesOption) {
489
- if (!cookiesOption)
501
+ async function provideCookies(req, res, { cookies }) {
502
+ if (!cookies)
490
503
  return;
491
504
  try {
492
- const cookies = isFunction(cookiesOption) ? await cookiesOption(req) : cookiesOption;
493
- Object.keys(cookies).forEach((key) => {
494
- const optional = cookies[key];
495
- if (isArray(optional)) {
496
- const [value, options] = optional;
505
+ const raw = isFunction(cookies) ? await cookies(req) : cookies;
506
+ Object.keys(raw).forEach((key) => {
507
+ const cookie = raw[key];
508
+ if (isArray(cookie)) {
509
+ const [value, options] = cookie;
497
510
  res.setCookie(key, value, options);
498
511
  } else {
499
- res.setCookie(key, optional);
512
+ res.setCookie(key, cookie);
500
513
  }
501
514
  });
502
515
  } catch (e) {
@@ -504,6 +517,16 @@ async function provideCookies(req, res, cookiesOption) {
504
517
  `, e);
505
518
  }
506
519
  }
520
+ function sendData(res, raw, type) {
521
+ if (isReadableStream(raw)) {
522
+ raw.pipe(res);
523
+ } else if (Buffer.isBuffer(raw)) {
524
+ res.end(type === "text" || type === "json" ? raw.toString("utf-8") : raw);
525
+ } else {
526
+ const content = typeof raw === "string" ? raw : JSON.stringify(raw);
527
+ res.end(type === "buffer" ? Buffer.from(content) : content);
528
+ }
529
+ }
507
530
  async function realDelay(startTime, delay) {
508
531
  if (!delay || delay <= 0)
509
532
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-mock-dev-server",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "keywords": [
5
5
  "vite",
6
6
  "plugin",
@@ -40,6 +40,7 @@
40
40
  "http-status": "^1.6.2",
41
41
  "is-core-module": "^2.12.0",
42
42
  "json5": "^2.2.3",
43
+ "mime-types": "^2.1.35",
43
44
  "path-to-regexp": "^6.2.1",
44
45
  "picocolors": "^1.0.0"
45
46
  },
@@ -51,15 +52,16 @@
51
52
  "@types/debug": "^4.1.7",
52
53
  "@types/formidable": "^2.0.5",
53
54
  "@types/is-core-module": "^2.2.0",
54
- "@types/node": "^18.15.12",
55
+ "@types/mime-types": "^2.1.1",
56
+ "@types/node": "^18.15.13",
55
57
  "bumpp": "^9.1.0",
56
58
  "conventional-changelog-cli": "^2.2.2",
57
- "eslint": "^8.38.0",
59
+ "eslint": "^8.39.0",
58
60
  "mockjs": "^1.1.0",
59
61
  "prettier": "^2.8.7",
60
62
  "tsup": "^6.7.0",
61
63
  "typescript": "^5.0.4",
62
- "vite": "^4.3.0",
64
+ "vite": "^4.3.1",
63
65
  "vitepress": "1.0.0-alpha.73",
64
66
  "vue": "^3.2.47"
65
67
  },