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 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` | Mock 服务器端口 | 8090 |
56
- | `-d` | 指定 mock 目录 | ./mock |
57
- | `-f` | 指定单个 mock 文件 | - |
58
- | `-s` 或 `--silent` | 抑制日志输出 | - |
59
- | `-v` 或 `--version` | 显示版本信息 | - |
60
- | `-S` 或 `--socket-server` | 启动 socket 服务器,用于保存 API 响应数据 | false |
61
- | `-a` 或 `--api-stat` | 打印 API URL 和文件路径信息 | false |
62
- | `-t` 或 `--track` | 记录操作信息到文件 | false |
63
- | `-o` 或 `--cors-origin` | 配置 CORS 允许的源,多个源用逗号分隔 | * |
64
- | `-O` 或 `--proxy-options` | 配置 HTTP 请求代理选项,支持 js 文件、json 文件或命令行参数 | - |
65
- | `-H` 或 `--cors-headers` | 配置 CORS 头信息 | * |
66
- | `-A` 或 `--append-headers`| 添加响应头信息,多个头用逗号分隔 | - |
67
- | `-D` 或 `--web-dir` | 启用 Web 服务器,指定 web 目录(SPA 应用) | - |
68
- | `-P` 或 `--web-port` | Web 服务器端口 | 9090 |
69
- | `-b` 或 `--web-baseurl` | 指定 SPA Web 服务器的公共路径 | - |
70
- | `-R` 或 `--static-server` | 启用静态服务器,指定静态资源目录 | - |
71
- | `-w` 或 `--open` | 自动打开 API 概览页面 | false |
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
- 'NO|+1': 1,
158
- city: '@city',
159
- 'value|1-100': 50
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', (data) => {
242
+ socket.on('mock-dir-stat', data => {
229
243
  console.log('统计信息:', data);
230
244
  });
231
245
  ```
@@ -2,7 +2,7 @@
2
2
  * @description: 异步任务队列
3
3
  * @Date: 2022-01-04 10:46:20
4
4
  * @LastEditors: chendq
5
- * @LastEditTime: 2022-01-06 13:17:21
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: 继续执行下一个异步任务
@@ -434,16 +434,55 @@
434
434
  }
435
435
 
436
436
  .text-preview {
437
- background: #f8f9fa;
438
- padding: 16px;
437
+ background: #1e1e1e;
438
+ color: #d4d4d4;
439
+ padding: 20px;
439
440
  border-radius: 8px;
440
- font-family: 'Courier New', monospace;
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-break: break-all;
445
- max-height: 70vh;
446
- overflow: auto;
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
- processAndRenderFiles();
625
+
626
+ // 确保在调用 processAndRenderFiles 之前 allFiles 已经设置好
627
+ setTimeout(() => {
628
+ processAndRenderFiles();
629
+ }, 10);
630
+
573
631
  updateBackButton(data.parentPath);
574
632
 
575
633
  // 清空搜索框
576
- document.getElementById('searchInput').value = '';
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
- if (files.length === 0) {
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
- body.innerHTML = `<pre class="text-preview">${escapeHtml(data.content)}</pre>`;
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.5.0",
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",