mock-service-cli 3.5.0 → 3.6.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/README.md +41 -27
- package/lib/asyncTaskQueue.js +2 -2
- package/lib/file-explorer.html +165 -11
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
## 📦 安装
|
|
32
32
|
|
|
33
33
|
### 全局安装
|
|
34
|
+
|
|
34
35
|
```bash
|
|
35
36
|
npm install --global mock-service-cli
|
|
36
37
|
# 或
|
|
@@ -38,42 +39,45 @@ brew install mock-service-cli
|
|
|
38
39
|
```
|
|
39
40
|
|
|
40
41
|
### 局部安装
|
|
42
|
+
|
|
41
43
|
```bash
|
|
42
44
|
npm install mock-service-cli --save-dev
|
|
43
45
|
```
|
|
44
46
|
|
|
45
47
|
### 直接运行
|
|
48
|
+
|
|
46
49
|
```bash
|
|
47
50
|
npx mock-service-cli [options] [path]
|
|
48
51
|
```
|
|
49
52
|
|
|
50
53
|
## 🛠️ 命令行选项
|
|
51
54
|
|
|
52
|
-
| 选项
|
|
53
|
-
|
|
|
54
|
-
| `-h` 或 `--help`
|
|
55
|
-
| `-p` 或 `--port`
|
|
56
|
-
| `-d`
|
|
57
|
-
| `-f`
|
|
58
|
-
| `-s` 或 `--silent`
|
|
59
|
-
| `-v` 或 `--version`
|
|
60
|
-
| `-S` 或 `--socket-server`
|
|
61
|
-
| `-a` 或 `--api-stat`
|
|
62
|
-
| `-t` 或 `--track`
|
|
63
|
-
| `-o` 或 `--cors-origin`
|
|
64
|
-
| `-O` 或 `--proxy-options`
|
|
65
|
-
| `-H` 或 `--cors-headers`
|
|
66
|
-
| `-A` 或 `--append-headers
|
|
67
|
-
| `-D` 或 `--web-dir`
|
|
68
|
-
| `-P` 或 `--web-port` | Web 服务器端口
|
|
69
|
-
| `-b` 或 `--web-baseurl` | 指定 SPA Web 服务器的公共路径
|
|
70
|
-
| `-R` 或 `--static-server` | 启用静态服务器,指定静态资源目录
|
|
71
|
-
| `-w` 或 `--open` | 自动打开 API 概览页面
|
|
72
|
-
| `-e` 或 `--explorer` | 启用文件浏览器服务器,指定要浏览的目录
|
|
55
|
+
| 选项 | 描述 | 默认值 |
|
|
56
|
+
| -------------------------- | ----------------------------------------------------------- | ------ |
|
|
57
|
+
| `-h` 或 `--help` | 显示帮助信息 | - |
|
|
58
|
+
| `-p` 或 `--port` | Mock 服务器端口 | 8090 |
|
|
59
|
+
| `-d` | 指定 mock 目录 | ./mock |
|
|
60
|
+
| `-f` | 指定单个 mock 文件 | - |
|
|
61
|
+
| `-s` 或 `--silent` | 抑制日志输出 | - |
|
|
62
|
+
| `-v` 或 `--version` | 显示版本信息 | - |
|
|
63
|
+
| `-S` 或 `--socket-server` | 启动 socket 服务器,用于保存 API 响应数据 | false |
|
|
64
|
+
| `-a` 或 `--api-stat` | 打印 API URL 和文件路径信息 | false |
|
|
65
|
+
| `-t` 或 `--track` | 记录操作信息到文件 | false |
|
|
66
|
+
| `-o` 或 `--cors-origin` | 配置 CORS 允许的源,多个源用逗号分隔 | \* |
|
|
67
|
+
| `-O` 或 `--proxy-options` | 配置 HTTP 请求代理选项,支持 js 文件、json 文件或命令行参数 | - |
|
|
68
|
+
| `-H` 或 `--cors-headers` | 配置 CORS 头信息 | \* |
|
|
69
|
+
| `-A` 或 `--append-headers` | 添加响应头信息,多个头用逗号分隔 | - |
|
|
70
|
+
| `-D` 或 `--web-dir` | 启用 Web 服务器,指定 web 目录(SPA 应用) | - |
|
|
71
|
+
| `-P` 或 `--web-port` | Web 服务器端口 | 9090 |
|
|
72
|
+
| `-b` 或 `--web-baseurl` | 指定 SPA Web 服务器的公共路径 | - |
|
|
73
|
+
| `-R` 或 `--static-server` | 启用静态服务器,指定静态资源目录 | - |
|
|
74
|
+
| `-w` 或 `--open` | 自动打开 API 概览页面 | false |
|
|
75
|
+
| `-e` 或 `--explorer` | 启用文件浏览器服务器,指定要浏览的目录 | ./ |
|
|
73
76
|
|
|
74
77
|
## 📖 使用示例
|
|
75
78
|
|
|
76
79
|
### Mock 服务器
|
|
80
|
+
|
|
77
81
|
```bash
|
|
78
82
|
# 使用默认配置启动 Mock 服务器
|
|
79
83
|
mock-service-cli
|
|
@@ -89,12 +93,14 @@ mock-service-cli -f ./mock.js
|
|
|
89
93
|
```
|
|
90
94
|
|
|
91
95
|
### 静态服务器
|
|
96
|
+
|
|
92
97
|
```bash
|
|
93
98
|
# 启动静态服务器
|
|
94
99
|
mock-service-cli -R ./public
|
|
95
100
|
```
|
|
96
101
|
|
|
97
102
|
### Web 服务器 (SPA)
|
|
103
|
+
|
|
98
104
|
```bash
|
|
99
105
|
# 启动 SPA 服务器
|
|
100
106
|
mock-service-cli -D ./dist
|
|
@@ -104,6 +110,7 @@ mock-service-cli -D ./dist -b /app -P 9090
|
|
|
104
110
|
```
|
|
105
111
|
|
|
106
112
|
### HTTP 代理
|
|
113
|
+
|
|
107
114
|
```bash
|
|
108
115
|
# 启动带代理的 Web 服务器
|
|
109
116
|
mock-service-cli -D ./ -O '/api|http://localhost:3000'
|
|
@@ -113,6 +120,7 @@ mock-service-cli -D ./ -O ./proxy.js
|
|
|
113
120
|
```
|
|
114
121
|
|
|
115
122
|
### 文件浏览器
|
|
123
|
+
|
|
116
124
|
```bash
|
|
117
125
|
# 浏览当前目录
|
|
118
126
|
mock-service-cli -e ./
|
|
@@ -153,11 +161,13 @@ const mockjs = require('mockjs');
|
|
|
153
161
|
|
|
154
162
|
module.exports = {
|
|
155
163
|
'GET /api/tags': mockjs.mock({
|
|
156
|
-
'list|100': [
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
164
|
+
'list|100': [
|
|
165
|
+
{
|
|
166
|
+
'NO|+1': 1,
|
|
167
|
+
city: '@city',
|
|
168
|
+
'value|1-100': 50
|
|
169
|
+
}
|
|
170
|
+
]
|
|
161
171
|
})
|
|
162
172
|
};
|
|
163
173
|
```
|
|
@@ -167,6 +177,7 @@ module.exports = {
|
|
|
167
177
|
现代化的 Web 界面,用于展示和测试所有已注册的 Mock API 接口。
|
|
168
178
|
|
|
169
179
|
### 功能特性
|
|
180
|
+
|
|
170
181
|
- 📁 按目录分类展示 API
|
|
171
182
|
- 🔍 支持搜索接口
|
|
172
183
|
- 🎯 每种 HTTP 方法使用不同颜色标签
|
|
@@ -174,6 +185,7 @@ module.exports = {
|
|
|
174
185
|
- 📋 响应数据格式化展示
|
|
175
186
|
|
|
176
187
|
### 访问方式
|
|
188
|
+
|
|
177
189
|
- **自动打开**:使用 `--open` 参数
|
|
178
190
|
- **手动访问**:`http://localhost:8090/__api-overview`
|
|
179
191
|
|
|
@@ -182,6 +194,7 @@ module.exports = {
|
|
|
182
194
|
现代化的文件浏览界面,支持目录导航、文件预览和管理。
|
|
183
195
|
|
|
184
196
|
### 功能特性
|
|
197
|
+
|
|
185
198
|
- 📁 **目录导航**:面包屑导航,支持快速跳转
|
|
186
199
|
- 👁️ **文件预览**:
|
|
187
200
|
- 图片文件:直接预览(JPG、PNG、GIF、WebP 等)
|
|
@@ -202,6 +215,7 @@ module.exports = {
|
|
|
202
215
|
- `Backspace`:返回上级
|
|
203
216
|
|
|
204
217
|
### 界面预览
|
|
218
|
+
|
|
205
219
|
- 渐变紫色现代化设计
|
|
206
220
|
- 响应式布局
|
|
207
221
|
- 流畅的交互体验
|
|
@@ -225,7 +239,7 @@ socket.emit('save-data', { url, method, data, dir });
|
|
|
225
239
|
|
|
226
240
|
// 获取目录统计信息
|
|
227
241
|
socket.emit('mock-dir-stat', dirPath);
|
|
228
|
-
socket.on('mock-dir-stat',
|
|
242
|
+
socket.on('mock-dir-stat', data => {
|
|
229
243
|
console.log('统计信息:', data);
|
|
230
244
|
});
|
|
231
245
|
```
|
package/lib/asyncTaskQueue.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @description: 异步任务队列
|
|
3
3
|
* @Date: 2022-01-04 10:46:20
|
|
4
4
|
* @LastEditors: chendq
|
|
5
|
-
* @LastEditTime:
|
|
5
|
+
* @LastEditTime: 2026-03-15 17:23:52
|
|
6
6
|
* @Author: chendq
|
|
7
7
|
*/
|
|
8
8
|
// const { getDataType } = require('./utils');
|
|
@@ -61,7 +61,7 @@ class AsyncTaskQueue {
|
|
|
61
61
|
*/
|
|
62
62
|
retry() {
|
|
63
63
|
this.isStop = false;
|
|
64
|
-
run();
|
|
64
|
+
this.run();
|
|
65
65
|
}
|
|
66
66
|
/**
|
|
67
67
|
* @description: 继续执行下一个异步任务
|
package/lib/file-explorer.html
CHANGED
|
@@ -434,16 +434,55 @@
|
|
|
434
434
|
}
|
|
435
435
|
|
|
436
436
|
.text-preview {
|
|
437
|
-
background: #
|
|
438
|
-
|
|
437
|
+
background: #1e1e1e;
|
|
438
|
+
color: #d4d4d4;
|
|
439
|
+
padding: 20px;
|
|
439
440
|
border-radius: 8px;
|
|
440
|
-
|
|
441
|
+
max-height: 70vh;
|
|
442
|
+
overflow: auto;
|
|
443
|
+
font-family: 'Fira Code', 'Monaco', 'Consolas', monospace;
|
|
441
444
|
font-size: 13px;
|
|
442
445
|
line-height: 1.6;
|
|
443
446
|
white-space: pre-wrap;
|
|
444
|
-
word-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
+
word-wrap: break-word;
|
|
448
|
+
margin: 0;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
.preview-container {
|
|
452
|
+
display: flex;
|
|
453
|
+
flex-direction: column;
|
|
454
|
+
height: 100%;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
.preview-header {
|
|
458
|
+
display: flex;
|
|
459
|
+
justify-content: flex-end;
|
|
460
|
+
margin-bottom: 12px;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
.preview-header .copy-btn {
|
|
464
|
+
padding: 8px 16px;
|
|
465
|
+
background: linear-gradient(135deg, #8b5cf6, #6366f1);
|
|
466
|
+
color: white;
|
|
467
|
+
border: none;
|
|
468
|
+
border-radius: 6px;
|
|
469
|
+
font-size: 14px;
|
|
470
|
+
cursor: pointer;
|
|
471
|
+
transition: all 0.2s;
|
|
472
|
+
display: flex;
|
|
473
|
+
align-items: center;
|
|
474
|
+
gap: 6px;
|
|
475
|
+
box-shadow: 0 2px 8px rgba(139, 92, 246, 0.3);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
.preview-header .copy-btn:hover {
|
|
479
|
+
transform: translateY(-1px);
|
|
480
|
+
box-shadow: 0 4px 12px rgba(139, 92, 246, 0.4);
|
|
481
|
+
background: linear-gradient(135deg, #7c3aed, #4f46e5);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
.preview-header .copy-btn:active {
|
|
485
|
+
transform: translateY(0);
|
|
447
486
|
}
|
|
448
487
|
|
|
449
488
|
.empty-state {
|
|
@@ -548,39 +587,69 @@
|
|
|
548
587
|
let sortBy = 'name';
|
|
549
588
|
let sortAscending = true;
|
|
550
589
|
let showHiddenFiles = true;
|
|
590
|
+
let currentPreviewContent = '';
|
|
551
591
|
|
|
552
592
|
document.addEventListener('DOMContentLoaded', () => {
|
|
553
593
|
loadDirectory('/');
|
|
554
594
|
});
|
|
555
595
|
|
|
556
596
|
async function loadDirectory(path) {
|
|
597
|
+
// console.log('Loading directory:', path);
|
|
557
598
|
const fileGrid = document.getElementById('fileGrid');
|
|
599
|
+
|
|
600
|
+
// 重置所有状态
|
|
601
|
+
currentPath = '/';
|
|
602
|
+
allFiles = [];
|
|
603
|
+
currentFiles = [];
|
|
604
|
+
|
|
605
|
+
// 显示加载状态
|
|
558
606
|
fileGrid.innerHTML = '<div class="loading"><div class="spinner"></div><p>加载中...</p></div>';
|
|
559
607
|
|
|
560
608
|
try {
|
|
561
609
|
const response = await fetch(`/__api/list?path=${encodeURIComponent(path)}`);
|
|
610
|
+
// console.log('Response status:', response.status);
|
|
611
|
+
|
|
562
612
|
const data = await response.json();
|
|
613
|
+
// console.log('API response data:', data);
|
|
563
614
|
|
|
564
615
|
if (data.error) {
|
|
565
616
|
throw new Error(data.error);
|
|
566
617
|
}
|
|
567
618
|
|
|
568
619
|
currentPath = data.currentPath;
|
|
569
|
-
allFiles = data.files;
|
|
620
|
+
allFiles = data.files || [];
|
|
621
|
+
|
|
622
|
+
// console.log('allFiles set to:', allFiles);
|
|
570
623
|
|
|
571
624
|
renderBreadcrumb(data.currentPath, data.parentPath);
|
|
572
|
-
|
|
625
|
+
|
|
626
|
+
// 确保在调用 processAndRenderFiles 之前 allFiles 已经设置好
|
|
627
|
+
setTimeout(() => {
|
|
628
|
+
processAndRenderFiles();
|
|
629
|
+
}, 10);
|
|
630
|
+
|
|
573
631
|
updateBackButton(data.parentPath);
|
|
574
632
|
|
|
575
633
|
// 清空搜索框
|
|
576
|
-
document.getElementById('searchInput')
|
|
634
|
+
if (document.getElementById('searchInput')) {
|
|
635
|
+
document.getElementById('searchInput').value = '';
|
|
636
|
+
}
|
|
577
637
|
searchQuery = '';
|
|
578
638
|
} catch (error) {
|
|
639
|
+
console.error('Error loading directory:', error);
|
|
579
640
|
fileGrid.innerHTML = `<div class="empty-state"><p>错误: ${error.message}</p></div>`;
|
|
580
641
|
}
|
|
581
642
|
}
|
|
582
643
|
|
|
583
644
|
function processAndRenderFiles() {
|
|
645
|
+
// console.log('processAndRenderFiles called, allFiles:', allFiles);
|
|
646
|
+
|
|
647
|
+
if (!allFiles) {
|
|
648
|
+
console.error('allFiles is null or undefined');
|
|
649
|
+
renderFiles([]);
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
|
|
584
653
|
let files = [...allFiles];
|
|
585
654
|
|
|
586
655
|
// 隐藏文件过滤
|
|
@@ -619,6 +688,7 @@
|
|
|
619
688
|
});
|
|
620
689
|
|
|
621
690
|
currentFiles = files;
|
|
691
|
+
// console.log('Rendering', files.length, 'files');
|
|
622
692
|
renderFiles(files);
|
|
623
693
|
}
|
|
624
694
|
|
|
@@ -718,7 +788,14 @@
|
|
|
718
788
|
function renderFiles(files) {
|
|
719
789
|
const fileGrid = document.getElementById('fileGrid');
|
|
720
790
|
|
|
721
|
-
|
|
791
|
+
// 确保视图类名正确
|
|
792
|
+
if (currentView === 'list') {
|
|
793
|
+
fileGrid.classList.add('list-view');
|
|
794
|
+
} else {
|
|
795
|
+
fileGrid.classList.remove('list-view');
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
if (!files || files.length === 0) {
|
|
722
799
|
fileGrid.innerHTML = '<div class="empty-state"><p>该目录为空</p></div>';
|
|
723
800
|
return;
|
|
724
801
|
}
|
|
@@ -777,6 +854,7 @@
|
|
|
777
854
|
}
|
|
778
855
|
});
|
|
779
856
|
|
|
857
|
+
// 强制更新 DOM
|
|
780
858
|
fileGrid.innerHTML = html;
|
|
781
859
|
}
|
|
782
860
|
|
|
@@ -788,6 +866,74 @@
|
|
|
788
866
|
}
|
|
789
867
|
}
|
|
790
868
|
|
|
869
|
+
async function copyText(text) {
|
|
870
|
+
try {
|
|
871
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
872
|
+
await navigator.clipboard.writeText(text);
|
|
873
|
+
showToast('已复制到剪贴板');
|
|
874
|
+
} else {
|
|
875
|
+
const textarea = document.createElement('textarea');
|
|
876
|
+
textarea.value = text;
|
|
877
|
+
textarea.style.position = 'fixed';
|
|
878
|
+
textarea.style.top = '-9999px';
|
|
879
|
+
document.body.appendChild(textarea);
|
|
880
|
+
textarea.select();
|
|
881
|
+
document.execCommand('copy');
|
|
882
|
+
document.body.removeChild(textarea);
|
|
883
|
+
showToast('已复制到剪贴板');
|
|
884
|
+
}
|
|
885
|
+
} catch (error) {
|
|
886
|
+
console.error('复制失败:', error);
|
|
887
|
+
showToast('复制失败');
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
function copyPreviewContent() {
|
|
892
|
+
if (currentPreviewContent !== '') {
|
|
893
|
+
copyText(currentPreviewContent);
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
function showToast(message) {
|
|
898
|
+
const toast = document.createElement('div');
|
|
899
|
+
toast.className = 'toast';
|
|
900
|
+
toast.textContent = message;
|
|
901
|
+
toast.style.cssText = `
|
|
902
|
+
position: fixed;
|
|
903
|
+
top: 20px;
|
|
904
|
+
right: 20px;
|
|
905
|
+
background: rgba(139, 92, 246, 0.95);
|
|
906
|
+
color: white;
|
|
907
|
+
padding: 12px 24px;
|
|
908
|
+
border-radius: 8px;
|
|
909
|
+
font-size: 14px;
|
|
910
|
+
z-index: 10000;
|
|
911
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
|
912
|
+
animation: fadeInOut 2s ease-in-out;
|
|
913
|
+
`;
|
|
914
|
+
document.body.appendChild(toast);
|
|
915
|
+
|
|
916
|
+
const style = document.createElement('style');
|
|
917
|
+
style.textContent = `
|
|
918
|
+
@keyframes fadeInOut {
|
|
919
|
+
0% { opacity: 0; transform: translateY(-20px); }
|
|
920
|
+
15% { opacity: 1; transform: translateY(0); }
|
|
921
|
+
85% { opacity: 1; transform: translateY(0); }
|
|
922
|
+
100% { opacity: 0; transform: translateY(-20px); }
|
|
923
|
+
}
|
|
924
|
+
`;
|
|
925
|
+
document.head.appendChild(style);
|
|
926
|
+
|
|
927
|
+
setTimeout(() => {
|
|
928
|
+
if (toast.parentNode) {
|
|
929
|
+
document.body.removeChild(toast);
|
|
930
|
+
}
|
|
931
|
+
if (style.parentNode) {
|
|
932
|
+
document.head.removeChild(style);
|
|
933
|
+
}
|
|
934
|
+
}, 2000);
|
|
935
|
+
}
|
|
936
|
+
|
|
791
937
|
async function previewFile(path) {
|
|
792
938
|
const modal = document.getElementById('previewModal');
|
|
793
939
|
const title = document.getElementById('previewTitle');
|
|
@@ -812,7 +958,15 @@
|
|
|
812
958
|
if (contentType && contentType.includes('application/json')) {
|
|
813
959
|
const data = await response.json();
|
|
814
960
|
if (data.type === 'text') {
|
|
815
|
-
|
|
961
|
+
currentPreviewContent = data.content;
|
|
962
|
+
body.innerHTML = `
|
|
963
|
+
<div class="preview-container">
|
|
964
|
+
<div class="preview-header">
|
|
965
|
+
<button class="copy-btn" onclick="copyPreviewContent()">📋 复制全部</button>
|
|
966
|
+
</div>
|
|
967
|
+
<pre class="text-preview">${escapeHtml(data.content)}</pre>
|
|
968
|
+
</div>
|
|
969
|
+
`;
|
|
816
970
|
} else {
|
|
817
971
|
body.innerHTML = `<div class="empty-state"><p>该文件类型暂不支持预览</p><p><a href="/__api/file?path=${encodeURIComponent(
|
|
818
972
|
path
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mock-service-cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.6.1",
|
|
4
4
|
"description": "🦅 Local Mock/Static/SPA server, Http?s request proxy, API overview page, File explorer",
|
|
5
5
|
"main": "./lib/mockServer.js",
|
|
6
6
|
"bin": {
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"test-watch": "tap --reporter=spec --watch test/*.test.js",
|
|
14
14
|
"fix": "eslint --ext .js,.vue,.css --fix",
|
|
15
15
|
"prettier": "prettier -c --write \"**/*.{ts,js,jsx,css,less,scss,json}\"",
|
|
16
|
+
"coverage": "cat ./coverage/lcov.info | ./node_modules/.bin/coveralls -v",
|
|
16
17
|
"release:prerelease": "standard-version --prerelease",
|
|
17
18
|
"release:prefix": "standard-version --prerelease alpha",
|
|
18
19
|
"release:patch": "standard-version --release-as patch && git push --follow-tags origin master",
|