warehouse-mock 1.0.0 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +103 -135
- package/dist/index.d.ts +18 -2
- package/dist/index.js +205 -34
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -1,90 +1,39 @@
|
|
|
1
|
-
# warehouse-mock
|
|
1
|
+
# warehouse-mock-plugin
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<div align="center">
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
[](https://github.com/CodeMomentYY/warehouse-mock/blob/main/LICENSE)
|
|
5
|
+
极简 Vue Mock 插件,零业务代码侵入,完美支持 RPC 风格接口
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
[](https://www.npmjs.com/package/warehouse-mock-plugin)
|
|
8
|
+
[](https://opensource.org/licenses/MIT)
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
- ✅ **零业务代码侵入**: 只需修改配置文件,无需改动业务逻辑
|
|
12
|
-
- ✅ **实时更新**: 修改 Mock 数据后刷新页面即可,无需重启服务
|
|
13
|
-
- ✅ **按需 Mock**: 只拦截配置了 Mock 数据的接口,其他接口不受影响
|
|
14
|
-
- ✅ **TypeScript 编写**: 类型安全,易于维护
|
|
15
|
-
- ✅ **兼容性强**: 支持 Webpack 4/5,Vue CLI 3/4/5
|
|
10
|
+
</div>
|
|
16
11
|
|
|
17
|
-
##
|
|
12
|
+
## 安装
|
|
18
13
|
|
|
19
14
|
```bash
|
|
20
|
-
npm install warehouse-mock --save-dev
|
|
15
|
+
npm install warehouse-mock-plugin --save-dev
|
|
21
16
|
```
|
|
22
17
|
|
|
23
|
-
##
|
|
18
|
+
## 快速开始
|
|
24
19
|
|
|
25
20
|
### 1. 配置 vue.config.js
|
|
26
21
|
|
|
27
22
|
```javascript
|
|
28
|
-
const WarehouseMockPlugin = require('warehouse-mock');
|
|
29
|
-
const webpack = require('webpack');
|
|
30
|
-
const path = require('path');
|
|
23
|
+
const WarehouseMockPlugin = require('warehouse-mock-plugin');
|
|
31
24
|
|
|
32
25
|
const isMock = process.env.MOCK === 'true';
|
|
33
|
-
const mockPlugin = isMock ? new WarehouseMockPlugin({
|
|
34
|
-
mockPath: path.resolve(__dirname, 'warehouseMock'),
|
|
35
|
-
}) : null;
|
|
36
26
|
|
|
37
27
|
module.exports = {
|
|
38
28
|
configureWebpack: config => {
|
|
39
|
-
if (isMock
|
|
40
|
-
config.plugins.push(
|
|
41
|
-
new webpack.DefinePlugin({
|
|
42
|
-
'process.env.VUE_APP_MOCK': JSON.stringify('true'),
|
|
43
|
-
})
|
|
44
|
-
);
|
|
45
|
-
config.plugins.push(mockPlugin);
|
|
29
|
+
if (isMock) {
|
|
30
|
+
config.plugins.push(new WarehouseMockPlugin());
|
|
46
31
|
}
|
|
47
|
-
},
|
|
48
|
-
|
|
49
|
-
devServer: {
|
|
50
|
-
// Vue CLI 5.x (Webpack 5)
|
|
51
|
-
setupMiddlewares: (middlewares, devServer) => {
|
|
52
|
-
if (isMock && mockPlugin) {
|
|
53
|
-
return mockPlugin.setupMiddlewares(middlewares, devServer);
|
|
54
|
-
}
|
|
55
|
-
return middlewares;
|
|
56
|
-
},
|
|
57
|
-
|
|
58
|
-
// Vue CLI 3.x/4.x (Webpack 4) 使用这个:
|
|
59
|
-
// before: isMock && mockPlugin ? (app) => mockPlugin.runBefore(app) : undefined,
|
|
60
32
|
}
|
|
61
33
|
};
|
|
62
34
|
```
|
|
63
35
|
|
|
64
|
-
### 2.
|
|
65
|
-
|
|
66
|
-
在 `src/const/config.js` 中添加 Mock 模式判断:
|
|
67
|
-
|
|
68
|
-
```javascript
|
|
69
|
-
const config = { env: process.env.VUE_APP_ENV || 'dev' };
|
|
70
|
-
|
|
71
|
-
// Mock 模式优先判断
|
|
72
|
-
if (process.env.VUE_APP_MOCK === 'true') {
|
|
73
|
-
Object.assign(config, {
|
|
74
|
-
API: '/mock-api',
|
|
75
|
-
});
|
|
76
|
-
} else if (config.env === 'dev') {
|
|
77
|
-
Object.assign(config, {
|
|
78
|
-
API: 'https://fat-api.hellobike.com/api',
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export default config;
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
### 3. 添加 Mock 脚本
|
|
86
|
-
|
|
87
|
-
在 `package.json` 中:
|
|
36
|
+
### 2. 添加 Mock 脚本
|
|
88
37
|
|
|
89
38
|
```json
|
|
90
39
|
{
|
|
@@ -94,114 +43,133 @@ export default config;
|
|
|
94
43
|
}
|
|
95
44
|
```
|
|
96
45
|
|
|
97
|
-
###
|
|
46
|
+
### 3. 配置 API 切换
|
|
98
47
|
|
|
99
|
-
|
|
48
|
+
在 `src/const/config.js` 中:
|
|
100
49
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
"data": {
|
|
107
|
-
"userId": 12345,
|
|
108
|
-
"userName": "Mock 测试用户"
|
|
109
|
-
}
|
|
50
|
+
```javascript
|
|
51
|
+
if (process.env.VUE_APP_MOCK === 'true') {
|
|
52
|
+
config.API = '/mock-api';
|
|
53
|
+
} else {
|
|
54
|
+
config.API = 'https://your-api.com';
|
|
110
55
|
}
|
|
111
56
|
```
|
|
112
57
|
|
|
58
|
+
### 4. 创建 Mock 数据
|
|
59
|
+
|
|
60
|
+
在项目根目录创建 `warehouseMock/` 文件夹,添加 JSON 文件:
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
warehouseMock/
|
|
64
|
+
├── user.account.getInfo.json
|
|
65
|
+
└── user.taurus.pointInfo.json
|
|
66
|
+
```
|
|
67
|
+
|
|
113
68
|
### 5. 启动
|
|
114
69
|
|
|
115
70
|
```bash
|
|
116
71
|
npm run mock
|
|
117
72
|
```
|
|
118
73
|
|
|
119
|
-
##
|
|
74
|
+
## 配置选项
|
|
120
75
|
|
|
121
76
|
```typescript
|
|
122
|
-
|
|
123
|
-
//
|
|
124
|
-
mockPath
|
|
77
|
+
interface MockPluginOptions {
|
|
78
|
+
// Mock 数据目录,默认 'warehouseMock'
|
|
79
|
+
mockPath?: string;
|
|
125
80
|
|
|
126
|
-
//
|
|
81
|
+
// 需要拦截的 API 路径前缀,默认 ['/api', '/mock-api']
|
|
127
82
|
apiPrefixes?: string[];
|
|
128
83
|
|
|
129
|
-
//
|
|
84
|
+
// 本地代理路径前缀,默认 '/mock-api'
|
|
130
85
|
localApiPrefix?: string;
|
|
131
|
-
|
|
86
|
+
|
|
87
|
+
// 是否启用,默认 true
|
|
88
|
+
enabled?: boolean;
|
|
89
|
+
|
|
90
|
+
// 是否自动注入环境变量 VUE_APP_MOCK,默认 true
|
|
91
|
+
injectEnv?: boolean;
|
|
92
|
+
|
|
93
|
+
// 响应延迟(毫秒),模拟网络延迟,默认 0
|
|
94
|
+
delay?: number;
|
|
95
|
+
|
|
96
|
+
// 代理配置:未匹配的请求转发到真实 API
|
|
97
|
+
proxy?: {
|
|
98
|
+
target: string;
|
|
99
|
+
changeOrigin?: boolean;
|
|
100
|
+
};
|
|
101
|
+
}
|
|
132
102
|
```
|
|
133
103
|
|
|
134
|
-
##
|
|
135
|
-
|
|
136
|
-
### RPC 风格(推荐)
|
|
137
|
-
|
|
138
|
-
| 请求 URL | Mock 文件名 |
|
|
139
|
-
|---------|-----------|
|
|
140
|
-
| `/api?user.account.getInfo` | `user.account.getInfo.json` |
|
|
141
|
-
| `/api?user.taurus.pointInfo` | `user.taurus.pointInfo.json` |
|
|
142
|
-
| `/api?common.welfare.banner.query` | `common.welfare.banner.query.json` |
|
|
143
|
-
|
|
144
|
-
### RESTful 风格
|
|
104
|
+
## 使用示例
|
|
145
105
|
|
|
146
|
-
|
|
147
|
-
|---------|---------------------|
|
|
148
|
-
| `/api/user/info` | `api_user_info.json` (扁平化) |
|
|
149
|
-
| `/api/user/info` | `api/user/info.json` (嵌套) |
|
|
106
|
+
### 基础配置
|
|
150
107
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
### 直接访问 Mock 数据
|
|
158
|
-
|
|
159
|
-
访问: `http://localhost:8080/mock-api?user.account.getInfo`
|
|
108
|
+
```javascript
|
|
109
|
+
new WarehouseMockPlugin({
|
|
110
|
+
mockPath: 'warehouseMock',
|
|
111
|
+
apiPrefixes: ['/api', '/mock-api']
|
|
112
|
+
})
|
|
113
|
+
```
|
|
160
114
|
|
|
161
|
-
|
|
115
|
+
### 代理模式
|
|
162
116
|
|
|
163
|
-
|
|
117
|
+
未匹配的请求转发到真实 API:
|
|
164
118
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
-
|
|
119
|
+
```javascript
|
|
120
|
+
new WarehouseMockPlugin({
|
|
121
|
+
proxy: {
|
|
122
|
+
target: 'https://fat-api.hellobike.com',
|
|
123
|
+
changeOrigin: true
|
|
124
|
+
}
|
|
125
|
+
})
|
|
126
|
+
```
|
|
169
127
|
|
|
170
|
-
###
|
|
128
|
+
### 模拟网络延迟
|
|
171
129
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
| TypeScript | ✅ | ❌ | ❌ |
|
|
178
|
-
| Webpack 集成 | ✅ | ❌ | ❌ |
|
|
130
|
+
```javascript
|
|
131
|
+
new WarehouseMockPlugin({
|
|
132
|
+
delay: 500 // 500ms 延迟
|
|
133
|
+
})
|
|
134
|
+
```
|
|
179
135
|
|
|
180
|
-
##
|
|
136
|
+
## Mock 文件命名规则
|
|
181
137
|
|
|
182
|
-
|
|
138
|
+
### RPC 风格 (推荐)
|
|
183
139
|
|
|
184
|
-
|
|
140
|
+
| 请求 URL | Mock 文件名 |
|
|
141
|
+
|---------|-----------|
|
|
142
|
+
| `GET /api?user.account.getInfo` | `user.account.getInfo.json` |
|
|
143
|
+
| `POST /mock-api?user.taurus.pointInfo` | `user.taurus.pointInfo.json` |
|
|
185
144
|
|
|
186
|
-
|
|
145
|
+
### RESTful 风格
|
|
187
146
|
|
|
188
|
-
|
|
147
|
+
| 请求 URL | Mock 文件名 |
|
|
148
|
+
|---------|-----------|
|
|
149
|
+
| `GET /api/user/info` | `api_user_info.json` (扁平化) |
|
|
150
|
+
| `GET /api/user/info` | `api/user/info.json` (嵌套) |
|
|
189
151
|
|
|
190
|
-
|
|
152
|
+
## 调试工具
|
|
191
153
|
|
|
192
|
-
|
|
154
|
+
### 查看可用 Mock 列表
|
|
193
155
|
|
|
194
|
-
|
|
156
|
+
访问: `http://localhost:8080/__mock_list__`
|
|
195
157
|
|
|
196
|
-
|
|
158
|
+
### 直接访问 Mock 数据
|
|
197
159
|
|
|
198
|
-
|
|
160
|
+
访问: `http://localhost:8080/mock-api?user.account.getInfo`
|
|
199
161
|
|
|
200
|
-
|
|
162
|
+
## 核心特性
|
|
201
163
|
|
|
202
|
-
|
|
164
|
+
- ✅ **极简配置** - vue.config.js 只需 3 行代码
|
|
165
|
+
- ✅ **零业务侵入** - 无需修改接口调用代码
|
|
166
|
+
- ✅ **RPC 风格支持** - 原生支持 RPC 接口
|
|
167
|
+
- ✅ **实时热更新** - 修改 Mock 数据,刷新即可
|
|
168
|
+
- ✅ **按需拦截** - 只拦截配置了 Mock 文件的接口
|
|
169
|
+
- ✅ **代理模式** - 未匹配请求转发到真实 API
|
|
170
|
+
- ✅ **自动注入环境变量** - 无需手动配置 DefinePlugin
|
|
171
|
+
- ✅ **兼容性强** - 支持 Webpack 4/5,Vue CLI 3/4/5
|
|
203
172
|
|
|
204
|
-
|
|
205
|
-
- [问题反馈](https://github.com/CodeMomentYY/warehouse-mock/issues)
|
|
206
|
-
- [更新日志](https://github.com/CodeMomentYY/warehouse-mock/releases)
|
|
173
|
+
## License
|
|
207
174
|
|
|
175
|
+
MIT
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import { Compiler } from 'webpack';
|
|
2
2
|
interface MockPluginOptions {
|
|
3
|
-
mockPath
|
|
3
|
+
mockPath?: string;
|
|
4
4
|
apiPrefixes?: string[];
|
|
5
5
|
localApiPrefix?: string;
|
|
6
|
+
enabled?: boolean;
|
|
7
|
+
proxy?: {
|
|
8
|
+
target: string;
|
|
9
|
+
changeOrigin?: boolean;
|
|
10
|
+
};
|
|
11
|
+
injectEnv?: boolean;
|
|
12
|
+
delay?: number;
|
|
6
13
|
}
|
|
7
14
|
declare class WarehouseMockPlugin {
|
|
8
15
|
private options;
|
|
9
16
|
private resolvedMockPath;
|
|
10
|
-
|
|
17
|
+
private isEnabled;
|
|
18
|
+
constructor(options?: MockPluginOptions);
|
|
11
19
|
/**
|
|
12
20
|
* 实时扫描 mock 目录,获取所有 mock 文件名列表
|
|
13
21
|
*/
|
|
@@ -17,6 +25,10 @@ declare class WarehouseMockPlugin {
|
|
|
17
25
|
*/
|
|
18
26
|
getLocalApiPrefix(): string;
|
|
19
27
|
apply(compiler: Compiler): void;
|
|
28
|
+
/**
|
|
29
|
+
* 自动注入环境变量 VUE_APP_MOCK
|
|
30
|
+
*/
|
|
31
|
+
private injectEnvironmentVariable;
|
|
20
32
|
/**
|
|
21
33
|
* 公共方法:设置中间件 (Webpack 5 / Vue CLI 5+)
|
|
22
34
|
*/
|
|
@@ -29,5 +41,9 @@ declare class WarehouseMockPlugin {
|
|
|
29
41
|
private ensureMockDirectory;
|
|
30
42
|
private createDemoFile;
|
|
31
43
|
private handleRequest;
|
|
44
|
+
/**
|
|
45
|
+
* 代理请求到真实 API(改进版)
|
|
46
|
+
*/
|
|
47
|
+
private proxyRequest;
|
|
32
48
|
}
|
|
33
49
|
export = WarehouseMockPlugin;
|
package/dist/index.js
CHANGED
|
@@ -6,9 +6,26 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
6
6
|
const path_1 = __importDefault(require("path"));
|
|
7
7
|
const chalk_1 = __importDefault(require("chalk"));
|
|
8
8
|
class WarehouseMockPlugin {
|
|
9
|
-
constructor(options) {
|
|
9
|
+
constructor(options = {}) {
|
|
10
10
|
this.resolvedMockPath = '';
|
|
11
|
-
this.
|
|
11
|
+
this.isEnabled = true;
|
|
12
|
+
// 默认配置
|
|
13
|
+
this.options = {
|
|
14
|
+
mockPath: options.mockPath || 'warehouseMock',
|
|
15
|
+
apiPrefixes: options.apiPrefixes || ['/api', '/mock-api'],
|
|
16
|
+
localApiPrefix: options.localApiPrefix || '/mock-api',
|
|
17
|
+
enabled: options.enabled !== undefined ? options.enabled : true,
|
|
18
|
+
proxy: options.proxy || undefined,
|
|
19
|
+
injectEnv: options.injectEnv !== undefined ? options.injectEnv : true,
|
|
20
|
+
delay: options.delay || 0,
|
|
21
|
+
};
|
|
22
|
+
// 判断是否启用(支持环境变量控制)
|
|
23
|
+
if (process.env.MOCK === 'false' || process.env.VUE_APP_MOCK === 'false') {
|
|
24
|
+
this.isEnabled = false;
|
|
25
|
+
}
|
|
26
|
+
if (this.options.enabled === false) {
|
|
27
|
+
this.isEnabled = false;
|
|
28
|
+
}
|
|
12
29
|
}
|
|
13
30
|
/**
|
|
14
31
|
* 实时扫描 mock 目录,获取所有 mock 文件名列表
|
|
@@ -33,7 +50,16 @@ class WarehouseMockPlugin {
|
|
|
33
50
|
return this.options.localApiPrefix || '/mock-api';
|
|
34
51
|
}
|
|
35
52
|
apply(compiler) {
|
|
53
|
+
// 如果未启用,直接返回
|
|
54
|
+
if (!this.isEnabled) {
|
|
55
|
+
console.log(chalk_1.default.yellow('[WarehouseMock] Mock 模式未启用'));
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
36
58
|
this.resolvedMockPath = path_1.default.resolve(compiler.context, this.options.mockPath);
|
|
59
|
+
// 自动注入环境变量 VUE_APP_MOCK
|
|
60
|
+
if (this.options.injectEnv) {
|
|
61
|
+
this.injectEnvironmentVariable(compiler);
|
|
62
|
+
}
|
|
37
63
|
// 确保 mock 目录存在
|
|
38
64
|
if (!fs_1.default.existsSync(this.resolvedMockPath)) {
|
|
39
65
|
try {
|
|
@@ -46,21 +72,51 @@ class WarehouseMockPlugin {
|
|
|
46
72
|
}
|
|
47
73
|
}
|
|
48
74
|
const mockFileList = this.getMockFileList();
|
|
49
|
-
console.log(chalk_1.default.cyan(`[WarehouseMock] 已加载 ${mockFileList.length} 个 mock
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
75
|
+
console.log(chalk_1.default.cyan(`[WarehouseMock] 已加载 ${mockFileList.length} 个 mock 文件`));
|
|
76
|
+
if (mockFileList.length > 0) {
|
|
77
|
+
console.log(chalk_1.default.gray(` → ${mockFileList.join(', ')}`));
|
|
78
|
+
}
|
|
79
|
+
if (this.options.proxy) {
|
|
80
|
+
console.log(chalk_1.default.cyan(`[WarehouseMock] 代理模式: 未匹配请求 → ${this.options.proxy.target}`));
|
|
81
|
+
}
|
|
82
|
+
// 注意:devServer 的配置需要在 vue.config.js 中手动配置
|
|
83
|
+
// 对于 Webpack 5 使用 setupMiddlewares
|
|
84
|
+
// 对于 Webpack 4 使用 before
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* 自动注入环境变量 VUE_APP_MOCK
|
|
88
|
+
*/
|
|
89
|
+
injectEnvironmentVariable(compiler) {
|
|
90
|
+
try {
|
|
91
|
+
// 使用 compiler 的 webpack 实例
|
|
92
|
+
const { webpack } = compiler;
|
|
93
|
+
if (webpack && webpack.DefinePlugin) {
|
|
94
|
+
const definePlugin = new webpack.DefinePlugin({
|
|
95
|
+
'process.env.VUE_APP_MOCK': JSON.stringify('true'),
|
|
96
|
+
});
|
|
97
|
+
if (!compiler.options.plugins) {
|
|
98
|
+
compiler.options.plugins = [];
|
|
99
|
+
}
|
|
100
|
+
compiler.options.plugins.push(definePlugin);
|
|
101
|
+
console.log(chalk_1.default.gray('[WarehouseMock] 已注入环境变量: VUE_APP_MOCK=true'));
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
// 备用方案:直接require webpack
|
|
105
|
+
const webpackModule = require('webpack');
|
|
106
|
+
const definePlugin = new webpackModule.DefinePlugin({
|
|
107
|
+
'process.env.VUE_APP_MOCK': JSON.stringify('true'),
|
|
108
|
+
});
|
|
109
|
+
if (!compiler.options.plugins) {
|
|
110
|
+
compiler.options.plugins = [];
|
|
111
|
+
}
|
|
112
|
+
compiler.options.plugins.push(definePlugin);
|
|
113
|
+
console.log(chalk_1.default.gray('[WarehouseMock] 已注入环境变量: VUE_APP_MOCK=true'));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
console.error(chalk_1.default.red(`[WarehouseMock] 注入环境变量失败: ${err}`));
|
|
118
|
+
console.log(chalk_1.default.yellow('[WarehouseMock] 请在 vue.config.js 中手动配置 DefinePlugin'));
|
|
119
|
+
}
|
|
64
120
|
}
|
|
65
121
|
/**
|
|
66
122
|
* 公共方法:设置中间件 (Webpack 5 / Vue CLI 5+)
|
|
@@ -131,7 +187,7 @@ class WarehouseMockPlugin {
|
|
|
131
187
|
}
|
|
132
188
|
}
|
|
133
189
|
handleRequest(req, res, next, mockPath) {
|
|
134
|
-
var _a, _b;
|
|
190
|
+
var _a, _b, _c;
|
|
135
191
|
const url = req.path || ((_a = req.url) === null || _a === void 0 ? void 0 : _a.split('?')[0]);
|
|
136
192
|
if (!url) {
|
|
137
193
|
return next();
|
|
@@ -144,6 +200,8 @@ class WarehouseMockPlugin {
|
|
|
144
200
|
res.end(JSON.stringify({
|
|
145
201
|
mockList: fileList,
|
|
146
202
|
localApiPrefix: this.getLocalApiPrefix(),
|
|
203
|
+
proxy: ((_b = this.options.proxy) === null || _b === void 0 ? void 0 : _b.target) || null,
|
|
204
|
+
enabled: this.isEnabled,
|
|
147
205
|
}));
|
|
148
206
|
return;
|
|
149
207
|
}
|
|
@@ -153,7 +211,7 @@ class WarehouseMockPlugin {
|
|
|
153
211
|
}
|
|
154
212
|
// 检查是否在允许的 API 前缀范围内
|
|
155
213
|
const { apiPrefixes } = this.options;
|
|
156
|
-
const isApiRequest = (
|
|
214
|
+
const isApiRequest = (_c = apiPrefixes === null || apiPrefixes === void 0 ? void 0 : apiPrefixes.some((prefix) => url.startsWith(prefix))) !== null && _c !== void 0 ? _c : true;
|
|
157
215
|
if (!isApiRequest) {
|
|
158
216
|
return next();
|
|
159
217
|
}
|
|
@@ -216,29 +274,142 @@ class WarehouseMockPlugin {
|
|
|
216
274
|
}
|
|
217
275
|
}
|
|
218
276
|
if (matched) {
|
|
219
|
-
console.log(chalk_1.default.green(`[WarehouseMock] 拦截: ${
|
|
220
|
-
|
|
221
|
-
|
|
277
|
+
console.log(chalk_1.default.green(`[WarehouseMock] ✓ 拦截: ${req.url || url}`));
|
|
278
|
+
console.log(chalk_1.default.gray(` → 返回: ${path_1.default.basename(filePath)}`));
|
|
279
|
+
// 模拟网络延迟
|
|
280
|
+
const respond = () => {
|
|
222
281
|
try {
|
|
223
|
-
const
|
|
282
|
+
const data = fs_1.default.readFileSync(filePath, 'utf-8');
|
|
283
|
+
try {
|
|
284
|
+
const jsonData = JSON.parse(data);
|
|
285
|
+
res.setHeader('Content-Type', 'application/json');
|
|
286
|
+
res.setHeader('X-Mock-By', 'WarehouseMock');
|
|
287
|
+
res.end(JSON.stringify(jsonData));
|
|
288
|
+
}
|
|
289
|
+
catch (jsonErr) {
|
|
290
|
+
console.warn(chalk_1.default.yellow(`[WarehouseMock] 无效的 JSON 文件: ${filePath}`));
|
|
291
|
+
res.setHeader('Content-Type', 'text/plain');
|
|
292
|
+
res.end(data);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
catch (e) {
|
|
296
|
+
console.error(chalk_1.default.red(`[WarehouseMock] 读取 Mock 文件失败: ${e}`));
|
|
297
|
+
res.statusCode = 500;
|
|
298
|
+
res.end('Mock Read Error');
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
if (this.options.delay > 0) {
|
|
302
|
+
setTimeout(respond, this.options.delay);
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
respond();
|
|
306
|
+
}
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
// 未匹配到 Mock 文件
|
|
310
|
+
if (this.options.proxy) {
|
|
311
|
+
// 如果配置了代理,转发到真实 API
|
|
312
|
+
console.log(chalk_1.default.gray(`[WarehouseMock] ⊳ 代理: ${req.url || url} → ${this.options.proxy.target}`));
|
|
313
|
+
this.proxyRequest(req, res, next);
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
// 未配置代理,直接放行
|
|
317
|
+
next();
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* 代理请求到真实 API(改进版)
|
|
322
|
+
*/
|
|
323
|
+
proxyRequest(req, res, next) {
|
|
324
|
+
if (!this.options.proxy) {
|
|
325
|
+
return next();
|
|
326
|
+
}
|
|
327
|
+
try {
|
|
328
|
+
const http = require('http');
|
|
329
|
+
const https = require('https');
|
|
330
|
+
const url = require('url');
|
|
331
|
+
// 构建完整的目标 URL
|
|
332
|
+
// 将 /mock-api?xxx 转换为 真实API/api?xxx
|
|
333
|
+
const targetUrl = this.options.proxy.target + req.url.replace('/mock-api', '/api');
|
|
334
|
+
const parsedUrl = url.parse(targetUrl);
|
|
335
|
+
const isHttps = parsedUrl.protocol === 'https:';
|
|
336
|
+
const lib = isHttps ? https : http;
|
|
337
|
+
// 清理请求头
|
|
338
|
+
const headers = Object.assign({}, req.headers);
|
|
339
|
+
delete headers.host;
|
|
340
|
+
headers.host = parsedUrl.hostname;
|
|
341
|
+
// 创建代理请求
|
|
342
|
+
const proxyReq = lib.request({
|
|
343
|
+
hostname: parsedUrl.hostname,
|
|
344
|
+
port: parsedUrl.port || (isHttps ? 443 : 80),
|
|
345
|
+
path: parsedUrl.path,
|
|
346
|
+
method: req.method,
|
|
347
|
+
headers: headers,
|
|
348
|
+
timeout: 30000, // 30秒超时
|
|
349
|
+
}, (proxyRes) => {
|
|
350
|
+
// 转发响应头
|
|
351
|
+
res.writeHead(proxyRes.statusCode, proxyRes.headers);
|
|
352
|
+
// 转发响应体
|
|
353
|
+
proxyRes.pipe(res);
|
|
354
|
+
});
|
|
355
|
+
// 错误处理
|
|
356
|
+
proxyReq.on('error', (err) => {
|
|
357
|
+
console.error(chalk_1.default.red(`[WarehouseMock] 代理请求失败: ${err.message}`));
|
|
358
|
+
console.error(chalk_1.default.red(` 目标地址: ${targetUrl}`));
|
|
359
|
+
// 返回友好的错误信息
|
|
360
|
+
if (!res.headersSent) {
|
|
361
|
+
res.statusCode = 502;
|
|
224
362
|
res.setHeader('Content-Type', 'application/json');
|
|
225
|
-
res.end(JSON.stringify(
|
|
363
|
+
res.end(JSON.stringify({
|
|
364
|
+
code: -1,
|
|
365
|
+
msg: `代理请求失败: ${err.message}`,
|
|
366
|
+
error: 'PROXY_ERROR'
|
|
367
|
+
}));
|
|
226
368
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
369
|
+
});
|
|
370
|
+
// 超时处理
|
|
371
|
+
proxyReq.on('timeout', () => {
|
|
372
|
+
proxyReq.destroy();
|
|
373
|
+
if (!res.headersSent) {
|
|
374
|
+
res.statusCode = 504;
|
|
375
|
+
res.setHeader('Content-Type', 'application/json');
|
|
376
|
+
res.end(JSON.stringify({
|
|
377
|
+
code: -1,
|
|
378
|
+
msg: '代理请求超时',
|
|
379
|
+
error: 'PROXY_TIMEOUT'
|
|
380
|
+
}));
|
|
231
381
|
}
|
|
232
|
-
|
|
382
|
+
});
|
|
383
|
+
// 转发请求体
|
|
384
|
+
if (req.method === 'POST' || req.method === 'PUT') {
|
|
385
|
+
// 处理 POST/PUT 请求的 body
|
|
386
|
+
let body = '';
|
|
387
|
+
req.on('data', (chunk) => {
|
|
388
|
+
body += chunk.toString();
|
|
389
|
+
});
|
|
390
|
+
req.on('end', () => {
|
|
391
|
+
if (body) {
|
|
392
|
+
proxyReq.write(body);
|
|
393
|
+
}
|
|
394
|
+
proxyReq.end();
|
|
395
|
+
});
|
|
233
396
|
}
|
|
234
|
-
|
|
235
|
-
|
|
397
|
+
else {
|
|
398
|
+
proxyReq.end();
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
catch (err) {
|
|
402
|
+
console.error(chalk_1.default.red(`[WarehouseMock] 代理配置错误: ${err}`));
|
|
403
|
+
if (!res.headersSent) {
|
|
236
404
|
res.statusCode = 500;
|
|
237
|
-
res.
|
|
238
|
-
|
|
405
|
+
res.setHeader('Content-Type', 'application/json');
|
|
406
|
+
res.end(JSON.stringify({
|
|
407
|
+
code: -1,
|
|
408
|
+
msg: `代理配置错误: ${err}`,
|
|
409
|
+
error: 'PROXY_CONFIG_ERROR'
|
|
410
|
+
}));
|
|
239
411
|
}
|
|
240
412
|
}
|
|
241
|
-
next();
|
|
242
413
|
}
|
|
243
414
|
}
|
|
244
415
|
module.exports = WarehouseMockPlugin;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "warehouse-mock",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "一个专为 Vue 2 项目设计的 Webpack 插件,支持 RPC 风格接口 mock,零业务代码侵入,实时更新",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -39,18 +39,18 @@
|
|
|
39
39
|
"chokidar": "^3.5.3"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"
|
|
43
|
-
"webpack": "^5.0.0",
|
|
44
|
-
"webpack-dev-server": "^4.0.0",
|
|
42
|
+
"@types/node": "^18.0.0",
|
|
45
43
|
"@types/webpack": "^5.0.0",
|
|
46
44
|
"@types/webpack-dev-server": "^4.0.0",
|
|
47
|
-
"
|
|
45
|
+
"typescript": "^5.0.0",
|
|
46
|
+
"webpack": "^5.0.0",
|
|
47
|
+
"webpack-dev-server": "^4.0.0"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
50
|
"webpack": "^4.0.0 || ^5.0.0"
|
|
51
51
|
},
|
|
52
52
|
"engines": {
|
|
53
53
|
"node": ">=12.0.0"
|
|
54
|
-
}
|
|
54
|
+
},
|
|
55
|
+
"gitHead": "dd7a56f686885eebc054fa738235ddf882ca02fa"
|
|
55
56
|
}
|
|
56
|
-
|