vite-plugin-mock-dev-server 1.1.5 → 1.1.7
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 +121 -10
- package/README.zh-CN.md +118 -12
- package/dist/index.cjs +26 -17
- package/dist/index.d.ts +76 -18
- package/dist/index.js +26 -17
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<a href="https://www.npmjs.com/package/vite-plugin-mock-dev-server"><img alt="npm" src="https://img.shields.io/npm/v/vite-plugin-mock-dev-server?style=flat-square"></a>
|
|
12
12
|
<img alt="node-current" src="https://img.shields.io/node/v/vite-plugin-mock-dev-server?style=flat-square">
|
|
13
13
|
<img alt="npm peer dependency version" src="https://img.shields.io/npm/dependency-version/vite-plugin-mock-dev-server/peer/vite?style=flat-square">
|
|
14
|
-
<img alt="npm" src="https://img.shields.io/npm/
|
|
14
|
+
<img alt="npm" src="https://img.shields.io/npm/dt/vite-plugin-mock-dev-server?style=flat-square">
|
|
15
15
|
<br>
|
|
16
16
|
<img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/pengzhanbo/vite-plugin-mock-dev-server/lint.yml?style=flat-square">
|
|
17
17
|
<a href="https://app.fossa.com/projects/git%2Bgithub.com%2Fpengzhanbo%2Fvite-plugin-mock-dev-server?ref=badge_shield"><img alt="fossa status" src="https://app.fossa.com/api/projects/git%2Bgithub.com%2Fpengzhanbo%2Fvite-plugin-mock-dev-server.svg?type=shield"></a>
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
- 🌈 Support `vite preview` mode.
|
|
44
44
|
- 📤 Support `multipart` content-type,mock upload file.
|
|
45
45
|
- 📥 Support mock download file.
|
|
46
|
+
- ⚜️ Support `WebSocket Mock`
|
|
46
47
|
- 🗂 Support building small independent deployable mock services.
|
|
47
48
|
|
|
48
49
|
|
|
@@ -148,6 +149,18 @@ export default defineConfig({
|
|
|
148
149
|
|
|
149
150
|
**Default:** `[]`
|
|
150
151
|
|
|
152
|
+
- `options.wsPrefix`
|
|
153
|
+
|
|
154
|
+
**类型:** `string | string[]`
|
|
155
|
+
|
|
156
|
+
Configure the matching rules for WebSocket service. Any request path starting with the value of `wsPrefix` and using the `ws/wss` protocol will be proxied to the corresponding target.
|
|
157
|
+
|
|
158
|
+
If the value of `wsPrefix` starts with `^`, it will be recognized as a RegExp.
|
|
159
|
+
|
|
160
|
+
> Different from using `viteConfig.server.proxy` by default for http mock, `websocket mock` does not use the ws-related configuration in `viteConfig.server.proxy`. Also, rules configured in `wsPrefix` cannot be configured simultaneously in `viteConfig.server.proxy`, as it will cause conflicts when starting the vite server because multiple instances of WebSocketServer cannot be implemented for the same request.
|
|
161
|
+
> This conflict is neither a problem with Vite nor with the plugin; it belongs to a reasonable error type. When switching between WebSocket Mock and WebSocket Proxy, please pay attention to avoid duplicate configurations that may cause conflicts.
|
|
162
|
+
|
|
163
|
+
|
|
151
164
|
- `option.include`
|
|
152
165
|
|
|
153
166
|
**Type:** `string | string[]`
|
|
@@ -162,14 +175,7 @@ export default defineConfig({
|
|
|
162
175
|
|
|
163
176
|
When reading mock files for configuration, the files that need to be excluded can be a directory, glob, or an array.
|
|
164
177
|
|
|
165
|
-
**Default:**
|
|
166
|
-
```ts
|
|
167
|
-
[
|
|
168
|
-
'**/node_modules/**',
|
|
169
|
-
'**/.vscode/**',
|
|
170
|
-
'**/.git/**',
|
|
171
|
-
]
|
|
172
|
-
```
|
|
178
|
+
**Default:** `['**/node_modules/**','**/.vscode/**','**/.git/**']`
|
|
173
179
|
|
|
174
180
|
- `options.reload`
|
|
175
181
|
|
|
@@ -258,6 +264,7 @@ export default defineApiMock({
|
|
|
258
264
|
## Mock Configuration
|
|
259
265
|
|
|
260
266
|
```ts
|
|
267
|
+
// Configure the http mock
|
|
261
268
|
export default defineMock({
|
|
262
269
|
/**
|
|
263
270
|
* Request address, supports the `/api/user/:id` format.
|
|
@@ -380,7 +387,41 @@ export default defineMock({
|
|
|
380
387
|
res.end()
|
|
381
388
|
}
|
|
382
389
|
})
|
|
383
|
-
|
|
390
|
+
```
|
|
391
|
+
```ts
|
|
392
|
+
// Configure the WebSocket mock
|
|
393
|
+
export default defineMock({
|
|
394
|
+
/**
|
|
395
|
+
* Request address, supports the `/api/user/:id` format.
|
|
396
|
+
* The plugin matches the path through `path-to-regexp`.
|
|
397
|
+
* @see https://github.com/pillarjs/path-to-regexp
|
|
398
|
+
*/
|
|
399
|
+
url: '/api/test',
|
|
400
|
+
/**
|
|
401
|
+
* Value must be explicitly specified as `true`.
|
|
402
|
+
* The plugin needs to make a judgment based on this field.
|
|
403
|
+
*/
|
|
404
|
+
ws: true,
|
|
405
|
+
/**
|
|
406
|
+
* Configure the WebSocketServer
|
|
407
|
+
* @see https://github.com/websockets/ws/blob/master/doc/ws.md#class-websocketserver
|
|
408
|
+
* If there are some additional automatically executed tasks or loop
|
|
409
|
+
* tasks in the setup function,a callback needs to be passed in
|
|
410
|
+
* `onCleanup()` to clear these tasks.
|
|
411
|
+
* This is because when the plugin is hot updated,
|
|
412
|
+
* it needs to re-execute setup and clear previous tasks; otherwise,
|
|
413
|
+
* duplicate tasks may cause conflicts.
|
|
414
|
+
* `onCleanup()` can be called multiple times within setup.
|
|
415
|
+
* @type `(wss: WebSocketServer, context: SetupContext) => void`
|
|
416
|
+
*/
|
|
417
|
+
setup(wss, { onCleanup }) {
|
|
418
|
+
wss.on('connection', (ws, request) => {
|
|
419
|
+
ws.on('message', (rawData) => {})
|
|
420
|
+
const timer = setInterval(() => ws.send('data'), 1000)
|
|
421
|
+
onCleanup(() => clearInterval(timer))
|
|
422
|
+
})
|
|
423
|
+
}
|
|
424
|
+
})
|
|
384
425
|
```
|
|
385
426
|
|
|
386
427
|
### Request/Response Enhance
|
|
@@ -650,6 +691,76 @@ export default defineMock({
|
|
|
650
691
|
})
|
|
651
692
|
```
|
|
652
693
|
|
|
694
|
+
**exp:** Graphql
|
|
695
|
+
```ts
|
|
696
|
+
import { buildSchema, graphql } from 'graphql'
|
|
697
|
+
const schema = buildSchema(`
|
|
698
|
+
type Query {
|
|
699
|
+
hello: String
|
|
700
|
+
}
|
|
701
|
+
`)
|
|
702
|
+
const rootValue = { hello: () => 'Hello world!' }
|
|
703
|
+
|
|
704
|
+
export default defineMock({
|
|
705
|
+
url: '/api/graphql',
|
|
706
|
+
method: 'POST',
|
|
707
|
+
body: async (request) => {
|
|
708
|
+
const source = request.body.source
|
|
709
|
+
const { data } = await graphql({ schema, rootValue, source })
|
|
710
|
+
return data
|
|
711
|
+
},
|
|
712
|
+
})
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
```ts
|
|
716
|
+
fetch('/api/graphql', {
|
|
717
|
+
method: 'POST',
|
|
718
|
+
body: JSON.stringify({ source: '{ hello }' })
|
|
719
|
+
})
|
|
720
|
+
```
|
|
721
|
+
|
|
722
|
+
**exp:** WebSocket Mock
|
|
723
|
+
```ts
|
|
724
|
+
// ws.mock.ts
|
|
725
|
+
export default defineMock({
|
|
726
|
+
url: '/socket.io',
|
|
727
|
+
ws: true,
|
|
728
|
+
setup(wss, { onCleanup }) {
|
|
729
|
+
const wsMap = new Map()
|
|
730
|
+
wss.on('connection', (ws, req) => {
|
|
731
|
+
const token = req.getCookie('token')
|
|
732
|
+
wsMap.set(token, ws)
|
|
733
|
+
ws.on('message', (raw) => {
|
|
734
|
+
const data = JSON.parse(String(raw))
|
|
735
|
+
if (data.type === 'ping') return
|
|
736
|
+
// Broadcast
|
|
737
|
+
for (const [_token, _ws] of wsMap.entires()) {
|
|
738
|
+
if (_token !== token)
|
|
739
|
+
_ws.send(raw)
|
|
740
|
+
}
|
|
741
|
+
})
|
|
742
|
+
})
|
|
743
|
+
wss.on('error', (err) => {
|
|
744
|
+
console.error(err)
|
|
745
|
+
})
|
|
746
|
+
onCleanup(() => wsMap.clear())
|
|
747
|
+
}
|
|
748
|
+
})
|
|
749
|
+
```
|
|
750
|
+
```ts
|
|
751
|
+
// app.ts
|
|
752
|
+
const ws = new WebSocket('ws://localhost:5173/socket.io')
|
|
753
|
+
ws.addEventListener('open', () => {
|
|
754
|
+
setInterval(() => {
|
|
755
|
+
// heartbeat
|
|
756
|
+
ws.send(JSON.stringify({ type: 'ping' }))
|
|
757
|
+
}, 1000)
|
|
758
|
+
}, { once: true })
|
|
759
|
+
ws.addEventListener('message', (raw) => {
|
|
760
|
+
console.log(raw)
|
|
761
|
+
})
|
|
762
|
+
```
|
|
763
|
+
|
|
653
764
|
## Mock Services
|
|
654
765
|
|
|
655
766
|
In some scenarios, it may be necessary to use the data provided by mock services for display purposes, but the project may have already been packaged, built and deployed without support from `vite` and this plugin's mock service. Since this plugin supports importing various `node` modules in mock files at the design stage, the mock file cannot be inline into client build code.
|
package/README.zh-CN.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
<a href="https://www.npmjs.com/package/vite-plugin-mock-dev-server"><img alt="npm" src="https://img.shields.io/npm/v/vite-plugin-mock-dev-server?style=flat-square"></a>
|
|
14
14
|
<img alt="node-current" src="https://img.shields.io/node/v/vite-plugin-mock-dev-server?style=flat-square">
|
|
15
15
|
<img alt="npm peer dependency version" src="https://img.shields.io/npm/dependency-version/vite-plugin-mock-dev-server/peer/vite?style=flat-square">
|
|
16
|
-
<img alt="npm" src="https://img.shields.io/npm/
|
|
16
|
+
<img alt="npm" src="https://img.shields.io/npm/dt/vite-plugin-mock-dev-server?style=flat-square">
|
|
17
17
|
<br>
|
|
18
18
|
<img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/pengzhanbo/vite-plugin-mock-dev-server/lint.yml?style=flat-square">
|
|
19
19
|
<a href="https://app.fossa.com/projects/git%2Bgithub.com%2Fpengzhanbo%2Fvite-plugin-mock-dev-server?ref=badge_shield"><img alt="fossa status" src="https://app.fossa.com/api/projects/git%2Bgithub.com%2Fpengzhanbo%2Fvite-plugin-mock-dev-server.svg?type=shield"></a>
|
|
@@ -37,13 +37,14 @@
|
|
|
37
37
|
- 🎨 可选择你喜欢的任意用于生成mock数据库,如 `mockjs`,或者不使用其他库
|
|
38
38
|
- 📥 路径规则匹配,请求参数匹配
|
|
39
39
|
- ⚙️ 随意开启或关闭对某个接口的 mock配置
|
|
40
|
-
-
|
|
40
|
+
- 📀 支持多种响应体数据类型,包括 `text/json/buffer/stream`.
|
|
41
41
|
- ⚖️ 使用 `server.proxy` 配置
|
|
42
42
|
- 🍕 支持在 mock文件中使用 `viteConfig.define`配置字段
|
|
43
43
|
- ⚓️ 支持在 mock文件中使用 `viteConfig.resolve.alias` 路径别名
|
|
44
44
|
- 🌈 支持 `vite preview` 模式
|
|
45
45
|
- 📤 支持 multipart 类型,模拟文件上传
|
|
46
46
|
- 📥 支持模拟文件下载
|
|
47
|
+
- ⚜️ 支持模拟 `WebSocket`
|
|
47
48
|
- 🗂 支持构建可独立部署的小型mock服务
|
|
48
49
|
|
|
49
50
|
|
|
@@ -140,12 +141,22 @@ export default defineConfig({
|
|
|
140
141
|
|
|
141
142
|
**类型:** `string | string[]`
|
|
142
143
|
|
|
143
|
-
为mock服务器配置自定义匹配规则。任何请求路径以 `prefix` 值开头的请求将被代理到对应的目标。如果 `prefix` 值以
|
|
144
|
+
为mock服务器配置自定义匹配规则。任何请求路径以 `prefix` 值开头的请求将被代理到对应的目标。如果 `prefix` 值以 `^` 开头,将被识别为 RegExp。
|
|
144
145
|
|
|
145
146
|
> 一般情况下, `server.proxy` 已经足够满足需求,添加此项是为了与某些场景兼容。
|
|
146
147
|
|
|
147
148
|
**默认值:** `[]`
|
|
148
149
|
|
|
150
|
+
- `options.wsPrefix`
|
|
151
|
+
|
|
152
|
+
**类型:** `string | string[]`
|
|
153
|
+
|
|
154
|
+
配置 webSocket 服务 匹配规则。任何请求路径以 `wsPrefix` 值开头的 `ws/wss` 协议请求,将被代理到对应的目标。
|
|
155
|
+
如果`wsPrefix`值以 `^` 开头,将被识别为 RegExp。
|
|
156
|
+
|
|
157
|
+
> 与 http mock 默认使用 `viteConfig.server.proxy` 不同的是,`websocket mock` 不会使用 `viteConfig.server.proxy` 中的 ws 相关的配置,且配置在 `wsPrefix` 中的规则,不能同时配置在 `viteConfig.server.proxy`中,因为会导致在 vite 在启动服务时产生冲突,因为不能对同一个请求实现多个的 `WebSocketServer`实例。
|
|
158
|
+
> 该冲突既不是 `vite` 的问题,也不是插件的问题,这属于合理的错误类型。在进行 `WebSocket Mock`和 `WebSocket Proxy` 切换时,请注意配置不要出现重复导致冲突。
|
|
159
|
+
|
|
149
160
|
- `option.include`
|
|
150
161
|
|
|
151
162
|
**类型:** `string | string[]`
|
|
@@ -160,14 +171,7 @@ export default defineConfig({
|
|
|
160
171
|
|
|
161
172
|
配置读取 mock文件时,需要排除的文件, 可以是一个 目录、glob、或者一个数组
|
|
162
173
|
|
|
163
|
-
**默认值:**
|
|
164
|
-
```ts
|
|
165
|
-
[
|
|
166
|
-
'**/node_modules/**',
|
|
167
|
-
'**/.vscode/**',
|
|
168
|
-
'**/.git/**',
|
|
169
|
-
]
|
|
170
|
-
```
|
|
174
|
+
**默认值:** `['**/node_modules/**', '**/.vscode/**', '**/.git/**']`
|
|
171
175
|
|
|
172
176
|
- `options.reload`
|
|
173
177
|
|
|
@@ -254,6 +258,7 @@ export default defineApiMock({
|
|
|
254
258
|
## Mock 配置
|
|
255
259
|
|
|
256
260
|
```ts
|
|
261
|
+
// 配置 http mock
|
|
257
262
|
export default defineMock({
|
|
258
263
|
/**
|
|
259
264
|
* 请求地址,支持 `/api/user/:id` 格式
|
|
@@ -376,7 +381,38 @@ export default defineMock({
|
|
|
376
381
|
res.end()
|
|
377
382
|
}
|
|
378
383
|
})
|
|
379
|
-
|
|
384
|
+
```
|
|
385
|
+
```ts
|
|
386
|
+
// 配置 WebSocket mock
|
|
387
|
+
export default defineMock({
|
|
388
|
+
/**
|
|
389
|
+
* 请求地址,支持 `/api/user/:id` 格式
|
|
390
|
+
* 插件通过 `path-to-regexp` 匹配路径
|
|
391
|
+
* @see https://github.com/pillarjs/path-to-regexp
|
|
392
|
+
*/
|
|
393
|
+
url: '/api/test',
|
|
394
|
+
/**
|
|
395
|
+
* 必须显式的指定值为 `true`
|
|
396
|
+
* 插件内部需要根据此值进行判断
|
|
397
|
+
*/
|
|
398
|
+
ws: true,
|
|
399
|
+
/**
|
|
400
|
+
* 配置 WebSocketServer
|
|
401
|
+
* @see https://github.com/websockets/ws/blob/master/doc/ws.md#class-websocketserver
|
|
402
|
+
* 如果在 setup 函数中有一些 额外的 自动执行任务或循环任务,
|
|
403
|
+
* 那么需要在 `onCleanup()` 传入一个回调,用于清除这些任务,
|
|
404
|
+
* 这是由于插件在热更新时,需要重新执行 setup,需要清除之前的任务,否则可能会导致重复任务产生冲突。
|
|
405
|
+
* `onCleanup()`可以在 setup 内部多次调用
|
|
406
|
+
* @type `(wss: WebSocketServer, context: SetupContext) => void`
|
|
407
|
+
*/
|
|
408
|
+
setup(wss, { onCleanup }) {
|
|
409
|
+
wss.on('connection', (ws, request) => {
|
|
410
|
+
ws.on('message', (rawData) => {})
|
|
411
|
+
const timer = setInterval(() => ws.send('data'), 1000)
|
|
412
|
+
onCleanup(() => clearInterval(timer))
|
|
413
|
+
})
|
|
414
|
+
}
|
|
415
|
+
})
|
|
380
416
|
```
|
|
381
417
|
|
|
382
418
|
### Request/Response 增强
|
|
@@ -645,6 +681,76 @@ export default defineMock({
|
|
|
645
681
|
})
|
|
646
682
|
```
|
|
647
683
|
|
|
684
|
+
**exp:** Graphql
|
|
685
|
+
```ts
|
|
686
|
+
import { buildSchema, graphql } from 'graphql'
|
|
687
|
+
const schema = buildSchema(`
|
|
688
|
+
type Query {
|
|
689
|
+
hello: String
|
|
690
|
+
}
|
|
691
|
+
`)
|
|
692
|
+
const rootValue = { hello: () => 'Hello world!' }
|
|
693
|
+
|
|
694
|
+
export default defineMock({
|
|
695
|
+
url: '/api/graphql',
|
|
696
|
+
method: 'POST',
|
|
697
|
+
body: async (request) => {
|
|
698
|
+
const source = request.body.source
|
|
699
|
+
const { data } = await graphql({ schema, rootValue, source })
|
|
700
|
+
return data
|
|
701
|
+
},
|
|
702
|
+
})
|
|
703
|
+
```
|
|
704
|
+
|
|
705
|
+
```ts
|
|
706
|
+
fetch('/api/graphql', {
|
|
707
|
+
method: 'POST',
|
|
708
|
+
body: JSON.stringify({ source: '{ hello }' })
|
|
709
|
+
})
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
**exp:** WebSocket Mock
|
|
713
|
+
```ts
|
|
714
|
+
// ws.mock.ts
|
|
715
|
+
export default defineMock({
|
|
716
|
+
url: '/socket.io',
|
|
717
|
+
ws: true,
|
|
718
|
+
setup(wss, { onCleanup }) {
|
|
719
|
+
const wsMap = new Map()
|
|
720
|
+
wss.on('connection', (ws, req) => {
|
|
721
|
+
const token = req.getCookie('token')
|
|
722
|
+
wsMap.set(token, ws)
|
|
723
|
+
ws.on('message', (raw) => {
|
|
724
|
+
const data = JSON.parse(String(raw))
|
|
725
|
+
if (data.type === 'ping') return
|
|
726
|
+
// Broadcast
|
|
727
|
+
for (const [_token, _ws] of wsMap.entires()) {
|
|
728
|
+
if (_token !== token)
|
|
729
|
+
_ws.send(raw)
|
|
730
|
+
}
|
|
731
|
+
})
|
|
732
|
+
})
|
|
733
|
+
wss.on('error', (err) => {
|
|
734
|
+
console.error(err)
|
|
735
|
+
})
|
|
736
|
+
onCleanup(() => wsMap.clear())
|
|
737
|
+
}
|
|
738
|
+
})
|
|
739
|
+
```
|
|
740
|
+
```ts
|
|
741
|
+
// app.ts
|
|
742
|
+
const ws = new WebSocket('ws://localhost:5173/socket.io')
|
|
743
|
+
ws.addEventListener('open', () => {
|
|
744
|
+
setInterval(() => {
|
|
745
|
+
// heartbeat
|
|
746
|
+
ws.send(JSON.stringify({ type: 'ping' }))
|
|
747
|
+
}, 1000)
|
|
748
|
+
}, { once: true })
|
|
749
|
+
ws.addEventListener('message', (raw) => {
|
|
750
|
+
console.log(raw)
|
|
751
|
+
})
|
|
752
|
+
```
|
|
753
|
+
|
|
648
754
|
## 独立部署的小型mock服务
|
|
649
755
|
|
|
650
756
|
在一些场景中,可能会需要使用mock服务提供的数据支持,用于展示,但可能项目已完成打包构建部署,已脱离 `vite` 和本插件提供的 mock服务支持。由于本插件在设计之初,支持在mock文件中引入各种 `node` 模块,所以不能将 mock文件打包内联到客户端构建代码中。
|
package/dist/index.cjs
CHANGED
|
@@ -1,32 +1,41 @@
|
|
|
1
|
-
"use strict";var
|
|
2
|
-
`,
|
|
3
|
-
`)}};function
|
|
1
|
+
"use strict";var Ye=Object.create;var A=Object.defineProperty;var Ze=Object.getOwnPropertyDescriptor;var et=Object.getOwnPropertyNames;var tt=Object.getPrototypeOf,ot=Object.prototype.hasOwnProperty;var rt=(e,o)=>{for(var t in o)A(e,t,{get:o[t],enumerable:!0})},ye=(e,o,t,r)=>{if(o&&typeof o=="object"||typeof o=="function")for(let n of et(o))!ot.call(e,n)&&n!==t&&A(e,n,{get:()=>o[n],enumerable:!(r=Ze(o,n))||r.enumerable});return e};var l=(e,o,t)=>(t=e!=null?Ye(tt(e)):{},ye(o||!e||!e.__esModule?A(t,"default",{value:e,enumerable:!0}):t,e)),nt=e=>ye(A({},"__esModule",{value:!0}),e);var jt={};rt(jt,{baseMiddleware:()=>ae,createDefineMock:()=>Ot,default:()=>St,defineMock:()=>wt,mockDevServerPlugin:()=>ge,mockWebSocket:()=>fe,transformMockData:()=>pe});module.exports=nt(jt);var st=()=>typeof document>"u"?new URL("file:"+__filename).href:document.currentScript&&document.currentScript.src||new URL("main.js",document.baseURI).href,x=st();var De=l(require("fs"),1),U=l(require("fs/promises"),1),I=l(require("path"),1),We=require("esbuild"),Ie=l(require("fast-glob"),1),Ee=l(require("is-core-module"),1),Y=require("vite");var ve="vite-plugin-mock-dev-server",Me="1.1.7";var oe=l(require("fs/promises"),1),be=l(require("path"),1),xe=l(require("json5"),1),B={name:"externalize-deps",setup(e){e.onResolve({filter:/.*/},({path:o})=>{if(o[0]!=="."&&!be.default.isAbsolute(o))return{external:!0}})}},J={name:"json5-loader",setup(e){e.onLoad({filter:/\.json5$/},async({path:o})=>{let t=await oe.default.readFile(o,"utf-8");return{contents:`export default ${JSON.stringify(xe.default.parse(t))}`,loader:"js"}})}},z={name:"json-loader",setup(e){e.onLoad({filter:/\.json$/},async({path:o})=>({contents:`export default ${await oe.default.readFile(o,"utf-8")}`,loader:"js"}))}},G=e=>({name:"alias-plugin",setup(o){o.onResolve({filter:/.*/},async({path:t})=>{let r=e.find(({find:c})=>it(c,t));if(!r)return null;let{find:n,replacement:s}=r;return{path:(await o.resolve(t.replace(n,s),{kind:"import-statement",resolveDir:s,namespace:"file"})).path,external:!1}})}});function it(e,o){return e instanceof RegExp?e.test(o):o.length<e.length?!1:o===e?!0:o.startsWith(`${e}/`)}var Q=l(require("fs"),1),X=l(require("path"),1),we=require("url"),Oe=l(require("debug"),1),Se=require("path-to-regexp"),re=l(require("picocolors"),1),S=e=>Array.isArray(e),C=e=>typeof e=="function",ct=e=>Object.prototype.toString.call(e)==="[object Object]",je=e=>ct(e)&&Object.keys(e).length===0,at=e=>e!==null&&typeof e=="object"&&typeof e.pipe=="function",Pe=e=>at(e)&&e.readable!==!1&&typeof e._read=="function"&&typeof e._readableState=="object";function Re(e){return new Promise(o=>setTimeout(o,e))}function Ce(e){return X.default.dirname((0,we.fileURLToPath)(e))}var D=(0,Oe.default)("vite:plugin-mock-dev-server"),j=e=>S(e)?e:e==null?[]:[e],T={info(...e){console.info(re.default.cyan("mock-dev-server: "),...e)},error(...e){console.error(`
|
|
2
|
+
`,re.default.cyan("mock-dev-server: "),...e,`
|
|
3
|
+
`)}};function H(e,o,t){for(let n of o){let s=X.default.join(e,n);if(Q.default.existsSync(s)&&Q.default.statSync(s).isFile()){let i=t!=null&&t.pathOnly?s:Q.default.readFileSync(s,"utf-8");if(!(t!=null&&t.predicate)||t.predicate(i))return i}}let r=X.default.dirname(e);if(r!==e&&(!(t!=null&&t.rootDir)||r.startsWith(t==null?void 0:t.rootDir)))return H(r,o,t)}var K=(e={})=>{let o=[],t=[];return Object.keys(e).forEach(r=>{var s,i;let n=e[r];typeof n=="string"||!n.ws&&!((s=n.target)!=null&&s.toString().startsWith("ws:"))&&!((i=n.target)!=null&&i.toString().startsWith("wss:"))?o.push(r):t.push(r)}),{httpProxies:o,wsProxies:t}};function V(e,o){return e[0]==="^"&&new RegExp(e).test(o)||o.startsWith(e)}function _(e,o){return((0,Se.match)(e,{decode:decodeURIComponent})(o)||{params:{}}).params||{}}async function Le(e,o,t){let r=j(t.include),n=j(t.exclude),s={};if(o.define)for(let k in o.define){let m=o.define[k];s[k]=typeof m=="string"?m:JSON.stringify(m)}let{httpProxies:i}=K(o.server.proxy||{});i.push(...j(t.prefix));let c=j(t.wsPrefix),a={};try{let k=H(o.root,["package.json"]);k&&(a=JSON.parse(k))}catch{}let p=t.build.dist,d=await mt(process.cwd(),r,n),u=I.default.join(o.root,`mock-data-${Date.now()}.js`);await U.default.writeFile(u,d,"utf-8");let{code:w,deps:v}=await dt(u,s,o.resolve.alias),g=pt(v);await U.default.unlink(u);let y=[{filename:I.default.join(p,"mock-data.js"),source:w},{filename:I.default.join(p,"index.js"),source:lt(i,c,t.cookiesOptions,t.build.serverPort)},{filename:I.default.join(p,"package.json"),source:ut(a,g)}];try{if(I.default.isAbsolute(p)){await U.default.rm(p,{recursive:!0}),De.default.mkdirSync(p,{recursive:!0});for(let{filename:k,source:m}of y)await U.default.writeFile(k,m,"utf-8")}else for(let{filename:k,source:m}of y)e.emitFile({type:"asset",fileName:k,source:m})}catch{}}function pt(e){let o=new Set,t=[ve,"connect","cors"];return Object.keys(e).forEach(r=>{e[r].imports.filter(s=>s.external).map(s=>s.path).forEach(s=>{!t.includes(s)&&!(0,Ee.default)(s)&&o.add(s)})}),Array.from(o)}function ut(e,o){let{dependencies:t={},devDependencies:r={}}=e,n={...t,...r},s={name:"mock-server",type:"module",scripts:{start:"node index.js"},dependencies:{connect:"^3.7.0","vite-plugin-mock-dev-server":`^${Me}`,cors:"^2.8.5"},pnpm:{peerDependencyRules:{ignoreMissing:["vite"]}}};return o.forEach(i=>{s.dependencies[i]=n[i]||"latest"}),JSON.stringify(s,null,2)}function lt(e,o,t={},r=8080){return`import { createServer } from 'node:http';
|
|
4
|
+
import connect from 'connect';
|
|
4
5
|
import corsMiddleware from 'cors';
|
|
5
|
-
import { baseMiddleware } from 'vite-plugin-mock-dev-server';
|
|
6
|
+
import { baseMiddleware, mockWebSocket } from 'vite-plugin-mock-dev-server';
|
|
6
7
|
import mockData from './mock-data.js';
|
|
7
8
|
|
|
8
9
|
const app = connect();
|
|
10
|
+
const server = createServer(app);
|
|
11
|
+
const httpProxies = ${JSON.stringify(e)};
|
|
12
|
+
const wxProxies = ${JSON.stringify(o)}
|
|
13
|
+
const cookiesOptions = ${JSON.stringify(t)};
|
|
14
|
+
|
|
15
|
+
mockWebSocket({ mockData }, server, wxProxies, cookiesOptions)
|
|
16
|
+
|
|
9
17
|
app.use(corsMiddleware());
|
|
10
18
|
app.use(baseMiddleware({ mockData }, {
|
|
11
19
|
formidableOptions: { multiples: true },
|
|
12
|
-
proxies:
|
|
13
|
-
cookiesOptions
|
|
20
|
+
proxies: httpProxies,
|
|
21
|
+
cookiesOptions,
|
|
14
22
|
}));
|
|
15
|
-
app.listen(${e});
|
|
16
23
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
24
|
+
server.listen(${r});
|
|
25
|
+
|
|
26
|
+
console.log('listen: http://localhost:${r}');
|
|
27
|
+
`}async function mt(e,o,t){let r=await(0,Ie.default)(o,{cwd:e}),n=(0,Y.createFilter)(o,t,{resolve:!1}),s=r.filter(n),i="",c="";return s.forEach((a,p)=>{let d=(0,Y.normalizePath)(I.default.join(e,a));i+=`import * as m${p} from '${d}';
|
|
28
|
+
`,c+=`m${p}, `}),`import { transformMockData } from 'vite-plugin-mock-dev-server';
|
|
20
29
|
${i}
|
|
21
|
-
const exporters = [${
|
|
30
|
+
const exporters = [${c}];
|
|
22
31
|
const mockList = exporters.map((raw) => raw && raw.default
|
|
23
32
|
? raw.default
|
|
24
33
|
: Object.keys(raw || {}).map((key) => raw[key])
|
|
25
34
|
)
|
|
26
35
|
export default transformMockData(mockList);
|
|
27
|
-
`}async function
|
|
28
|
-
`,
|
|
29
|
-
`,
|
|
30
|
-
`,
|
|
31
|
-
`,s)}}async function
|
|
32
|
-
`,r)}}function
|
|
36
|
+
`}async function dt(e,o,t){var r;try{let n=await(0,We.build)({entryPoints:[e],outfile:"out.js",write:!1,target:["node14.18","node16"],platform:"node",bundle:!0,metafile:!0,format:"esm",define:o,plugins:[G(t),B,J,z]});return{code:n.outputFiles[0].text,deps:((r=n.metafile)==null?void 0:r.inputs)||{}}}catch(n){console.error(n)}return{code:"",deps:{}}}var se=require("buffer"),ie=require("url"),_e=l(require("cookies"),1),Ue=l(require("http-status"),1),N=l(require("mime-types"),1),ce=require("path-to-regexp"),F=l(require("picocolors"),1);var Z=l(require("co-body"),1),Te=l(require("formidable"),1);async function Fe(e,o){var n;let t=e.method.toUpperCase();if(["GET","DELETE","HEAD"].includes(t))return;let r=((n=e.headers["content-type"])==null?void 0:n.toLocaleLowerCase())||"";try{if(r.startsWith("application/json"))return await Z.default.json(e);if(r.startsWith("application/x-www-form-urlencoded"))return await Z.default.form(e);if(r.startsWith("text/plain"))return await Z.default.text(e);if(r.startsWith("multipart/form-data"))return await ft(e,o)}catch(s){console.error(s)}}async function ft(e,o){let t=(0,Te.default)(o);return new Promise((r,n)=>{t.parse(e,(s,i,c)=>{if(s){n(s);return}r({...i,...c})})})}function $e(e,o){return E(e.headers,o.headers)&&E(e.body,o.body)&&E(e.params,o.params)&&E(e.query,o.query)&&E(e.refererQuery,o.refererQuery)}function E(e,o){if(!o)return!0;for(let t in o)if(o[t]!==e[t])return!1;return!0}function ae(e,{formidableOptions:o={},proxies:t,cookiesOptions:r}){return async function(n,s,i){let c=Date.now(),{query:a,pathname:p}=(0,ie.parse)(n.url,!0);if(!p||t.length===0||!t.some(P=>V(P,n.url)))return i();let d=e.mockData,u=Object.keys(d).find(P=>(0,ce.pathToRegexp)(P).test(p));if(!u)return i();let{query:w}=(0,ie.parse)(n.headers.referer||"",!0),v=await Fe(n,o),g=new _e.default(n,s,r),y=g.get.bind(g),k=n.method.toUpperCase(),m=kt(d[u],{pathname:p,method:k,request:{query:a,refererQuery:w,body:v,headers:n.headers,getCookie:y}});if(!m)return i();D("middleware: ",k,n.url);let M=n,h=s;M.body=v,M.query=a,M.refererQuery=w,M.params=_(m.url,p),M.getCookie=y,h.setCookie=g.set.bind(g);let{body:b,delay:f,type:O="json",response:R,status:$=200,statusText:Ve}=m;if(ne(h,$,Ve),await gt(M,h,m),await yt(M,h,m),b){try{let P=C(b)?await b(M):b;await He(c,f),ht(h,P,O)}catch(P){T.error(`${F.default.red("[body error]")} ${n.url}
|
|
37
|
+
`,P),ne(h,500),s.end("")}return}if(R){try{await He(c,f),await R(M,h,i)}catch(P){T.error(`${F.default.red("[response error]")} ${n.url}
|
|
38
|
+
`,P),ne(h,500),s.end("")}return}s.end("")}}function kt(e,{pathname:o,method:t,request:r}){return e.find(n=>{if(!o||!n||!n.url||n.ws===!0||!(n.method?S(n.method)?n.method:[n.method]:["GET","POST"]).includes(t))return!1;let i=(0,ce.pathToRegexp)(n.url).test(o);if(i&&n.validator){let c=_(n.url,o);if(C(n.validator))return n.validator({params:c,...r});try{return $e({params:c,...r},n.validator)}catch(a){return T.error(`${F.default.red("[validator error]")} ${o}
|
|
39
|
+
`,a),!1}}return i})}function ne(e,o=200,t){e.statusCode=o,e.statusMessage=t||vt(o)}async function gt(e,o,{headers:t,type:r="json"}){let n=N.contentType(r)||N.contentType(N.lookup(r)||"");if(n&&o.setHeader("Content-Type",n),o.setHeader("Cache-Control","no-cache,max-age=0"),o.setHeader("X-Mock","generate by vite:plugin-mock-dev-server"),!!t)try{let s=C(t)?await t(e):t;Object.keys(s).forEach(i=>{o.setHeader(i,s[i])})}catch(s){T.error(`${F.default.red("[headers error]")} ${e.url}
|
|
40
|
+
`,s)}}async function yt(e,o,{cookies:t}){if(t)try{let r=C(t)?await t(e):t;Object.keys(r).forEach(n=>{let s=r[n];if(S(s)){let[i,c]=s;o.setCookie(n,i,c)}else o.setCookie(n,s)})}catch(r){T.error(`${F.default.red("[cookies error]")} ${e.url}
|
|
41
|
+
`,r)}}function ht(e,o,t){if(Pe(o))o.pipe(e);else if(se.Buffer.isBuffer(o))e.end(t==="text"||t==="json"?o.toString("utf-8"):o);else{let r=typeof o=="string"?o:JSON.stringify(o);e.end(t==="buffer"?se.Buffer.from(r):r)}}async function He(e,o){if(!o||o<=0)return;let t=Date.now()-e,r=o-t;r>0&&await Re(r)}function vt(e){return Ue.default[e]||"Unknown"}var Ae=l(require("events"),1),ee=l(require("fs"),1),Be=require("module"),ue=l(require("path"),1),Je=require("url"),le=l(require("chokidar"),1),ze=require("esbuild"),Ge=l(require("fast-glob"),1),W=require("vite");var Ne=require("url"),qe=l(require("lodash.sortby"),1);function pe(e){let o=[];for(let[,r]of e.entries())r&&(S(r)?o.push(...r):o.push(r));let t={};return o.filter(r=>(r.enabled||typeof r.enabled>"u")&&r.url).forEach(r=>{let{pathname:n,query:s}=(0,Ne.parse)(r.url,!0),i=t[n]??(t[n]=[]),c={...r,url:n};if(c.ws!==!0){let a=c.validator;je(s)||(C(a)?c.validator=function(p){return E(p.query,s)&&a(p)}:a?(c.validator={...a},c.validator.query=c.validator.query?{...s,...c.validator.query}:s):c.validator={query:s})}i.push(c)}),Object.keys(t).forEach(r=>{t[r]=(0,qe.default)(t[r],n=>{if(n.ws===!0)return 0;let{validator:s}=n;if(!s)return 1;if(C(s))return 0;let{query:i,params:c,headers:a,body:p,refererQuery:d}=s;return 1/(q(i)+q(c)+q(a)+q(p)+q(d))})}),t}function q(e){return e?Object.keys(e).length:0}var Mt=Ce(x),L=(0,Be.createRequire)(Mt),te=class extends Ae.default{constructor(t){super();this.options=t;this.moduleCache=new Map;this.moduleDeps=new Map;this.moduleType="cjs";this._mockData={};this.cwd=t.cwd||process.cwd();try{let r=H(this.cwd,["package.json"]);this.moduleType=r&&JSON.parse(r).type==="module"?"esm":"cjs"}catch{}}get mockData(){return this._mockData}async load(){let{include:t,exclude:r}=this.options,n=await(0,Ge.default)(t,{cwd:this.cwd}),s=(0,W.createFilter)(t,r,{resolve:!1});this.watchMockEntry(),this.watchDeps();for(let c of n.filter(s))await this.loadMock(c);this.updateMockList();let i=null;this.on("mock:update",async c=>{s(c)&&(await this.loadMock(c),i&&clearTimeout(i),i=setTimeout(()=>{this.updateMockList(),this.emit("mock:update-end",c),i=null},0))}),this.on("mock:unlink",async c=>{s(c)&&(this.moduleCache.delete(c),this.updateMockList(),this.emit("mock:update-end",c))})}watchMockEntry(){let{include:t}=this.options,[r,...n]=t,s=le.default.watch(r,{ignoreInitial:!0,cwd:this.cwd});n.length>0&&n.forEach(i=>s.add(i)),s.on("add",async i=>{i=(0,W.normalizePath)(i),this.emit("mock:update",i),D("watcher:add",i)}),s.on("change",async i=>{i=(0,W.normalizePath)(i),this.emit("mock:update",i),D("watcher:change",i)}),s.on("unlink",async i=>{i=(0,W.normalizePath)(i),this.emit("mock:unlink",i),D("watcher:unlink",i)}),this.mockWatcher=s}watchDeps(){let t=[];this.depsWatcher=le.default.watch([],{ignoreInitial:!0,cwd:this.cwd}),this.depsWatcher.on("change",r=>{r=(0,W.normalizePath)(r);let n=this.moduleDeps.get(r);n&&n.forEach(s=>{this.emit("mock:update",s)})}),this.depsWatcher.on("unlink",r=>{r=(0,W.normalizePath)(r),this.moduleDeps.delete(r)}),this.on("update:deps",()=>{let r=[];for(let[s]of this.moduleDeps.entries())r.push(s);let n=r.filter(s=>!t.includes(s));n.length>0&&this.depsWatcher.add(n)})}close(){var t,r;(t=this.mockWatcher)==null||t.close(),(r=this.depsWatcher)==null||r.close()}updateMockList(){this._mockData=pe(this.moduleCache)}updateModuleDeps(t,r){Object.keys(r).forEach(n=>{r[n].imports.map(i=>i.path).forEach(i=>{this.moduleDeps.has(i)||this.moduleDeps.set(i,new Set),this.moduleDeps.get(i).add(t)})}),this.emit("update:deps")}async loadMock(t){if(!t)return;let r=!1;/\.m[jt]s$/.test(t)?r=!0:/\.c[jt]s$/.test(t)?r=!1:r=this.moduleType==="esm";let{code:n,deps:s}=await this.transformWithEsbuild(t,r);try{let i=await this.loadFromCode(t,n,r),c=i&&i.default?i.default:Object.keys(i||{}).map(a=>i[a]);S(c)?c.forEach(a=>a.__filepath__=t):c.__filepath__=t,this.moduleCache.set(t,c),this.updateModuleDeps(t,s)}catch(i){console.error(i)}}async loadFromCode(t,r,n){if(n){let s=`${t}.timestamp-${Date.now()}`,i=`${s}.mjs`,c=`${(0,Je.pathToFileURL)(s)}.mjs`;await ee.default.promises.writeFile(i,r,"utf8");try{return await import(c)}finally{try{ee.default.unlinkSync(i)}catch{}}}else{t=ue.default.resolve(this.cwd,t);let s=ue.default.extname(t),i=ee.default.realpathSync(t),c=s in L.extensions?s:".js",a=L.extensions[c];L.extensions[c]=(d,u)=>{u===i?d._compile(r,u):a(d,u)},delete L.cache[L.resolve(t)];let p=L(t);return L.extensions[c]=a,p.__esModule?p:{default:p}}}async transformWithEsbuild(t,r){var n;try{let s=await(0,ze.build)({entryPoints:[t],outfile:"out.js",write:!1,target:["node14.18","node16"],platform:"node",bundle:!0,metafile:!0,format:r?"esm":"cjs",define:this.options.define,plugins:[G(this.options.alias),B,z,J]});return{code:s.outputFiles[0].text,deps:((n=s.metafile)==null?void 0:n.inputs)||{}}}catch(s){console.error(s)}return{code:"",deps:{}}}};var me=require("url"),Xe=l(require("cookies"),1),de=require("path-to-regexp"),Ke=require("ws");function fe(e,o,t,r){var c;let n=new Set,s=new Map,i=new Map;(c=e.on)==null||c.call(e,"mock:update-end",a=>{if(!n.has(a))return;let p={};for(let[d,u]of i.entries())e.mockData[d].forEach(w=>{w.ws&&w.__filepath__===a&&u.forEach(({pathname:v,req:g,ws:y})=>{p[v]??(p[v]={mock:w,list:[],mockUrl:d}),p[v].list.push({req:g,ws:y})})});Object.keys(p).forEach(d=>{var m;let{wss:u,cleanupList:w,context:v}=s.get(d),{mock:g,list:y,mockUrl:k}=p[d];Qe(w),u.removeAllListeners(),(m=g.setup)==null||m.call(g,u,v),u.on("close",()=>s.delete(d)),y.forEach(({req:M,ws:h})=>{h.removeAllListeners(),u.emit("connection",h,M),h.on("close",()=>{let b=i.get(k),f=(b==null?void 0:b.findIndex(O=>O.ws===h))||-1;f!==-1&&(b==null||b.splice(f,1))})})})}),o==null||o.on("upgrade",(a,p,d)=>{var b;let{pathname:u,query:w}=(0,me.parse)(a.url,!0);if(!u||t.length===0||!t.some(f=>V(f,a.url)))return;let v=e.mockData,g=Object.keys(v).find(f=>(0,de.pathToRegexp)(f).test(u));if(!g)return;let y=v[g].find(f=>f.url&&f.ws&&(0,de.pathToRegexp)(f.url).test(u));if(!y)return;n.add(y.__filepath__);let k=s.get(u);if(!k){let f=new Ke.WebSocketServer({noServer:!0}),O=[],R={onCleanup:$=>O.push($)};(b=y.setup)==null||b.call(y,f,R),f.on("close",()=>s.delete(u)),s.set(u,k={wss:f,cleanupList:O,context:R})}let m=a,M=new Xe.default(a,a,r),{query:h}=(0,me.parse)(a.headers.referer||"",!0);m.query=w,m.refererQuery=h,m.params=_(g,u),m.getCookie=M.get.bind(M),k.wss.handleUpgrade(m,p,d,f=>{D(`websocket-mock: ${a.url} connected`),k.wss.emit("connection",f,m);let O=i.get(g);O||i.set(g,O=[]),O.push({req:m,ws:f,pathname:u}),f.on("close",()=>{let R=O.findIndex($=>$.ws===f);R!==-1&&O.splice(R,1)})})}),o==null||o.on("close",()=>{s.forEach(({wss:a,cleanupList:p})=>{Qe(p),a.close()}),s.clear(),i.clear(),n.clear()})}function Qe(e){let o;for(;o=e.shift();)o==null||o()}async function ke(e,o,t,r){let n=j(o.include),s=j(o.exclude),i={};if(e.define)for(let d in e.define){let u=e.define[d];i[d]=typeof u=="string"?u:JSON.stringify(u)}let c=new te({include:n,exclude:s,define:i,alias:e.resolve.alias});await c.load(),c.on("mock:update-end",()=>{o.reload&&(r==null||r.send({type:"full-reload"}))}),t==null||t.on("close",()=>c.close());let{httpProxies:a}=K(e.server.proxy||{}),p=j(o.prefix);return fe(c,t,j(o.wsPrefix),o.cookiesOptions),ae(c,{formidableOptions:o.formidableOptions,proxies:[...p,...a],cookiesOptions:o.cookiesOptions})}function ge({prefix:e=[],wsPrefix:o=[],include:t=["mock/**/*.mock.{js,ts,cjs,mjs,json,json5}"],exclude:r=["**/node_modules/**","**/.vscode/**","**/.git/**"],reload:n=!1,formidableOptions:s={},build:i=!1,cookiesOptions:c={}}={}){let a={prefix:e,wsPrefix:o,include:t,exclude:r,reload:n,cookiesOptions:c,formidableOptions:{multiples:!0,...s},build:i?Object.assign({serverPort:8080,dist:"mockServer"},typeof i=="object"?i:{}):!1},p=[xt(a)];return a.build&&p.push(bt(a)),p}function bt(e){let o={};return{name:"vite-plugin-mock-dev-server-generator",enforce:"post",apply:"build",configResolved(t){o=t,t.logger.warn("")},async buildEnd(t){t||o.command==="build"&&await Le(this,o,e)}}}function xt(e){let o={};return{name:"vite-plugin-mock-dev-server",enforce:"pre",apply:"serve",configResolved(t){o=t,t.logger.warn("")},async configureServer({middlewares:t,config:r,httpServer:n,ws:s}){let i=await ke(r,e,n,s);t.use(i)},async configurePreviewServer({middlewares:t,httpServer:r}){let n=await ke(o,e,r);t.use(n)}}}function wt(e){return e}function Ot(e){return t=>(S(t)?t=t.map(r=>e(r)||r):t=e(t)||t,t)}var St=ge;0&&(module.exports={baseMiddleware,createDefineMock,defineMock,mockDevServerPlugin,mockWebSocket,transformMockData});
|
package/dist/index.d.ts
CHANGED
|
@@ -3,21 +3,35 @@ import http from 'node:http';
|
|
|
3
3
|
import { Readable } from 'node:stream';
|
|
4
4
|
import Cookies from 'cookies';
|
|
5
5
|
import formidable from 'formidable';
|
|
6
|
+
import { WebSocketServer } from 'ws';
|
|
6
7
|
import EventEmitter from 'node:events';
|
|
7
8
|
import chokidar from 'chokidar';
|
|
8
9
|
|
|
9
10
|
interface MockServerPluginOptions {
|
|
10
11
|
/**
|
|
11
|
-
* To configure the path matching rules for mock services,
|
|
12
|
+
* To configure the path matching rules for http mock services,
|
|
12
13
|
* any request path starting with prefix will be intercepted and proxied.
|
|
13
14
|
* If the prefix starts with `^`, it will be recognized as a `RegExp`.
|
|
14
15
|
*
|
|
15
|
-
* 为 mock 服务配置 路径匹配规则,任何请求路径以 prefix 开头的都将被拦截代理。
|
|
16
|
+
* 为 http mock 服务配置 路径匹配规则,任何请求路径以 prefix 开头的都将被拦截代理。
|
|
16
17
|
* 如果 prefix 以 `^` 开头,将被识别为 `RegExp`。
|
|
17
18
|
* @default []
|
|
18
19
|
* @example ['^/api']
|
|
19
20
|
*/
|
|
20
21
|
prefix?: string | string[];
|
|
22
|
+
/**
|
|
23
|
+
* Configure path matching rules for WebSocket mock service.
|
|
24
|
+
* Any ws/wss requests with a request path starting with wsPrefix
|
|
25
|
+
* will be intercepted by the proxy.
|
|
26
|
+
* If wsPrefix starts with `^`, it will be recognized as a `RegExp`.
|
|
27
|
+
*
|
|
28
|
+
* 为 websocket mock 服务配置 路径匹配规则, 任何请求路径以 wsPrefix 开头的 ws/wss请求,
|
|
29
|
+
* 都将被代理拦截。
|
|
30
|
+
* 如果 wsPrefix 以 `^` 开头,将被识别为 `RegExp`。
|
|
31
|
+
* @default []
|
|
32
|
+
* @example ['/socket.io']
|
|
33
|
+
*/
|
|
34
|
+
wsPrefix?: string | string[];
|
|
21
35
|
/**
|
|
22
36
|
* glob string matching mock includes files
|
|
23
37
|
*
|
|
@@ -128,7 +142,7 @@ type ResponseHeaderFn = (request: MockRequest) => Headers | Promise<Headers>;
|
|
|
128
142
|
type CookieValue = string | [string, Cookies.SetOption];
|
|
129
143
|
type ResponseCookies = Record<string, CookieValue>;
|
|
130
144
|
type ResponseCookiesFn = (request: MockRequest) => ResponseCookies | Promise<ResponseCookies>;
|
|
131
|
-
interface
|
|
145
|
+
interface MockBaseItem {
|
|
132
146
|
/**
|
|
133
147
|
* The interface address that needs to be mocked,
|
|
134
148
|
* supported by `path-to-regexp` for path matching.
|
|
@@ -144,23 +158,33 @@ interface MockOptionsItem {
|
|
|
144
158
|
*/
|
|
145
159
|
url: string;
|
|
146
160
|
/**
|
|
147
|
-
*
|
|
161
|
+
* Enable WebSocket interface simulation
|
|
148
162
|
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
163
|
+
* 开启 websocket 接口模拟
|
|
164
|
+
*
|
|
165
|
+
* @default false
|
|
151
166
|
*/
|
|
152
|
-
|
|
167
|
+
ws?: boolean;
|
|
153
168
|
/**
|
|
154
169
|
* Whether to enable mock for this interface.
|
|
155
170
|
* In most scenarios, we only need to mock some interfaces instead of all requests that
|
|
156
171
|
* have been configured with mock.
|
|
157
172
|
* Therefore, it is important to be able to configure whether to enable it or not.
|
|
158
173
|
*
|
|
159
|
-
* 是否启动对该接口的mock
|
|
174
|
+
* 是否启动对该接口的mock,在多数场景下,我们仅需要对部分接口进行 mock,
|
|
160
175
|
* 而不是对所有配置了mock的请求进行全量mock,所以是否能够配置是否启用很重要
|
|
161
176
|
* @default true
|
|
162
177
|
*/
|
|
163
178
|
enabled?: boolean;
|
|
179
|
+
}
|
|
180
|
+
interface MockHttpItem extends MockBaseItem {
|
|
181
|
+
/**
|
|
182
|
+
* The interface allows request methods, and by default allows both GET and POST.
|
|
183
|
+
*
|
|
184
|
+
* 该接口允许的 请求方法,默认同时支持 GET 和 POST
|
|
185
|
+
* @default ['POST','GET']
|
|
186
|
+
*/
|
|
187
|
+
method?: Method | Method[];
|
|
164
188
|
/**
|
|
165
189
|
* Configure the response body headers
|
|
166
190
|
*
|
|
@@ -330,11 +354,45 @@ interface MockOptionsItem {
|
|
|
330
354
|
* ```
|
|
331
355
|
*/
|
|
332
356
|
validator?: Partial<Omit<ExtraRequest, 'getCookie'>> | ((request: ExtraRequest) => boolean);
|
|
357
|
+
ws?: false;
|
|
358
|
+
}
|
|
359
|
+
interface MockWebsocketItem extends MockBaseItem {
|
|
360
|
+
ws: true;
|
|
361
|
+
/**
|
|
362
|
+
* Configure Websocket Server
|
|
363
|
+
*
|
|
364
|
+
* 配置 Websocket Server
|
|
365
|
+
* @example
|
|
366
|
+
* ```ts
|
|
367
|
+
* export default {
|
|
368
|
+
* ws: true
|
|
369
|
+
* setup: (wss, { onCleanup }) => {
|
|
370
|
+
* wss.on('connection', (ws,req) => {
|
|
371
|
+
* ws.on('message', (raw) => console.log(raw))
|
|
372
|
+
* const timer = setInterval(
|
|
373
|
+
* () => ws.send(JSON.stringify({ type: 'connected' })),
|
|
374
|
+
* 1000,
|
|
375
|
+
* )
|
|
376
|
+
* onCleanup(() => clearInterval(timer))
|
|
377
|
+
* })
|
|
378
|
+
* wss.on('error', (error) => console.error(error))
|
|
379
|
+
* }
|
|
380
|
+
* }
|
|
381
|
+
* ```
|
|
382
|
+
*/
|
|
383
|
+
setup: (wss: WebSocketServer, context: WebSocketSetupContext) => void;
|
|
333
384
|
}
|
|
334
|
-
|
|
385
|
+
interface WebSocketSetupContext {
|
|
386
|
+
/**
|
|
387
|
+
* 当你在定义 WSS 时,可能会执行一些自动任务或循环任务,
|
|
388
|
+
* 但是当热更新时,插件内部会重新执行 setup() ,这可能导致出现
|
|
389
|
+
*/
|
|
390
|
+
onCleanup: (cleanup: () => void) => void;
|
|
391
|
+
}
|
|
392
|
+
type MockOptions = (MockHttpItem | MockWebsocketItem)[];
|
|
335
393
|
type FormidableFile = formidable.File | formidable.File[];
|
|
336
394
|
|
|
337
|
-
declare function mockDevServerPlugin({ prefix, include, exclude, reload, formidableOptions, build, cookiesOptions, }?: MockServerPluginOptions): Plugin[];
|
|
395
|
+
declare function mockDevServerPlugin({ prefix, wsPrefix, include, exclude, reload, formidableOptions, build, cookiesOptions, }?: MockServerPluginOptions): Plugin[];
|
|
338
396
|
|
|
339
397
|
/**
|
|
340
398
|
* mock config helper
|
|
@@ -351,8 +409,9 @@ declare function mockDevServerPlugin({ prefix, include, exclude, reload, formida
|
|
|
351
409
|
* })
|
|
352
410
|
* ```
|
|
353
411
|
*/
|
|
354
|
-
declare function defineMock(config:
|
|
412
|
+
declare function defineMock(config: MockHttpItem): MockHttpItem;
|
|
355
413
|
declare function defineMock(config: MockOptions): MockOptions;
|
|
414
|
+
declare function defineMock(config: MockWebsocketItem): MockWebsocketItem;
|
|
356
415
|
/**
|
|
357
416
|
* 返回一个自定义的 defineMock 函数,用于支持对 mock config 的预处理。
|
|
358
417
|
*
|
|
@@ -360,7 +419,7 @@ declare function defineMock(config: MockOptions): MockOptions;
|
|
|
360
419
|
*
|
|
361
420
|
* @param transformer preprocessing function
|
|
362
421
|
*/
|
|
363
|
-
declare function createDefineMock(transformer: (mock:
|
|
422
|
+
declare function createDefineMock(transformer: (mock: MockHttpItem | MockWebsocketItem) => MockHttpItem | MockWebsocketItem | void): typeof defineMock;
|
|
364
423
|
|
|
365
424
|
interface MockLoaderOptions {
|
|
366
425
|
cwd?: string;
|
|
@@ -374,8 +433,7 @@ interface MockLoaderOptions {
|
|
|
374
433
|
*/
|
|
375
434
|
declare class MockLoader extends EventEmitter {
|
|
376
435
|
options: MockLoaderOptions;
|
|
377
|
-
|
|
378
|
-
moduleCache: Map<string, MockOptions | MockOptionsItem>;
|
|
436
|
+
moduleCache: Map<string, MockOptions | MockHttpItem | MockWebsocketItem>;
|
|
379
437
|
moduleDeps: Map<string, Set<string>>;
|
|
380
438
|
cwd: string;
|
|
381
439
|
mockWatcher: chokidar.FSWatcher;
|
|
@@ -395,8 +453,6 @@ declare class MockLoader extends EventEmitter {
|
|
|
395
453
|
private updateMockList;
|
|
396
454
|
private updateModuleDeps;
|
|
397
455
|
private loadMock;
|
|
398
|
-
private loadJson;
|
|
399
|
-
private loadModule;
|
|
400
456
|
private loadFromCode;
|
|
401
457
|
private transformWithEsbuild;
|
|
402
458
|
}
|
|
@@ -408,6 +464,8 @@ interface BaseMiddlewareOptions {
|
|
|
408
464
|
}
|
|
409
465
|
declare function baseMiddleware(mockLoader: MockLoader, { formidableOptions, proxies, cookiesOptions }: BaseMiddlewareOptions): Connect.NextHandleFunction;
|
|
410
466
|
|
|
411
|
-
declare function
|
|
467
|
+
declare function mockWebSocket(loader: MockLoader, httpServer: http.Server | null, proxies: string[], cookiesOptions: MockServerPluginOptions['cookiesOptions']): void;
|
|
468
|
+
|
|
469
|
+
declare function transformMockData(mockList: Map<string, MockHttpItem | MockWebsocketItem | MockOptions> | (MockHttpItem | MockWebsocketItem | MockOptions)[]): Record<string, MockOptions>;
|
|
412
470
|
|
|
413
|
-
export { BaseMiddlewareOptions, FormidableFile, MockOptions,
|
|
471
|
+
export { BaseMiddlewareOptions, FormidableFile, MockHttpItem, MockOptions, MockRequest, MockServerPluginOptions, MockWebsocketItem, baseMiddleware, createDefineMock, mockDevServerPlugin as default, defineMock, mockDevServerPlugin, mockWebSocket, transformMockData };
|
package/dist/index.js
CHANGED
|
@@ -1,32 +1,41 @@
|
|
|
1
|
-
import Fe from"fs";import
|
|
2
|
-
`,
|
|
3
|
-
`)}};function
|
|
1
|
+
import Fe from"fs";import Q from"fs/promises";import L from"path";import{build as $e}from"esbuild";import He from"fast-glob";import _e from"is-core-module";import{createFilter as Ue,normalizePath as Ne}from"vite";var re="vite-plugin-mock-dev-server",ne="1.1.7";import se from"fs/promises";import Re from"path";import Ce from"json5";var q={name:"externalize-deps",setup(t){t.onResolve({filter:/.*/},({path:o})=>{if(o[0]!=="."&&!Re.isAbsolute(o))return{external:!0}})}},A={name:"json5-loader",setup(t){t.onLoad({filter:/\.json5$/},async({path:o})=>{let e=await se.readFile(o,"utf-8");return{contents:`export default ${JSON.stringify(Ce.parse(e))}`,loader:"js"}})}},B={name:"json-loader",setup(t){t.onLoad({filter:/\.json$/},async({path:o})=>({contents:`export default ${await se.readFile(o,"utf-8")}`,loader:"js"}))}},J=t=>({name:"alias-plugin",setup(o){o.onResolve({filter:/.*/},async({path:e})=>{let r=t.find(({find:c})=>De(c,e));if(!r)return null;let{find:s,replacement:n}=r;return{path:(await o.resolve(e.replace(s,n),{kind:"import-statement",resolveDir:n,namespace:"file"})).path,external:!1}})}});function De(t,o){return t instanceof RegExp?t.test(o):o.length<t.length?!1:o===t?!0:o.startsWith(`${t}/`)}import K from"fs";import V from"path";import{fileURLToPath as We}from"url";import Ee from"debug";import{match as Ie}from"path-to-regexp";import ie from"picocolors";var S=t=>Array.isArray(t),C=t=>typeof t=="function",Le=t=>Object.prototype.toString.call(t)==="[object Object]",ce=t=>Le(t)&&Object.keys(t).length===0,Te=t=>t!==null&&typeof t=="object"&&typeof t.pipe=="function",ae=t=>Te(t)&&t.readable!==!1&&typeof t._read=="function"&&typeof t._readableState=="object";function pe(t){return new Promise(o=>setTimeout(o,t))}function ue(t){return V.dirname(We(t))}var D=Ee("vite:plugin-mock-dev-server"),P=t=>S(t)?t:t==null?[]:[t],I={info(...t){console.info(ie.cyan("mock-dev-server: "),...t)},error(...t){console.error(`
|
|
2
|
+
`,ie.cyan("mock-dev-server: "),...t,`
|
|
3
|
+
`)}};function F(t,o,e){for(let s of o){let n=V.join(t,s);if(K.existsSync(n)&&K.statSync(n).isFile()){let i=e!=null&&e.pathOnly?n:K.readFileSync(n,"utf-8");if(!(e!=null&&e.predicate)||e.predicate(i))return i}}let r=V.dirname(t);if(r!==t&&(!(e!=null&&e.rootDir)||r.startsWith(e==null?void 0:e.rootDir)))return F(r,o,e)}var z=(t={})=>{let o=[],e=[];return Object.keys(t).forEach(r=>{var n,i;let s=t[r];typeof s=="string"||!s.ws&&!((n=s.target)!=null&&n.toString().startsWith("ws:"))&&!((i=s.target)!=null&&i.toString().startsWith("wss:"))?o.push(r):e.push(r)}),{httpProxies:o,wsProxies:e}};function G(t,o){return t[0]==="^"&&new RegExp(t).test(o)||o.startsWith(t)}function $(t,o){return(Ie(t,{decode:decodeURIComponent})(o)||{params:{}}).params||{}}async function le(t,o,e){let r=P(e.include),s=P(e.exclude),n={};if(o.define)for(let f in o.define){let l=o.define[f];n[f]=typeof l=="string"?l:JSON.stringify(l)}let{httpProxies:i}=z(o.server.proxy||{});i.push(...P(e.prefix));let c=P(e.wsPrefix),a={};try{let f=F(o.root,["package.json"]);f&&(a=JSON.parse(f))}catch{}let p=e.build.dist,m=await Je(process.cwd(),r,s),u=L.join(o.root,`mock-data-${Date.now()}.js`);await Q.writeFile(u,m,"utf-8");let{code:b,deps:h}=await ze(u,n,o.resolve.alias),k=qe(h);await Q.unlink(u);let g=[{filename:L.join(p,"mock-data.js"),source:b},{filename:L.join(p,"index.js"),source:Be(i,c,e.cookiesOptions,e.build.serverPort)},{filename:L.join(p,"package.json"),source:Ae(a,k)}];try{if(L.isAbsolute(p)){await Q.rm(p,{recursive:!0}),Fe.mkdirSync(p,{recursive:!0});for(let{filename:f,source:l}of g)await Q.writeFile(f,l,"utf-8")}else for(let{filename:f,source:l}of g)t.emitFile({type:"asset",fileName:f,source:l})}catch{}}function qe(t){let o=new Set,e=[re,"connect","cors"];return Object.keys(t).forEach(r=>{t[r].imports.filter(n=>n.external).map(n=>n.path).forEach(n=>{!e.includes(n)&&!_e(n)&&o.add(n)})}),Array.from(o)}function Ae(t,o){let{dependencies:e={},devDependencies:r={}}=t,s={...e,...r},n={name:"mock-server",type:"module",scripts:{start:"node index.js"},dependencies:{connect:"^3.7.0","vite-plugin-mock-dev-server":`^${ne}`,cors:"^2.8.5"},pnpm:{peerDependencyRules:{ignoreMissing:["vite"]}}};return o.forEach(i=>{n.dependencies[i]=s[i]||"latest"}),JSON.stringify(n,null,2)}function Be(t,o,e={},r=8080){return`import { createServer } from 'node:http';
|
|
4
|
+
import connect from 'connect';
|
|
4
5
|
import corsMiddleware from 'cors';
|
|
5
|
-
import { baseMiddleware } from 'vite-plugin-mock-dev-server';
|
|
6
|
+
import { baseMiddleware, mockWebSocket } from 'vite-plugin-mock-dev-server';
|
|
6
7
|
import mockData from './mock-data.js';
|
|
7
8
|
|
|
8
9
|
const app = connect();
|
|
10
|
+
const server = createServer(app);
|
|
11
|
+
const httpProxies = ${JSON.stringify(t)};
|
|
12
|
+
const wxProxies = ${JSON.stringify(o)}
|
|
13
|
+
const cookiesOptions = ${JSON.stringify(e)};
|
|
14
|
+
|
|
15
|
+
mockWebSocket({ mockData }, server, wxProxies, cookiesOptions)
|
|
16
|
+
|
|
9
17
|
app.use(corsMiddleware());
|
|
10
18
|
app.use(baseMiddleware({ mockData }, {
|
|
11
19
|
formidableOptions: { multiples: true },
|
|
12
|
-
proxies:
|
|
13
|
-
cookiesOptions
|
|
20
|
+
proxies: httpProxies,
|
|
21
|
+
cookiesOptions,
|
|
14
22
|
}));
|
|
15
|
-
app.listen(${e});
|
|
16
23
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
24
|
+
server.listen(${r});
|
|
25
|
+
|
|
26
|
+
console.log('listen: http://localhost:${r}');
|
|
27
|
+
`}async function Je(t,o,e){let r=await He(o,{cwd:t}),s=Ue(o,e,{resolve:!1}),n=r.filter(s),i="",c="";return n.forEach((a,p)=>{let m=Ne(L.join(t,a));i+=`import * as m${p} from '${m}';
|
|
28
|
+
`,c+=`m${p}, `}),`import { transformMockData } from 'vite-plugin-mock-dev-server';
|
|
20
29
|
${i}
|
|
21
|
-
const exporters = [${
|
|
30
|
+
const exporters = [${c}];
|
|
22
31
|
const mockList = exporters.map((raw) => raw && raw.default
|
|
23
32
|
? raw.default
|
|
24
33
|
: Object.keys(raw || {}).map((key) => raw[key])
|
|
25
34
|
)
|
|
26
35
|
export default transformMockData(mockList);
|
|
27
|
-
`}async function
|
|
28
|
-
`,
|
|
29
|
-
`,
|
|
30
|
-
`,
|
|
31
|
-
`,
|
|
32
|
-
`,
|
|
36
|
+
`}async function ze(t,o,e){var r;try{let s=await $e({entryPoints:[t],outfile:"out.js",write:!1,target:["node14.18","node16"],platform:"node",bundle:!0,metafile:!0,format:"esm",define:o,plugins:[J(e),q,A,B]});return{code:s.outputFiles[0].text,deps:((r=s.metafile)==null?void 0:r.inputs)||{}}}catch(s){console.error(s)}return{code:"",deps:{}}}import{Buffer as fe}from"buffer";import{parse as ke}from"url";import Xe from"cookies";import Ke from"http-status";import*as H from"mime-types";import{pathToRegexp as ye}from"path-to-regexp";import _ from"picocolors";import Y from"co-body";import Ge from"formidable";async function me(t,o){var s;let e=t.method.toUpperCase();if(["GET","DELETE","HEAD"].includes(e))return;let r=((s=t.headers["content-type"])==null?void 0:s.toLocaleLowerCase())||"";try{if(r.startsWith("application/json"))return await Y.json(t);if(r.startsWith("application/x-www-form-urlencoded"))return await Y.form(t);if(r.startsWith("text/plain"))return await Y.text(t);if(r.startsWith("multipart/form-data"))return await Qe(t,o)}catch(n){console.error(n)}}async function Qe(t,o){let e=Ge(o);return new Promise((r,s)=>{e.parse(t,(n,i,c)=>{if(n){s(n);return}r({...i,...c})})})}function de(t,o){return W(t.headers,o.headers)&&W(t.body,o.body)&&W(t.params,o.params)&&W(t.query,o.query)&&W(t.refererQuery,o.refererQuery)}function W(t,o){if(!o)return!0;for(let e in o)if(o[e]!==t[e])return!1;return!0}function he(t,{formidableOptions:o={},proxies:e,cookiesOptions:r}){return async function(s,n,i){let c=Date.now(),{query:a,pathname:p}=ke(s.url,!0);if(!p||e.length===0||!e.some(j=>G(j,s.url)))return i();let m=t.mockData,u=Object.keys(m).find(j=>ye(j).test(p));if(!u)return i();let{query:b}=ke(s.headers.referer||"",!0),h=await me(s,o),k=new Xe(s,n,r),g=k.get.bind(k),f=s.method.toUpperCase(),l=Ve(m[u],{pathname:p,method:f,request:{query:a,refererQuery:b,body:h,headers:s.headers,getCookie:g}});if(!l)return i();D("middleware: ",f,s.url);let v=s,y=n;v.body=h,v.query=a,v.refererQuery=b,v.params=$(l.url,p),v.getCookie=g,y.setCookie=k.set.bind(k);let{body:M,delay:d,type:x="json",response:R,status:T=200,statusText:je}=l;if(Z(y,T,je),await Ye(v,y,l),await Ze(v,y,l),M){try{let j=C(M)?await M(v):M;await ge(c,d),et(y,j,x)}catch(j){I.error(`${_.red("[body error]")} ${s.url}
|
|
37
|
+
`,j),Z(y,500),n.end("")}return}if(R){try{await ge(c,d),await R(v,y,i)}catch(j){I.error(`${_.red("[response error]")} ${s.url}
|
|
38
|
+
`,j),Z(y,500),n.end("")}return}n.end("")}}function Ve(t,{pathname:o,method:e,request:r}){return t.find(s=>{if(!o||!s||!s.url||s.ws===!0||!(s.method?S(s.method)?s.method:[s.method]:["GET","POST"]).includes(e))return!1;let i=ye(s.url).test(o);if(i&&s.validator){let c=$(s.url,o);if(C(s.validator))return s.validator({params:c,...r});try{return de({params:c,...r},s.validator)}catch(a){return I.error(`${_.red("[validator error]")} ${o}
|
|
39
|
+
`,a),!1}}return i})}function Z(t,o=200,e){t.statusCode=o,t.statusMessage=e||tt(o)}async function Ye(t,o,{headers:e,type:r="json"}){let s=H.contentType(r)||H.contentType(H.lookup(r)||"");if(s&&o.setHeader("Content-Type",s),o.setHeader("Cache-Control","no-cache,max-age=0"),o.setHeader("X-Mock","generate by vite:plugin-mock-dev-server"),!!e)try{let n=C(e)?await e(t):e;Object.keys(n).forEach(i=>{o.setHeader(i,n[i])})}catch(n){I.error(`${_.red("[headers error]")} ${t.url}
|
|
40
|
+
`,n)}}async function Ze(t,o,{cookies:e}){if(e)try{let r=C(e)?await e(t):e;Object.keys(r).forEach(s=>{let n=r[s];if(S(n)){let[i,c]=n;o.setCookie(s,i,c)}else o.setCookie(s,n)})}catch(r){I.error(`${_.red("[cookies error]")} ${t.url}
|
|
41
|
+
`,r)}}function et(t,o,e){if(ae(o))o.pipe(t);else if(fe.isBuffer(o))t.end(e==="text"||e==="json"?o.toString("utf-8"):o);else{let r=typeof o=="string"?o:JSON.stringify(o);t.end(e==="buffer"?fe.from(r):r)}}async function ge(t,o){if(!o||o<=0)return;let e=Date.now()-t,r=o-e;r>0&&await pe(r)}function tt(t){return Ke[t]||"Unknown"}import nt from"events";import ee from"fs";import{createRequire as st}from"module";import Me from"path";import{pathToFileURL as it}from"url";import be from"chokidar";import{build as ct}from"esbuild";import at from"fast-glob";import{createFilter as pt,normalizePath as N}from"vite";import{parse as ot}from"url";import rt from"lodash.sortby";function ve(t){let o=[];for(let[,r]of t.entries())r&&(S(r)?o.push(...r):o.push(r));let e={};return o.filter(r=>(r.enabled||typeof r.enabled>"u")&&r.url).forEach(r=>{let{pathname:s,query:n}=ot(r.url,!0),i=e[s]??(e[s]=[]),c={...r,url:s};if(c.ws!==!0){let a=c.validator;ce(n)||(C(a)?c.validator=function(p){return W(p.query,n)&&a(p)}:a?(c.validator={...a},c.validator.query=c.validator.query?{...n,...c.validator.query}:n):c.validator={query:n})}i.push(c)}),Object.keys(e).forEach(r=>{e[r]=rt(e[r],s=>{if(s.ws===!0)return 0;let{validator:n}=s;if(!n)return 1;if(C(n))return 0;let{query:i,params:c,headers:a,body:p,refererQuery:m}=n;return 1/(U(i)+U(c)+U(a)+U(p)+U(m))})}),e}function U(t){return t?Object.keys(t).length:0}var ut=ue(import.meta.url),E=st(ut),X=class extends nt{constructor(e){super();this.options=e;this.moduleCache=new Map;this.moduleDeps=new Map;this.moduleType="cjs";this._mockData={};this.cwd=e.cwd||process.cwd();try{let r=F(this.cwd,["package.json"]);this.moduleType=r&&JSON.parse(r).type==="module"?"esm":"cjs"}catch{}}get mockData(){return this._mockData}async load(){let{include:e,exclude:r}=this.options,s=await at(e,{cwd:this.cwd}),n=pt(e,r,{resolve:!1});this.watchMockEntry(),this.watchDeps();for(let c of s.filter(n))await this.loadMock(c);this.updateMockList();let i=null;this.on("mock:update",async c=>{n(c)&&(await this.loadMock(c),i&&clearTimeout(i),i=setTimeout(()=>{this.updateMockList(),this.emit("mock:update-end",c),i=null},0))}),this.on("mock:unlink",async c=>{n(c)&&(this.moduleCache.delete(c),this.updateMockList(),this.emit("mock:update-end",c))})}watchMockEntry(){let{include:e}=this.options,[r,...s]=e,n=be.watch(r,{ignoreInitial:!0,cwd:this.cwd});s.length>0&&s.forEach(i=>n.add(i)),n.on("add",async i=>{i=N(i),this.emit("mock:update",i),D("watcher:add",i)}),n.on("change",async i=>{i=N(i),this.emit("mock:update",i),D("watcher:change",i)}),n.on("unlink",async i=>{i=N(i),this.emit("mock:unlink",i),D("watcher:unlink",i)}),this.mockWatcher=n}watchDeps(){let e=[];this.depsWatcher=be.watch([],{ignoreInitial:!0,cwd:this.cwd}),this.depsWatcher.on("change",r=>{r=N(r);let s=this.moduleDeps.get(r);s&&s.forEach(n=>{this.emit("mock:update",n)})}),this.depsWatcher.on("unlink",r=>{r=N(r),this.moduleDeps.delete(r)}),this.on("update:deps",()=>{let r=[];for(let[n]of this.moduleDeps.entries())r.push(n);let s=r.filter(n=>!e.includes(n));s.length>0&&this.depsWatcher.add(s)})}close(){var e,r;(e=this.mockWatcher)==null||e.close(),(r=this.depsWatcher)==null||r.close()}updateMockList(){this._mockData=ve(this.moduleCache)}updateModuleDeps(e,r){Object.keys(r).forEach(s=>{r[s].imports.map(i=>i.path).forEach(i=>{this.moduleDeps.has(i)||this.moduleDeps.set(i,new Set),this.moduleDeps.get(i).add(e)})}),this.emit("update:deps")}async loadMock(e){if(!e)return;let r=!1;/\.m[jt]s$/.test(e)?r=!0:/\.c[jt]s$/.test(e)?r=!1:r=this.moduleType==="esm";let{code:s,deps:n}=await this.transformWithEsbuild(e,r);try{let i=await this.loadFromCode(e,s,r),c=i&&i.default?i.default:Object.keys(i||{}).map(a=>i[a]);S(c)?c.forEach(a=>a.__filepath__=e):c.__filepath__=e,this.moduleCache.set(e,c),this.updateModuleDeps(e,n)}catch(i){console.error(i)}}async loadFromCode(e,r,s){if(s){let n=`${e}.timestamp-${Date.now()}`,i=`${n}.mjs`,c=`${it(n)}.mjs`;await ee.promises.writeFile(i,r,"utf8");try{return await import(c)}finally{try{ee.unlinkSync(i)}catch{}}}else{e=Me.resolve(this.cwd,e);let n=Me.extname(e),i=ee.realpathSync(e),c=n in E.extensions?n:".js",a=E.extensions[c];E.extensions[c]=(m,u)=>{u===i?m._compile(r,u):a(m,u)},delete E.cache[E.resolve(e)];let p=E(e);return E.extensions[c]=a,p.__esModule?p:{default:p}}}async transformWithEsbuild(e,r){var s;try{let n=await ct({entryPoints:[e],outfile:"out.js",write:!1,target:["node14.18","node16"],platform:"node",bundle:!0,metafile:!0,format:r?"esm":"cjs",define:this.options.define,plugins:[J(this.options.alias),q,B,A]});return{code:n.outputFiles[0].text,deps:((s=n.metafile)==null?void 0:s.inputs)||{}}}catch(n){console.error(n)}return{code:"",deps:{}}}};import{parse as xe}from"url";import lt from"cookies";import{pathToRegexp as we}from"path-to-regexp";import{WebSocketServer as mt}from"ws";function Se(t,o,e,r){var c;let s=new Set,n=new Map,i=new Map;(c=t.on)==null||c.call(t,"mock:update-end",a=>{if(!s.has(a))return;let p={};for(let[m,u]of i.entries())t.mockData[m].forEach(b=>{b.ws&&b.__filepath__===a&&u.forEach(({pathname:h,req:k,ws:g})=>{p[h]??(p[h]={mock:b,list:[],mockUrl:m}),p[h].list.push({req:k,ws:g})})});Object.keys(p).forEach(m=>{var l;let{wss:u,cleanupList:b,context:h}=n.get(m),{mock:k,list:g,mockUrl:f}=p[m];Oe(b),u.removeAllListeners(),(l=k.setup)==null||l.call(k,u,h),u.on("close",()=>n.delete(m)),g.forEach(({req:v,ws:y})=>{y.removeAllListeners(),u.emit("connection",y,v),y.on("close",()=>{let M=i.get(f),d=(M==null?void 0:M.findIndex(x=>x.ws===y))||-1;d!==-1&&(M==null||M.splice(d,1))})})})}),o==null||o.on("upgrade",(a,p,m)=>{var M;let{pathname:u,query:b}=xe(a.url,!0);if(!u||e.length===0||!e.some(d=>G(d,a.url)))return;let h=t.mockData,k=Object.keys(h).find(d=>we(d).test(u));if(!k)return;let g=h[k].find(d=>d.url&&d.ws&&we(d.url).test(u));if(!g)return;s.add(g.__filepath__);let f=n.get(u);if(!f){let d=new mt({noServer:!0}),x=[],R={onCleanup:T=>x.push(T)};(M=g.setup)==null||M.call(g,d,R),d.on("close",()=>n.delete(u)),n.set(u,f={wss:d,cleanupList:x,context:R})}let l=a,v=new lt(a,a,r),{query:y}=xe(a.headers.referer||"",!0);l.query=b,l.refererQuery=y,l.params=$(k,u),l.getCookie=v.get.bind(v),f.wss.handleUpgrade(l,p,m,d=>{D(`websocket-mock: ${a.url} connected`),f.wss.emit("connection",d,l);let x=i.get(k);x||i.set(k,x=[]),x.push({req:l,ws:d,pathname:u}),d.on("close",()=>{let R=x.findIndex(T=>T.ws===d);R!==-1&&x.splice(R,1)})})}),o==null||o.on("close",()=>{n.forEach(({wss:a,cleanupList:p})=>{Oe(p),a.close()}),n.clear(),i.clear(),s.clear()})}function Oe(t){let o;for(;o=t.shift();)o==null||o()}async function te(t,o,e,r){let s=P(o.include),n=P(o.exclude),i={};if(t.define)for(let m in t.define){let u=t.define[m];i[m]=typeof u=="string"?u:JSON.stringify(u)}let c=new X({include:s,exclude:n,define:i,alias:t.resolve.alias});await c.load(),c.on("mock:update-end",()=>{o.reload&&(r==null||r.send({type:"full-reload"}))}),e==null||e.on("close",()=>c.close());let{httpProxies:a}=z(t.server.proxy||{}),p=P(o.prefix);return Se(c,e,P(o.wsPrefix),o.cookiesOptions),he(c,{formidableOptions:o.formidableOptions,proxies:[...p,...a],cookiesOptions:o.cookiesOptions})}function Pe({prefix:t=[],wsPrefix:o=[],include:e=["mock/**/*.mock.{js,ts,cjs,mjs,json,json5}"],exclude:r=["**/node_modules/**","**/.vscode/**","**/.git/**"],reload:s=!1,formidableOptions:n={},build:i=!1,cookiesOptions:c={}}={}){let a={prefix:t,wsPrefix:o,include:e,exclude:r,reload:s,cookiesOptions:c,formidableOptions:{multiples:!0,...n},build:i?Object.assign({serverPort:8080,dist:"mockServer"},typeof i=="object"?i:{}):!1},p=[ft(a)];return a.build&&p.push(dt(a)),p}function dt(t){let o={};return{name:"vite-plugin-mock-dev-server-generator",enforce:"post",apply:"build",configResolved(e){o=e,e.logger.warn("")},async buildEnd(e){e||o.command==="build"&&await le(this,o,t)}}}function ft(t){let o={};return{name:"vite-plugin-mock-dev-server",enforce:"pre",apply:"serve",configResolved(e){o=e,e.logger.warn("")},async configureServer({middlewares:e,config:r,httpServer:s,ws:n}){let i=await te(r,t,s,n);e.use(i)},async configurePreviewServer({middlewares:e,httpServer:r}){let s=await te(o,t,r);e.use(s)}}}function Ao(t){return t}function Bo(t){return e=>(S(e)?e=e.map(r=>t(r)||r):e=t(e)||e,e)}var Qo=Pe;export{he as baseMiddleware,Bo as createDefineMock,Qo as default,Ao as defineMock,Pe as mockDevServerPlugin,Se as mockWebSocket,ve as transformMockData};
|
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.7",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"vite",
|
|
6
6
|
"plugin",
|
|
@@ -43,7 +43,8 @@
|
|
|
43
43
|
"lodash.sortby": "^4.7.0",
|
|
44
44
|
"mime-types": "^2.1.35",
|
|
45
45
|
"path-to-regexp": "^6.2.1",
|
|
46
|
-
"picocolors": "^1.0.0"
|
|
46
|
+
"picocolors": "^1.0.0",
|
|
47
|
+
"ws": "^8.13.0"
|
|
47
48
|
},
|
|
48
49
|
"devDependencies": {
|
|
49
50
|
"@pengzhanbo/eslint-config-ts": "^0.3.4",
|
|
@@ -56,6 +57,7 @@
|
|
|
56
57
|
"@types/lodash.sortby": "^4.7.7",
|
|
57
58
|
"@types/mime-types": "^2.1.1",
|
|
58
59
|
"@types/node": "^18.16.1",
|
|
60
|
+
"@types/ws": "^8.5.4",
|
|
59
61
|
"bumpp": "^9.1.0",
|
|
60
62
|
"conventional-changelog-cli": "^2.2.2",
|
|
61
63
|
"eslint": "^8.39.0",
|