vite-plugin-mock-dev-server 0.2.3 → 0.3.2
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 +108 -92
- package/README.zh-CN.md +397 -0
- package/dist/index.cjs +100 -37
- package/dist/index.d.ts +6 -1
- package/dist/index.js +97 -35
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,28 +1,35 @@
|
|
|
1
1
|
# vite-plugin-mock-dev-server
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<p align="center">Vite Plugin for API mock dev server.</p>
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
<br>
|
|
6
|
+
<br>
|
|
7
|
+
<p align="center">
|
|
8
|
+
<span>English</span> | <a href="/README.zh-CN.md">简体中文</a>
|
|
9
|
+
</p>
|
|
10
|
+
<br>
|
|
11
|
+
<br>
|
|
6
12
|
|
|
7
|
-
## 特性
|
|
8
13
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
-
|
|
14
|
+
## Feature
|
|
15
|
+
|
|
16
|
+
- ⚡️ light weight,flexible,fast
|
|
17
|
+
- 🧲 Non - injection, no intrusion to client code
|
|
18
|
+
- 💡 ESModule/commonjs
|
|
12
19
|
- 🦾 Typescript
|
|
13
|
-
- 🏷
|
|
14
|
-
- 📦
|
|
15
|
-
- 🎨
|
|
16
|
-
- 📥
|
|
17
|
-
- ⚙️
|
|
18
|
-
- 🔥
|
|
19
|
-
- ⚖️
|
|
20
|
-
- 🍕
|
|
20
|
+
- 🏷 Support json / json5
|
|
21
|
+
- 📦 Auto import mock file
|
|
22
|
+
- 🎨 Support any lib,like `mockjs`,or not use it.
|
|
23
|
+
- 📥 Path rules match and request parameters match
|
|
24
|
+
- ⚙️ Support Enabled/Disabled any one of api mock
|
|
25
|
+
- 🔥 Hot reload
|
|
26
|
+
- ⚖️ Use `server.proxy`
|
|
27
|
+
- 🍕 Support `viteConfig.define` in mock file
|
|
21
28
|
|
|
22
29
|
|
|
23
|
-
##
|
|
30
|
+
## Usage
|
|
24
31
|
|
|
25
|
-
###
|
|
32
|
+
### Install
|
|
26
33
|
|
|
27
34
|
```sh
|
|
28
35
|
# npm
|
|
@@ -33,7 +40,7 @@ yarn add vite-plugin-mock-dev-server
|
|
|
33
40
|
pnpm add -D vite-plugin-mock-dev-server
|
|
34
41
|
```
|
|
35
42
|
|
|
36
|
-
###
|
|
43
|
+
### Configuration
|
|
37
44
|
|
|
38
45
|
`vite.config.ts`
|
|
39
46
|
```ts
|
|
@@ -44,7 +51,6 @@ export default defineConfig({
|
|
|
44
51
|
plugins: [
|
|
45
52
|
mockDevServerPlugin(),
|
|
46
53
|
],
|
|
47
|
-
// 这里定义的字段,在mock中也能使用
|
|
48
54
|
define: {},
|
|
49
55
|
server: {
|
|
50
56
|
proxy: {
|
|
@@ -55,15 +61,15 @@ export default defineConfig({
|
|
|
55
61
|
}
|
|
56
62
|
})
|
|
57
63
|
```
|
|
58
|
-
|
|
64
|
+
The plugin reads the configuration for `server.proxy` and enables mock matching only for urls where the proxy is set.
|
|
59
65
|
|
|
60
|
-
|
|
66
|
+
The plugin also reads the `define` configuration and supports direct use in mock files.
|
|
61
67
|
|
|
62
|
-
>
|
|
68
|
+
> In a general case, we only need to mock the url with the proxy so that we can proxy and mock through the http service provided by vite
|
|
63
69
|
|
|
64
|
-
###
|
|
70
|
+
### Edit Mock File
|
|
65
71
|
|
|
66
|
-
|
|
72
|
+
By default, write mock data in the `mock` directory of your project root:
|
|
67
73
|
|
|
68
74
|
`mock/api.mock.ts` :
|
|
69
75
|
```ts
|
|
@@ -78,7 +84,7 @@ export default defineMock({
|
|
|
78
84
|
})
|
|
79
85
|
```
|
|
80
86
|
|
|
81
|
-
##
|
|
87
|
+
## Methods
|
|
82
88
|
|
|
83
89
|
### mockDevServerPlugin(options)
|
|
84
90
|
|
|
@@ -101,20 +107,31 @@ export default defineConfig({
|
|
|
101
107
|
|
|
102
108
|
- `option.include`
|
|
103
109
|
|
|
104
|
-
|
|
110
|
+
Configure to read mock files, which can be a directory, glob, or array
|
|
105
111
|
|
|
106
|
-
|
|
112
|
+
Default: `['mock/**/*.mock.{js,ts,cjs,mjs,json,json5}']` (relative for `process.cwd()`)
|
|
107
113
|
|
|
108
114
|
- `options.exclude`
|
|
109
115
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
116
|
+
When you configure the mock files to be read, the files you want to exclude can be a directory, a glob, or an array
|
|
117
|
+
|
|
118
|
+
Default:
|
|
119
|
+
```ts
|
|
120
|
+
[
|
|
121
|
+
'**/node_modules/**',
|
|
122
|
+
'**/test/**',
|
|
123
|
+
'**/cypress/**',
|
|
124
|
+
'src/**',
|
|
125
|
+
'**/.vscode/**',
|
|
126
|
+
'**/.git/**',
|
|
127
|
+
'**/dist/**'
|
|
128
|
+
]
|
|
129
|
+
```
|
|
113
130
|
|
|
114
131
|
|
|
115
132
|
### defineMock(config)
|
|
116
133
|
|
|
117
|
-
|
|
134
|
+
Mock Type Helper
|
|
118
135
|
|
|
119
136
|
```ts
|
|
120
137
|
import { defineMock } from 'vite-plugin-mock-dev-server'
|
|
@@ -125,16 +142,16 @@ export default defineMock({
|
|
|
125
142
|
})
|
|
126
143
|
```
|
|
127
144
|
|
|
128
|
-
## Mock
|
|
145
|
+
## Mock Configuration
|
|
129
146
|
|
|
130
147
|
```ts
|
|
131
148
|
export default defineMock({
|
|
132
149
|
/**
|
|
133
|
-
*
|
|
150
|
+
* Address of request,support `/api/user/:id`
|
|
134
151
|
*/
|
|
135
152
|
url: '/api/test',
|
|
136
153
|
/**
|
|
137
|
-
*
|
|
154
|
+
* The request method supported by the API
|
|
138
155
|
*
|
|
139
156
|
* @type string | string[]
|
|
140
157
|
* @default ['POST','GET']
|
|
@@ -142,45 +159,52 @@ export default defineMock({
|
|
|
142
159
|
*/
|
|
143
160
|
method: ['GET', 'POST'],
|
|
144
161
|
/**
|
|
145
|
-
*
|
|
162
|
+
* enable/disable the current mock request
|
|
146
163
|
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
* 对当前不需要mock的接口,可设置为 false
|
|
164
|
+
* we typically only need a few mock interfaces to work.
|
|
165
|
+
* set `false` to disable current mock
|
|
150
166
|
*
|
|
151
167
|
* @default true
|
|
152
168
|
*/
|
|
153
169
|
enable: true,
|
|
154
170
|
/**
|
|
155
|
-
*
|
|
171
|
+
* response delay, unit:ms
|
|
156
172
|
*
|
|
157
173
|
* @default 0
|
|
158
174
|
*/
|
|
159
175
|
delay: 1000,
|
|
160
176
|
/**
|
|
161
|
-
*
|
|
177
|
+
* response status
|
|
162
178
|
*
|
|
163
179
|
* @default 200
|
|
164
180
|
*/
|
|
165
181
|
status: 200,
|
|
166
182
|
/**
|
|
167
|
-
*
|
|
183
|
+
* response status text
|
|
168
184
|
*/
|
|
169
185
|
statusText: 'OK',
|
|
170
186
|
/**
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
187
|
+
* Request a validator, through which the mock data
|
|
188
|
+
* is returned, otherwise not the current mock.
|
|
189
|
+
* In some scenarios where an interface needs to
|
|
190
|
+
* return different data through different inputs,
|
|
191
|
+
* the validator can solve this kind of problem well.
|
|
192
|
+
* It divides the same url into multiple mock
|
|
193
|
+
* configurations and determines which mock configuration
|
|
194
|
+
* is valid according to the validator.
|
|
175
195
|
*
|
|
176
196
|
* @type { header?: object; body?: object; query?: object; params?: object }
|
|
177
197
|
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
198
|
+
* If the validator incoming is an object,
|
|
199
|
+
* then the validation method is the comparison of the
|
|
200
|
+
* strict request of interface, headers/body/query/params
|
|
201
|
+
* each `key-value` congruent, congruent check through
|
|
181
202
|
*
|
|
182
203
|
* @type ({ header: object; body: object; query: object; params: object }) => boolean
|
|
183
|
-
*
|
|
204
|
+
* If the validator is passed a function,
|
|
205
|
+
* it takes the requested interface-related data as an input,
|
|
206
|
+
* gives it to the consumer for custom validation,
|
|
207
|
+
* and returns a boolean
|
|
184
208
|
*
|
|
185
209
|
*/
|
|
186
210
|
validator: {
|
|
@@ -191,41 +215,31 @@ export default defineMock({
|
|
|
191
215
|
},
|
|
192
216
|
/**
|
|
193
217
|
*
|
|
194
|
-
*
|
|
218
|
+
* response headers
|
|
195
219
|
*
|
|
196
220
|
* @type Record<string, any>
|
|
197
221
|
*
|
|
198
222
|
* @type (({ query, body, params, headers }) => Record<string, any>)
|
|
199
|
-
* 入参部分为 请求相关信息
|
|
200
223
|
*/
|
|
201
224
|
headers: {
|
|
202
225
|
'Content-Type': 'application/json'
|
|
203
226
|
},
|
|
204
227
|
|
|
205
228
|
/**
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
*
|
|
209
|
-
* 同时,你也可以使用如 `mockjs` 等库来生成数据内容
|
|
229
|
+
* Response Body
|
|
230
|
+
* Support `string/number/array/object`
|
|
231
|
+
* You can also use libraries such as' mockjs' to generate data content
|
|
210
232
|
*
|
|
211
233
|
* @type string | number | array | object
|
|
212
|
-
* 直接返回定义的数据
|
|
213
234
|
*
|
|
214
235
|
* @type (request: { header, query, body, params }) => any | Promise<any>
|
|
215
|
-
* 如果传入一个函数,那么可以更加灵活的定义返回响应体数据
|
|
216
236
|
*/
|
|
217
237
|
body: {},
|
|
218
238
|
|
|
219
239
|
/**
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
* 在 req参数中,已内置了 query、body、params 的解析,
|
|
225
|
-
* 你可以直接使用它们
|
|
226
|
-
*
|
|
227
|
-
* 别忘了,需要通过 `res.end()` 返回响应体数据,
|
|
228
|
-
* 或者需要跳过mock,那么别忘了调用 `next()`
|
|
240
|
+
* If the mock requirement cannot be addressed with the body configuration,
|
|
241
|
+
* Then you can expose the http server interface by configuring response,
|
|
242
|
+
* Achieve fully controlled custom configuration.
|
|
229
243
|
*/
|
|
230
244
|
response(req, res, next) {
|
|
231
245
|
res.end()
|
|
@@ -234,24 +248,26 @@ export default defineMock({
|
|
|
234
248
|
|
|
235
249
|
```
|
|
236
250
|
|
|
237
|
-
>
|
|
251
|
+
> Tips:
|
|
238
252
|
>
|
|
239
|
-
>
|
|
253
|
+
> If you write mock files using json/json5,
|
|
254
|
+
> the 'response' method is not supported,
|
|
255
|
+
> as is the function form that uses other fields.
|
|
240
256
|
|
|
241
257
|
`mock/**/*.mock.{ts,js,mjs,cjs,json,json5}`
|
|
242
258
|
|
|
243
|
-
|
|
259
|
+
See more examples: [example](/example/)
|
|
244
260
|
|
|
245
|
-
####
|
|
246
|
-
|
|
261
|
+
#### Example 1:
|
|
262
|
+
Match `/api/test`,And returns a response body content with empty data
|
|
247
263
|
```ts
|
|
248
264
|
export default defineMock({
|
|
249
265
|
url: '/api/test',
|
|
250
266
|
})
|
|
251
267
|
```
|
|
252
268
|
|
|
253
|
-
####
|
|
254
|
-
|
|
269
|
+
#### Example 2:
|
|
270
|
+
Match `/api/test` ,And returns a fixed content data
|
|
255
271
|
```ts
|
|
256
272
|
export default defineMock({
|
|
257
273
|
url: '/api/test',
|
|
@@ -261,8 +277,8 @@ export default defineMock({
|
|
|
261
277
|
})
|
|
262
278
|
```
|
|
263
279
|
|
|
264
|
-
####
|
|
265
|
-
|
|
280
|
+
#### Example 3:
|
|
281
|
+
Only Support `GET` Method
|
|
266
282
|
```ts
|
|
267
283
|
export default defineMock({
|
|
268
284
|
url: '/api/test',
|
|
@@ -270,8 +286,8 @@ export default defineMock({
|
|
|
270
286
|
})
|
|
271
287
|
```
|
|
272
288
|
|
|
273
|
-
####
|
|
274
|
-
|
|
289
|
+
#### Example 4:
|
|
290
|
+
In the response header, add a custom header
|
|
275
291
|
```ts
|
|
276
292
|
export default defineMock({
|
|
277
293
|
url: '/api/test',
|
|
@@ -291,11 +307,11 @@ export default defineMock({
|
|
|
291
307
|
})
|
|
292
308
|
```
|
|
293
309
|
|
|
294
|
-
####
|
|
295
|
-
|
|
310
|
+
#### Example 5:
|
|
311
|
+
Define multiple mock requests for the same url and match valid rules with validators
|
|
296
312
|
```ts
|
|
297
313
|
export default defineMock([
|
|
298
|
-
//
|
|
314
|
+
// Match /api/test?a=1
|
|
299
315
|
{
|
|
300
316
|
url: '/api/test',
|
|
301
317
|
validator: {
|
|
@@ -307,7 +323,7 @@ export default defineMock([
|
|
|
307
323
|
message: 'query.a === 1'
|
|
308
324
|
}
|
|
309
325
|
},
|
|
310
|
-
//
|
|
326
|
+
// Match /api/test?a=2
|
|
311
327
|
{
|
|
312
328
|
url: '/api/test',
|
|
313
329
|
validator: {
|
|
@@ -322,8 +338,8 @@ export default defineMock([
|
|
|
322
338
|
])
|
|
323
339
|
```
|
|
324
340
|
|
|
325
|
-
####
|
|
326
|
-
|
|
341
|
+
#### Example 6:
|
|
342
|
+
Response Delay
|
|
327
343
|
```ts
|
|
328
344
|
export default defineMock({
|
|
329
345
|
url: '/api/test',
|
|
@@ -331,8 +347,8 @@ export default defineMock({
|
|
|
331
347
|
})
|
|
332
348
|
```
|
|
333
349
|
|
|
334
|
-
####
|
|
335
|
-
|
|
350
|
+
#### Example 7:
|
|
351
|
+
The interface request failed
|
|
336
352
|
```ts
|
|
337
353
|
export default defineMock({
|
|
338
354
|
url: '/api/test',
|
|
@@ -341,8 +357,8 @@ export default defineMock({
|
|
|
341
357
|
})
|
|
342
358
|
```
|
|
343
359
|
|
|
344
|
-
####
|
|
345
|
-
|
|
360
|
+
#### Example 8:
|
|
361
|
+
Use `mockjs`:
|
|
346
362
|
```ts
|
|
347
363
|
import Mock from 'mockjs'
|
|
348
364
|
export default defineMock({
|
|
@@ -354,10 +370,10 @@ export default defineMock({
|
|
|
354
370
|
})
|
|
355
371
|
})
|
|
356
372
|
```
|
|
357
|
-
|
|
373
|
+
You need installed `mockjs`
|
|
358
374
|
|
|
359
|
-
###
|
|
360
|
-
|
|
375
|
+
### Example 9:
|
|
376
|
+
Use `response` to customize the response
|
|
361
377
|
```ts
|
|
362
378
|
export default defineMock({
|
|
363
379
|
url: '/api/test',
|
|
@@ -376,11 +392,11 @@ export default defineMock({
|
|
|
376
392
|
})
|
|
377
393
|
```
|
|
378
394
|
|
|
379
|
-
###
|
|
380
|
-
|
|
395
|
+
### Example 10:
|
|
396
|
+
Use json / json5
|
|
381
397
|
```json
|
|
382
398
|
{
|
|
383
|
-
//
|
|
399
|
+
// Support comment
|
|
384
400
|
"url": "/api/test",
|
|
385
401
|
"body": {
|
|
386
402
|
"a": 1
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
# vite-plugin-mock-dev-server
|
|
2
|
+
|
|
3
|
+
<p align="center">vite mock开发服务(mock-dev-server)插件。</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">在 vite 开发环境中,注入一个 mock-dev-server。</p>
|
|
6
|
+
|
|
7
|
+
<br>
|
|
8
|
+
<br>
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="/README.md">English</a> | <span>简体中文</span>
|
|
11
|
+
</p>
|
|
12
|
+
<br>
|
|
13
|
+
<br>
|
|
14
|
+
|
|
15
|
+
## 特性
|
|
16
|
+
|
|
17
|
+
- ⚡️ 轻量,灵活,快速
|
|
18
|
+
- 🧲 非注入式,对客户端代码无侵入
|
|
19
|
+
- 💡 ESModule/commonjs
|
|
20
|
+
- 🦾 Typescript
|
|
21
|
+
- 🏷 支持 json / json5 编写 mock 数据
|
|
22
|
+
- 📦 自动加载 mock 文件
|
|
23
|
+
- 🎨 可选择你喜欢的任意用于生成mock数据库,如 `mockjs`,或者不是用其他库
|
|
24
|
+
- 📥 路径规则匹配,请求参数匹配
|
|
25
|
+
- ⚙️ 随意开启或关闭对某个接口的 mock配置
|
|
26
|
+
- 🔥 热更新
|
|
27
|
+
- ⚖️ 使用 `server.proxy` 配置
|
|
28
|
+
- 🍕 支持在 mock文件中使用 `viteConfig.define`配置字段
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
## 使用
|
|
32
|
+
|
|
33
|
+
### 安装
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
# npm
|
|
37
|
+
npm i -D vite-plugin-mock-dev-server
|
|
38
|
+
# yarn
|
|
39
|
+
yarn add vite-plugin-mock-dev-server
|
|
40
|
+
# pnpm
|
|
41
|
+
pnpm add -D vite-plugin-mock-dev-server
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 配置
|
|
45
|
+
|
|
46
|
+
`vite.config.ts`
|
|
47
|
+
```ts
|
|
48
|
+
import { defineConfig } from 'vite'
|
|
49
|
+
import mockDevServerPlugin from 'vite-plugin-mock-dev-server'
|
|
50
|
+
|
|
51
|
+
export default defineConfig({
|
|
52
|
+
plugins: [
|
|
53
|
+
mockDevServerPlugin(),
|
|
54
|
+
],
|
|
55
|
+
// 这里定义的字段,在mock中也能使用
|
|
56
|
+
define: {},
|
|
57
|
+
server: {
|
|
58
|
+
proxy: {
|
|
59
|
+
'^/api': {
|
|
60
|
+
target: 'http://example.com'
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
```
|
|
66
|
+
插件会读取 `server.proxy` 的配置, 仅对设置了代理的 url 匹配,启用mock 匹配。
|
|
67
|
+
|
|
68
|
+
插件也会读取 `define` 配置, 支持在 mock 文件中直接使用。
|
|
69
|
+
|
|
70
|
+
> 因为一般场景下,我们只需要对有代理的url进行mock,这样才能通过 vite 提供的 http 服务进行 代理和 mock
|
|
71
|
+
|
|
72
|
+
### 编写mock文件
|
|
73
|
+
|
|
74
|
+
默认配置,在你的项目根目录的 `mock` 目录中编写mock数据:
|
|
75
|
+
|
|
76
|
+
`mock/api.mock.ts` :
|
|
77
|
+
```ts
|
|
78
|
+
import { defineMock } from 'vite-plugin-mock-dev-server'
|
|
79
|
+
|
|
80
|
+
export default defineMock({
|
|
81
|
+
url: '/api/test',
|
|
82
|
+
body: {
|
|
83
|
+
a: 1,
|
|
84
|
+
b: 2,
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## 方法
|
|
90
|
+
|
|
91
|
+
### mockDevServerPlugin(options)
|
|
92
|
+
|
|
93
|
+
vite plugin
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
`vite.config.ts`
|
|
97
|
+
```ts
|
|
98
|
+
import { defineConfig } from 'vite'
|
|
99
|
+
import mockDevServerPlugin from 'vite-plugin-mock-dev-server'
|
|
100
|
+
|
|
101
|
+
export default defineConfig({
|
|
102
|
+
plugins: [
|
|
103
|
+
mockDevServerPlugin(),
|
|
104
|
+
]
|
|
105
|
+
})
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
#### options
|
|
109
|
+
|
|
110
|
+
- `option.include`
|
|
111
|
+
|
|
112
|
+
配置读取 mock文件,可以是一个 目录,glob,或者一个数组
|
|
113
|
+
|
|
114
|
+
默认值: `['mock/**/*.mock.{js,ts,cjs,mjs,json,json5}']` (相对于根目录)
|
|
115
|
+
|
|
116
|
+
- `options.exclude`
|
|
117
|
+
|
|
118
|
+
配置读取 mock文件时,需要排除的文件, 可以是一个 目录、glob、或者一个数组
|
|
119
|
+
|
|
120
|
+
默认值:`['**/node_modules/**','**/test/**','**/cypress/**','src/**','**/.vscode/**','**/.git/**','**/dist/**',]`
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
### defineMock(config)
|
|
124
|
+
|
|
125
|
+
mock 配置帮助函数,提供类型检查帮助
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
import { defineMock } from 'vite-plugin-mock-dev-server'
|
|
129
|
+
|
|
130
|
+
export default defineMock({
|
|
131
|
+
url: '/api/test',
|
|
132
|
+
body: {}
|
|
133
|
+
})
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Mock 配置
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
export default defineMock({
|
|
140
|
+
/**
|
|
141
|
+
* 请求地址,支持 `/api/user/:id` 格式
|
|
142
|
+
*/
|
|
143
|
+
url: '/api/test',
|
|
144
|
+
/**
|
|
145
|
+
* 接口支持的请求方法
|
|
146
|
+
*
|
|
147
|
+
* @type string | string[]
|
|
148
|
+
* @default ['POST','GET']
|
|
149
|
+
*
|
|
150
|
+
*/
|
|
151
|
+
method: ['GET', 'POST'],
|
|
152
|
+
/**
|
|
153
|
+
* 是否启用当前 mock请求
|
|
154
|
+
*
|
|
155
|
+
* 在实际场景中,我们一般只需要某几个mock接口生效,
|
|
156
|
+
* 而不是所以mock接口都启用。
|
|
157
|
+
* 对当前不需要mock的接口,可设置为 false
|
|
158
|
+
*
|
|
159
|
+
* @default true
|
|
160
|
+
*/
|
|
161
|
+
enable: true,
|
|
162
|
+
/**
|
|
163
|
+
* 设置接口响应延迟, 单位:ms
|
|
164
|
+
*
|
|
165
|
+
* @default 0
|
|
166
|
+
*/
|
|
167
|
+
delay: 1000,
|
|
168
|
+
/**
|
|
169
|
+
* 响应状态码
|
|
170
|
+
*
|
|
171
|
+
* @default 200
|
|
172
|
+
*/
|
|
173
|
+
status: 200,
|
|
174
|
+
/**
|
|
175
|
+
* 响应状态文本
|
|
176
|
+
*/
|
|
177
|
+
statusText: 'OK',
|
|
178
|
+
/**
|
|
179
|
+
* 请求验证器,通过验证器则返回 mock数据,否则不是用当前mock。
|
|
180
|
+
* 这对于一些场景中,某个接口需要通过不同的入参来返回不同的数据,
|
|
181
|
+
* 验证器可以很好的解决这一类问题,将同个 url 分为多个 mock配置,
|
|
182
|
+
* 根据 验证器来判断哪个mock配置生效。
|
|
183
|
+
*
|
|
184
|
+
* @type { header?: object; body?: object; query?: object; params?: object }
|
|
185
|
+
*
|
|
186
|
+
* 如果 validator 传入的是一个对象,那么验证方式是严格比较 请求的接口
|
|
187
|
+
* 中,headers/body/query/params 的各个`key`的`value`是否全等,
|
|
188
|
+
* 全等则校验通过
|
|
189
|
+
*
|
|
190
|
+
* @type ({ header: object; body: object; query: object; params: object }) => boolean
|
|
191
|
+
* 如果 validator 传入的是一个函数,那么会讲 请求的接口相关数据作为入参,提供给使用者进行自定义校验,并返回一个 boolean
|
|
192
|
+
*
|
|
193
|
+
*/
|
|
194
|
+
validator: {
|
|
195
|
+
headers: {},
|
|
196
|
+
body: {},
|
|
197
|
+
query: {},
|
|
198
|
+
params: {},
|
|
199
|
+
},
|
|
200
|
+
/**
|
|
201
|
+
*
|
|
202
|
+
* 响应状态 headers
|
|
203
|
+
*
|
|
204
|
+
* @type Record<string, any>
|
|
205
|
+
*
|
|
206
|
+
* @type (({ query, body, params, headers }) => Record<string, any>)
|
|
207
|
+
* 入参部分为 请求相关信息
|
|
208
|
+
*/
|
|
209
|
+
headers: {
|
|
210
|
+
'Content-Type': 'application/json'
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* 响应体数据
|
|
215
|
+
* 定义返回的响应体数据内容。
|
|
216
|
+
* 在这里,你可以直接返回JavaScript支持的数据类型如 `string/number/array/object` 等
|
|
217
|
+
* 同时,你也可以使用如 `mockjs` 等库来生成数据内容
|
|
218
|
+
*
|
|
219
|
+
* @type string | number | array | object
|
|
220
|
+
* 直接返回定义的数据
|
|
221
|
+
*
|
|
222
|
+
* @type (request: { header, query, body, params }) => any | Promise<any>
|
|
223
|
+
* 如果传入一个函数,那么可以更加灵活的定义返回响应体数据
|
|
224
|
+
*/
|
|
225
|
+
body: {},
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* 如果通过 body 配置不能解决mock需求,
|
|
229
|
+
* 那么可以通过 配置 response,暴露http server 的接口,
|
|
230
|
+
* 实现完全可控的自定义配置
|
|
231
|
+
*
|
|
232
|
+
* 在 req参数中,已内置了 query、body、params 的解析,
|
|
233
|
+
* 你可以直接使用它们
|
|
234
|
+
*
|
|
235
|
+
* 别忘了,需要通过 `res.end()` 返回响应体数据,
|
|
236
|
+
* 或者需要跳过mock,那么别忘了调用 `next()`
|
|
237
|
+
*/
|
|
238
|
+
response(req, res, next) {
|
|
239
|
+
res.end()
|
|
240
|
+
}
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
> 注意:
|
|
246
|
+
>
|
|
247
|
+
> 如果使用 json/json5 编写 mock文件,则不支持使用 `response` 方法,以及不支持使用其他字段的函数形式。
|
|
248
|
+
|
|
249
|
+
`mock/**/*.mock.{ts,js,mjs,cjs,json,json5}`
|
|
250
|
+
|
|
251
|
+
查看更多示例: [example](/example/)
|
|
252
|
+
|
|
253
|
+
#### 示例1:
|
|
254
|
+
命中 `/api/test` 请求,并返回一个 数据为空的响应体内容
|
|
255
|
+
```ts
|
|
256
|
+
export default defineMock({
|
|
257
|
+
url: '/api/test',
|
|
258
|
+
})
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
#### 示例2:
|
|
262
|
+
命中 `/api/test` 请求,并返回一个固定内容数据
|
|
263
|
+
```ts
|
|
264
|
+
export default defineMock({
|
|
265
|
+
url: '/api/test',
|
|
266
|
+
body: {
|
|
267
|
+
a: 1
|
|
268
|
+
}
|
|
269
|
+
})
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
#### 示例3:
|
|
273
|
+
限定只允许 `GET` 请求
|
|
274
|
+
```ts
|
|
275
|
+
export default defineMock({
|
|
276
|
+
url: '/api/test',
|
|
277
|
+
method: 'GET'
|
|
278
|
+
})
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
#### 示例4:
|
|
282
|
+
在返回的响应头中,添加自定义header
|
|
283
|
+
```ts
|
|
284
|
+
export default defineMock({
|
|
285
|
+
url: '/api/test',
|
|
286
|
+
headers: {
|
|
287
|
+
'X-Custom': '12345678'
|
|
288
|
+
}
|
|
289
|
+
})
|
|
290
|
+
```
|
|
291
|
+
```ts
|
|
292
|
+
export default defineMock({
|
|
293
|
+
url: '/api/test',
|
|
294
|
+
headers({ query, body, params, headers }) {
|
|
295
|
+
return {
|
|
296
|
+
'X-Custom': query.custom
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
})
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
#### 示例5:
|
|
303
|
+
定义多个相同url请求mock,并使用验证器匹配生效规则
|
|
304
|
+
```ts
|
|
305
|
+
export default defineMock([
|
|
306
|
+
// 命中 /api/test?a=1
|
|
307
|
+
{
|
|
308
|
+
url: '/api/test',
|
|
309
|
+
validator: {
|
|
310
|
+
query: {
|
|
311
|
+
a: 1
|
|
312
|
+
}
|
|
313
|
+
},
|
|
314
|
+
body: {
|
|
315
|
+
message: 'query.a === 1'
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
// 命中 /api/test?a=2
|
|
319
|
+
{
|
|
320
|
+
url: '/api/test',
|
|
321
|
+
validator: {
|
|
322
|
+
query: {
|
|
323
|
+
a: 2
|
|
324
|
+
}
|
|
325
|
+
},
|
|
326
|
+
body: {
|
|
327
|
+
message: 'query.a === 2'
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
])
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
#### 示例6:
|
|
334
|
+
延迟接口响应:
|
|
335
|
+
```ts
|
|
336
|
+
export default defineMock({
|
|
337
|
+
url: '/api/test',
|
|
338
|
+
delay: 6000, // 延迟 6秒
|
|
339
|
+
})
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
#### 示例7:
|
|
343
|
+
使接口请求失败
|
|
344
|
+
```ts
|
|
345
|
+
export default defineMock({
|
|
346
|
+
url: '/api/test',
|
|
347
|
+
status: 504,
|
|
348
|
+
statusText: 'Bad Gateway'
|
|
349
|
+
})
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
#### 示例8:
|
|
353
|
+
使用 `mockjs` 生成响应数据:
|
|
354
|
+
```ts
|
|
355
|
+
import Mock from 'mockjs'
|
|
356
|
+
export default defineMock({
|
|
357
|
+
url: '/api/test',
|
|
358
|
+
body: Mock.mock({
|
|
359
|
+
'list|1-10': [{
|
|
360
|
+
'id|+1': 1
|
|
361
|
+
}]
|
|
362
|
+
})
|
|
363
|
+
})
|
|
364
|
+
```
|
|
365
|
+
请先安装 `mockjs`
|
|
366
|
+
|
|
367
|
+
### 示例9:
|
|
368
|
+
使用 `response` 自定义响应
|
|
369
|
+
```ts
|
|
370
|
+
export default defineMock({
|
|
371
|
+
url: '/api/test',
|
|
372
|
+
response(req, res, next) {
|
|
373
|
+
const { query, body, params, headers } = req
|
|
374
|
+
console.log(query, body, params, headers)
|
|
375
|
+
|
|
376
|
+
res.status = 200
|
|
377
|
+
res.setHeader('Content-Type', 'application/json')
|
|
378
|
+
res.end(JSON.stringify({
|
|
379
|
+
query,
|
|
380
|
+
body,
|
|
381
|
+
params,
|
|
382
|
+
}))
|
|
383
|
+
}
|
|
384
|
+
})
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### 示例10:
|
|
388
|
+
使用 json / json5
|
|
389
|
+
```json
|
|
390
|
+
{
|
|
391
|
+
// 支持 comment
|
|
392
|
+
"url": "/api/test",
|
|
393
|
+
"body": {
|
|
394
|
+
"a": 1
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
```
|
package/dist/index.cjs
CHANGED
|
@@ -33,13 +33,15 @@ __export(src_exports, {
|
|
|
33
33
|
module.exports = __toCommonJS(src_exports);
|
|
34
34
|
|
|
35
35
|
// src/mockMiddleware.ts
|
|
36
|
-
var
|
|
36
|
+
var import_node_url2 = require("url");
|
|
37
37
|
var import_path_to_regexp = require("path-to-regexp");
|
|
38
38
|
|
|
39
39
|
// src/MockLoader.ts
|
|
40
40
|
var import_node_events = __toESM(require("events"), 1);
|
|
41
|
-
var
|
|
41
|
+
var import_node_fs2 = __toESM(require("fs"), 1);
|
|
42
|
+
var import_node_module = require("module");
|
|
42
43
|
var import_node_path2 = __toESM(require("path"), 1);
|
|
44
|
+
var import_node_url = require("url");
|
|
43
45
|
var import_pluginutils = require("@rollup/pluginutils");
|
|
44
46
|
var import_chokidar = __toESM(require("chokidar"), 1);
|
|
45
47
|
var import_esbuild = require("esbuild");
|
|
@@ -47,7 +49,7 @@ var import_fast_glob = __toESM(require("fast-glob"), 1);
|
|
|
47
49
|
var import_json5 = __toESM(require("json5"), 1);
|
|
48
50
|
|
|
49
51
|
// src/utils.ts
|
|
50
|
-
var
|
|
52
|
+
var import_node_fs = __toESM(require("fs"), 1);
|
|
51
53
|
var import_node_path = __toESM(require("path"), 1);
|
|
52
54
|
var import_debug = __toESM(require("debug"), 1);
|
|
53
55
|
var isArray = (val) => Array.isArray(val);
|
|
@@ -56,25 +58,39 @@ function sleep(timeout) {
|
|
|
56
58
|
return new Promise((resolve) => setTimeout(resolve, timeout));
|
|
57
59
|
}
|
|
58
60
|
var debug = (0, import_debug.default)("vite:plugin-mock-dev-server");
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
61
|
+
function lookupFile(dir, formats, options) {
|
|
62
|
+
for (const format of formats) {
|
|
63
|
+
const fullPath = import_node_path.default.join(dir, format);
|
|
64
|
+
if (import_node_fs.default.existsSync(fullPath) && import_node_fs.default.statSync(fullPath).isFile()) {
|
|
65
|
+
const result = (options == null ? void 0 : options.pathOnly) ? fullPath : import_node_fs.default.readFileSync(fullPath, "utf-8");
|
|
66
|
+
if (!(options == null ? void 0 : options.predicate) || options.predicate(result)) {
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const parentDir = import_node_path.default.dirname(dir);
|
|
72
|
+
if (parentDir !== dir && (!(options == null ? void 0 : options.rootDir) || parentDir.startsWith(options == null ? void 0 : options.rootDir))) {
|
|
73
|
+
return lookupFile(parentDir, formats, options);
|
|
74
|
+
}
|
|
66
75
|
}
|
|
67
76
|
|
|
68
77
|
// src/MockLoader.ts
|
|
78
|
+
var import_meta = {};
|
|
79
|
+
var _require = (0, import_node_module.createRequire)(import_meta.url);
|
|
69
80
|
var _MockLoader = class extends import_node_events.default {
|
|
70
81
|
constructor(options) {
|
|
71
82
|
super();
|
|
72
83
|
this.moduleCache = /* @__PURE__ */ new Map();
|
|
73
84
|
this.moduleDeps = /* @__PURE__ */ new Map();
|
|
74
85
|
this._mockList = [];
|
|
86
|
+
this.moduleType = "cjs";
|
|
75
87
|
this.options = options;
|
|
76
88
|
this.cwd = options.cwd || process.cwd();
|
|
77
|
-
|
|
89
|
+
try {
|
|
90
|
+
const pkg = lookupFile(this.cwd, ["package.json"]);
|
|
91
|
+
this.moduleType = !!pkg && JSON.parse(pkg).type === "module" ? "esm" : "cjs";
|
|
92
|
+
} catch (e) {
|
|
93
|
+
}
|
|
78
94
|
}
|
|
79
95
|
get mockList() {
|
|
80
96
|
return this._mockList;
|
|
@@ -90,13 +106,13 @@ var _MockLoader = class extends import_node_events.default {
|
|
|
90
106
|
this.watchMockEntry();
|
|
91
107
|
this.watchDeps();
|
|
92
108
|
for (const filepath of includePaths.filter(includeFilter)) {
|
|
93
|
-
await this.
|
|
109
|
+
await this.loadMock(filepath);
|
|
94
110
|
}
|
|
95
111
|
this.updateMockList();
|
|
96
112
|
this.on("mock:update", async (filepath) => {
|
|
97
113
|
if (!includeFilter(filepath))
|
|
98
114
|
return;
|
|
99
|
-
await this.
|
|
115
|
+
await this.loadMock(filepath);
|
|
100
116
|
this.updateMockList();
|
|
101
117
|
});
|
|
102
118
|
this.on("mock:unlink", async (filepath) => {
|
|
@@ -181,58 +197,105 @@ var _MockLoader = class extends import_node_events.default {
|
|
|
181
197
|
});
|
|
182
198
|
this.emit("update:deps");
|
|
183
199
|
}
|
|
184
|
-
async
|
|
200
|
+
async loadMock(filepath) {
|
|
185
201
|
if (!filepath)
|
|
186
202
|
return;
|
|
187
203
|
if (_MockLoader.EXT_JSON.test(filepath)) {
|
|
188
204
|
await this.loadJson(filepath);
|
|
189
205
|
} else {
|
|
190
|
-
await this.
|
|
206
|
+
await this.loadModule(filepath);
|
|
191
207
|
}
|
|
192
208
|
}
|
|
193
209
|
async loadJson(filepath) {
|
|
194
|
-
const content = await
|
|
210
|
+
const content = await import_node_fs2.default.promises.readFile(filepath, "utf-8");
|
|
195
211
|
try {
|
|
196
212
|
const mockConfig = import_json5.default.parse(content);
|
|
197
213
|
this.moduleCache.set(filepath, mockConfig);
|
|
198
214
|
} catch (e) {
|
|
199
215
|
}
|
|
200
216
|
}
|
|
201
|
-
async
|
|
217
|
+
async loadModule(filepath) {
|
|
202
218
|
if (!filepath)
|
|
203
219
|
return;
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
await
|
|
220
|
+
let isESM = false;
|
|
221
|
+
if (/\.m[jt]s$/.test(filepath)) {
|
|
222
|
+
isESM = true;
|
|
223
|
+
} else if (/\.c[jt]s$/.test(filepath)) {
|
|
224
|
+
isESM = false;
|
|
225
|
+
} else {
|
|
226
|
+
isESM = this.moduleType === "esm";
|
|
227
|
+
}
|
|
228
|
+
const { code, deps } = await this.transformWithEsbuild(filepath, isESM);
|
|
213
229
|
try {
|
|
214
|
-
const
|
|
215
|
-
const mockConfig =
|
|
230
|
+
const raw = await this.loadFromCode(filepath, code, isESM);
|
|
231
|
+
const mockConfig = raw && raw.default ? raw.default : Object.keys(raw || {}).map((key) => raw[key]);
|
|
216
232
|
this.moduleCache.set(filepath, mockConfig);
|
|
217
233
|
this.updateModuleDeps(filepath, deps);
|
|
218
234
|
} catch (e) {
|
|
219
235
|
console.error(e);
|
|
220
236
|
}
|
|
221
237
|
}
|
|
222
|
-
async
|
|
238
|
+
async loadFromCode(filepath, code, isESM) {
|
|
239
|
+
if (isESM) {
|
|
240
|
+
const fileBase = `${filepath}.timestamp-${Date.now()}`;
|
|
241
|
+
const fileNameTmp = `${fileBase}.mjs`;
|
|
242
|
+
const fileUrl = `${(0, import_node_url.pathToFileURL)(fileBase)}.mjs`;
|
|
243
|
+
await import_node_fs2.default.promises.writeFile(fileNameTmp, code, "utf8");
|
|
244
|
+
try {
|
|
245
|
+
return await import(fileUrl);
|
|
246
|
+
} finally {
|
|
247
|
+
try {
|
|
248
|
+
import_node_fs2.default.unlinkSync(fileNameTmp);
|
|
249
|
+
} catch {
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
} else {
|
|
253
|
+
filepath = import_node_path2.default.resolve(this.cwd, filepath);
|
|
254
|
+
const extension = import_node_path2.default.extname(filepath);
|
|
255
|
+
const realFileName = import_node_fs2.default.realpathSync(filepath);
|
|
256
|
+
const loaderExt = extension in _require.extensions ? extension : ".js";
|
|
257
|
+
const defaultLoader = _require.extensions[loaderExt];
|
|
258
|
+
_require.extensions[loaderExt] = (module2, filename) => {
|
|
259
|
+
if (filename === realFileName) {
|
|
260
|
+
;
|
|
261
|
+
module2._compile(code, filename);
|
|
262
|
+
} else {
|
|
263
|
+
defaultLoader(module2, filename);
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
delete _require.cache[_require.resolve(filepath)];
|
|
267
|
+
const raw = _require(filepath);
|
|
268
|
+
_require.extensions[loaderExt] = defaultLoader;
|
|
269
|
+
return raw.__esModule ? raw : { default: raw };
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
async transformWithEsbuild(filepath, isESM) {
|
|
223
273
|
var _a;
|
|
224
274
|
try {
|
|
225
275
|
const result = await (0, import_esbuild.build)({
|
|
226
276
|
entryPoints: [filepath],
|
|
227
277
|
outfile: "out.js",
|
|
228
278
|
write: false,
|
|
229
|
-
target: "
|
|
279
|
+
target: ["node14.18", "node16"],
|
|
230
280
|
platform: "node",
|
|
231
281
|
bundle: true,
|
|
232
|
-
external: this.options.external,
|
|
233
282
|
metafile: true,
|
|
234
|
-
format: "esm",
|
|
235
|
-
define: this.options.define
|
|
283
|
+
format: isESM ? "esm" : "cjs",
|
|
284
|
+
define: this.options.define,
|
|
285
|
+
plugins: [
|
|
286
|
+
{
|
|
287
|
+
name: "externalize-deps",
|
|
288
|
+
setup(build2) {
|
|
289
|
+
build2.onResolve({ filter: /.*/ }, ({ path: id }) => {
|
|
290
|
+
if (id[0] !== "." && !import_node_path2.default.isAbsolute(id)) {
|
|
291
|
+
return {
|
|
292
|
+
external: true
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
]
|
|
236
299
|
});
|
|
237
300
|
return {
|
|
238
301
|
code: result.outputFiles[0].text,
|
|
@@ -286,11 +349,9 @@ function equalObj(left, right) {
|
|
|
286
349
|
async function mockServerMiddleware(httpServer, config, options) {
|
|
287
350
|
const include = isArray(options.include) ? options.include : [options.include];
|
|
288
351
|
const exclude = isArray(options.exclude) ? options.exclude : [options.exclude];
|
|
289
|
-
const external = await getPackageDeps(process.cwd());
|
|
290
352
|
const loader = new MockLoader({
|
|
291
353
|
include,
|
|
292
354
|
exclude,
|
|
293
|
-
external,
|
|
294
355
|
define: config.define || {}
|
|
295
356
|
});
|
|
296
357
|
await loader.load();
|
|
@@ -301,7 +362,7 @@ async function mockServerMiddleware(httpServer, config, options) {
|
|
|
301
362
|
return next();
|
|
302
363
|
}
|
|
303
364
|
const method = req.method.toUpperCase();
|
|
304
|
-
const { query, pathname } = (0,
|
|
365
|
+
const { query, pathname } = (0, import_node_url2.parse)(req.url, true);
|
|
305
366
|
const reqBody = await parseReqBody(req);
|
|
306
367
|
const currentMock = loader.mockList.find((mock) => {
|
|
307
368
|
if (!pathname || !mock || !mock.url)
|
|
@@ -415,7 +476,9 @@ function mockDevServerPlugin({
|
|
|
415
476
|
}
|
|
416
477
|
|
|
417
478
|
// src/define.ts
|
|
418
|
-
|
|
479
|
+
function defineMock(config) {
|
|
480
|
+
return config;
|
|
481
|
+
}
|
|
419
482
|
|
|
420
483
|
// src/index.ts
|
|
421
484
|
var src_default = mockDevServerPlugin;
|
package/dist/index.d.ts
CHANGED
|
@@ -112,16 +112,21 @@ declare type MockOptions = MockOptionsItem[];
|
|
|
112
112
|
declare function mockDevServerPlugin({ include, exclude, }?: MockServerPluginOptions): Plugin;
|
|
113
113
|
|
|
114
114
|
/**
|
|
115
|
+
* mock config helper
|
|
116
|
+
*
|
|
115
117
|
* mock配置帮助函数
|
|
118
|
+
*
|
|
116
119
|
* @param config
|
|
117
120
|
* @example
|
|
118
121
|
* ```ts
|
|
119
122
|
* export default defineMock({
|
|
120
123
|
* url: '/api/example',
|
|
124
|
+
* method: ['GET', 'POST'],
|
|
121
125
|
* body: { a: 1 },
|
|
122
126
|
* })
|
|
123
127
|
* ```
|
|
124
128
|
*/
|
|
125
|
-
declare
|
|
129
|
+
declare function defineMock(config: MockOptionsItem): MockOptionsItem;
|
|
130
|
+
declare function defineMock(config: MockOptions): MockOptions;
|
|
126
131
|
|
|
127
132
|
export { MockOptions, MockOptionsItem, MockServerPluginOptions, mockDevServerPlugin as default, defineMock, mockDevServerPlugin };
|
package/dist/index.js
CHANGED
|
@@ -4,8 +4,10 @@ import { match, pathToRegexp } from "path-to-regexp";
|
|
|
4
4
|
|
|
5
5
|
// src/MockLoader.ts
|
|
6
6
|
import EventEmitter from "events";
|
|
7
|
-
import fs2 from "fs
|
|
7
|
+
import fs2 from "fs";
|
|
8
|
+
import { createRequire } from "module";
|
|
8
9
|
import path2 from "path";
|
|
10
|
+
import { pathToFileURL } from "url";
|
|
9
11
|
import { createFilter } from "@rollup/pluginutils";
|
|
10
12
|
import chokidar from "chokidar";
|
|
11
13
|
import { build } from "esbuild";
|
|
@@ -13,7 +15,7 @@ import fastGlob from "fast-glob";
|
|
|
13
15
|
import JSON5 from "json5";
|
|
14
16
|
|
|
15
17
|
// src/utils.ts
|
|
16
|
-
import fs from "fs
|
|
18
|
+
import fs from "fs";
|
|
17
19
|
import path from "path";
|
|
18
20
|
import Debug from "debug";
|
|
19
21
|
var isArray = (val) => Array.isArray(val);
|
|
@@ -22,25 +24,38 @@ function sleep(timeout) {
|
|
|
22
24
|
return new Promise((resolve) => setTimeout(resolve, timeout));
|
|
23
25
|
}
|
|
24
26
|
var debug = Debug("vite:plugin-mock-dev-server");
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
function lookupFile(dir, formats, options) {
|
|
28
|
+
for (const format of formats) {
|
|
29
|
+
const fullPath = path.join(dir, format);
|
|
30
|
+
if (fs.existsSync(fullPath) && fs.statSync(fullPath).isFile()) {
|
|
31
|
+
const result = (options == null ? void 0 : options.pathOnly) ? fullPath : fs.readFileSync(fullPath, "utf-8");
|
|
32
|
+
if (!(options == null ? void 0 : options.predicate) || options.predicate(result)) {
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const parentDir = path.dirname(dir);
|
|
38
|
+
if (parentDir !== dir && (!(options == null ? void 0 : options.rootDir) || parentDir.startsWith(options == null ? void 0 : options.rootDir))) {
|
|
39
|
+
return lookupFile(parentDir, formats, options);
|
|
40
|
+
}
|
|
32
41
|
}
|
|
33
42
|
|
|
34
43
|
// src/MockLoader.ts
|
|
44
|
+
var _require = createRequire(import.meta.url);
|
|
35
45
|
var _MockLoader = class extends EventEmitter {
|
|
36
46
|
constructor(options) {
|
|
37
47
|
super();
|
|
38
48
|
this.moduleCache = /* @__PURE__ */ new Map();
|
|
39
49
|
this.moduleDeps = /* @__PURE__ */ new Map();
|
|
40
50
|
this._mockList = [];
|
|
51
|
+
this.moduleType = "cjs";
|
|
41
52
|
this.options = options;
|
|
42
53
|
this.cwd = options.cwd || process.cwd();
|
|
43
|
-
|
|
54
|
+
try {
|
|
55
|
+
const pkg = lookupFile(this.cwd, ["package.json"]);
|
|
56
|
+
this.moduleType = !!pkg && JSON.parse(pkg).type === "module" ? "esm" : "cjs";
|
|
57
|
+
} catch (e) {
|
|
58
|
+
}
|
|
44
59
|
}
|
|
45
60
|
get mockList() {
|
|
46
61
|
return this._mockList;
|
|
@@ -56,13 +71,13 @@ var _MockLoader = class extends EventEmitter {
|
|
|
56
71
|
this.watchMockEntry();
|
|
57
72
|
this.watchDeps();
|
|
58
73
|
for (const filepath of includePaths.filter(includeFilter)) {
|
|
59
|
-
await this.
|
|
74
|
+
await this.loadMock(filepath);
|
|
60
75
|
}
|
|
61
76
|
this.updateMockList();
|
|
62
77
|
this.on("mock:update", async (filepath) => {
|
|
63
78
|
if (!includeFilter(filepath))
|
|
64
79
|
return;
|
|
65
|
-
await this.
|
|
80
|
+
await this.loadMock(filepath);
|
|
66
81
|
this.updateMockList();
|
|
67
82
|
});
|
|
68
83
|
this.on("mock:unlink", async (filepath) => {
|
|
@@ -147,58 +162,105 @@ var _MockLoader = class extends EventEmitter {
|
|
|
147
162
|
});
|
|
148
163
|
this.emit("update:deps");
|
|
149
164
|
}
|
|
150
|
-
async
|
|
165
|
+
async loadMock(filepath) {
|
|
151
166
|
if (!filepath)
|
|
152
167
|
return;
|
|
153
168
|
if (_MockLoader.EXT_JSON.test(filepath)) {
|
|
154
169
|
await this.loadJson(filepath);
|
|
155
170
|
} else {
|
|
156
|
-
await this.
|
|
171
|
+
await this.loadModule(filepath);
|
|
157
172
|
}
|
|
158
173
|
}
|
|
159
174
|
async loadJson(filepath) {
|
|
160
|
-
const content = await fs2.readFile(filepath, "utf-8");
|
|
175
|
+
const content = await fs2.promises.readFile(filepath, "utf-8");
|
|
161
176
|
try {
|
|
162
177
|
const mockConfig = JSON5.parse(content);
|
|
163
178
|
this.moduleCache.set(filepath, mockConfig);
|
|
164
179
|
} catch (e) {
|
|
165
180
|
}
|
|
166
181
|
}
|
|
167
|
-
async
|
|
182
|
+
async loadModule(filepath) {
|
|
168
183
|
if (!filepath)
|
|
169
184
|
return;
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
await
|
|
185
|
+
let isESM = false;
|
|
186
|
+
if (/\.m[jt]s$/.test(filepath)) {
|
|
187
|
+
isESM = true;
|
|
188
|
+
} else if (/\.c[jt]s$/.test(filepath)) {
|
|
189
|
+
isESM = false;
|
|
190
|
+
} else {
|
|
191
|
+
isESM = this.moduleType === "esm";
|
|
192
|
+
}
|
|
193
|
+
const { code, deps } = await this.transformWithEsbuild(filepath, isESM);
|
|
179
194
|
try {
|
|
180
|
-
const
|
|
181
|
-
const mockConfig =
|
|
195
|
+
const raw = await this.loadFromCode(filepath, code, isESM);
|
|
196
|
+
const mockConfig = raw && raw.default ? raw.default : Object.keys(raw || {}).map((key) => raw[key]);
|
|
182
197
|
this.moduleCache.set(filepath, mockConfig);
|
|
183
198
|
this.updateModuleDeps(filepath, deps);
|
|
184
199
|
} catch (e) {
|
|
185
200
|
console.error(e);
|
|
186
201
|
}
|
|
187
202
|
}
|
|
188
|
-
async
|
|
203
|
+
async loadFromCode(filepath, code, isESM) {
|
|
204
|
+
if (isESM) {
|
|
205
|
+
const fileBase = `${filepath}.timestamp-${Date.now()}`;
|
|
206
|
+
const fileNameTmp = `${fileBase}.mjs`;
|
|
207
|
+
const fileUrl = `${pathToFileURL(fileBase)}.mjs`;
|
|
208
|
+
await fs2.promises.writeFile(fileNameTmp, code, "utf8");
|
|
209
|
+
try {
|
|
210
|
+
return await import(fileUrl);
|
|
211
|
+
} finally {
|
|
212
|
+
try {
|
|
213
|
+
fs2.unlinkSync(fileNameTmp);
|
|
214
|
+
} catch {
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
} else {
|
|
218
|
+
filepath = path2.resolve(this.cwd, filepath);
|
|
219
|
+
const extension = path2.extname(filepath);
|
|
220
|
+
const realFileName = fs2.realpathSync(filepath);
|
|
221
|
+
const loaderExt = extension in _require.extensions ? extension : ".js";
|
|
222
|
+
const defaultLoader = _require.extensions[loaderExt];
|
|
223
|
+
_require.extensions[loaderExt] = (module, filename) => {
|
|
224
|
+
if (filename === realFileName) {
|
|
225
|
+
;
|
|
226
|
+
module._compile(code, filename);
|
|
227
|
+
} else {
|
|
228
|
+
defaultLoader(module, filename);
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
delete _require.cache[_require.resolve(filepath)];
|
|
232
|
+
const raw = _require(filepath);
|
|
233
|
+
_require.extensions[loaderExt] = defaultLoader;
|
|
234
|
+
return raw.__esModule ? raw : { default: raw };
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
async transformWithEsbuild(filepath, isESM) {
|
|
189
238
|
var _a;
|
|
190
239
|
try {
|
|
191
240
|
const result = await build({
|
|
192
241
|
entryPoints: [filepath],
|
|
193
242
|
outfile: "out.js",
|
|
194
243
|
write: false,
|
|
195
|
-
target: "
|
|
244
|
+
target: ["node14.18", "node16"],
|
|
196
245
|
platform: "node",
|
|
197
246
|
bundle: true,
|
|
198
|
-
external: this.options.external,
|
|
199
247
|
metafile: true,
|
|
200
|
-
format: "esm",
|
|
201
|
-
define: this.options.define
|
|
248
|
+
format: isESM ? "esm" : "cjs",
|
|
249
|
+
define: this.options.define,
|
|
250
|
+
plugins: [
|
|
251
|
+
{
|
|
252
|
+
name: "externalize-deps",
|
|
253
|
+
setup(build2) {
|
|
254
|
+
build2.onResolve({ filter: /.*/ }, ({ path: id }) => {
|
|
255
|
+
if (id[0] !== "." && !path2.isAbsolute(id)) {
|
|
256
|
+
return {
|
|
257
|
+
external: true
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
]
|
|
202
264
|
});
|
|
203
265
|
return {
|
|
204
266
|
code: result.outputFiles[0].text,
|
|
@@ -252,11 +314,9 @@ function equalObj(left, right) {
|
|
|
252
314
|
async function mockServerMiddleware(httpServer, config, options) {
|
|
253
315
|
const include = isArray(options.include) ? options.include : [options.include];
|
|
254
316
|
const exclude = isArray(options.exclude) ? options.exclude : [options.exclude];
|
|
255
|
-
const external = await getPackageDeps(process.cwd());
|
|
256
317
|
const loader = new MockLoader({
|
|
257
318
|
include,
|
|
258
319
|
exclude,
|
|
259
|
-
external,
|
|
260
320
|
define: config.define || {}
|
|
261
321
|
});
|
|
262
322
|
await loader.load();
|
|
@@ -381,7 +441,9 @@ function mockDevServerPlugin({
|
|
|
381
441
|
}
|
|
382
442
|
|
|
383
443
|
// src/define.ts
|
|
384
|
-
|
|
444
|
+
function defineMock(config) {
|
|
445
|
+
return config;
|
|
446
|
+
}
|
|
385
447
|
|
|
386
448
|
// src/index.ts
|
|
387
449
|
var src_default = mockDevServerPlugin;
|