vite-plugin-mock-dev-server 1.1.2 → 1.1.4
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 +99 -73
- package/README.zh-CN.md +97 -73
- package/dist/index.cjs +66 -34
- package/dist/index.d.ts +36 -5
- package/dist/index.js +66 -34
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -30,18 +30,19 @@
|
|
|
30
30
|
- 🧲 Not injection-based, non-intrusive to client code.
|
|
31
31
|
- 💡 ESModule/commonjs.
|
|
32
32
|
- 🦾 Typescript.
|
|
33
|
+
- 🔥 HMR
|
|
33
34
|
- 🏷 Support `json` / `json5`.
|
|
34
35
|
- 📦 Auto import mock file.
|
|
35
36
|
- 🎨 Support any lib,like `mockjs`,or not use it.
|
|
36
37
|
- 📥 Path rule matching, request parameter matching.
|
|
37
38
|
- ⚙️ Support Enabled/Disabled any one of api mock
|
|
38
|
-
-
|
|
39
|
+
- 📀 Supports response body content type such as `text/json/buffer/stream`.
|
|
39
40
|
- ⚖️ Use `server.proxy`
|
|
40
41
|
- 🍕 Support `viteConfig.define` in mock file.
|
|
41
|
-
- ⚓️ Support `resolve.alias` in mock file.
|
|
42
|
+
- ⚓️ Support `viteConfig.resolve.alias` in mock file.
|
|
43
|
+
- 🌈 Support `vite preview` mode.
|
|
42
44
|
- 📤 Support `multipart` content-type,mock upload file.
|
|
43
|
-
-
|
|
44
|
-
- 🌈 Support `vite preview` mode
|
|
45
|
+
- 📥 Support mock download file.
|
|
45
46
|
- 🗂 Support building small independent deployable mock services.
|
|
46
47
|
|
|
47
48
|
|
|
@@ -260,11 +261,12 @@ export default defineApiMock({
|
|
|
260
261
|
export default defineMock({
|
|
261
262
|
/**
|
|
262
263
|
* Request address, supports the `/api/user/:id` format.
|
|
264
|
+
* The plugin matches the path through `path-to-regexp`.
|
|
265
|
+
* @see https://github.com/pillarjs/path-to-regexp
|
|
263
266
|
*/
|
|
264
267
|
url: '/api/test',
|
|
265
268
|
/**
|
|
266
269
|
* Supported request methods of the interface.
|
|
267
|
-
*
|
|
268
270
|
* @type string | string[]
|
|
269
271
|
* @default ['POST','GET']
|
|
270
272
|
*
|
|
@@ -282,13 +284,11 @@ export default defineMock({
|
|
|
282
284
|
enable: true,
|
|
283
285
|
/**
|
|
284
286
|
* Set interface response delay, unit: ms.
|
|
285
|
-
*
|
|
286
287
|
* @default 0
|
|
287
288
|
*/
|
|
288
289
|
delay: 1000,
|
|
289
290
|
/**
|
|
290
291
|
* response status
|
|
291
|
-
*
|
|
292
292
|
* @default 200
|
|
293
293
|
*/
|
|
294
294
|
status: 200,
|
|
@@ -309,8 +309,8 @@ export default defineMock({
|
|
|
309
309
|
* then the validation method is to strictly compare whether the `value`
|
|
310
310
|
* of each `key` in headers/body/query/params in the request interface is exactly equal.
|
|
311
311
|
* If they are all equal, then the validation passes.
|
|
312
|
-
*
|
|
313
312
|
* @type ({ headers: object; body: object; query: object; params: object; refererQuery: object }) => boolean
|
|
313
|
+
*
|
|
314
314
|
* If the validator passed in is a function,
|
|
315
315
|
* then the data related to the requested interface will be provided as input parameters
|
|
316
316
|
* for users to perform custom validation and return a boolean.
|
|
@@ -329,11 +329,8 @@ export default defineMock({
|
|
|
329
329
|
refererQuery: {}
|
|
330
330
|
},
|
|
331
331
|
/**
|
|
332
|
-
*
|
|
333
332
|
* response headers
|
|
334
|
-
*
|
|
335
333
|
* @type Record<string, any>
|
|
336
|
-
*
|
|
337
334
|
* @type (({ query, body, params, headers }) => Record<string, any>)
|
|
338
335
|
*/
|
|
339
336
|
headers: {
|
|
@@ -349,17 +346,28 @@ export default defineMock({
|
|
|
349
346
|
'your-cookie': 'your cookie value',
|
|
350
347
|
'cookie&option': ['cookie value', { path: '/', httpOnly: true }]
|
|
351
348
|
},
|
|
349
|
+
/**
|
|
350
|
+
* Response body data type, optional values include `text, json, buffer`.
|
|
351
|
+
* And also support types included in `mime-db`.
|
|
352
|
+
* When the response body returns a file and you are not sure which type to use,
|
|
353
|
+
* you can pass the file name as the value. The plugin will internally search for matching
|
|
354
|
+
* `content-type` based on the file name suffix.
|
|
355
|
+
* However, if it is a TypeScript file such as `a.ts`, it may not be correctly matched
|
|
356
|
+
* as a JavaScript script. You need to modify `a.ts` to `a.js` as the value passed
|
|
357
|
+
* in order to recognize it correctly.
|
|
358
|
+
* @see https://github.com/jshttp/mime-db
|
|
359
|
+
* @default 'json'
|
|
360
|
+
*/
|
|
361
|
+
type: 'json',
|
|
352
362
|
|
|
353
363
|
/**
|
|
354
364
|
* Response Body
|
|
355
|
-
* Support `string/number/array/object`
|
|
365
|
+
* Support `string/number/array/object/buffer/ReadableStream`
|
|
356
366
|
* You can also use libraries such as' mockjs' to generate data content
|
|
357
|
-
*
|
|
358
|
-
* @type string | number | array | object
|
|
359
|
-
*
|
|
367
|
+
* @type string | number | array | object | ReadableStream | buffer
|
|
360
368
|
* @type (request: { headers, query, body, params, refererQuery, getCookie }) => any | Promise<any>
|
|
361
369
|
*/
|
|
362
|
-
body:
|
|
370
|
+
body: '',
|
|
363
371
|
|
|
364
372
|
/**
|
|
365
373
|
* If the mock requirement cannot be solved through `body` configuration,
|
|
@@ -408,37 +416,35 @@ type Response = http.ServerResponse<http.IncomingMessage> & {
|
|
|
408
416
|
```
|
|
409
417
|
|
|
410
418
|
|
|
411
|
-
> Tips
|
|
419
|
+
> **Tips:**
|
|
412
420
|
>
|
|
413
421
|
> If you write mock files using json/json5,
|
|
414
422
|
> the 'response' method is not supported,
|
|
415
423
|
> as is the function form that uses other fields.
|
|
416
424
|
|
|
425
|
+
## Example
|
|
426
|
+
|
|
417
427
|
`mock/**/*.mock.{ts,js,mjs,cjs,json,json5}`
|
|
418
428
|
|
|
419
429
|
See more examples: [example](/example/)
|
|
420
430
|
|
|
421
|
-
|
|
422
|
-
Match `/api/test`,And returns a response body content with empty data
|
|
431
|
+
**exp:** Match `/api/test`,And returns a response body content with empty data
|
|
423
432
|
```ts
|
|
424
433
|
export default defineMock({
|
|
425
434
|
url: '/api/test',
|
|
426
435
|
})
|
|
427
436
|
```
|
|
428
437
|
|
|
429
|
-
|
|
430
|
-
Match `/api/test` ,And returns a static content data
|
|
438
|
+
**exp:** Match `/api/test` ,And returns a static content data
|
|
431
439
|
```ts
|
|
432
440
|
export default defineMock({
|
|
433
441
|
url: '/api/test',
|
|
434
|
-
body: {
|
|
435
|
-
a: 1
|
|
436
|
-
}
|
|
442
|
+
body: { a: 1 },
|
|
437
443
|
})
|
|
438
444
|
```
|
|
439
445
|
|
|
440
|
-
|
|
441
|
-
Only Support `GET` Method
|
|
446
|
+
|
|
447
|
+
**exp:** Only Support `GET` Method
|
|
442
448
|
```ts
|
|
443
449
|
export default defineMock({
|
|
444
450
|
url: '/api/test',
|
|
@@ -446,69 +452,56 @@ export default defineMock({
|
|
|
446
452
|
})
|
|
447
453
|
```
|
|
448
454
|
|
|
449
|
-
|
|
450
|
-
In the response header, add a custom header
|
|
455
|
+
**exp:** In the response header, add a custom header and cookie
|
|
451
456
|
```ts
|
|
452
457
|
export default defineMock({
|
|
453
458
|
url: '/api/test',
|
|
454
|
-
headers: {
|
|
455
|
-
|
|
456
|
-
}
|
|
459
|
+
headers: { 'X-Custom': '12345678' },
|
|
460
|
+
cookies: { 'my-cookie': '123456789' },
|
|
457
461
|
})
|
|
458
462
|
```
|
|
459
463
|
```ts
|
|
460
464
|
export default defineMock({
|
|
461
465
|
url: '/api/test',
|
|
462
466
|
headers({ query, body, params, headers }) {
|
|
463
|
-
return {
|
|
464
|
-
|
|
465
|
-
|
|
467
|
+
return { 'X-Custom': query.custom }
|
|
468
|
+
},
|
|
469
|
+
cookies() {
|
|
470
|
+
return { 'my-cookie': '123456789' }
|
|
466
471
|
}
|
|
467
472
|
})
|
|
468
473
|
```
|
|
469
474
|
|
|
470
|
-
|
|
471
|
-
Define multiple mock requests for the same url and match valid rules with validators
|
|
475
|
+
|
|
476
|
+
**exp:** Define multiple mock requests for the same url and match valid rules with validators
|
|
472
477
|
```ts
|
|
473
478
|
export default defineMock([
|
|
474
479
|
// Match /api/test?a=1
|
|
475
480
|
{
|
|
476
481
|
url: '/api/test',
|
|
477
482
|
validator: {
|
|
478
|
-
query: {
|
|
479
|
-
a: 1
|
|
480
|
-
}
|
|
483
|
+
query: { a: 1 },
|
|
481
484
|
},
|
|
482
|
-
body: {
|
|
483
|
-
message: 'query.a == 1'
|
|
484
|
-
}
|
|
485
|
+
body: { message: 'query.a == 1' },
|
|
485
486
|
},
|
|
486
487
|
// Match /api/test?a=2
|
|
487
488
|
{
|
|
488
489
|
url: '/api/test',
|
|
489
490
|
validator: {
|
|
490
|
-
query: {
|
|
491
|
-
a: 2
|
|
492
|
-
}
|
|
491
|
+
query: { a: 2 },
|
|
493
492
|
},
|
|
494
|
-
body: {
|
|
495
|
-
message: 'query.a == 2'
|
|
496
|
-
}
|
|
493
|
+
body: { message: 'query.a == 2' },
|
|
497
494
|
},
|
|
498
495
|
{
|
|
499
|
-
|
|
500
|
-
* `?a=3` will resolve to `validator.query`
|
|
501
|
-
*/
|
|
496
|
+
// `?a=3` will resolve to `validator.query`
|
|
502
497
|
url: '/api/test?a=3',
|
|
503
|
-
body: {
|
|
504
|
-
message: 'query.a == 3'
|
|
505
|
-
}
|
|
498
|
+
body: { message: 'query.a == 3' }
|
|
506
499
|
}
|
|
507
500
|
])
|
|
508
501
|
```
|
|
509
502
|
|
|
510
|
-
|
|
511
|
-
Response Delay
|
|
503
|
+
|
|
504
|
+
**exp:** Response Delay
|
|
512
505
|
```ts
|
|
513
506
|
export default defineMock({
|
|
514
507
|
url: '/api/test',
|
|
@@ -516,33 +509,67 @@ export default defineMock({
|
|
|
516
509
|
})
|
|
517
510
|
```
|
|
518
511
|
|
|
519
|
-
|
|
520
|
-
The interface request failed
|
|
512
|
+
|
|
513
|
+
**exp:** The interface request failed
|
|
521
514
|
```ts
|
|
522
515
|
export default defineMock({
|
|
523
516
|
url: '/api/test',
|
|
524
|
-
status:
|
|
517
|
+
status: 502,
|
|
525
518
|
statusText: 'Bad Gateway'
|
|
526
519
|
})
|
|
527
520
|
```
|
|
528
521
|
|
|
529
|
-
|
|
530
|
-
Dynamic route matching
|
|
522
|
+
|
|
523
|
+
**exp:** Dynamic route matching
|
|
531
524
|
```ts
|
|
532
525
|
export default defineMock({
|
|
533
526
|
url: '/api/user/:userId',
|
|
534
527
|
body({ params }) {
|
|
535
|
-
return {
|
|
536
|
-
userId: params.userId,
|
|
537
|
-
}
|
|
528
|
+
return { userId: params.userId }
|
|
538
529
|
}
|
|
539
530
|
})
|
|
540
531
|
```
|
|
541
532
|
|
|
542
533
|
The `userId` in the route will be resolved into the `request.params` object.
|
|
543
534
|
|
|
544
|
-
|
|
545
|
-
|
|
535
|
+
**exp:** Use buffer to respond data
|
|
536
|
+
```ts
|
|
537
|
+
// Since the default value of type is json,
|
|
538
|
+
// although buffer is used for body during transmission,
|
|
539
|
+
// the content-type is still json.
|
|
540
|
+
export default defineMock({
|
|
541
|
+
url: 'api/buffer',
|
|
542
|
+
body: Buffer.from(JSON.stringify({ a: 1 }))
|
|
543
|
+
})
|
|
544
|
+
```
|
|
545
|
+
```ts
|
|
546
|
+
// When the type is buffer, the content-type is application/octet-stream.
|
|
547
|
+
// The data passed in through body will be converted to a buffer.
|
|
548
|
+
export default defineMock({
|
|
549
|
+
url: 'api/buffer',
|
|
550
|
+
type: 'buffer',
|
|
551
|
+
// Convert using Buffer.from(body) for internal use
|
|
552
|
+
body: { a: 1 }
|
|
553
|
+
})
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
**exp:** Response file type
|
|
557
|
+
|
|
558
|
+
Simulate file download, pass in the file reading stream.
|
|
559
|
+
```ts
|
|
560
|
+
import { createReadStream } from 'node:fs'
|
|
561
|
+
export default defineMock({
|
|
562
|
+
url: '/api/download',
|
|
563
|
+
// When you are unsure of the type, you can pass in the file name for internal parsing by the plugin.
|
|
564
|
+
type: 'my-app.dmg',
|
|
565
|
+
body: () => createReadStream('./my-app.dmg')
|
|
566
|
+
})
|
|
567
|
+
```
|
|
568
|
+
```html
|
|
569
|
+
<a href="/api/download" download="my-app.dmg">Download File</a>
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
**exp:** Use `mockjs`:
|
|
546
573
|
```ts
|
|
547
574
|
import Mock from 'mockjs'
|
|
548
575
|
export default defineMock({
|
|
@@ -556,8 +583,8 @@ export default defineMock({
|
|
|
556
583
|
```
|
|
557
584
|
You need installed `mockjs`
|
|
558
585
|
|
|
559
|
-
|
|
560
|
-
Use `response` to customize the response
|
|
586
|
+
|
|
587
|
+
**exp:** Use `response` to customize the response
|
|
561
588
|
```ts
|
|
562
589
|
export default defineMock({
|
|
563
590
|
url: '/api/test',
|
|
@@ -576,11 +603,10 @@ export default defineMock({
|
|
|
576
603
|
})
|
|
577
604
|
```
|
|
578
605
|
|
|
579
|
-
|
|
580
|
-
Use json / json5
|
|
606
|
+
|
|
607
|
+
**exp:** Use json / json5
|
|
581
608
|
```json
|
|
582
609
|
{
|
|
583
|
-
// Support comment
|
|
584
610
|
"url": "/api/test",
|
|
585
611
|
"body": {
|
|
586
612
|
"a": 1
|
|
@@ -588,9 +614,9 @@ Use json / json5
|
|
|
588
614
|
}
|
|
589
615
|
```
|
|
590
616
|
|
|
591
|
-
### Example 12:
|
|
592
617
|
|
|
593
|
-
|
|
618
|
+
|
|
619
|
+
**exp:** multipart, upload file.
|
|
594
620
|
|
|
595
621
|
use [`formidable`](https://www.npmjs.com/package/formidable#readme) to supported.
|
|
596
622
|
``` html
|
package/README.zh-CN.md
CHANGED
|
@@ -31,18 +31,19 @@
|
|
|
31
31
|
- 🧲 非注入式,对客户端代码无侵入
|
|
32
32
|
- 💡 ESModule/commonjs
|
|
33
33
|
- 🦾 Typescript
|
|
34
|
+
- 🔥 热更新
|
|
34
35
|
- 🏷 支持 `json` / `json5` 编写 mock 数据
|
|
35
36
|
- 📦 自动加载 mock 文件
|
|
36
37
|
- 🎨 可选择你喜欢的任意用于生成mock数据库,如 `mockjs`,或者不使用其他库
|
|
37
38
|
- 📥 路径规则匹配,请求参数匹配
|
|
38
39
|
- ⚙️ 随意开启或关闭对某个接口的 mock配置
|
|
39
|
-
-
|
|
40
|
+
- - 📀 支持多种响应体数据类型,包括 `text/json/buffer/stream`.
|
|
40
41
|
- ⚖️ 使用 `server.proxy` 配置
|
|
41
42
|
- 🍕 支持在 mock文件中使用 `viteConfig.define`配置字段
|
|
42
|
-
- ⚓️
|
|
43
|
-
- 📤 支持 multipart 类型,模拟文件上传
|
|
44
|
-
- 🍪 支持 cookies
|
|
43
|
+
- ⚓️ 支持在 mock文件中使用 `viteConfig.resolve.alias` 路径别名
|
|
45
44
|
- 🌈 支持 `vite preview` 模式
|
|
45
|
+
- 📤 支持 multipart 类型,模拟文件上传
|
|
46
|
+
- 📥 支持模拟文件下载
|
|
46
47
|
- 🗂 支持构建可独立部署的小型mock服务
|
|
47
48
|
|
|
48
49
|
|
|
@@ -255,12 +256,13 @@ export default defineApiMock({
|
|
|
255
256
|
```ts
|
|
256
257
|
export default defineMock({
|
|
257
258
|
/**
|
|
258
|
-
* 请求地址,支持 `/api/user/:id` 格式
|
|
259
|
+
* 请求地址,支持 `/api/user/:id` 格式
|
|
260
|
+
* 插件通过 `path-to-regexp` 匹配路径
|
|
261
|
+
* @see https://github.com/pillarjs/path-to-regexp
|
|
259
262
|
*/
|
|
260
263
|
url: '/api/test',
|
|
261
264
|
/**
|
|
262
265
|
* 接口支持的请求方法
|
|
263
|
-
*
|
|
264
266
|
* @type string | string[]
|
|
265
267
|
* @default ['POST','GET']
|
|
266
268
|
*
|
|
@@ -268,23 +270,19 @@ export default defineMock({
|
|
|
268
270
|
method: ['GET', 'POST'],
|
|
269
271
|
/**
|
|
270
272
|
* 是否启用当前 mock请求
|
|
271
|
-
*
|
|
272
273
|
* 在实际场景中,我们一般只需要某几个mock接口生效,
|
|
273
274
|
* 而不是所以mock接口都启用。
|
|
274
275
|
* 对当前不需要mock的接口,可设置为 false
|
|
275
|
-
*
|
|
276
276
|
* @default true
|
|
277
277
|
*/
|
|
278
278
|
enable: true,
|
|
279
279
|
/**
|
|
280
280
|
* 设置接口响应延迟, 单位:ms
|
|
281
|
-
*
|
|
282
281
|
* @default 0
|
|
283
282
|
*/
|
|
284
283
|
delay: 1000,
|
|
285
284
|
/**
|
|
286
285
|
* 响应状态码
|
|
287
|
-
*
|
|
288
286
|
* @default 200
|
|
289
287
|
*/
|
|
290
288
|
status: 200,
|
|
@@ -320,11 +318,8 @@ export default defineMock({
|
|
|
320
318
|
refererQuery: {}
|
|
321
319
|
},
|
|
322
320
|
/**
|
|
323
|
-
*
|
|
324
321
|
* 响应状态 headers
|
|
325
|
-
*
|
|
326
322
|
* @type Record<string, any>
|
|
327
|
-
*
|
|
328
323
|
* @type (({ query, body, params, headers }) => Record<string, any>)
|
|
329
324
|
* 入参部分为 请求相关信息
|
|
330
325
|
*/
|
|
@@ -342,6 +337,18 @@ export default defineMock({
|
|
|
342
337
|
'cookie&option': ['cookie value', { path: '/', httpOnly: true }]
|
|
343
338
|
},
|
|
344
339
|
|
|
340
|
+
/**
|
|
341
|
+
* 响应体数据类型, 可选值包括 `text, json, buffer`,
|
|
342
|
+
* 还支持`mime-db`中的包含的类型。
|
|
343
|
+
* 当响应体返回的是一个文件,而你不确定应该使用哪个类型时,可以将文件名作为值传入,
|
|
344
|
+
* 插件内部会根据文件名后缀查找匹配的`content-type`。
|
|
345
|
+
* 但如果是 `typescript`文件如 `a.ts`,可能不会被正确匹配为 `javascript`脚本,
|
|
346
|
+
* 你需要将 `a.ts` 修改为 `a.js`作为值传入才能正确识别。
|
|
347
|
+
* @see https://github.com/jshttp/mime-db
|
|
348
|
+
* @default 'json'
|
|
349
|
+
*/
|
|
350
|
+
type: 'json',
|
|
351
|
+
|
|
345
352
|
/**
|
|
346
353
|
* 响应体数据
|
|
347
354
|
* 定义返回的响应体数据内容。
|
|
@@ -360,10 +367,8 @@ export default defineMock({
|
|
|
360
367
|
* 如果通过 body 配置不能解决mock需求,
|
|
361
368
|
* 那么可以通过 配置 response,暴露http server 的接口,
|
|
362
369
|
* 实现完全可控的自定义配置
|
|
363
|
-
*
|
|
364
370
|
* 在 req参数中,已内置了 query、body、params 的解析,
|
|
365
|
-
*
|
|
366
|
-
*
|
|
371
|
+
* 你可以直接使用它们。
|
|
367
372
|
* 别忘了,需要通过 `res.end()` 返回响应体数据,
|
|
368
373
|
* 或者需要跳过mock,那么别忘了调用 `next()`
|
|
369
374
|
*/
|
|
@@ -407,35 +412,38 @@ type Response = http.ServerResponse<http.IncomingMessage> & {
|
|
|
407
412
|
```
|
|
408
413
|
|
|
409
414
|
|
|
410
|
-
>
|
|
415
|
+
> **注意:**
|
|
411
416
|
>
|
|
412
417
|
> 如果使用 json/json5 编写 mock文件,则不支持使用 `response` 方法,以及不支持使用其他字段的函数形式。
|
|
413
418
|
|
|
419
|
+
## Example
|
|
420
|
+
|
|
414
421
|
`mock/**/*.mock.{ts,js,mjs,cjs,json,json5}`
|
|
415
422
|
|
|
416
423
|
查看更多示例: [example](/example/)
|
|
417
424
|
|
|
418
|
-
|
|
419
|
-
命中 `/api/test` 请求,并返回一个 数据为空的响应体内容
|
|
425
|
+
**exp:** 命中 `/api/test` 请求,并返回一个 数据为空的响应体内容
|
|
420
426
|
```ts
|
|
421
427
|
export default defineMock({
|
|
422
428
|
url: '/api/test',
|
|
423
429
|
})
|
|
424
430
|
```
|
|
425
431
|
|
|
426
|
-
|
|
427
|
-
命中 `/api/test` 请求,并返回一个固定内容数据
|
|
432
|
+
**exp:** 命中 `/api/test` 请求,并返回一个固定内容数据
|
|
428
433
|
```ts
|
|
429
434
|
export default defineMock({
|
|
430
435
|
url: '/api/test',
|
|
431
|
-
body: {
|
|
432
|
-
|
|
433
|
-
|
|
436
|
+
body: { a: 1 },
|
|
437
|
+
})
|
|
438
|
+
```
|
|
439
|
+
```ts
|
|
440
|
+
export default defineMock({
|
|
441
|
+
url: '/api/test',
|
|
442
|
+
body: () => ({ a: 1 })
|
|
434
443
|
})
|
|
435
444
|
```
|
|
436
445
|
|
|
437
|
-
|
|
438
|
-
限定只允许 `GET` 请求
|
|
446
|
+
**exp:** 限定只允许 `GET` 请求
|
|
439
447
|
```ts
|
|
440
448
|
export default defineMock({
|
|
441
449
|
url: '/api/test',
|
|
@@ -443,69 +451,56 @@ export default defineMock({
|
|
|
443
451
|
})
|
|
444
452
|
```
|
|
445
453
|
|
|
446
|
-
|
|
447
|
-
在返回的响应头中,添加自定义header
|
|
454
|
+
|
|
455
|
+
**exp:** 在返回的响应头中,添加自定义 header 和 cookie
|
|
448
456
|
```ts
|
|
449
457
|
export default defineMock({
|
|
450
458
|
url: '/api/test',
|
|
451
|
-
headers: {
|
|
452
|
-
|
|
453
|
-
}
|
|
459
|
+
headers: { 'X-Custom': '12345678' },
|
|
460
|
+
cookies: { 'my-cookie': '123456789' },
|
|
454
461
|
})
|
|
455
462
|
```
|
|
456
463
|
```ts
|
|
457
464
|
export default defineMock({
|
|
458
465
|
url: '/api/test',
|
|
459
466
|
headers({ query, body, params, headers }) {
|
|
460
|
-
return {
|
|
461
|
-
|
|
462
|
-
|
|
467
|
+
return { 'X-Custom': query.custom }
|
|
468
|
+
},
|
|
469
|
+
cookies() {
|
|
470
|
+
return { 'my-cookie': '123456789' }
|
|
463
471
|
}
|
|
464
472
|
})
|
|
465
473
|
```
|
|
466
474
|
|
|
467
|
-
|
|
468
|
-
定义多个相同url请求mock,并使用验证器匹配生效规则
|
|
475
|
+
**exp:** 定义多个相同url请求mock,并使用验证器匹配生效规则
|
|
469
476
|
```ts
|
|
470
477
|
export default defineMock([
|
|
471
478
|
// 命中 /api/test?a=1
|
|
472
479
|
{
|
|
473
480
|
url: '/api/test',
|
|
474
481
|
validator: {
|
|
475
|
-
query: {
|
|
476
|
-
a: 1
|
|
477
|
-
}
|
|
482
|
+
query: { a: 1 },
|
|
478
483
|
},
|
|
479
|
-
body: {
|
|
480
|
-
message: 'query.a === 1'
|
|
481
|
-
}
|
|
484
|
+
body: { message: 'query.a === 1' },
|
|
482
485
|
},
|
|
483
486
|
// 命中 /api/test?a=2
|
|
484
487
|
{
|
|
485
488
|
url: '/api/test',
|
|
486
489
|
validator: {
|
|
487
|
-
query: {
|
|
488
|
-
a: 2
|
|
489
|
-
}
|
|
490
|
+
query: { a: 2 },
|
|
490
491
|
},
|
|
491
|
-
body: {
|
|
492
|
-
message: 'query.a === 2'
|
|
493
|
-
}
|
|
492
|
+
body: { message: 'query.a === 2' },
|
|
494
493
|
},
|
|
495
494
|
{
|
|
496
|
-
|
|
497
|
-
* `?a=3` 将会解析到 `validator.query`
|
|
498
|
-
*/
|
|
495
|
+
// `?a=3` 将会解析到 `validator.query`
|
|
499
496
|
url: '/api/test?a=3',
|
|
500
|
-
body: {
|
|
501
|
-
message: 'query.a == 3'
|
|
502
|
-
}
|
|
497
|
+
body: { message: 'query.a == 3' },
|
|
503
498
|
}
|
|
504
499
|
])
|
|
505
500
|
```
|
|
506
501
|
|
|
507
|
-
|
|
508
|
-
延迟接口响应:
|
|
502
|
+
|
|
503
|
+
**exp:** 延迟接口响应:
|
|
509
504
|
```ts
|
|
510
505
|
export default defineMock({
|
|
511
506
|
url: '/api/test',
|
|
@@ -513,33 +508,64 @@ export default defineMock({
|
|
|
513
508
|
})
|
|
514
509
|
```
|
|
515
510
|
|
|
516
|
-
|
|
517
|
-
使接口请求失败
|
|
511
|
+
**exp:** 使接口请求失败
|
|
518
512
|
```ts
|
|
519
513
|
export default defineMock({
|
|
520
514
|
url: '/api/test',
|
|
521
|
-
status:
|
|
515
|
+
status: 502,
|
|
522
516
|
statusText: 'Bad Gateway'
|
|
523
517
|
})
|
|
524
518
|
```
|
|
525
519
|
|
|
526
|
-
|
|
527
|
-
动态路由匹配
|
|
520
|
+
**exp:** 动态路由匹配
|
|
528
521
|
```ts
|
|
529
522
|
export default defineMock({
|
|
530
523
|
url: '/api/user/:userId',
|
|
531
524
|
body({ params }) {
|
|
532
|
-
return {
|
|
533
|
-
userId: params.userId,
|
|
534
|
-
}
|
|
525
|
+
return { userId: params.userId }
|
|
535
526
|
}
|
|
536
527
|
})
|
|
537
528
|
```
|
|
538
529
|
|
|
539
530
|
路由中的 `userId`将会解析到 `request.params` 对象中.
|
|
540
531
|
|
|
541
|
-
|
|
542
|
-
|
|
532
|
+
**exp:** 使用 buffer 响应数据
|
|
533
|
+
```ts
|
|
534
|
+
// 由于 type 默认值是 json,虽然在传输过程中body使用buffer,
|
|
535
|
+
// 但是 content-type 还是为 json
|
|
536
|
+
export default defineMock({
|
|
537
|
+
url: 'api/buffer',
|
|
538
|
+
body: Buffer.from(JSON.stringify({ a: 1 }))
|
|
539
|
+
})
|
|
540
|
+
```
|
|
541
|
+
```ts
|
|
542
|
+
// 当 type 为 buffer 时,content-type 为 application/octet-stream,
|
|
543
|
+
// body 传入的数据会被转为 buffer
|
|
544
|
+
export default defineMock({
|
|
545
|
+
url: 'api/buffer',
|
|
546
|
+
type: 'buffer',
|
|
547
|
+
// 内部使用 Buffer.from(body) 进行转换
|
|
548
|
+
body: { a: 1 }
|
|
549
|
+
})
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
**exp:** 响应文件类型
|
|
553
|
+
|
|
554
|
+
模拟文件下载,传入文件读取流
|
|
555
|
+
```ts
|
|
556
|
+
import { createReadStream } from 'node:fs'
|
|
557
|
+
export default defineMock({
|
|
558
|
+
url: '/api/download',
|
|
559
|
+
// 当你不确定类型,可传入文件名由插件内部进行解析
|
|
560
|
+
type: 'my-app.dmg',
|
|
561
|
+
body: () => createReadStream('./my-app.dmg')
|
|
562
|
+
})
|
|
563
|
+
```
|
|
564
|
+
```html
|
|
565
|
+
<a href="/api/download" download="my-app.dmg">下载文件</a>
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
**exp:** 使用 `mockjs` 生成响应数据:
|
|
543
569
|
```ts
|
|
544
570
|
import Mock from 'mockjs'
|
|
545
571
|
export default defineMock({
|
|
@@ -553,8 +579,8 @@ export default defineMock({
|
|
|
553
579
|
```
|
|
554
580
|
请先安装 `mockjs`
|
|
555
581
|
|
|
556
|
-
|
|
557
|
-
使用 `response` 自定义响应
|
|
582
|
+
|
|
583
|
+
**exp:** 使用 `response` 自定义响应
|
|
558
584
|
```ts
|
|
559
585
|
export default defineMock({
|
|
560
586
|
url: '/api/test',
|
|
@@ -573,11 +599,10 @@ export default defineMock({
|
|
|
573
599
|
})
|
|
574
600
|
```
|
|
575
601
|
|
|
576
|
-
|
|
577
|
-
使用 json / json5
|
|
602
|
+
|
|
603
|
+
**exp:** 使用 json / json5
|
|
578
604
|
```json
|
|
579
605
|
{
|
|
580
|
-
// 支持 comment
|
|
581
606
|
"url": "/api/test",
|
|
582
607
|
"body": {
|
|
583
608
|
"a": 1
|
|
@@ -585,9 +610,8 @@ export default defineMock({
|
|
|
585
610
|
}
|
|
586
611
|
```
|
|
587
612
|
|
|
588
|
-
### Example 12:
|
|
589
613
|
|
|
590
|
-
multipart, 文件上传.
|
|
614
|
+
**exp:** multipart, 文件上传.
|
|
591
615
|
|
|
592
616
|
通过 [`formidable`](https://www.npmjs.com/package/formidable#readme) 支持。
|
|
593
617
|
``` 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.
|
|
57
|
+
var version = "1.1.4";
|
|
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
|
|
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,35 +442,49 @@ 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);
|
|
447
|
+
const cookies = new import_cookies.default(req, res, cookiesOptions);
|
|
448
|
+
const getCookie = cookies.get.bind(cookies);
|
|
442
449
|
const method = req.method.toUpperCase();
|
|
443
|
-
const mock = fineMock(mockData[mockUrl],
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
450
|
+
const mock = fineMock(mockData[mockUrl], {
|
|
451
|
+
pathname,
|
|
452
|
+
method,
|
|
453
|
+
request: {
|
|
454
|
+
query,
|
|
455
|
+
refererQuery,
|
|
456
|
+
body: reqBody,
|
|
457
|
+
headers: req.headers,
|
|
458
|
+
getCookie
|
|
459
|
+
}
|
|
448
460
|
});
|
|
449
461
|
if (!mock)
|
|
450
462
|
return next();
|
|
451
463
|
debug("middleware: ", method, pathname);
|
|
452
|
-
const cookies = new import_cookies.default(req, res, cookiesOptions);
|
|
453
464
|
const request = req;
|
|
454
465
|
const response = res;
|
|
455
466
|
request.body = reqBody;
|
|
456
467
|
request.query = query;
|
|
457
468
|
request.refererQuery = refererQuery;
|
|
458
469
|
request.params = parseParams(mock.url, pathname);
|
|
459
|
-
request.getCookie =
|
|
470
|
+
request.getCookie = getCookie;
|
|
460
471
|
response.setCookie = cookies.set.bind(cookies);
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
472
|
+
const {
|
|
473
|
+
body,
|
|
474
|
+
delay,
|
|
475
|
+
type = "json",
|
|
476
|
+
response: responseFn,
|
|
477
|
+
status = 200,
|
|
478
|
+
statusText
|
|
479
|
+
} = mock;
|
|
480
|
+
responseStatus(response, status, statusText);
|
|
481
|
+
await provideHeaders(request, response, mock);
|
|
482
|
+
await provideCookies(request, response, mock);
|
|
465
483
|
if (body) {
|
|
466
484
|
try {
|
|
467
|
-
const
|
|
485
|
+
const content = isFunction(body) ? await body(request) : body;
|
|
468
486
|
await realDelay(startTime, delay);
|
|
469
|
-
|
|
487
|
+
sendData(response, content, type);
|
|
470
488
|
} catch (e) {
|
|
471
489
|
log.error(`${import_picocolors2.default.red("[body error]")} ${req.url}
|
|
472
490
|
`, e);
|
|
@@ -490,7 +508,11 @@ function baseMiddleware(mockLoader, { formidableOptions = {}, proxies, cookiesOp
|
|
|
490
508
|
res.end("");
|
|
491
509
|
};
|
|
492
510
|
}
|
|
493
|
-
function fineMock(mockList,
|
|
511
|
+
function fineMock(mockList, {
|
|
512
|
+
pathname,
|
|
513
|
+
method,
|
|
514
|
+
request
|
|
515
|
+
}) {
|
|
494
516
|
return mockList.find((mock) => {
|
|
495
517
|
if (!pathname || !mock || !mock.url)
|
|
496
518
|
return false;
|
|
@@ -500,11 +522,10 @@ function fineMock(mockList, pathname, method, request) {
|
|
|
500
522
|
const hasMock = (0, import_path_to_regexp2.pathToRegexp)(mock.url).test(pathname);
|
|
501
523
|
if (hasMock && mock.validator) {
|
|
502
524
|
const params = parseParams(mock.url, pathname);
|
|
503
|
-
const extraRequest = { params, ...request };
|
|
504
525
|
if (isFunction(mock.validator)) {
|
|
505
|
-
return mock.validator(
|
|
526
|
+
return mock.validator({ params, ...request });
|
|
506
527
|
} else {
|
|
507
|
-
return validate(
|
|
528
|
+
return validate({ params, ...request }, mock.validator);
|
|
508
529
|
}
|
|
509
530
|
}
|
|
510
531
|
return hasMock;
|
|
@@ -514,34 +535,35 @@ function responseStatus(response, status = 200, statusText) {
|
|
|
514
535
|
response.statusCode = status;
|
|
515
536
|
response.statusMessage = statusText || getHTTPStatusText(status);
|
|
516
537
|
}
|
|
517
|
-
async function provideHeaders(req, res,
|
|
518
|
-
|
|
538
|
+
async function provideHeaders(req, res, { headers, type = "json" }) {
|
|
539
|
+
const contentType2 = mime.contentType(type) || mime.contentType(mime.lookup(type) || "");
|
|
540
|
+
contentType2 && res.setHeader("Content-Type", contentType2);
|
|
519
541
|
res.setHeader("Cache-Control", "no-cache,max-age=0");
|
|
520
542
|
res.setHeader("X-Mock", "generate by vite:plugin-mock-dev-server");
|
|
521
|
-
if (!
|
|
543
|
+
if (!headers)
|
|
522
544
|
return;
|
|
523
545
|
try {
|
|
524
|
-
const
|
|
525
|
-
Object.keys(
|
|
526
|
-
res.setHeader(key,
|
|
546
|
+
const raw = isFunction(headers) ? await headers(req) : headers;
|
|
547
|
+
Object.keys(raw).forEach((key) => {
|
|
548
|
+
res.setHeader(key, raw[key]);
|
|
527
549
|
});
|
|
528
550
|
} catch (e) {
|
|
529
551
|
log.error(`${import_picocolors2.default.red("[headers error]")} ${req.url}
|
|
530
552
|
`, e);
|
|
531
553
|
}
|
|
532
554
|
}
|
|
533
|
-
async function provideCookies(req, res,
|
|
534
|
-
if (!
|
|
555
|
+
async function provideCookies(req, res, { cookies }) {
|
|
556
|
+
if (!cookies)
|
|
535
557
|
return;
|
|
536
558
|
try {
|
|
537
|
-
const
|
|
538
|
-
Object.keys(
|
|
539
|
-
const
|
|
540
|
-
if (isArray(
|
|
541
|
-
const [value, options] =
|
|
559
|
+
const raw = isFunction(cookies) ? await cookies(req) : cookies;
|
|
560
|
+
Object.keys(raw).forEach((key) => {
|
|
561
|
+
const cookie = raw[key];
|
|
562
|
+
if (isArray(cookie)) {
|
|
563
|
+
const [value, options] = cookie;
|
|
542
564
|
res.setCookie(key, value, options);
|
|
543
565
|
} else {
|
|
544
|
-
res.setCookie(key,
|
|
566
|
+
res.setCookie(key, cookie);
|
|
545
567
|
}
|
|
546
568
|
});
|
|
547
569
|
} catch (e) {
|
|
@@ -549,6 +571,16 @@ async function provideCookies(req, res, cookiesOption) {
|
|
|
549
571
|
`, e);
|
|
550
572
|
}
|
|
551
573
|
}
|
|
574
|
+
function sendData(res, raw, type) {
|
|
575
|
+
if (isReadableStream(raw)) {
|
|
576
|
+
raw.pipe(res);
|
|
577
|
+
} else if (import_node_buffer.Buffer.isBuffer(raw)) {
|
|
578
|
+
res.end(type === "text" || type === "json" ? raw.toString("utf-8") : raw);
|
|
579
|
+
} else {
|
|
580
|
+
const content = typeof raw === "string" ? raw : JSON.stringify(raw);
|
|
581
|
+
res.end(type === "buffer" ? import_node_buffer.Buffer.from(content) : content);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
552
584
|
async function realDelay(startTime, delay) {
|
|
553
585
|
if (!delay || delay <= 0)
|
|
554
586
|
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.
|
|
@@ -110,13 +111,12 @@ interface ExtraRequest {
|
|
|
110
111
|
* 请求体中 headers
|
|
111
112
|
*/
|
|
112
113
|
headers: Headers;
|
|
113
|
-
}
|
|
114
|
-
type MockRequest = Connect.IncomingMessage & ExtraRequest & {
|
|
115
114
|
/**
|
|
116
115
|
* @see [cookies](https://github.com/pillarjs/cookies#cookiesgetname--options)
|
|
117
116
|
*/
|
|
118
117
|
getCookie: (name: string, option?: Cookies.GetOption) => string | undefined;
|
|
119
|
-
}
|
|
118
|
+
}
|
|
119
|
+
type MockRequest = Connect.IncomingMessage & ExtraRequest;
|
|
120
120
|
type MockResponse = http.ServerResponse<http.IncomingMessage> & {
|
|
121
121
|
/**
|
|
122
122
|
* @see [cookies](https://github.com/pillarjs/cookies#cookiessetname--values--options)
|
|
@@ -218,6 +218,37 @@ interface MockOptionsItem {
|
|
|
218
218
|
* ```
|
|
219
219
|
*/
|
|
220
220
|
cookies?: ResponseCookies | ResponseCookiesFn;
|
|
221
|
+
/**
|
|
222
|
+
* Response body data type, optional values include `text, json, buffer`.
|
|
223
|
+
*
|
|
224
|
+
* And also support types included in `mime-db`.
|
|
225
|
+
* When the response body returns a file and you are not sure which type to use,
|
|
226
|
+
* you can pass the file name as the value. The plugin will internally search for matching
|
|
227
|
+
* `content-type` based on the file name suffix.
|
|
228
|
+
*
|
|
229
|
+
* However, if it is a TypeScript file such as `a.ts`, it may not be correctly matched
|
|
230
|
+
* as a JavaScript script. You need to modify `a.ts` to `a.js` as the value passed
|
|
231
|
+
* in order to recognize it correctly.
|
|
232
|
+
*
|
|
233
|
+
* 响应体数据类型, 可选值包括 `text, json, buffer`,
|
|
234
|
+
*
|
|
235
|
+
* 还支持`mime-db`中的包含的类型。
|
|
236
|
+
* 当响应体返回的是一个文件,而你不确定应该使用哪个类型时,可以将文件名作为值传入,
|
|
237
|
+
* 插件内部会根据文件名后缀查找匹配的`content-type`。
|
|
238
|
+
*
|
|
239
|
+
* 但如果是 `typescript`文件如 `a.ts`,可能不会被正确匹配为 `javascript`脚本,
|
|
240
|
+
* 你需要将 `a.ts` 修改为 `a.js`作为值传入才能正确识别。
|
|
241
|
+
* @see [mime-db](https://github.com/jshttp/mime-db)
|
|
242
|
+
* @default 'json'
|
|
243
|
+
* @example
|
|
244
|
+
* ```txt
|
|
245
|
+
* json
|
|
246
|
+
* buffer
|
|
247
|
+
* my-app.dmg
|
|
248
|
+
* music.mp4
|
|
249
|
+
* ```
|
|
250
|
+
*/
|
|
251
|
+
type?: 'text' | 'json' | 'buffer' | string;
|
|
221
252
|
/**
|
|
222
253
|
* Configure response body data content
|
|
223
254
|
*
|
|
@@ -298,7 +329,7 @@ interface MockOptionsItem {
|
|
|
298
329
|
* }
|
|
299
330
|
* ```
|
|
300
331
|
*/
|
|
301
|
-
validator?: Partial<ExtraRequest
|
|
332
|
+
validator?: Partial<Omit<ExtraRequest, 'getCookie'>> | ((request: ExtraRequest) => boolean);
|
|
302
333
|
}
|
|
303
334
|
type MockOptions = MockOptionsItem[];
|
|
304
335
|
type FormidableFile = formidable.File | formidable.File[];
|
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.
|
|
12
|
+
var version = "1.1.4";
|
|
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
|
|
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,35 +397,49 @@ 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);
|
|
402
|
+
const cookies = new Cookies(req, res, cookiesOptions);
|
|
403
|
+
const getCookie = cookies.get.bind(cookies);
|
|
397
404
|
const method = req.method.toUpperCase();
|
|
398
|
-
const mock = fineMock(mockData[mockUrl],
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
405
|
+
const mock = fineMock(mockData[mockUrl], {
|
|
406
|
+
pathname,
|
|
407
|
+
method,
|
|
408
|
+
request: {
|
|
409
|
+
query,
|
|
410
|
+
refererQuery,
|
|
411
|
+
body: reqBody,
|
|
412
|
+
headers: req.headers,
|
|
413
|
+
getCookie
|
|
414
|
+
}
|
|
403
415
|
});
|
|
404
416
|
if (!mock)
|
|
405
417
|
return next();
|
|
406
418
|
debug("middleware: ", method, pathname);
|
|
407
|
-
const cookies = new Cookies(req, res, cookiesOptions);
|
|
408
419
|
const request = req;
|
|
409
420
|
const response = res;
|
|
410
421
|
request.body = reqBody;
|
|
411
422
|
request.query = query;
|
|
412
423
|
request.refererQuery = refererQuery;
|
|
413
424
|
request.params = parseParams(mock.url, pathname);
|
|
414
|
-
request.getCookie =
|
|
425
|
+
request.getCookie = getCookie;
|
|
415
426
|
response.setCookie = cookies.set.bind(cookies);
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
427
|
+
const {
|
|
428
|
+
body,
|
|
429
|
+
delay,
|
|
430
|
+
type = "json",
|
|
431
|
+
response: responseFn,
|
|
432
|
+
status = 200,
|
|
433
|
+
statusText
|
|
434
|
+
} = mock;
|
|
435
|
+
responseStatus(response, status, statusText);
|
|
436
|
+
await provideHeaders(request, response, mock);
|
|
437
|
+
await provideCookies(request, response, mock);
|
|
420
438
|
if (body) {
|
|
421
439
|
try {
|
|
422
|
-
const
|
|
440
|
+
const content = isFunction(body) ? await body(request) : body;
|
|
423
441
|
await realDelay(startTime, delay);
|
|
424
|
-
|
|
442
|
+
sendData(response, content, type);
|
|
425
443
|
} catch (e) {
|
|
426
444
|
log.error(`${colors2.red("[body error]")} ${req.url}
|
|
427
445
|
`, e);
|
|
@@ -445,7 +463,11 @@ function baseMiddleware(mockLoader, { formidableOptions = {}, proxies, cookiesOp
|
|
|
445
463
|
res.end("");
|
|
446
464
|
};
|
|
447
465
|
}
|
|
448
|
-
function fineMock(mockList,
|
|
466
|
+
function fineMock(mockList, {
|
|
467
|
+
pathname,
|
|
468
|
+
method,
|
|
469
|
+
request
|
|
470
|
+
}) {
|
|
449
471
|
return mockList.find((mock) => {
|
|
450
472
|
if (!pathname || !mock || !mock.url)
|
|
451
473
|
return false;
|
|
@@ -455,11 +477,10 @@ function fineMock(mockList, pathname, method, request) {
|
|
|
455
477
|
const hasMock = pathToRegexp(mock.url).test(pathname);
|
|
456
478
|
if (hasMock && mock.validator) {
|
|
457
479
|
const params = parseParams(mock.url, pathname);
|
|
458
|
-
const extraRequest = { params, ...request };
|
|
459
480
|
if (isFunction(mock.validator)) {
|
|
460
|
-
return mock.validator(
|
|
481
|
+
return mock.validator({ params, ...request });
|
|
461
482
|
} else {
|
|
462
|
-
return validate(
|
|
483
|
+
return validate({ params, ...request }, mock.validator);
|
|
463
484
|
}
|
|
464
485
|
}
|
|
465
486
|
return hasMock;
|
|
@@ -469,34 +490,35 @@ function responseStatus(response, status = 200, statusText) {
|
|
|
469
490
|
response.statusCode = status;
|
|
470
491
|
response.statusMessage = statusText || getHTTPStatusText(status);
|
|
471
492
|
}
|
|
472
|
-
async function provideHeaders(req, res,
|
|
473
|
-
|
|
493
|
+
async function provideHeaders(req, res, { headers, type = "json" }) {
|
|
494
|
+
const contentType2 = mime.contentType(type) || mime.contentType(mime.lookup(type) || "");
|
|
495
|
+
contentType2 && res.setHeader("Content-Type", contentType2);
|
|
474
496
|
res.setHeader("Cache-Control", "no-cache,max-age=0");
|
|
475
497
|
res.setHeader("X-Mock", "generate by vite:plugin-mock-dev-server");
|
|
476
|
-
if (!
|
|
498
|
+
if (!headers)
|
|
477
499
|
return;
|
|
478
500
|
try {
|
|
479
|
-
const
|
|
480
|
-
Object.keys(
|
|
481
|
-
res.setHeader(key,
|
|
501
|
+
const raw = isFunction(headers) ? await headers(req) : headers;
|
|
502
|
+
Object.keys(raw).forEach((key) => {
|
|
503
|
+
res.setHeader(key, raw[key]);
|
|
482
504
|
});
|
|
483
505
|
} catch (e) {
|
|
484
506
|
log.error(`${colors2.red("[headers error]")} ${req.url}
|
|
485
507
|
`, e);
|
|
486
508
|
}
|
|
487
509
|
}
|
|
488
|
-
async function provideCookies(req, res,
|
|
489
|
-
if (!
|
|
510
|
+
async function provideCookies(req, res, { cookies }) {
|
|
511
|
+
if (!cookies)
|
|
490
512
|
return;
|
|
491
513
|
try {
|
|
492
|
-
const
|
|
493
|
-
Object.keys(
|
|
494
|
-
const
|
|
495
|
-
if (isArray(
|
|
496
|
-
const [value, options] =
|
|
514
|
+
const raw = isFunction(cookies) ? await cookies(req) : cookies;
|
|
515
|
+
Object.keys(raw).forEach((key) => {
|
|
516
|
+
const cookie = raw[key];
|
|
517
|
+
if (isArray(cookie)) {
|
|
518
|
+
const [value, options] = cookie;
|
|
497
519
|
res.setCookie(key, value, options);
|
|
498
520
|
} else {
|
|
499
|
-
res.setCookie(key,
|
|
521
|
+
res.setCookie(key, cookie);
|
|
500
522
|
}
|
|
501
523
|
});
|
|
502
524
|
} catch (e) {
|
|
@@ -504,6 +526,16 @@ async function provideCookies(req, res, cookiesOption) {
|
|
|
504
526
|
`, e);
|
|
505
527
|
}
|
|
506
528
|
}
|
|
529
|
+
function sendData(res, raw, type) {
|
|
530
|
+
if (isReadableStream(raw)) {
|
|
531
|
+
raw.pipe(res);
|
|
532
|
+
} else if (Buffer.isBuffer(raw)) {
|
|
533
|
+
res.end(type === "text" || type === "json" ? raw.toString("utf-8") : raw);
|
|
534
|
+
} else {
|
|
535
|
+
const content = typeof raw === "string" ? raw : JSON.stringify(raw);
|
|
536
|
+
res.end(type === "buffer" ? Buffer.from(content) : content);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
507
539
|
async function realDelay(startTime, delay) {
|
|
508
540
|
if (!delay || delay <= 0)
|
|
509
541
|
return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vite-plugin-mock-dev-server",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.4",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"vite",
|
|
6
6
|
"plugin",
|
|
@@ -34,12 +34,13 @@
|
|
|
34
34
|
"co-body": "^6.1.0",
|
|
35
35
|
"cookies": "^0.8.0",
|
|
36
36
|
"debug": "^4.3.4",
|
|
37
|
-
"esbuild": "^0.17.
|
|
37
|
+
"esbuild": "^0.17.18",
|
|
38
38
|
"fast-glob": "^3.2.12",
|
|
39
39
|
"formidable": "^2.1.1",
|
|
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,17 +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/
|
|
55
|
+
"@types/mime-types": "^2.1.1",
|
|
56
|
+
"@types/node": "^18.16.0",
|
|
55
57
|
"bumpp": "^9.1.0",
|
|
56
58
|
"conventional-changelog-cli": "^2.2.2",
|
|
57
|
-
"eslint": "^8.
|
|
59
|
+
"eslint": "^8.39.0",
|
|
58
60
|
"mockjs": "^1.1.0",
|
|
59
|
-
"prettier": "^2.8.
|
|
61
|
+
"prettier": "^2.8.8",
|
|
60
62
|
"tsup": "^6.7.0",
|
|
61
63
|
"typescript": "^5.0.4",
|
|
62
|
-
"
|
|
63
|
-
"vitepress": "1.0.0-alpha.73",
|
|
64
|
-
"vue": "^3.2.47"
|
|
64
|
+
"vitepress": "1.0.0-alpha.74"
|
|
65
65
|
},
|
|
66
66
|
"peerDependencies": {
|
|
67
67
|
"vite": ">=3.0.0"
|