vite-plugin-mock-dev-server 1.0.6 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -2
- package/README.zh-CN.md +65 -0
- package/dist/index.cjs +166 -71
- package/dist/index.d.ts +37 -6
- package/dist/index.js +163 -69
- package/package.json +14 -12
package/README.md
CHANGED
|
@@ -37,7 +37,8 @@
|
|
|
37
37
|
- ⚙️ Support Enabled/Disabled any one of api mock
|
|
38
38
|
- 🔥 HMR
|
|
39
39
|
- ⚖️ Use `server.proxy`
|
|
40
|
-
- 🍕 Support `viteConfig.define` in mock file
|
|
40
|
+
- 🍕 Support `viteConfig.define` in mock file.
|
|
41
|
+
- ⚓️ Support `resolve.alias` in mock file.
|
|
41
42
|
- 📤 Support `multipart` content-type,mock upload file.
|
|
42
43
|
- 🌈 Support `vite preview` mode
|
|
43
44
|
- 🗂 Support building small independent deployable mock services.
|
|
@@ -132,6 +133,7 @@ export default defineConfig({
|
|
|
132
133
|
})
|
|
133
134
|
```
|
|
134
135
|
|
|
136
|
+
|
|
135
137
|
#### options
|
|
136
138
|
|
|
137
139
|
- `options.prefix`
|
|
@@ -227,6 +229,24 @@ export default defineMock({
|
|
|
227
229
|
})
|
|
228
230
|
```
|
|
229
231
|
|
|
232
|
+
### createDefineMock(transformer)
|
|
233
|
+
|
|
234
|
+
Return a custom defineMock function to support preprocessing of mock config.
|
|
235
|
+
|
|
236
|
+
```ts
|
|
237
|
+
import path from 'path'
|
|
238
|
+
import { createDefineMock } from 'vite-plugin-mock-dev-server'
|
|
239
|
+
|
|
240
|
+
// Preprocessed mock url
|
|
241
|
+
const defineAPIMock = createDefineMock((mock) => {
|
|
242
|
+
mock.url = path.join('/api', mock.url)
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
export default defineApiMock({
|
|
246
|
+
url: '/test' // Complete as '/api/test'
|
|
247
|
+
})
|
|
248
|
+
```
|
|
249
|
+
|
|
230
250
|
## Mock Configuration
|
|
231
251
|
|
|
232
252
|
```ts
|
|
@@ -313,6 +333,19 @@ export default defineMock({
|
|
|
313
333
|
'Content-Type': 'application/json'
|
|
314
334
|
},
|
|
315
335
|
|
|
336
|
+
/**
|
|
337
|
+
* response cookies
|
|
338
|
+
* @type Record<string, string | { value: string, option: CookieOption }>
|
|
339
|
+
* @see https://github.com/pillarjs/cookies#cookiessetname--values--options
|
|
340
|
+
*/
|
|
341
|
+
cookies: {
|
|
342
|
+
'your-cookie': 'your cookie value',
|
|
343
|
+
'cookie&option': {
|
|
344
|
+
value: 'cookie value',
|
|
345
|
+
option: { path: '/', httpOnly: true }
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
|
|
316
349
|
/**
|
|
317
350
|
* Response Body
|
|
318
351
|
* Support `string/number/array/object`
|
|
@@ -320,7 +353,7 @@ export default defineMock({
|
|
|
320
353
|
*
|
|
321
354
|
* @type string | number | array | object
|
|
322
355
|
*
|
|
323
|
-
* @type (request: { headers, query, body, params }) => any | Promise<any>
|
|
356
|
+
* @type (request: { headers, query, body, params, refererQuery, getCookie }) => any | Promise<any>
|
|
324
357
|
*/
|
|
325
358
|
body: {},
|
|
326
359
|
|
|
@@ -338,6 +371,39 @@ export default defineMock({
|
|
|
338
371
|
|
|
339
372
|
```
|
|
340
373
|
|
|
374
|
+
### Request/Response Enhance
|
|
375
|
+
|
|
376
|
+
When defining methods using `headers`, `body`, and `response`, the plugin adds new content to the `request` and `response` parameters.
|
|
377
|
+
|
|
378
|
+
**In Request:**
|
|
379
|
+
|
|
380
|
+
The original type of `request` is [`Connect.IncomingMessage`](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/connect/index.d.ts). The plugin adds data such as `query`, `params`, `body`, `refererQuery`, and the `getCookie(name)` method for obtaining cookie information on this basis.
|
|
381
|
+
|
|
382
|
+
```ts
|
|
383
|
+
type Request = Connect.IncomingMessage & {
|
|
384
|
+
query: object
|
|
385
|
+
params: object
|
|
386
|
+
body: any
|
|
387
|
+
refererQuery: object
|
|
388
|
+
getCookie: (name: string, option?: Cookies.GetOption) => string | undefined
|
|
389
|
+
}
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
**In Response:**
|
|
393
|
+
|
|
394
|
+
The original type of `response` is `http.ServerResponse<http.IncomingMessage>`. The plugin adds `setCookie(name, value)` method for configuration cookie on this basis.
|
|
395
|
+
|
|
396
|
+
```ts
|
|
397
|
+
type Response = http.ServerResponse<http.IncomingMessage> & {
|
|
398
|
+
setCookie: (
|
|
399
|
+
name: string,
|
|
400
|
+
value?: string | null,
|
|
401
|
+
option?: Cookies.SetOption,
|
|
402
|
+
) => void
|
|
403
|
+
}
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
|
|
341
407
|
> Tips:
|
|
342
408
|
>
|
|
343
409
|
> If you write mock files using json/json5,
|
package/README.zh-CN.md
CHANGED
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
- 🔥 热更新
|
|
40
40
|
- ⚖️ 使用 `server.proxy` 配置
|
|
41
41
|
- 🍕 支持在 mock文件中使用 `viteConfig.define`配置字段
|
|
42
|
+
- ⚓️ 支持 `resolve.alias`
|
|
42
43
|
- 📤 支持 multipart 类型,模拟文件上传
|
|
43
44
|
- 🌈 支持 `vite preview` 模式
|
|
44
45
|
- 🗂 支持构建可独立部署的小型mock服务
|
|
@@ -225,6 +226,24 @@ export default defineMock({
|
|
|
225
226
|
})
|
|
226
227
|
```
|
|
227
228
|
|
|
229
|
+
### createDefineMock(transformer)
|
|
230
|
+
|
|
231
|
+
返回一个自定义的 defineMock 函数,用于支持对 mock config 的预处理。
|
|
232
|
+
|
|
233
|
+
```ts
|
|
234
|
+
import path from 'path'
|
|
235
|
+
import { createDefineMock } from 'vite-plugin-mock-dev-server'
|
|
236
|
+
|
|
237
|
+
// 预处理 mock url
|
|
238
|
+
const defineAPIMock = createDefineMock((mock) => {
|
|
239
|
+
mock.url = path.join('/api', mock.url)
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
export default defineApiMock({
|
|
243
|
+
url: '/test' // 补全为 '/api/test'
|
|
244
|
+
})
|
|
245
|
+
```
|
|
246
|
+
|
|
228
247
|
## Mock 配置
|
|
229
248
|
|
|
230
249
|
```ts
|
|
@@ -307,6 +326,19 @@ export default defineMock({
|
|
|
307
326
|
'Content-Type': 'application/json'
|
|
308
327
|
},
|
|
309
328
|
|
|
329
|
+
/**
|
|
330
|
+
* 响应体 cookies
|
|
331
|
+
* @type Record<string, string | { value: string, option: CookieOption }>
|
|
332
|
+
* @see https://github.com/pillarjs/cookies#cookiessetname--values--options
|
|
333
|
+
*/
|
|
334
|
+
cookies: {
|
|
335
|
+
'your-cookie': 'your cookie value',
|
|
336
|
+
'cookie&option': {
|
|
337
|
+
value: 'cookie value',
|
|
338
|
+
option: { path: '/', httpOnly: true }
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
|
|
310
342
|
/**
|
|
311
343
|
* 响应体数据
|
|
312
344
|
* 定义返回的响应体数据内容。
|
|
@@ -339,6 +371,39 @@ export default defineMock({
|
|
|
339
371
|
|
|
340
372
|
```
|
|
341
373
|
|
|
374
|
+
### Request/Response 增强
|
|
375
|
+
|
|
376
|
+
当你配置 `headers`, `body`, and `response` 的函数形式时, 插件在参数 `request` 和 `response` 添加了新的内容用于帮助获取必要的数据.
|
|
377
|
+
|
|
378
|
+
**Request:**
|
|
379
|
+
|
|
380
|
+
`request`的原始数据类型是[`Connect.IncomingMessage`](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/connect/index.d.ts). 插件在此基础上,增加了 `query`, `params`, `body`, `refererQuery`,以及 `getCookie(name)` 方法用于获取cookie信息。
|
|
381
|
+
|
|
382
|
+
```ts
|
|
383
|
+
type Request = Connect.IncomingMessage & {
|
|
384
|
+
query: object
|
|
385
|
+
params: object
|
|
386
|
+
body: any
|
|
387
|
+
refererQuery: object
|
|
388
|
+
getCookie: (name: string, option?: Cookies.GetOption) => string | undefined
|
|
389
|
+
}
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
**Response:**
|
|
393
|
+
|
|
394
|
+
`response` 的原始数据类型是`http.ServerResponse<http.IncomingMessage>`. 插件在此基础上增加了 `setCookie(name, value)` 方法用于设置cookie
|
|
395
|
+
|
|
396
|
+
```ts
|
|
397
|
+
type Response = http.ServerResponse<http.IncomingMessage> & {
|
|
398
|
+
setCookie: (
|
|
399
|
+
name: string,
|
|
400
|
+
value?: string | null,
|
|
401
|
+
option?: Cookies.SetOption,
|
|
402
|
+
) => void
|
|
403
|
+
}
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
|
|
342
407
|
> 注意:
|
|
343
408
|
>
|
|
344
409
|
> 如果使用 json/json5 编写 mock文件,则不支持使用 `response` 方法,以及不支持使用其他字段的函数形式。
|
package/dist/index.cjs
CHANGED
|
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var src_exports = {};
|
|
32
32
|
__export(src_exports, {
|
|
33
33
|
baseMiddleware: () => baseMiddleware,
|
|
34
|
+
createDefineMock: () => createDefineMock,
|
|
34
35
|
default: () => src_default,
|
|
35
36
|
defineMock: () => defineMock,
|
|
36
37
|
mockDevServerPlugin: () => mockDevServerPlugin,
|
|
@@ -38,7 +39,7 @@ __export(src_exports, {
|
|
|
38
39
|
});
|
|
39
40
|
module.exports = __toCommonJS(src_exports);
|
|
40
41
|
|
|
41
|
-
// node_modules/.pnpm/tsup@6.7.0_typescript@5.0.
|
|
42
|
+
// node_modules/.pnpm/tsup@6.7.0_typescript@5.0.4/node_modules/tsup/assets/cjs_shims.js
|
|
42
43
|
var getImportMetaUrl = () => typeof document === "undefined" ? new URL("file:" + __filename).href : document.currentScript && document.currentScript.src || new URL("main.js", document.baseURI).href;
|
|
43
44
|
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
44
45
|
|
|
@@ -53,7 +54,7 @@ var import_vite = require("vite");
|
|
|
53
54
|
|
|
54
55
|
// package.json
|
|
55
56
|
var name = "vite-plugin-mock-dev-server";
|
|
56
|
-
var version = "1.0
|
|
57
|
+
var version = "1.1.0";
|
|
57
58
|
|
|
58
59
|
// src/esbuildPlugin.ts
|
|
59
60
|
var import_promises = __toESM(require("fs/promises"), 1);
|
|
@@ -95,12 +96,48 @@ var jsonLoader = {
|
|
|
95
96
|
});
|
|
96
97
|
}
|
|
97
98
|
};
|
|
99
|
+
var aliasPlugin = (alias) => {
|
|
100
|
+
return {
|
|
101
|
+
name: "alias-plugin",
|
|
102
|
+
setup(build3) {
|
|
103
|
+
build3.onResolve({ filter: /.*/ }, async ({ path: id }) => {
|
|
104
|
+
const matchedEntry = alias.find(({ find: find2 }) => matches(find2, id));
|
|
105
|
+
if (!matchedEntry) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
const { find, replacement } = matchedEntry;
|
|
109
|
+
const result = await build3.resolve(id.replace(find, replacement), {
|
|
110
|
+
kind: "import-statement",
|
|
111
|
+
resolveDir: replacement,
|
|
112
|
+
namespace: "file"
|
|
113
|
+
});
|
|
114
|
+
return {
|
|
115
|
+
path: result.path,
|
|
116
|
+
external: false
|
|
117
|
+
};
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
};
|
|
122
|
+
function matches(pattern, importee) {
|
|
123
|
+
if (pattern instanceof RegExp) {
|
|
124
|
+
return pattern.test(importee);
|
|
125
|
+
}
|
|
126
|
+
if (importee.length < pattern.length) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
if (importee === pattern) {
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
return importee.startsWith(`${pattern}/`);
|
|
133
|
+
}
|
|
98
134
|
|
|
99
135
|
// src/utils.ts
|
|
100
136
|
var import_node_fs = __toESM(require("fs"), 1);
|
|
101
137
|
var import_node_path2 = __toESM(require("path"), 1);
|
|
102
138
|
var import_node_url = require("url");
|
|
103
139
|
var import_debug = __toESM(require("debug"), 1);
|
|
140
|
+
var import_path_to_regexp = require("path-to-regexp");
|
|
104
141
|
var import_picocolors = __toESM(require("picocolors"), 1);
|
|
105
142
|
var isArray = (val) => Array.isArray(val);
|
|
106
143
|
var isFunction = (val) => typeof val === "function";
|
|
@@ -148,6 +185,12 @@ var ensureProxies = (serverProxy = {}) => {
|
|
|
148
185
|
}).filter(Boolean);
|
|
149
186
|
return proxies;
|
|
150
187
|
};
|
|
188
|
+
function parseParams(pattern, url) {
|
|
189
|
+
const urlMatch = (0, import_path_to_regexp.match)(pattern, { decode: decodeURIComponent })(url) || {
|
|
190
|
+
params: {}
|
|
191
|
+
};
|
|
192
|
+
return urlMatch.params || {};
|
|
193
|
+
}
|
|
151
194
|
|
|
152
195
|
// src/build.ts
|
|
153
196
|
async function generateMockServer(ctx, config, options) {
|
|
@@ -174,7 +217,11 @@ async function generateMockServer(ctx, config, options) {
|
|
|
174
217
|
const content = await generateMockEntryCode(process.cwd(), include, exclude);
|
|
175
218
|
const mockEntry = import_node_path3.default.join(config.root, `mock-data-${Date.now()}.js`);
|
|
176
219
|
await import_promises2.default.writeFile(mockEntry, content, "utf-8");
|
|
177
|
-
const { code, deps } = await buildMockEntry(
|
|
220
|
+
const { code, deps } = await buildMockEntry(
|
|
221
|
+
mockEntry,
|
|
222
|
+
define,
|
|
223
|
+
config.resolve.alias
|
|
224
|
+
);
|
|
178
225
|
const mockDeps = getMockDependencies(deps);
|
|
179
226
|
await import_promises2.default.unlink(mockEntry);
|
|
180
227
|
const outputList = [
|
|
@@ -186,6 +233,7 @@ async function generateMockServer(ctx, config, options) {
|
|
|
186
233
|
filename: import_node_path3.default.join(outputDir, "index.js"),
|
|
187
234
|
source: generatorServerEntryCode(
|
|
188
235
|
[...prefix, ...proxies],
|
|
236
|
+
options.cookiesOptions,
|
|
189
237
|
options.build.serverPort
|
|
190
238
|
)
|
|
191
239
|
},
|
|
@@ -247,7 +295,7 @@ function generatePackageJson(pkg, mockDeps) {
|
|
|
247
295
|
});
|
|
248
296
|
return JSON.stringify(mockPkg, null, 2);
|
|
249
297
|
}
|
|
250
|
-
function generatorServerEntryCode(proxies, port = 8080) {
|
|
298
|
+
function generatorServerEntryCode(proxies, cookiesOptions = {}, port = 8080) {
|
|
251
299
|
return `import connect from 'connect';
|
|
252
300
|
import corsMiddleware from 'cors';
|
|
253
301
|
import { baseMiddleware } from 'vite-plugin-mock-dev-server';
|
|
@@ -258,6 +306,7 @@ app.use(corsMiddleware());
|
|
|
258
306
|
app.use(baseMiddleware({ mockData }, {
|
|
259
307
|
formidableOptions: { multiples: true },
|
|
260
308
|
proxies: ${JSON.stringify(proxies)}
|
|
309
|
+
cookiesOptions: ${JSON.stringify(cookiesOptions)}
|
|
261
310
|
}));
|
|
262
311
|
app.listen(${port});
|
|
263
312
|
|
|
@@ -288,7 +337,7 @@ const mockList = exporters.map((raw) => raw && raw.default
|
|
|
288
337
|
export default transformMockData(mockList);
|
|
289
338
|
`;
|
|
290
339
|
}
|
|
291
|
-
async function buildMockEntry(inputFile, define) {
|
|
340
|
+
async function buildMockEntry(inputFile, define, alias) {
|
|
292
341
|
var _a;
|
|
293
342
|
try {
|
|
294
343
|
const result = await (0, import_esbuild.build)({
|
|
@@ -301,7 +350,7 @@ async function buildMockEntry(inputFile, define) {
|
|
|
301
350
|
metafile: true,
|
|
302
351
|
format: "esm",
|
|
303
352
|
define,
|
|
304
|
-
plugins: [externalizeDeps, json5Loader, jsonLoader]
|
|
353
|
+
plugins: [aliasPlugin(alias), externalizeDeps, json5Loader, jsonLoader]
|
|
305
354
|
});
|
|
306
355
|
return {
|
|
307
356
|
code: result.outputFiles[0].text,
|
|
@@ -315,8 +364,9 @@ async function buildMockEntry(inputFile, define) {
|
|
|
315
364
|
|
|
316
365
|
// src/baseMiddleware.ts
|
|
317
366
|
var import_node_url2 = require("url");
|
|
367
|
+
var import_cookies = __toESM(require("cookies"), 1);
|
|
318
368
|
var import_http_status = __toESM(require("http-status"), 1);
|
|
319
|
-
var
|
|
369
|
+
var import_path_to_regexp2 = require("path-to-regexp");
|
|
320
370
|
var import_picocolors2 = __toESM(require("picocolors"), 1);
|
|
321
371
|
|
|
322
372
|
// src/parseReqBody.ts
|
|
@@ -374,9 +424,9 @@ function equalObj(left, right) {
|
|
|
374
424
|
}
|
|
375
425
|
|
|
376
426
|
// src/baseMiddleware.ts
|
|
377
|
-
function baseMiddleware(mockLoader, { formidableOptions = {}, proxies }) {
|
|
427
|
+
function baseMiddleware(mockLoader, { formidableOptions = {}, proxies, cookiesOptions }) {
|
|
378
428
|
return async function(req, res, next) {
|
|
379
|
-
const
|
|
429
|
+
const startTime = Date.now();
|
|
380
430
|
const { query, pathname } = (0, import_node_url2.parse)(req.url, true);
|
|
381
431
|
const { query: refererQuery } = (0, import_node_url2.parse)(req.headers.referer || "", true);
|
|
382
432
|
if (!pathname || proxies.length === 0 || !proxies.some((context) => doesProxyContextMatchUrl(context, req.url))) {
|
|
@@ -384,71 +434,35 @@ function baseMiddleware(mockLoader, { formidableOptions = {}, proxies }) {
|
|
|
384
434
|
}
|
|
385
435
|
const mockData = mockLoader.mockData;
|
|
386
436
|
const mockUrl = Object.keys(mockData).find((key) => {
|
|
387
|
-
return (0,
|
|
437
|
+
return (0, import_path_to_regexp2.pathToRegexp)(key).test(pathname);
|
|
388
438
|
});
|
|
389
439
|
if (!mockUrl) {
|
|
390
440
|
return next();
|
|
391
441
|
}
|
|
392
|
-
const mockList = mockData[mockUrl];
|
|
393
442
|
const reqBody = await parseReqBody(req, formidableOptions);
|
|
394
|
-
const
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
if (hasMock && mock.validator) {
|
|
402
|
-
const urlMatch2 = (0, import_path_to_regexp.match)(mock.url, { decode: decodeURIComponent })(
|
|
403
|
-
pathname
|
|
404
|
-
) || { params: {} };
|
|
405
|
-
const params2 = urlMatch2.params || {};
|
|
406
|
-
const request2 = {
|
|
407
|
-
query,
|
|
408
|
-
refererQuery,
|
|
409
|
-
params: params2,
|
|
410
|
-
body: reqBody,
|
|
411
|
-
headers: req.headers
|
|
412
|
-
};
|
|
413
|
-
if (isFunction(mock.validator)) {
|
|
414
|
-
return mock.validator(request2);
|
|
415
|
-
} else {
|
|
416
|
-
return validate(request2, mock.validator);
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
return hasMock;
|
|
443
|
+
const cookies = new import_cookies.default(req, res, cookiesOptions);
|
|
444
|
+
const method = req.method.toUpperCase();
|
|
445
|
+
const currentMock = fineMock(mockData[mockUrl], pathname, method, {
|
|
446
|
+
query,
|
|
447
|
+
refererQuery,
|
|
448
|
+
body: reqBody,
|
|
449
|
+
headers: req.headers
|
|
420
450
|
});
|
|
421
451
|
if (!currentMock)
|
|
422
452
|
return next();
|
|
423
453
|
debug("middleware: ", method, pathname);
|
|
424
|
-
if (currentMock.delay && currentMock.delay > 0) {
|
|
425
|
-
await sleep(currentMock.delay);
|
|
426
|
-
}
|
|
427
|
-
res.statusCode = currentMock.status || 200;
|
|
428
|
-
res.statusMessage = currentMock.statusText || getHTTPStatusText(res.statusCode);
|
|
429
|
-
const urlMatch = (0, import_path_to_regexp.match)(currentMock.url, { decode: decodeURIComponent })(
|
|
430
|
-
pathname
|
|
431
|
-
) || { params: {} };
|
|
432
|
-
const params = urlMatch.params || {};
|
|
433
454
|
const request = req;
|
|
455
|
+
const response = res;
|
|
434
456
|
request.body = reqBody;
|
|
435
457
|
request.query = query;
|
|
436
458
|
request.refererQuery = refererQuery;
|
|
437
|
-
request.params =
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
Object.keys(headers).forEach((key) => {
|
|
445
|
-
res.setHeader(key, headers[key]);
|
|
446
|
-
});
|
|
447
|
-
} catch (e) {
|
|
448
|
-
log.error(`${import_picocolors2.default.red("[headers error]")} ${req.url}
|
|
449
|
-
`, e);
|
|
450
|
-
}
|
|
451
|
-
}
|
|
459
|
+
request.params = parseParams(currentMock.url, pathname);
|
|
460
|
+
request.getCookie = cookies.get.bind(cookies);
|
|
461
|
+
response.setCookie = cookies.set.bind(cookies);
|
|
462
|
+
await provideHeaders(request, response, currentMock.headers);
|
|
463
|
+
await provideCookies(request, response, currentMock.cookies);
|
|
464
|
+
res.statusCode = currentMock.status || 200;
|
|
465
|
+
res.statusMessage = currentMock.statusText || getHTTPStatusText(res.statusCode);
|
|
452
466
|
if (currentMock.body) {
|
|
453
467
|
try {
|
|
454
468
|
let body;
|
|
@@ -457,6 +471,7 @@ function baseMiddleware(mockLoader, { formidableOptions = {}, proxies }) {
|
|
|
457
471
|
} else {
|
|
458
472
|
body = currentMock.body;
|
|
459
473
|
}
|
|
474
|
+
await realDelay(startTime, currentMock.delay);
|
|
460
475
|
res.end(JSON.stringify(body));
|
|
461
476
|
} catch (e) {
|
|
462
477
|
log.error(`${import_picocolors2.default.red("[body error]")} ${req.url}
|
|
@@ -469,7 +484,12 @@ function baseMiddleware(mockLoader, { formidableOptions = {}, proxies }) {
|
|
|
469
484
|
}
|
|
470
485
|
if (currentMock.response) {
|
|
471
486
|
try {
|
|
472
|
-
|
|
487
|
+
const end = response.end.bind(response);
|
|
488
|
+
response.end = (...args) => {
|
|
489
|
+
realDelay(startTime, currentMock.delay).then(() => end(...args));
|
|
490
|
+
return response;
|
|
491
|
+
};
|
|
492
|
+
await currentMock.response(request, response, next);
|
|
473
493
|
} catch (e) {
|
|
474
494
|
log.error(`${import_picocolors2.default.red("[response error]")} ${req.url}
|
|
475
495
|
`, e);
|
|
@@ -482,6 +502,69 @@ function baseMiddleware(mockLoader, { formidableOptions = {}, proxies }) {
|
|
|
482
502
|
res.end("");
|
|
483
503
|
};
|
|
484
504
|
}
|
|
505
|
+
function fineMock(mockList, pathname, method, request) {
|
|
506
|
+
return mockList.find((mock) => {
|
|
507
|
+
if (!pathname || !mock || !mock.url)
|
|
508
|
+
return false;
|
|
509
|
+
const methods = mock.method ? isArray(mock.method) ? mock.method : [mock.method] : ["GET", "POST"];
|
|
510
|
+
if (!methods.includes(method))
|
|
511
|
+
return false;
|
|
512
|
+
const hasMock = (0, import_path_to_regexp2.pathToRegexp)(mock.url).test(pathname);
|
|
513
|
+
if (hasMock && mock.validator) {
|
|
514
|
+
const params = parseParams(mock.url, pathname);
|
|
515
|
+
const extraRequest = { params, ...request };
|
|
516
|
+
if (isFunction(mock.validator)) {
|
|
517
|
+
return mock.validator(extraRequest);
|
|
518
|
+
} else {
|
|
519
|
+
return validate(extraRequest, mock.validator);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
return hasMock;
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
async function provideHeaders(req, res, headersOption) {
|
|
526
|
+
res.setHeader("Content-Type", "application/json");
|
|
527
|
+
res.setHeader("Cache-Control", "no-cache,max-age=0");
|
|
528
|
+
res.setHeader("X-Mock", "generate by vite:plugin-mock-dev-server");
|
|
529
|
+
if (!headersOption)
|
|
530
|
+
return;
|
|
531
|
+
try {
|
|
532
|
+
const headers = isFunction(headersOption) ? await headersOption(req) : headersOption;
|
|
533
|
+
Object.keys(headers).forEach((key) => {
|
|
534
|
+
res.setHeader(key, headers[key]);
|
|
535
|
+
});
|
|
536
|
+
} catch (e) {
|
|
537
|
+
log.error(`${import_picocolors2.default.red("[headers error]")} ${req.url}
|
|
538
|
+
`, e);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
async function provideCookies(req, res, cookiesOption) {
|
|
542
|
+
if (!cookiesOption)
|
|
543
|
+
return;
|
|
544
|
+
try {
|
|
545
|
+
const cookies = isFunction(cookiesOption) ? await cookiesOption(req) : cookiesOption;
|
|
546
|
+
Object.keys(cookies).forEach((key) => {
|
|
547
|
+
const optional = cookies[key];
|
|
548
|
+
if (typeof optional === "string") {
|
|
549
|
+
res.setCookie(key, optional);
|
|
550
|
+
} else {
|
|
551
|
+
const { value, options } = optional;
|
|
552
|
+
res.setCookie(key, value, options);
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
} catch (e) {
|
|
556
|
+
log.error(`${import_picocolors2.default.red("[cookies error]")} ${req.url}
|
|
557
|
+
`, e);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
async function realDelay(startTime, delay) {
|
|
561
|
+
if (!delay || delay <= 0)
|
|
562
|
+
return;
|
|
563
|
+
const diff = Date.now() - startTime;
|
|
564
|
+
const realDelay2 = delay - diff;
|
|
565
|
+
if (realDelay2 > 0)
|
|
566
|
+
await sleep(realDelay2);
|
|
567
|
+
}
|
|
485
568
|
function doesProxyContextMatchUrl(context, url) {
|
|
486
569
|
return context.startsWith("^") && new RegExp(context).test(url) || url.startsWith(context);
|
|
487
570
|
}
|
|
@@ -740,7 +823,7 @@ var _MockLoader = class extends import_node_events.default {
|
|
|
740
823
|
metafile: true,
|
|
741
824
|
format: isESM ? "esm" : "cjs",
|
|
742
825
|
define: this.options.define,
|
|
743
|
-
plugins: [externalizeDeps]
|
|
826
|
+
plugins: [aliasPlugin(this.options.alias), externalizeDeps]
|
|
744
827
|
});
|
|
745
828
|
return {
|
|
746
829
|
code: result.outputFiles[0].text,
|
|
@@ -771,7 +854,8 @@ async function mockServerMiddleware(config, options, httpServer, ws) {
|
|
|
771
854
|
const loader = new MockLoader({
|
|
772
855
|
include,
|
|
773
856
|
exclude,
|
|
774
|
-
define
|
|
857
|
+
define,
|
|
858
|
+
alias: config.resolve.alias
|
|
775
859
|
});
|
|
776
860
|
await loader.load();
|
|
777
861
|
httpServer == null ? void 0 : httpServer.on("close", () => loader.close());
|
|
@@ -784,7 +868,8 @@ async function mockServerMiddleware(config, options, httpServer, ws) {
|
|
|
784
868
|
const prefix = ensureArray(options.prefix);
|
|
785
869
|
return baseMiddleware(loader, {
|
|
786
870
|
formidableOptions: options.formidableOptions,
|
|
787
|
-
proxies: [...prefix, ...proxies]
|
|
871
|
+
proxies: [...prefix, ...proxies],
|
|
872
|
+
cookiesOptions: options.cookiesOptions
|
|
788
873
|
});
|
|
789
874
|
}
|
|
790
875
|
|
|
@@ -792,20 +877,18 @@ async function mockServerMiddleware(config, options, httpServer, ws) {
|
|
|
792
877
|
function mockDevServerPlugin({
|
|
793
878
|
prefix = [],
|
|
794
879
|
include = ["mock/**/*.mock.{js,ts,cjs,mjs,json,json5}"],
|
|
795
|
-
exclude = [
|
|
796
|
-
"**/node_modules/**",
|
|
797
|
-
"**/.vscode/**",
|
|
798
|
-
"**/.git/**"
|
|
799
|
-
],
|
|
880
|
+
exclude = ["**/node_modules/**", "**/.vscode/**", "**/.git/**"],
|
|
800
881
|
reload = false,
|
|
801
882
|
formidableOptions = {},
|
|
802
|
-
build: build3 = false
|
|
883
|
+
build: build3 = false,
|
|
884
|
+
cookiesOptions = {}
|
|
803
885
|
} = {}) {
|
|
804
886
|
const pluginOptions = {
|
|
805
887
|
prefix,
|
|
806
888
|
include,
|
|
807
889
|
exclude,
|
|
808
890
|
reload,
|
|
891
|
+
cookiesOptions,
|
|
809
892
|
formidableOptions: {
|
|
810
893
|
multiples: true,
|
|
811
894
|
...formidableOptions
|
|
@@ -877,12 +960,24 @@ function serverPlugin(pluginOptions) {
|
|
|
877
960
|
function defineMock(config) {
|
|
878
961
|
return config;
|
|
879
962
|
}
|
|
963
|
+
function createDefineMock(transformer) {
|
|
964
|
+
const define = (config) => {
|
|
965
|
+
if (isArray(config)) {
|
|
966
|
+
config = config.map((item) => transformer(item) || item);
|
|
967
|
+
} else {
|
|
968
|
+
config = transformer(config) || config;
|
|
969
|
+
}
|
|
970
|
+
return config;
|
|
971
|
+
};
|
|
972
|
+
return define;
|
|
973
|
+
}
|
|
880
974
|
|
|
881
975
|
// src/index.ts
|
|
882
976
|
var src_default = mockDevServerPlugin;
|
|
883
977
|
// Annotate the CommonJS export names for ESM import in node:
|
|
884
978
|
0 && (module.exports = {
|
|
885
979
|
baseMiddleware,
|
|
980
|
+
createDefineMock,
|
|
886
981
|
defineMock,
|
|
887
982
|
mockDevServerPlugin,
|
|
888
983
|
transformMockData
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { Connect, Plugin } from 'vite';
|
|
1
|
+
import { Connect, Plugin, ResolvedConfig } from 'vite';
|
|
2
2
|
import http from 'node:http';
|
|
3
|
+
import Cookies from 'cookies';
|
|
3
4
|
import formidable from 'formidable';
|
|
4
5
|
import EventEmitter from 'node:events';
|
|
5
6
|
import chokidar from 'chokidar';
|
|
@@ -35,6 +36,11 @@ interface MockServerPluginOptions {
|
|
|
35
36
|
* @see https://github.com/node-formidable/formidable#options
|
|
36
37
|
*/
|
|
37
38
|
formidableOptions?: formidable.Options;
|
|
39
|
+
/**
|
|
40
|
+
* cookies options
|
|
41
|
+
* @see https://github.com/pillarjs/cookies#new-cookiesrequest-response--options
|
|
42
|
+
*/
|
|
43
|
+
cookiesOptions?: Cookies.Option;
|
|
38
44
|
/**
|
|
39
45
|
* 当需要构建一个小型mock服务时,可配置此项
|
|
40
46
|
*
|
|
@@ -81,9 +87,20 @@ interface ExtraRequest {
|
|
|
81
87
|
*/
|
|
82
88
|
headers: Headers;
|
|
83
89
|
}
|
|
84
|
-
type MockRequest = ExtraRequest &
|
|
90
|
+
type MockRequest = Connect.IncomingMessage & ExtraRequest & {
|
|
91
|
+
getCookie: (name: string, option?: Cookies.GetOption) => string | undefined;
|
|
92
|
+
};
|
|
93
|
+
type MockResponse = http.ServerResponse<http.IncomingMessage> & {
|
|
94
|
+
setCookie: (name: string, value?: string | null, option?: Cookies.SetOption) => void;
|
|
95
|
+
};
|
|
85
96
|
type ResponseBodyFn = (request: MockRequest) => ResponseBody | Promise<ResponseBody>;
|
|
86
97
|
type ResponseHeaderFn = (request: MockRequest) => Headers | Promise<Headers>;
|
|
98
|
+
type CookieValue = string | {
|
|
99
|
+
value: string;
|
|
100
|
+
options?: Cookies.SetOption;
|
|
101
|
+
};
|
|
102
|
+
type ResponseCookies = Record<string, CookieValue>;
|
|
103
|
+
type ResponseCookiesFn = (request: MockRequest) => ResponseCookies | Promise<ResponseCookies>;
|
|
87
104
|
interface MockOptionsItem {
|
|
88
105
|
/**
|
|
89
106
|
* 需要做mock的接口地址,
|
|
@@ -127,6 +144,10 @@ interface MockOptionsItem {
|
|
|
127
144
|
* @default 0
|
|
128
145
|
*/
|
|
129
146
|
delay?: number;
|
|
147
|
+
/**
|
|
148
|
+
* 设置响应体 cookies
|
|
149
|
+
*/
|
|
150
|
+
cookies?: ResponseCookies | ResponseCookiesFn;
|
|
130
151
|
/**
|
|
131
152
|
* 配置响应体数据内容
|
|
132
153
|
*
|
|
@@ -140,7 +161,7 @@ interface MockOptionsItem {
|
|
|
140
161
|
*
|
|
141
162
|
* 在 req 中,还可以拿到 query、params、body等已解析的请求信息
|
|
142
163
|
*/
|
|
143
|
-
response?: (req: MockRequest, res:
|
|
164
|
+
response?: (req: MockRequest, res: MockResponse, next: Connect.NextFunction) => void | Promise<void>;
|
|
144
165
|
/**
|
|
145
166
|
* 请求验证器
|
|
146
167
|
*
|
|
@@ -153,7 +174,7 @@ interface MockOptionsItem {
|
|
|
153
174
|
type MockOptions = MockOptionsItem[];
|
|
154
175
|
type FormidableFile = formidable.File | formidable.File[];
|
|
155
176
|
|
|
156
|
-
declare function mockDevServerPlugin({ prefix, include, exclude, reload, formidableOptions, build, }?: MockServerPluginOptions): Plugin[];
|
|
177
|
+
declare function mockDevServerPlugin({ prefix, include, exclude, reload, formidableOptions, build, cookiesOptions, }?: MockServerPluginOptions): Plugin[];
|
|
157
178
|
|
|
158
179
|
/**
|
|
159
180
|
* mock config helper
|
|
@@ -172,12 +193,21 @@ declare function mockDevServerPlugin({ prefix, include, exclude, reload, formida
|
|
|
172
193
|
*/
|
|
173
194
|
declare function defineMock(config: MockOptionsItem): MockOptionsItem;
|
|
174
195
|
declare function defineMock(config: MockOptions): MockOptions;
|
|
196
|
+
/**
|
|
197
|
+
* 返回一个自定义的 defineMock 函数,用于支持对 mock config 的预处理。
|
|
198
|
+
*
|
|
199
|
+
* Return a custom defineMock function to support preprocessing of mock config.
|
|
200
|
+
*
|
|
201
|
+
* @param transformer preprocessing function
|
|
202
|
+
*/
|
|
203
|
+
declare function createDefineMock(transformer: (mock: MockOptionsItem) => MockOptionsItem | void): typeof defineMock;
|
|
175
204
|
|
|
176
205
|
interface MockLoaderOptions {
|
|
177
206
|
cwd?: string;
|
|
178
207
|
include: string[];
|
|
179
208
|
exclude: string[];
|
|
180
209
|
define: Record<string, any>;
|
|
210
|
+
alias: ResolvedConfig['resolve']['alias'];
|
|
181
211
|
}
|
|
182
212
|
/**
|
|
183
213
|
* mock配置加载器
|
|
@@ -213,10 +243,11 @@ declare class MockLoader extends EventEmitter {
|
|
|
213
243
|
|
|
214
244
|
interface BaseMiddlewareOptions {
|
|
215
245
|
formidableOptions: MockServerPluginOptions['formidableOptions'];
|
|
246
|
+
cookiesOptions: MockServerPluginOptions['cookiesOptions'];
|
|
216
247
|
proxies: string[];
|
|
217
248
|
}
|
|
218
|
-
declare function baseMiddleware(mockLoader: MockLoader, { formidableOptions, proxies }: BaseMiddlewareOptions): Connect.NextHandleFunction;
|
|
249
|
+
declare function baseMiddleware(mockLoader: MockLoader, { formidableOptions, proxies, cookiesOptions }: BaseMiddlewareOptions): Connect.NextHandleFunction;
|
|
219
250
|
|
|
220
251
|
declare function transformMockData(mockList: Map<string, MockOptionsItem | MockOptions> | (MockOptionsItem | MockOptions)[]): Record<string, MockOptions>;
|
|
221
252
|
|
|
222
|
-
export { BaseMiddlewareOptions, FormidableFile, MockOptions, MockOptionsItem, MockServerPluginOptions, baseMiddleware, mockDevServerPlugin as default, defineMock, mockDevServerPlugin, transformMockData };
|
|
253
|
+
export { BaseMiddlewareOptions, FormidableFile, MockOptions, MockOptionsItem, MockServerPluginOptions, baseMiddleware, createDefineMock, mockDevServerPlugin as default, defineMock, mockDevServerPlugin, transformMockData };
|
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import { createFilter, normalizePath } from "vite";
|
|
|
9
9
|
|
|
10
10
|
// package.json
|
|
11
11
|
var name = "vite-plugin-mock-dev-server";
|
|
12
|
-
var version = "1.0
|
|
12
|
+
var version = "1.1.0";
|
|
13
13
|
|
|
14
14
|
// src/esbuildPlugin.ts
|
|
15
15
|
import fsp from "fs/promises";
|
|
@@ -51,12 +51,48 @@ var jsonLoader = {
|
|
|
51
51
|
});
|
|
52
52
|
}
|
|
53
53
|
};
|
|
54
|
+
var aliasPlugin = (alias) => {
|
|
55
|
+
return {
|
|
56
|
+
name: "alias-plugin",
|
|
57
|
+
setup(build3) {
|
|
58
|
+
build3.onResolve({ filter: /.*/ }, async ({ path: id }) => {
|
|
59
|
+
const matchedEntry = alias.find(({ find: find2 }) => matches(find2, id));
|
|
60
|
+
if (!matchedEntry) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
const { find, replacement } = matchedEntry;
|
|
64
|
+
const result = await build3.resolve(id.replace(find, replacement), {
|
|
65
|
+
kind: "import-statement",
|
|
66
|
+
resolveDir: replacement,
|
|
67
|
+
namespace: "file"
|
|
68
|
+
});
|
|
69
|
+
return {
|
|
70
|
+
path: result.path,
|
|
71
|
+
external: false
|
|
72
|
+
};
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
function matches(pattern, importee) {
|
|
78
|
+
if (pattern instanceof RegExp) {
|
|
79
|
+
return pattern.test(importee);
|
|
80
|
+
}
|
|
81
|
+
if (importee.length < pattern.length) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
if (importee === pattern) {
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
return importee.startsWith(`${pattern}/`);
|
|
88
|
+
}
|
|
54
89
|
|
|
55
90
|
// src/utils.ts
|
|
56
91
|
import fs from "fs";
|
|
57
92
|
import path2 from "path";
|
|
58
93
|
import { fileURLToPath } from "url";
|
|
59
94
|
import Debug from "debug";
|
|
95
|
+
import { match } from "path-to-regexp";
|
|
60
96
|
import colors from "picocolors";
|
|
61
97
|
var isArray = (val) => Array.isArray(val);
|
|
62
98
|
var isFunction = (val) => typeof val === "function";
|
|
@@ -104,6 +140,12 @@ var ensureProxies = (serverProxy = {}) => {
|
|
|
104
140
|
}).filter(Boolean);
|
|
105
141
|
return proxies;
|
|
106
142
|
};
|
|
143
|
+
function parseParams(pattern, url) {
|
|
144
|
+
const urlMatch = match(pattern, { decode: decodeURIComponent })(url) || {
|
|
145
|
+
params: {}
|
|
146
|
+
};
|
|
147
|
+
return urlMatch.params || {};
|
|
148
|
+
}
|
|
107
149
|
|
|
108
150
|
// src/build.ts
|
|
109
151
|
async function generateMockServer(ctx, config, options) {
|
|
@@ -130,7 +172,11 @@ async function generateMockServer(ctx, config, options) {
|
|
|
130
172
|
const content = await generateMockEntryCode(process.cwd(), include, exclude);
|
|
131
173
|
const mockEntry = path3.join(config.root, `mock-data-${Date.now()}.js`);
|
|
132
174
|
await fsp2.writeFile(mockEntry, content, "utf-8");
|
|
133
|
-
const { code, deps } = await buildMockEntry(
|
|
175
|
+
const { code, deps } = await buildMockEntry(
|
|
176
|
+
mockEntry,
|
|
177
|
+
define,
|
|
178
|
+
config.resolve.alias
|
|
179
|
+
);
|
|
134
180
|
const mockDeps = getMockDependencies(deps);
|
|
135
181
|
await fsp2.unlink(mockEntry);
|
|
136
182
|
const outputList = [
|
|
@@ -142,6 +188,7 @@ async function generateMockServer(ctx, config, options) {
|
|
|
142
188
|
filename: path3.join(outputDir, "index.js"),
|
|
143
189
|
source: generatorServerEntryCode(
|
|
144
190
|
[...prefix, ...proxies],
|
|
191
|
+
options.cookiesOptions,
|
|
145
192
|
options.build.serverPort
|
|
146
193
|
)
|
|
147
194
|
},
|
|
@@ -203,7 +250,7 @@ function generatePackageJson(pkg, mockDeps) {
|
|
|
203
250
|
});
|
|
204
251
|
return JSON.stringify(mockPkg, null, 2);
|
|
205
252
|
}
|
|
206
|
-
function generatorServerEntryCode(proxies, port = 8080) {
|
|
253
|
+
function generatorServerEntryCode(proxies, cookiesOptions = {}, port = 8080) {
|
|
207
254
|
return `import connect from 'connect';
|
|
208
255
|
import corsMiddleware from 'cors';
|
|
209
256
|
import { baseMiddleware } from 'vite-plugin-mock-dev-server';
|
|
@@ -214,6 +261,7 @@ app.use(corsMiddleware());
|
|
|
214
261
|
app.use(baseMiddleware({ mockData }, {
|
|
215
262
|
formidableOptions: { multiples: true },
|
|
216
263
|
proxies: ${JSON.stringify(proxies)}
|
|
264
|
+
cookiesOptions: ${JSON.stringify(cookiesOptions)}
|
|
217
265
|
}));
|
|
218
266
|
app.listen(${port});
|
|
219
267
|
|
|
@@ -244,7 +292,7 @@ const mockList = exporters.map((raw) => raw && raw.default
|
|
|
244
292
|
export default transformMockData(mockList);
|
|
245
293
|
`;
|
|
246
294
|
}
|
|
247
|
-
async function buildMockEntry(inputFile, define) {
|
|
295
|
+
async function buildMockEntry(inputFile, define, alias) {
|
|
248
296
|
var _a;
|
|
249
297
|
try {
|
|
250
298
|
const result = await build({
|
|
@@ -257,7 +305,7 @@ async function buildMockEntry(inputFile, define) {
|
|
|
257
305
|
metafile: true,
|
|
258
306
|
format: "esm",
|
|
259
307
|
define,
|
|
260
|
-
plugins: [externalizeDeps, json5Loader, jsonLoader]
|
|
308
|
+
plugins: [aliasPlugin(alias), externalizeDeps, json5Loader, jsonLoader]
|
|
261
309
|
});
|
|
262
310
|
return {
|
|
263
311
|
code: result.outputFiles[0].text,
|
|
@@ -271,8 +319,9 @@ async function buildMockEntry(inputFile, define) {
|
|
|
271
319
|
|
|
272
320
|
// src/baseMiddleware.ts
|
|
273
321
|
import { parse as urlParse } from "url";
|
|
322
|
+
import Cookies from "cookies";
|
|
274
323
|
import HTTP_STATUS from "http-status";
|
|
275
|
-
import {
|
|
324
|
+
import { pathToRegexp } from "path-to-regexp";
|
|
276
325
|
import colors2 from "picocolors";
|
|
277
326
|
|
|
278
327
|
// src/parseReqBody.ts
|
|
@@ -330,9 +379,9 @@ function equalObj(left, right) {
|
|
|
330
379
|
}
|
|
331
380
|
|
|
332
381
|
// src/baseMiddleware.ts
|
|
333
|
-
function baseMiddleware(mockLoader, { formidableOptions = {}, proxies }) {
|
|
382
|
+
function baseMiddleware(mockLoader, { formidableOptions = {}, proxies, cookiesOptions }) {
|
|
334
383
|
return async function(req, res, next) {
|
|
335
|
-
const
|
|
384
|
+
const startTime = Date.now();
|
|
336
385
|
const { query, pathname } = urlParse(req.url, true);
|
|
337
386
|
const { query: refererQuery } = urlParse(req.headers.referer || "", true);
|
|
338
387
|
if (!pathname || proxies.length === 0 || !proxies.some((context) => doesProxyContextMatchUrl(context, req.url))) {
|
|
@@ -345,66 +394,30 @@ function baseMiddleware(mockLoader, { formidableOptions = {}, proxies }) {
|
|
|
345
394
|
if (!mockUrl) {
|
|
346
395
|
return next();
|
|
347
396
|
}
|
|
348
|
-
const mockList = mockData[mockUrl];
|
|
349
397
|
const reqBody = await parseReqBody(req, formidableOptions);
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
if (hasMock && mock.validator) {
|
|
358
|
-
const urlMatch2 = match(mock.url, { decode: decodeURIComponent })(
|
|
359
|
-
pathname
|
|
360
|
-
) || { params: {} };
|
|
361
|
-
const params2 = urlMatch2.params || {};
|
|
362
|
-
const request2 = {
|
|
363
|
-
query,
|
|
364
|
-
refererQuery,
|
|
365
|
-
params: params2,
|
|
366
|
-
body: reqBody,
|
|
367
|
-
headers: req.headers
|
|
368
|
-
};
|
|
369
|
-
if (isFunction(mock.validator)) {
|
|
370
|
-
return mock.validator(request2);
|
|
371
|
-
} else {
|
|
372
|
-
return validate(request2, mock.validator);
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
return hasMock;
|
|
398
|
+
const cookies = new Cookies(req, res, cookiesOptions);
|
|
399
|
+
const method = req.method.toUpperCase();
|
|
400
|
+
const currentMock = fineMock(mockData[mockUrl], pathname, method, {
|
|
401
|
+
query,
|
|
402
|
+
refererQuery,
|
|
403
|
+
body: reqBody,
|
|
404
|
+
headers: req.headers
|
|
376
405
|
});
|
|
377
406
|
if (!currentMock)
|
|
378
407
|
return next();
|
|
379
408
|
debug("middleware: ", method, pathname);
|
|
380
|
-
if (currentMock.delay && currentMock.delay > 0) {
|
|
381
|
-
await sleep(currentMock.delay);
|
|
382
|
-
}
|
|
383
|
-
res.statusCode = currentMock.status || 200;
|
|
384
|
-
res.statusMessage = currentMock.statusText || getHTTPStatusText(res.statusCode);
|
|
385
|
-
const urlMatch = match(currentMock.url, { decode: decodeURIComponent })(
|
|
386
|
-
pathname
|
|
387
|
-
) || { params: {} };
|
|
388
|
-
const params = urlMatch.params || {};
|
|
389
409
|
const request = req;
|
|
410
|
+
const response = res;
|
|
390
411
|
request.body = reqBody;
|
|
391
412
|
request.query = query;
|
|
392
413
|
request.refererQuery = refererQuery;
|
|
393
|
-
request.params =
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
Object.keys(headers).forEach((key) => {
|
|
401
|
-
res.setHeader(key, headers[key]);
|
|
402
|
-
});
|
|
403
|
-
} catch (e) {
|
|
404
|
-
log.error(`${colors2.red("[headers error]")} ${req.url}
|
|
405
|
-
`, e);
|
|
406
|
-
}
|
|
407
|
-
}
|
|
414
|
+
request.params = parseParams(currentMock.url, pathname);
|
|
415
|
+
request.getCookie = cookies.get.bind(cookies);
|
|
416
|
+
response.setCookie = cookies.set.bind(cookies);
|
|
417
|
+
await provideHeaders(request, response, currentMock.headers);
|
|
418
|
+
await provideCookies(request, response, currentMock.cookies);
|
|
419
|
+
res.statusCode = currentMock.status || 200;
|
|
420
|
+
res.statusMessage = currentMock.statusText || getHTTPStatusText(res.statusCode);
|
|
408
421
|
if (currentMock.body) {
|
|
409
422
|
try {
|
|
410
423
|
let body;
|
|
@@ -413,6 +426,7 @@ function baseMiddleware(mockLoader, { formidableOptions = {}, proxies }) {
|
|
|
413
426
|
} else {
|
|
414
427
|
body = currentMock.body;
|
|
415
428
|
}
|
|
429
|
+
await realDelay(startTime, currentMock.delay);
|
|
416
430
|
res.end(JSON.stringify(body));
|
|
417
431
|
} catch (e) {
|
|
418
432
|
log.error(`${colors2.red("[body error]")} ${req.url}
|
|
@@ -425,7 +439,12 @@ function baseMiddleware(mockLoader, { formidableOptions = {}, proxies }) {
|
|
|
425
439
|
}
|
|
426
440
|
if (currentMock.response) {
|
|
427
441
|
try {
|
|
428
|
-
|
|
442
|
+
const end = response.end.bind(response);
|
|
443
|
+
response.end = (...args) => {
|
|
444
|
+
realDelay(startTime, currentMock.delay).then(() => end(...args));
|
|
445
|
+
return response;
|
|
446
|
+
};
|
|
447
|
+
await currentMock.response(request, response, next);
|
|
429
448
|
} catch (e) {
|
|
430
449
|
log.error(`${colors2.red("[response error]")} ${req.url}
|
|
431
450
|
`, e);
|
|
@@ -438,6 +457,69 @@ function baseMiddleware(mockLoader, { formidableOptions = {}, proxies }) {
|
|
|
438
457
|
res.end("");
|
|
439
458
|
};
|
|
440
459
|
}
|
|
460
|
+
function fineMock(mockList, pathname, method, request) {
|
|
461
|
+
return mockList.find((mock) => {
|
|
462
|
+
if (!pathname || !mock || !mock.url)
|
|
463
|
+
return false;
|
|
464
|
+
const methods = mock.method ? isArray(mock.method) ? mock.method : [mock.method] : ["GET", "POST"];
|
|
465
|
+
if (!methods.includes(method))
|
|
466
|
+
return false;
|
|
467
|
+
const hasMock = pathToRegexp(mock.url).test(pathname);
|
|
468
|
+
if (hasMock && mock.validator) {
|
|
469
|
+
const params = parseParams(mock.url, pathname);
|
|
470
|
+
const extraRequest = { params, ...request };
|
|
471
|
+
if (isFunction(mock.validator)) {
|
|
472
|
+
return mock.validator(extraRequest);
|
|
473
|
+
} else {
|
|
474
|
+
return validate(extraRequest, mock.validator);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
return hasMock;
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
async function provideHeaders(req, res, headersOption) {
|
|
481
|
+
res.setHeader("Content-Type", "application/json");
|
|
482
|
+
res.setHeader("Cache-Control", "no-cache,max-age=0");
|
|
483
|
+
res.setHeader("X-Mock", "generate by vite:plugin-mock-dev-server");
|
|
484
|
+
if (!headersOption)
|
|
485
|
+
return;
|
|
486
|
+
try {
|
|
487
|
+
const headers = isFunction(headersOption) ? await headersOption(req) : headersOption;
|
|
488
|
+
Object.keys(headers).forEach((key) => {
|
|
489
|
+
res.setHeader(key, headers[key]);
|
|
490
|
+
});
|
|
491
|
+
} catch (e) {
|
|
492
|
+
log.error(`${colors2.red("[headers error]")} ${req.url}
|
|
493
|
+
`, e);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
async function provideCookies(req, res, cookiesOption) {
|
|
497
|
+
if (!cookiesOption)
|
|
498
|
+
return;
|
|
499
|
+
try {
|
|
500
|
+
const cookies = isFunction(cookiesOption) ? await cookiesOption(req) : cookiesOption;
|
|
501
|
+
Object.keys(cookies).forEach((key) => {
|
|
502
|
+
const optional = cookies[key];
|
|
503
|
+
if (typeof optional === "string") {
|
|
504
|
+
res.setCookie(key, optional);
|
|
505
|
+
} else {
|
|
506
|
+
const { value, options } = optional;
|
|
507
|
+
res.setCookie(key, value, options);
|
|
508
|
+
}
|
|
509
|
+
});
|
|
510
|
+
} catch (e) {
|
|
511
|
+
log.error(`${colors2.red("[cookies error]")} ${req.url}
|
|
512
|
+
`, e);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
async function realDelay(startTime, delay) {
|
|
516
|
+
if (!delay || delay <= 0)
|
|
517
|
+
return;
|
|
518
|
+
const diff = Date.now() - startTime;
|
|
519
|
+
const realDelay2 = delay - diff;
|
|
520
|
+
if (realDelay2 > 0)
|
|
521
|
+
await sleep(realDelay2);
|
|
522
|
+
}
|
|
441
523
|
function doesProxyContextMatchUrl(context, url) {
|
|
442
524
|
return context.startsWith("^") && new RegExp(context).test(url) || url.startsWith(context);
|
|
443
525
|
}
|
|
@@ -696,7 +778,7 @@ var _MockLoader = class extends EventEmitter {
|
|
|
696
778
|
metafile: true,
|
|
697
779
|
format: isESM ? "esm" : "cjs",
|
|
698
780
|
define: this.options.define,
|
|
699
|
-
plugins: [externalizeDeps]
|
|
781
|
+
plugins: [aliasPlugin(this.options.alias), externalizeDeps]
|
|
700
782
|
});
|
|
701
783
|
return {
|
|
702
784
|
code: result.outputFiles[0].text,
|
|
@@ -727,7 +809,8 @@ async function mockServerMiddleware(config, options, httpServer, ws) {
|
|
|
727
809
|
const loader = new MockLoader({
|
|
728
810
|
include,
|
|
729
811
|
exclude,
|
|
730
|
-
define
|
|
812
|
+
define,
|
|
813
|
+
alias: config.resolve.alias
|
|
731
814
|
});
|
|
732
815
|
await loader.load();
|
|
733
816
|
httpServer == null ? void 0 : httpServer.on("close", () => loader.close());
|
|
@@ -740,7 +823,8 @@ async function mockServerMiddleware(config, options, httpServer, ws) {
|
|
|
740
823
|
const prefix = ensureArray(options.prefix);
|
|
741
824
|
return baseMiddleware(loader, {
|
|
742
825
|
formidableOptions: options.formidableOptions,
|
|
743
|
-
proxies: [...prefix, ...proxies]
|
|
826
|
+
proxies: [...prefix, ...proxies],
|
|
827
|
+
cookiesOptions: options.cookiesOptions
|
|
744
828
|
});
|
|
745
829
|
}
|
|
746
830
|
|
|
@@ -748,20 +832,18 @@ async function mockServerMiddleware(config, options, httpServer, ws) {
|
|
|
748
832
|
function mockDevServerPlugin({
|
|
749
833
|
prefix = [],
|
|
750
834
|
include = ["mock/**/*.mock.{js,ts,cjs,mjs,json,json5}"],
|
|
751
|
-
exclude = [
|
|
752
|
-
"**/node_modules/**",
|
|
753
|
-
"**/.vscode/**",
|
|
754
|
-
"**/.git/**"
|
|
755
|
-
],
|
|
835
|
+
exclude = ["**/node_modules/**", "**/.vscode/**", "**/.git/**"],
|
|
756
836
|
reload = false,
|
|
757
837
|
formidableOptions = {},
|
|
758
|
-
build: build3 = false
|
|
838
|
+
build: build3 = false,
|
|
839
|
+
cookiesOptions = {}
|
|
759
840
|
} = {}) {
|
|
760
841
|
const pluginOptions = {
|
|
761
842
|
prefix,
|
|
762
843
|
include,
|
|
763
844
|
exclude,
|
|
764
845
|
reload,
|
|
846
|
+
cookiesOptions,
|
|
765
847
|
formidableOptions: {
|
|
766
848
|
multiples: true,
|
|
767
849
|
...formidableOptions
|
|
@@ -833,11 +915,23 @@ function serverPlugin(pluginOptions) {
|
|
|
833
915
|
function defineMock(config) {
|
|
834
916
|
return config;
|
|
835
917
|
}
|
|
918
|
+
function createDefineMock(transformer) {
|
|
919
|
+
const define = (config) => {
|
|
920
|
+
if (isArray(config)) {
|
|
921
|
+
config = config.map((item) => transformer(item) || item);
|
|
922
|
+
} else {
|
|
923
|
+
config = transformer(config) || config;
|
|
924
|
+
}
|
|
925
|
+
return config;
|
|
926
|
+
};
|
|
927
|
+
return define;
|
|
928
|
+
}
|
|
836
929
|
|
|
837
930
|
// src/index.ts
|
|
838
931
|
var src_default = mockDevServerPlugin;
|
|
839
932
|
export {
|
|
840
933
|
baseMiddleware,
|
|
934
|
+
createDefineMock,
|
|
841
935
|
src_default as default,
|
|
842
936
|
defineMock,
|
|
843
937
|
mockDevServerPlugin,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vite-plugin-mock-dev-server",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"vite",
|
|
6
6
|
"plugin",
|
|
@@ -32,33 +32,35 @@
|
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"chokidar": "^3.5.3",
|
|
34
34
|
"co-body": "^6.1.0",
|
|
35
|
+
"cookies": "^0.8.0",
|
|
35
36
|
"debug": "^4.3.4",
|
|
36
|
-
"esbuild": "^0.17.
|
|
37
|
+
"esbuild": "^0.17.17",
|
|
37
38
|
"fast-glob": "^3.2.12",
|
|
38
39
|
"formidable": "^2.1.1",
|
|
39
40
|
"http-status": "^1.6.2",
|
|
40
|
-
"is-core-module": "^2.
|
|
41
|
+
"is-core-module": "^2.12.0",
|
|
41
42
|
"json5": "^2.2.3",
|
|
42
43
|
"path-to-regexp": "^6.2.1",
|
|
43
44
|
"picocolors": "^1.0.0"
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
46
|
-
"@pengzhanbo/eslint-config-ts": "^0.3.
|
|
47
|
-
"@pengzhanbo/prettier-config": "^0.3.
|
|
47
|
+
"@pengzhanbo/eslint-config-ts": "^0.3.4",
|
|
48
|
+
"@pengzhanbo/prettier-config": "^0.3.4",
|
|
48
49
|
"@types/co-body": "^6.1.0",
|
|
50
|
+
"@types/cookies": "^0.7.7",
|
|
49
51
|
"@types/debug": "^4.1.7",
|
|
50
52
|
"@types/formidable": "^2.0.5",
|
|
51
53
|
"@types/is-core-module": "^2.2.0",
|
|
52
|
-
"@types/node": "^18.15.
|
|
53
|
-
"bumpp": "^9.
|
|
54
|
+
"@types/node": "^18.15.12",
|
|
55
|
+
"bumpp": "^9.1.0",
|
|
54
56
|
"conventional-changelog-cli": "^2.2.2",
|
|
55
|
-
"eslint": "^8.
|
|
57
|
+
"eslint": "^8.38.0",
|
|
56
58
|
"mockjs": "^1.1.0",
|
|
57
|
-
"prettier": "^2.8.
|
|
59
|
+
"prettier": "^2.8.7",
|
|
58
60
|
"tsup": "^6.7.0",
|
|
59
|
-
"typescript": "^5.0.
|
|
60
|
-
"vite": "^4.
|
|
61
|
-
"vitepress": "1.0.0-alpha.
|
|
61
|
+
"typescript": "^5.0.4",
|
|
62
|
+
"vite": "^4.3.0",
|
|
63
|
+
"vitepress": "1.0.0-alpha.73",
|
|
62
64
|
"vue": "^3.2.47"
|
|
63
65
|
},
|
|
64
66
|
"peerDependencies": {
|