warehouse-mock 1.0.3 → 1.1.1

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/dist/index.d.ts CHANGED
@@ -10,11 +10,16 @@ interface MockPluginOptions {
10
10
  };
11
11
  injectEnv?: boolean;
12
12
  delay?: number;
13
+ admin?: {
14
+ enabled?: boolean;
15
+ port?: number;
16
+ };
13
17
  }
14
18
  declare class WarehouseMockPlugin {
15
19
  private options;
16
20
  private resolvedMockPath;
17
21
  private isEnabled;
22
+ private adminServer;
18
23
  constructor(options?: MockPluginOptions);
19
24
  /**
20
25
  * 实时扫描 mock 目录,获取所有 mock 文件名列表
@@ -25,6 +30,10 @@ declare class WarehouseMockPlugin {
25
30
  */
26
31
  getLocalApiPrefix(): string;
27
32
  apply(compiler: Compiler): void;
33
+ /**
34
+ * 启动管理后台服务
35
+ */
36
+ private startAdminServer;
28
37
  /**
29
38
  * 自动注入环境变量 VUE_APP_MOCK
30
39
  */
@@ -40,6 +49,7 @@ declare class WarehouseMockPlugin {
40
49
  private getResolvedMockPath;
41
50
  private ensureMockDirectory;
42
51
  private createDemoFile;
52
+ private readInterfaceConfig;
43
53
  private handleRequest;
44
54
  /**
45
55
  * 代理请求到真实 API(改进版)
package/dist/index.js CHANGED
@@ -7,8 +7,10 @@ const path_1 = __importDefault(require("path"));
7
7
  const chalk_1 = __importDefault(require("chalk"));
8
8
  class WarehouseMockPlugin {
9
9
  constructor(options = {}) {
10
+ var _a, _b;
10
11
  this.resolvedMockPath = '';
11
12
  this.isEnabled = true;
13
+ this.adminServer = null;
12
14
  // 默认配置
13
15
  this.options = {
14
16
  mockPath: options.mockPath || 'warehouseMock',
@@ -18,6 +20,10 @@ class WarehouseMockPlugin {
18
20
  proxy: options.proxy || undefined,
19
21
  injectEnv: options.injectEnv !== undefined ? options.injectEnv : true,
20
22
  delay: options.delay || 0,
23
+ admin: {
24
+ enabled: ((_a = options.admin) === null || _a === void 0 ? void 0 : _a.enabled) !== undefined ? options.admin.enabled : true,
25
+ port: ((_b = options.admin) === null || _b === void 0 ? void 0 : _b.port) || 3100,
26
+ },
21
27
  };
22
28
  // 判断是否启用(支持环境变量控制)
23
29
  if (process.env.MOCK === 'false' || process.env.VUE_APP_MOCK === 'false') {
@@ -79,10 +85,55 @@ class WarehouseMockPlugin {
79
85
  if (this.options.proxy) {
80
86
  console.log(chalk_1.default.cyan(`[WarehouseMock] 代理模式: 未匹配请求 → ${this.options.proxy.target}`));
81
87
  }
88
+ // 启动管理后台
89
+ if (this.options.admin && this.options.admin.enabled) {
90
+ this.startAdminServer();
91
+ }
82
92
  // 注意:devServer 的配置需要在 vue.config.js 中手动配置
83
93
  // 对于 Webpack 5 使用 setupMiddlewares
84
94
  // 对于 Webpack 4 使用 before
85
95
  }
96
+ /**
97
+ * 启动管理后台服务
98
+ */
99
+ startAdminServer() {
100
+ var _a;
101
+ try {
102
+ // 动态导入管理后台服务
103
+ let createAdminServer;
104
+ try {
105
+ // 尝试导入已安装的包
106
+ createAdminServer = require('warehouse-mock-admin').createAdminServer;
107
+ }
108
+ catch (e) {
109
+ // 如果找不到,尝试相对路径(开发环境)
110
+ const adminPath = require('path').resolve(__dirname, '../../mock-admin/dist/server/index.js');
111
+ createAdminServer = require(adminPath).createAdminServer;
112
+ }
113
+ if (!createAdminServer) {
114
+ throw new Error('无法加载管理后台模块');
115
+ }
116
+ const adminResult = createAdminServer({
117
+ mockPath: this.resolvedMockPath,
118
+ port: ((_a = this.options.admin) === null || _a === void 0 ? void 0 : _a.port) || 3100,
119
+ });
120
+ this.adminServer = adminResult;
121
+ console.log(chalk_1.default.green(`\n╭────────────────────────────────────────────────────╮`));
122
+ console.log(chalk_1.default.green(`│ 🎨 Mock 数据管理后台已启动 │`));
123
+ console.log(chalk_1.default.green(`│ │`));
124
+ console.log(chalk_1.default.green(`│ ➜ 访问地址: ${chalk_1.default.cyan(adminResult.url).padEnd(31)} │`));
125
+ console.log(chalk_1.default.green(`│ │`));
126
+ console.log(chalk_1.default.green(`│ 在浏览器中打开上面的地址来管理 Mock 数据 │`));
127
+ console.log(chalk_1.default.green(`╰────────────────────────────────────────────────────╯\n`));
128
+ }
129
+ catch (err) {
130
+ console.log(chalk_1.default.yellow('[WarehouseMock] 管理后台包未安装或加载失败,跳过启动'));
131
+ console.log(chalk_1.default.gray(' 提示:运行 npm install warehouse-mock-admin 来安装管理后台'));
132
+ if (err.message && !err.message.includes('Cannot find module')) {
133
+ console.log(chalk_1.default.gray(` 错误详情: ${err.message}`));
134
+ }
135
+ }
136
+ }
86
137
  /**
87
138
  * 自动注入环境变量 VUE_APP_MOCK
88
139
  */
@@ -186,6 +237,20 @@ class WarehouseMockPlugin {
186
237
  console.error(chalk_1.default.yellow(`[WarehouseMock] 创建示例文件失败: ${e}`));
187
238
  }
188
239
  }
240
+ // 读取接口配置
241
+ readInterfaceConfig(apiName, mockPath) {
242
+ const configPath = path_1.default.join(mockPath, apiName, '.config.json');
243
+ if (fs_1.default.existsSync(configPath)) {
244
+ try {
245
+ const content = fs_1.default.readFileSync(configPath, 'utf-8');
246
+ return JSON.parse(content);
247
+ }
248
+ catch (e) {
249
+ return null;
250
+ }
251
+ }
252
+ return null;
253
+ }
189
254
  handleRequest(req, res, next, mockPath) {
190
255
  var _a, _b, _c;
191
256
  const url = req.path || ((_a = req.url) === null || _a === void 0 ? void 0 : _a.split('?')[0]);
@@ -218,31 +283,125 @@ class WarehouseMockPlugin {
218
283
  let filePath = '';
219
284
  let matched = false;
220
285
  let matchedName = '';
286
+ let interfaceConfig = null;
221
287
  // 1. 优先尝试 Query String 匹配 (RPC 风格接口,如 /api?user.taurus.pointInfo)
222
288
  if (req.url && req.url.includes('?')) {
223
289
  const queryPart = req.url.split('?')[1];
224
290
  if (queryPart) {
225
291
  const params = new URLSearchParams(queryPart);
226
292
  for (const key of params.keys()) {
227
- // 尝试匹配 key.json (例如 user.taurus.pointInfo.json)
228
- const queryFilePath = path_1.default.join(mockPath, `${key}.json`);
229
- if (fs_1.default.existsSync(queryFilePath) && fs_1.default.statSync(queryFilePath).isFile()) {
230
- filePath = queryFilePath;
231
- matchedName = key;
232
- matched = true;
233
- break;
293
+ // 先尝试目录模式(优先,支持多场景)
294
+ const dirPath = path_1.default.join(mockPath, key);
295
+ if (fs_1.default.existsSync(dirPath) && fs_1.default.statSync(dirPath).isDirectory()) {
296
+ // 读取接口配置(新格式)
297
+ const config = this.readInterfaceConfig(key, mockPath);
298
+ if (config) {
299
+ // 新格式:检查是否启用
300
+ if (config.enabled === false) {
301
+ console.log(chalk_1.default.yellow(`[WarehouseMock] ✗ 接口已禁用: ${key}`));
302
+ break; // 接口被禁用,不拦截
303
+ }
304
+ // 根据 activeScene 读取场景文件
305
+ const activeScene = config.activeScene || 'default';
306
+ const sceneFilePath = path_1.default.join(dirPath, `${activeScene}.json`);
307
+ if (fs_1.default.existsSync(sceneFilePath)) {
308
+ filePath = sceneFilePath;
309
+ matchedName = key;
310
+ matched = true;
311
+ interfaceConfig = config;
312
+ break;
313
+ }
314
+ }
315
+ else {
316
+ // 旧格式:查找 mock: true 的文件(向后兼容)
317
+ const files = fs_1.default.readdirSync(dirPath).filter(f => f.endsWith('.json') && f !== '.config.json');
318
+ for (const file of files) {
319
+ const fullPath = path_1.default.join(dirPath, file);
320
+ try {
321
+ const content = fs_1.default.readFileSync(fullPath, 'utf-8');
322
+ const jsonData = JSON.parse(content);
323
+ if (jsonData.mock === true) {
324
+ filePath = fullPath;
325
+ matchedName = key;
326
+ matched = true;
327
+ break;
328
+ }
329
+ }
330
+ catch (e) {
331
+ // 文件解析失败,跳过
332
+ }
333
+ }
334
+ if (matched)
335
+ break;
336
+ }
234
337
  }
235
- // 也尝试匹配 value (例如 method=user.taurus.pointInfo)
236
- const value = params.get(key);
237
- if (value) {
238
- const valueFilePath = path_1.default.join(mockPath, `${value}.json`);
239
- if (fs_1.default.existsSync(valueFilePath) && fs_1.default.statSync(valueFilePath).isFile()) {
240
- filePath = valueFilePath;
241
- matchedName = value;
338
+ // 再尝试单文件模式(向后兼容)
339
+ if (!matched) {
340
+ const queryFilePath = path_1.default.join(mockPath, `${key}.json`);
341
+ if (fs_1.default.existsSync(queryFilePath) && fs_1.default.statSync(queryFilePath).isFile()) {
342
+ filePath = queryFilePath;
343
+ matchedName = key;
242
344
  matched = true;
243
345
  break;
244
346
  }
245
347
  }
348
+ // 也尝试匹配 value (例如 method=user.taurus.pointInfo)
349
+ const value = params.get(key);
350
+ if (value && !matched) {
351
+ const valueDirPath = path_1.default.join(mockPath, value);
352
+ if (fs_1.default.existsSync(valueDirPath) && fs_1.default.statSync(valueDirPath).isDirectory()) {
353
+ // 读取接口配置(新格式)
354
+ const config = this.readInterfaceConfig(value, mockPath);
355
+ if (config) {
356
+ // 新格式:检查是否启用
357
+ if (config.enabled === false) {
358
+ console.log(chalk_1.default.yellow(`[WarehouseMock] ✗ 接口已禁用: ${value}`));
359
+ break;
360
+ }
361
+ // 根据 activeScene 读取场景文件
362
+ const activeScene = config.activeScene || 'default';
363
+ const sceneFilePath = path_1.default.join(valueDirPath, `${activeScene}.json`);
364
+ if (fs_1.default.existsSync(sceneFilePath)) {
365
+ filePath = sceneFilePath;
366
+ matchedName = value;
367
+ matched = true;
368
+ interfaceConfig = config;
369
+ break;
370
+ }
371
+ }
372
+ else {
373
+ // 旧格式:查找 mock: true 的文件(向后兼容)
374
+ const files = fs_1.default.readdirSync(valueDirPath).filter(f => f.endsWith('.json') && f !== '.config.json');
375
+ for (const file of files) {
376
+ const fullPath = path_1.default.join(valueDirPath, file);
377
+ try {
378
+ const content = fs_1.default.readFileSync(fullPath, 'utf-8');
379
+ const jsonData = JSON.parse(content);
380
+ if (jsonData.mock === true) {
381
+ filePath = fullPath;
382
+ matchedName = value;
383
+ matched = true;
384
+ break;
385
+ }
386
+ }
387
+ catch (e) {
388
+ // 跳过
389
+ }
390
+ }
391
+ if (matched)
392
+ break;
393
+ }
394
+ }
395
+ if (!matched) {
396
+ const valueFilePath = path_1.default.join(mockPath, `${value}.json`);
397
+ if (fs_1.default.existsSync(valueFilePath) && fs_1.default.statSync(valueFilePath).isFile()) {
398
+ filePath = valueFilePath;
399
+ matchedName = value;
400
+ matched = true;
401
+ break;
402
+ }
403
+ }
404
+ }
246
405
  }
247
406
  }
248
407
  }
@@ -275,21 +434,38 @@ class WarehouseMockPlugin {
275
434
  }
276
435
  if (matched) {
277
436
  console.log(chalk_1.default.green(`[WarehouseMock] ✓ 拦截: ${req.url || url}`));
278
- console.log(chalk_1.default.gray(` → 返回: ${path_1.default.basename(filePath)}`));
437
+ // 获取延时配置(优先使用接口级别的 delay)
438
+ const delayTime = (interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.delay) !== undefined
439
+ ? interfaceConfig.delay
440
+ : this.options.delay || 0;
441
+ // 获取场景名称
442
+ const sceneName = (interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.activeScene) || path_1.default.basename(filePath, '.json');
443
+ console.log(chalk_1.default.gray(` → 场景: ${sceneName}`));
444
+ if (delayTime > 0) {
445
+ console.log(chalk_1.default.gray(` → 延迟: ${delayTime}ms`));
446
+ }
279
447
  // 模拟网络延迟
280
448
  const respond = () => {
281
449
  try {
282
- const data = fs_1.default.readFileSync(filePath, 'utf-8');
450
+ const fileContent = fs_1.default.readFileSync(filePath, 'utf-8');
283
451
  try {
284
- const jsonData = JSON.parse(data);
452
+ const jsonData = JSON.parse(fileContent);
453
+ // 新格式:纯净的场景数据(不包含 mock、scene、delay 等元数据)
454
+ // 旧格式:包含 mock、scene、delay、data 字段
455
+ let responseData = jsonData;
456
+ // 兼容旧格式
457
+ if (jsonData.mock !== undefined && jsonData.data !== undefined) {
458
+ responseData = jsonData.data;
459
+ }
285
460
  res.setHeader('Content-Type', 'application/json');
286
461
  res.setHeader('X-Mock-By', 'WarehouseMock');
287
- res.end(JSON.stringify(jsonData));
462
+ res.setHeader('X-Mock-Scene', sceneName);
463
+ res.end(JSON.stringify(responseData));
288
464
  }
289
465
  catch (jsonErr) {
290
466
  console.warn(chalk_1.default.yellow(`[WarehouseMock] 无效的 JSON 文件: ${filePath}`));
291
467
  res.setHeader('Content-Type', 'text/plain');
292
- res.end(data);
468
+ res.end(fileContent);
293
469
  }
294
470
  }
295
471
  catch (e) {
@@ -298,8 +474,9 @@ class WarehouseMockPlugin {
298
474
  res.end('Mock Read Error');
299
475
  }
300
476
  };
301
- if (this.options.delay > 0) {
302
- setTimeout(respond, this.options.delay);
477
+ // 应用延迟
478
+ if (delayTime > 0) {
479
+ setTimeout(respond, delayTime);
303
480
  }
304
481
  else {
305
482
  respond();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "warehouse-mock",
3
- "version": "1.0.3",
3
+ "version": "1.1.1",
4
4
  "description": "一个专为 Vue 2 项目设计的 Webpack 插件,支持 RPC 风格接口 mock,零业务代码侵入,实时更新",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -36,7 +36,12 @@
36
36
  "homepage": "https://github.com/CodeMomentYY/warehouse-mock#readme",
37
37
  "dependencies": {
38
38
  "chalk": "^4.1.2",
39
- "chokidar": "^3.5.3"
39
+ "chokidar": "^3.5.3",
40
+ "i": "^0.3.7",
41
+ "npm": "^11.7.0"
42
+ },
43
+ "optionalDependencies": {
44
+ "warehouse-mock-admin": "^1.0.0"
40
45
  },
41
46
  "devDependencies": {
42
47
  "@types/node": "^18.0.0",
@@ -51,6 +56,5 @@
51
56
  },
52
57
  "engines": {
53
58
  "node": ">=12.0.0"
54
- },
55
- "gitHead": "dd7a56f686885eebc054fa738235ddf882ca02fa"
59
+ }
56
60
  }