mock-service-cli 3.3.1 → 3.3.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 +2 -0
- package/bin/mock-service-cli +73 -45
- package/lib/manageMockFiles.js +3 -3
- package/lib/mockServer.js +202 -177
- package/lib/staticServer.js +27 -26
- package/package.json +5 -10
package/README.md
CHANGED
package/bin/mock-service-cli
CHANGED
|
@@ -9,6 +9,7 @@ const argv = require('minimist')(process.argv.slice(2));
|
|
|
9
9
|
const { logger } = require('../lib/utils');
|
|
10
10
|
const spawn = require('child_process').spawn;
|
|
11
11
|
const node = process.execPath;
|
|
12
|
+
|
|
12
13
|
process.title = 'mock-service-cli';
|
|
13
14
|
|
|
14
15
|
if (argv.h || argv.help) {
|
|
@@ -113,6 +114,7 @@ if (proxyArg) {
|
|
|
113
114
|
}
|
|
114
115
|
process.env.PROXY_OPTIONS = JSON.stringify(proxyTable);
|
|
115
116
|
}
|
|
117
|
+
// whether to start Web server or not
|
|
116
118
|
if (argv.D || argv['web-dir']) {
|
|
117
119
|
// 解析SPA应用参数:SPA Web目录
|
|
118
120
|
process.env.WEB_ROOT = resolve(process.cwd(), argv.D || argv['web-dir']);
|
|
@@ -122,24 +124,38 @@ if (argv.D || argv['web-dir']) {
|
|
|
122
124
|
}
|
|
123
125
|
// Web Server port
|
|
124
126
|
portfinder.basePort = argv.P || argv['web-port'] || 9090;
|
|
127
|
+
portfinder.getPort(function (err, webPort) {
|
|
128
|
+
if (err) {
|
|
129
|
+
throw err;
|
|
130
|
+
}
|
|
131
|
+
process.env.WEB_PORT = webPort;
|
|
132
|
+
// Mock Server port
|
|
133
|
+
portfinder.basePort = port || 8090;
|
|
134
|
+
portfinder.getPort(function (err, port) {
|
|
135
|
+
if (err) {
|
|
136
|
+
throw err;
|
|
137
|
+
}
|
|
138
|
+
process.env.PORT = port;
|
|
139
|
+
initServe();
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
} else {
|
|
143
|
+
// Mock Server port
|
|
144
|
+
portfinder.basePort = port || 8090;
|
|
125
145
|
portfinder.getPort(function (err, port) {
|
|
126
146
|
if (err) {
|
|
127
147
|
throw err;
|
|
128
148
|
}
|
|
129
|
-
process.env.
|
|
149
|
+
process.env.PORT = port;
|
|
150
|
+
initServe();
|
|
130
151
|
});
|
|
131
152
|
}
|
|
132
153
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
process.env.PORT = port;
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
const watchMockFiles = function (watchDir) {
|
|
154
|
+
/**
|
|
155
|
+
* Watch change of mock files, restart serve
|
|
156
|
+
* @param watchDir
|
|
157
|
+
*/
|
|
158
|
+
function watchMockFiles(watchDir) {
|
|
143
159
|
/**
|
|
144
160
|
* script 重启的脚本
|
|
145
161
|
* ext 检测的文件
|
|
@@ -164,50 +180,62 @@ const watchMockFiles = function (watchDir) {
|
|
|
164
180
|
[colors.green('\nnodemon: mockServer restarted due to: '), files, colors.green(' have changed'), '\n'].join('')
|
|
165
181
|
);
|
|
166
182
|
});
|
|
167
|
-
}
|
|
183
|
+
}
|
|
168
184
|
|
|
169
185
|
// Node子进程启动MockServer
|
|
170
|
-
|
|
186
|
+
function startMockServer() {
|
|
171
187
|
spawn(node, [resolve(__dirname, '../lib/mockServer.js')], {
|
|
172
188
|
stdio: 'inherit'
|
|
173
189
|
});
|
|
174
|
-
}
|
|
190
|
+
}
|
|
175
191
|
|
|
176
192
|
// Node子进程启动StaticServer
|
|
177
|
-
|
|
193
|
+
function startStaticServer() {
|
|
178
194
|
spawn(node, [resolve(__dirname, '../lib/staticServer.js')], {
|
|
179
195
|
stdio: 'inherit'
|
|
180
196
|
});
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
if (
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
} else
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
} else
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
// log.info('SPECIFIED_DIR::', process.env.SPECIFIED_DIR);
|
|
208
|
-
if (isStartSocketServer) {
|
|
209
|
-
startMockServer();
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* 初始化服务
|
|
200
|
+
*/
|
|
201
|
+
function initServe() {
|
|
202
|
+
if (specifiedFile) {
|
|
203
|
+
// Mock服务器:指定文件
|
|
204
|
+
process.env.SPECIFIED_FILE = resolve(process.cwd(), specifiedFile);
|
|
205
|
+
if (isStartSocketServer) {
|
|
206
|
+
startMockServer();
|
|
207
|
+
} else {
|
|
208
|
+
watchMockFiles(process.env.SPECIFIED_FILE);
|
|
209
|
+
}
|
|
210
|
+
} else if (watchDir) {
|
|
211
|
+
// Mock服务器:指定目录
|
|
212
|
+
process.env.SPECIFIED_DIR = resolve(process.cwd(), watchDir);
|
|
213
|
+
// log.info('SPECIFIED_DIR::', process.env.SPECIFIED_DIR);
|
|
214
|
+
if (isStartSocketServer) {
|
|
215
|
+
startMockServer();
|
|
216
|
+
} else {
|
|
217
|
+
watchMockFiles(process.env.SPECIFIED_DIR);
|
|
218
|
+
}
|
|
219
|
+
} else if (process.env.STATIC_DIRECTORY) {
|
|
220
|
+
// 静态服务器
|
|
221
|
+
process.env.STATIC_DIRECTORY = resolve(process.cwd(), process.env.STATIC_DIRECTORY);
|
|
222
|
+
startStaticServer();
|
|
210
223
|
} else {
|
|
211
|
-
|
|
224
|
+
// Mock服务:使用默认目录
|
|
225
|
+
watchDir = process.env.SPECIFIED_DIR = resolve(process.cwd(), './mock');
|
|
226
|
+
// log.info('SPECIFIED_DIR::', process.env.SPECIFIED_DIR);
|
|
227
|
+
if (isStartSocketServer) {
|
|
228
|
+
startMockServer();
|
|
229
|
+
} else {
|
|
230
|
+
// whether include web serve or not
|
|
231
|
+
const includesWebServe = process.env.STATIC_DIRECTORY || process.env.WEB_ROOT;
|
|
232
|
+
|
|
233
|
+
if (includesWebServe && !existsSync(resolve(process.cwd(), watchDir))) {
|
|
234
|
+
// 若包含web服务,则允许不启动mock服务
|
|
235
|
+
startMockServer();
|
|
236
|
+
} else {
|
|
237
|
+
watchMockFiles(watchDir);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
212
240
|
}
|
|
213
241
|
}
|
package/lib/manageMockFiles.js
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
* @description: 生成Mock文件、获取mock数据统计
|
|
3
3
|
* @Date: 2021-12-22 16:57:08
|
|
4
4
|
* @LastEditors: chendq
|
|
5
|
-
* @LastEditTime:
|
|
5
|
+
* @LastEditTime: 2025-06-15 22:25:45
|
|
6
6
|
* @Author: chendq
|
|
7
7
|
*/
|
|
8
8
|
const fsa = require('fs-extra'),
|
|
9
9
|
fs = require('fs'),
|
|
10
10
|
path = require('path'),
|
|
11
|
-
|
|
11
|
+
isEqual = require('lodash/isEqual'),
|
|
12
12
|
colors = require('colors/safe');
|
|
13
13
|
const {
|
|
14
14
|
getFileLatestContent,
|
|
@@ -96,7 +96,7 @@ const genMockFiles = async function ({ url: apiUrl, method, data: resJsonData, d
|
|
|
96
96
|
if (fsa.pathExistsSync(mockFilePath)) {
|
|
97
97
|
const mockFileContent = getFileLatestContent(mockFilePath);
|
|
98
98
|
// 内容一样不作修改
|
|
99
|
-
if (mockFileContent.hasOwnProperty(method) &&
|
|
99
|
+
if (mockFileContent.hasOwnProperty(method) && isEqual(mockFileContent[method], resJsonData)) {
|
|
100
100
|
// log.assert(
|
|
101
101
|
// !_.isEqual(mockFileContent[method], resJsonData),
|
|
102
102
|
// colors.bgYellow(`${method} ${apiUrl}, Mock data is same`)
|
package/lib/mockServer.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const express = require('express'), // 引入express
|
|
2
2
|
bodyParser = require('body-parser'),
|
|
3
|
-
|
|
3
|
+
{ readdirSync, existsSync } = require('fs'),
|
|
4
4
|
os = require('os'),
|
|
5
5
|
path = require('path'),
|
|
6
6
|
colors = require('colors/safe'),
|
|
@@ -25,15 +25,20 @@ const app = express();
|
|
|
25
25
|
const log = logger(process.env.SILENT);
|
|
26
26
|
const argv = JSON.parse(process.env.ARGV);
|
|
27
27
|
let socketServer = null; // Socket server instance
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
let corsOrigin = [],
|
|
29
|
+
corsHeaders = '';
|
|
30
30
|
|
|
31
31
|
const httpsRE = /^https:\/\//;
|
|
32
32
|
let count = 0,
|
|
33
33
|
fileCount = 0, // 记录mock api个数,mock file个数
|
|
34
34
|
mockDirStat = {}, // 缓存mock目录的统计信息
|
|
35
35
|
mockFileStat = {}; // 缓存mock文件的统计信息
|
|
36
|
-
|
|
36
|
+
|
|
37
|
+
// whether include web serve or not
|
|
38
|
+
const includesWebServe = process.env.STATIC_DIRECTORY || process.env.WEB_ROOT;
|
|
39
|
+
|
|
40
|
+
const mockFileOrDir = process.env.SPECIFIED_FILE || process.env.SPECIFIED_DIR;
|
|
41
|
+
|
|
37
42
|
if (!process.env.PORT) {
|
|
38
43
|
portfinder.basePort = 8090;
|
|
39
44
|
portfinder.getPort(function (err, port) {
|
|
@@ -41,53 +46,72 @@ if (!process.env.PORT) {
|
|
|
41
46
|
throw err;
|
|
42
47
|
}
|
|
43
48
|
process.env.PORT = port;
|
|
44
|
-
|
|
49
|
+
init();
|
|
45
50
|
});
|
|
46
51
|
} else {
|
|
47
|
-
|
|
52
|
+
init();
|
|
48
53
|
}
|
|
49
|
-
// 将异步函数转换为同步代码代码执行
|
|
50
|
-
require('deasync').loopWhile(function () {
|
|
51
|
-
return !done;
|
|
52
|
-
});
|
|
53
|
-
let corsOrigin = [],
|
|
54
|
-
corsHeaders = '';
|
|
55
|
-
if (process.env.CORS_ORIGIN) {
|
|
56
|
-
process.env.CORS_ORIGIN.split(/\s*,\s*/).forEach(function (h) {
|
|
57
|
-
corsOrigin.push(h);
|
|
58
|
-
}, this);
|
|
59
54
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Init mock server
|
|
57
|
+
*/
|
|
58
|
+
function init() {
|
|
59
|
+
if (process.env.CORS_ORIGIN) {
|
|
60
|
+
process.env.CORS_ORIGIN.split(/\s*,\s*/).forEach(function (h) {
|
|
61
|
+
corsOrigin.push(h);
|
|
62
|
+
}, this);
|
|
63
|
+
|
|
64
|
+
corsHeaders = DefaultHeaders;
|
|
65
|
+
}
|
|
66
|
+
if (process.env.CORS_HEADERS) {
|
|
67
|
+
process.env.CORS_HEADERS.split(/\s*,\s*/).forEach(function (h) {
|
|
68
|
+
corsHeaders += corsHeaders ? ', ' + h : h;
|
|
69
|
+
}, this);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
app.use(crossDomain()); // 允许跨域
|
|
73
|
+
app.use(bodyParser.json()); // 解析body
|
|
74
|
+
|
|
75
|
+
let requireMockServe = true;
|
|
76
|
+
if (process.env.SPECIFIED_FILE) {
|
|
77
|
+
composeRouteFromJsFile(process.env.SPECIFIED_FILE);
|
|
78
|
+
} else if (includesWebServe && !existsSync(path.resolve(process.cwd(), process.env.SPECIFIED_DIR))) {
|
|
79
|
+
// 若包含web服务,则允许不启动mock服务
|
|
80
|
+
requireMockServe = false;
|
|
81
|
+
} else {
|
|
82
|
+
parseMockFiles(process.env.SPECIFIED_DIR);
|
|
83
|
+
}
|
|
84
|
+
requireMockServe && argv.a && log.info(colors.yellow(`${fileCount} mock file are parsed in total.`));
|
|
85
|
+
|
|
86
|
+
startServer();
|
|
66
87
|
}
|
|
88
|
+
|
|
67
89
|
/**
|
|
68
90
|
* @description: 跨域设置
|
|
69
91
|
* @param {*}
|
|
70
92
|
* @return {*}
|
|
71
93
|
*/
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
94
|
+
function crossDomain() {
|
|
95
|
+
return (req, res, next) => {
|
|
96
|
+
// 设置withCredentials: true时,需设置以下两项
|
|
97
|
+
res.header('Access-Control-Allow-Credentials', true);
|
|
98
|
+
// withCredentials, must be specified value instead of *
|
|
99
|
+
res.header('Access-Control-Allow-Origin', corsOrigin.includes(req.headers.origin) ? req.headers.origin : '*');
|
|
100
|
+
// res.header('Access-Control-Allow-Origin', '*');
|
|
101
|
+
res.header('Access-Control-Allow-Methods', SupportMethods.join(','));
|
|
102
|
+
res.header('Access-Control-Allow-Headers', corsHeaders ? corsHeaders : '*');
|
|
103
|
+
// res.header('Access-Control-Allow-Headers', '*');
|
|
104
|
+
if (req.method === 'OPTIONS') res.status(200); // 让OPTIONS快速返回
|
|
105
|
+
next();
|
|
106
|
+
};
|
|
107
|
+
}
|
|
84
108
|
|
|
85
109
|
/**
|
|
86
110
|
* @description: 构建本地服务的路由请求
|
|
87
111
|
* @param {*} file
|
|
88
112
|
* @returns {*}
|
|
89
113
|
*/
|
|
90
|
-
|
|
114
|
+
function composeRouteFromJsFile(file) {
|
|
91
115
|
// 先删除require之前导入的缓存文件,否则获取到的文件内容还是旧内容
|
|
92
116
|
const fileObject = getFileLatestContent(file);
|
|
93
117
|
|
|
@@ -121,14 +145,14 @@ const composeRouteFromJsFile = function (file) {
|
|
|
121
145
|
});
|
|
122
146
|
}
|
|
123
147
|
});
|
|
124
|
-
}
|
|
148
|
+
}
|
|
125
149
|
/**
|
|
126
150
|
* @description: 解析mock目录下的js文件,为构建路由请求做准备
|
|
127
151
|
* @param {string} specialDir 目录路径
|
|
128
152
|
* @return {void}
|
|
129
153
|
*/
|
|
130
|
-
|
|
131
|
-
const files =
|
|
154
|
+
function parseMockFiles(specialDir = '../mock') {
|
|
155
|
+
const files = readdirSync(path.resolve(process.cwd(), specialDir), { withFileTypes: true });
|
|
132
156
|
const curFilesSize = files.length;
|
|
133
157
|
for (let index = 0; index < curFilesSize; index++) {
|
|
134
158
|
const el = files[index];
|
|
@@ -171,66 +195,60 @@ const parseMockFiles = function (specialDir = '../mock') {
|
|
|
171
195
|
}
|
|
172
196
|
composeRouteFromJsFile(filePath);
|
|
173
197
|
}
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
app.use(crossDomain()); // 允许跨域
|
|
177
|
-
app.use(bodyParser.json()); // 解析body
|
|
178
|
-
|
|
179
|
-
if (process.env.SPECIFIED_FILE) {
|
|
180
|
-
composeRouteFromJsFile(process.env.SPECIFIED_FILE);
|
|
181
|
-
} else {
|
|
182
|
-
parseMockFiles(process.env.SPECIFIED_DIR);
|
|
183
198
|
}
|
|
184
|
-
argv.a && log.info(colors.yellow(`${fileCount} mock file are parsed in total.`));
|
|
185
199
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
200
|
+
/**
|
|
201
|
+
* Start web server、socket server
|
|
202
|
+
*/
|
|
203
|
+
function startServer() {
|
|
204
|
+
let webApp = null,
|
|
205
|
+
webPublicPath = '/',
|
|
206
|
+
proxyTable = {};
|
|
207
|
+
// Enable Web Server
|
|
208
|
+
if (process.env.WEB_ROOT) {
|
|
209
|
+
webApp = express();
|
|
210
|
+
// Enable web proxy
|
|
211
|
+
if (process.env.PROXY_OPTIONS) {
|
|
212
|
+
try {
|
|
213
|
+
const options = JSON.parse(process.env.PROXY_OPTIONS);
|
|
214
|
+
Object.keys(options).forEach(function (prefix) {
|
|
215
|
+
proxyTable[prefix] = options[prefix];
|
|
216
|
+
const opts = {
|
|
217
|
+
target: options[prefix],
|
|
218
|
+
changeOrigin: true,
|
|
219
|
+
ws: true
|
|
220
|
+
};
|
|
221
|
+
if (httpsRE.test(options[prefix])) {
|
|
222
|
+
// https is require secure=false
|
|
223
|
+
opts.secure = false;
|
|
224
|
+
}
|
|
225
|
+
if (process.env.PREFIX_REWRITE) {
|
|
226
|
+
opts.pathRewrite = path => path.replace(new RegExp(`^${prefix}`), '');
|
|
227
|
+
}
|
|
228
|
+
// 接口代理
|
|
229
|
+
// https://github.com/http-party/node-http-proxy#options
|
|
230
|
+
webApp.use(prefix, createProxyMiddleware(opts));
|
|
231
|
+
}, this);
|
|
232
|
+
} catch (error) {
|
|
233
|
+
console.warn(colors.yellow('\nEnable web proxy Failed:', error));
|
|
234
|
+
}
|
|
216
235
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
.send(`Hi, welcome to web server entrance ui, you can test http proxy on browser console or api request tools. Examples as follows:<br />
|
|
236
|
+
// console.log(process.env.WEB_PUBLIC_PATH, process.env.WEB_ROOT);
|
|
237
|
+
if (process.env.WEB_PUBLIC_PATH) {
|
|
238
|
+
webPublicPath = process.env.WEB_PUBLIC_PATH;
|
|
239
|
+
}
|
|
240
|
+
webApp.use(webPublicPath, express.static(process.env.WEB_ROOT)); // 将dist目录下所有文件作为静态文件来管理
|
|
241
|
+
const defaultStaticEntry = `${process.env.WEB_ROOT}/index.html`;
|
|
242
|
+
if (existsSync(defaultStaticEntry)) {
|
|
243
|
+
// Used as static HTTP server for SPA
|
|
244
|
+
webApp.get('*', (req, res) => {
|
|
245
|
+
res.sendFile(defaultStaticEntry);
|
|
246
|
+
});
|
|
247
|
+
} else {
|
|
248
|
+
// Used as api server for test on browser console or api request tools
|
|
249
|
+
webApp.use(webPublicPath, (req, res) => {
|
|
250
|
+
res.status(200)
|
|
251
|
+
.send(`Hi, welcome to web server entrance ui, you can test http proxy on browser console or api request tools. Examples as follows:<br />
|
|
234
252
|
fetch("http://localhost:${process.env.WEB_PORT}/api/permission/auth/login", {
|
|
235
253
|
method: 'post',
|
|
236
254
|
headers: {
|
|
@@ -240,101 +258,108 @@ if (process.env.WEB_ROOT) {
|
|
|
240
258
|
body:JSON.stringify({"loginName":"admin","password":"admin"})
|
|
241
259
|
})
|
|
242
260
|
`);
|
|
243
|
-
|
|
261
|
+
});
|
|
262
|
+
}
|
|
244
263
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
log.info(colors.bgBlue(`Socket server has started. path: /ws/mock-service, `));
|
|
250
|
-
socketServer.of('/mock-data').on('connection', function (socket) {
|
|
251
|
-
const { headers, address } = socket.handshake;
|
|
252
|
-
const clientIp = headers.hasOwnProperty('x-forwarded-for') ? headers['x-forwarded-for'] : address;
|
|
253
|
-
log.info(
|
|
254
|
-
colors.green(`Socket client ${clientIp} has connected. --- ${dateFormat('YYYY-mm-dd HH:MM:SS', new Date())}`)
|
|
255
|
-
);
|
|
256
|
-
socket.on('mock-dir-stat', function (dir) {
|
|
257
|
-
socket.emit('mock-dir-stat', isEmptyObj(mockDirStat) ? (mockDirStat = getMockStatFromDir(dir)) : mockDirStat);
|
|
258
|
-
});
|
|
259
|
-
socket.on('mock-file-stat', function (filePath) {
|
|
260
|
-
socket.emit(
|
|
261
|
-
'mock-file-stat',
|
|
262
|
-
isEmptyObj(mockFileStat) ? (mockFileStat = getMockStatFromFile(filePath)) : mockFileStat
|
|
263
|
-
);
|
|
264
|
-
});
|
|
265
|
-
const async = args => {
|
|
266
|
-
return async next => {
|
|
267
|
-
await genMockFiles(args);
|
|
268
|
-
next();
|
|
269
|
-
};
|
|
270
|
-
};
|
|
271
|
-
socket.on('save-data', function (data) {
|
|
272
|
-
// console.log('lis save-data:', data);
|
|
273
|
-
saveDataAsyncTask.add(async(data));
|
|
264
|
+
const http = require('http').createServer(app);
|
|
265
|
+
if (process.env.SOCKET_SERVER) {
|
|
266
|
+
const AsyncTaskQueue = require('./asyncTaskQueue');
|
|
267
|
+
const saveDataAsyncTask = new AsyncTaskQueue();
|
|
274
268
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
// await genMockFiles(data);
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
socket.on('disconnect', function () {
|
|
269
|
+
socketServer = new Server(http, { path: '/ws/mock-service' });
|
|
270
|
+
log.info(colors.bgBlue(`Socket server has started. path: /ws/mock-service, `));
|
|
271
|
+
socketServer.of('/mock-data').on('connection', function (socket) {
|
|
272
|
+
const { headers, address } = socket.handshake;
|
|
273
|
+
const clientIp = headers.hasOwnProperty('x-forwarded-for') ? headers['x-forwarded-for'] : address;
|
|
284
274
|
log.info(
|
|
285
|
-
colors.
|
|
286
|
-
`Socket client ${clientIp} has
|
|
275
|
+
colors.green(
|
|
276
|
+
`Socket client ${clientIp} has connected. --- ${dateFormat('YYYY-mm-dd HH:MM:SS', new Date())}`
|
|
287
277
|
)
|
|
288
278
|
);
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
279
|
+
socket.on('mock-dir-stat', function (dir) {
|
|
280
|
+
socket.emit('mock-dir-stat', isEmptyObj(mockDirStat) ? (mockDirStat = getMockStatFromDir(dir)) : mockDirStat);
|
|
281
|
+
});
|
|
282
|
+
socket.on('mock-file-stat', function (filePath) {
|
|
283
|
+
socket.emit(
|
|
284
|
+
'mock-file-stat',
|
|
285
|
+
isEmptyObj(mockFileStat) ? (mockFileStat = getMockStatFromFile(filePath)) : mockFileStat
|
|
286
|
+
);
|
|
287
|
+
});
|
|
288
|
+
const async = args => {
|
|
289
|
+
return async next => {
|
|
290
|
+
await genMockFiles(args);
|
|
291
|
+
next();
|
|
292
|
+
};
|
|
293
|
+
};
|
|
294
|
+
socket.on('save-data', function (data) {
|
|
295
|
+
// console.log('lis save-data:', data);
|
|
296
|
+
saveDataAsyncTask.add(async(data));
|
|
295
297
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
colors.yellow(` ${dateFormat('YYYY-mm-dd HH:MM:SS', new Date())}`)
|
|
301
|
-
].join('')
|
|
302
|
-
);
|
|
303
|
-
if (!process.env.RESTARTED) {
|
|
304
|
-
console.info(
|
|
305
|
-
[colors.yellow('\n🌍 mock-server version: '), colors.cyan(require('../package.json').version), '\n'].join('')
|
|
306
|
-
);
|
|
307
|
-
console.info(colors.yellow(`\n Mock server available on:\n`));
|
|
308
|
-
console.info(' http://localhost:' + colors.green(process.env.PORT));
|
|
309
|
-
Object.keys(ifaces).forEach(function (dev) {
|
|
310
|
-
ifaces[dev].forEach(function (details) {
|
|
311
|
-
if (details.family === 'IPv4') {
|
|
312
|
-
console.info(' http://' + details.address + ':' + colors.green(process.env.PORT));
|
|
298
|
+
if (saveDataAsyncTask.list.length === 1) {
|
|
299
|
+
saveDataAsyncTask.run();
|
|
300
|
+
} else {
|
|
301
|
+
saveDataAsyncTask.next();
|
|
313
302
|
}
|
|
303
|
+
// await genMockFiles(data);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
socket.on('disconnect', function () {
|
|
307
|
+
log.info(
|
|
308
|
+
colors.gray(
|
|
309
|
+
`Socket client ${clientIp} has disconnected. --- ${dateFormat('YYYY-mm-dd HH:MM:SS', new Date())}`
|
|
310
|
+
)
|
|
311
|
+
);
|
|
314
312
|
});
|
|
315
313
|
});
|
|
316
314
|
}
|
|
317
|
-
});
|
|
318
315
|
|
|
319
|
-
if (
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
316
|
+
if (existsSync(mockFileOrDir)) {
|
|
317
|
+
http.listen(Number.parseInt(process.env.PORT, 10), () => {
|
|
318
|
+
console.info(colors.red('\n Mock File仅支持commonjs规范的js、cjs文件,不支持ES Module'));
|
|
319
|
+
|
|
320
|
+
console.info(
|
|
321
|
+
[
|
|
322
|
+
colors.yellow(`\n${process.env.RESTARTED ? 'Restarting' : 'Starting'} up mock-server, serving `),
|
|
323
|
+
colors.cyan(process.env.SPECIFIED_FILE || process.env.SPECIFIED_DIR),
|
|
324
|
+
colors.yellow(` ${dateFormat('YYYY-mm-dd HH:MM:SS', new Date())}`)
|
|
325
|
+
].join('')
|
|
326
|
+
);
|
|
327
|
+
if (!process.env.RESTARTED) {
|
|
328
|
+
console.info(
|
|
329
|
+
[colors.yellow('\n🌍 mock-server version: '), colors.cyan(require('../package.json').version), '\n'].join('')
|
|
330
|
+
);
|
|
331
|
+
console.info(colors.yellow(`\n Mock server available on:\n`));
|
|
332
|
+
console.info(' http://localhost:' + colors.green(process.env.PORT));
|
|
333
|
+
Object.keys(ifaces).forEach(function (dev) {
|
|
334
|
+
ifaces[dev].forEach(function (details) {
|
|
335
|
+
if (details.family === 'IPv4') {
|
|
336
|
+
console.info(' http://' + details.address + ':' + colors.green(process.env.PORT));
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
}
|
|
330
341
|
});
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (webApp && !process.env.RESTARTED) {
|
|
345
|
+
webApp.listen(Number.parseInt(process.env.WEB_PORT, 10), () => {
|
|
346
|
+
console.info(colors.yellow(`\n Web server available on:\n`));
|
|
347
|
+
console.info(' http://localhost:' + colors.green(process.env.WEB_PORT) + webPublicPath);
|
|
348
|
+
Object.keys(ifaces).forEach(function (dev) {
|
|
349
|
+
ifaces[dev].forEach(function (details) {
|
|
350
|
+
if (details.family === 'IPv4') {
|
|
351
|
+
console.info(' http://' + details.address + ':' + colors.green(process.env.WEB_PORT) + webPublicPath);
|
|
352
|
+
}
|
|
353
|
+
});
|
|
335
354
|
});
|
|
336
|
-
|
|
337
|
-
|
|
355
|
+
if (!isEmptyObj(proxyTable)) {
|
|
356
|
+
console.info(colors.yellow(`\n Enable proxy at web server on:`));
|
|
357
|
+
Object.keys(proxyTable).forEach(prefix => {
|
|
358
|
+
console.info(` ${prefix} -> ${proxyTable[prefix]}`);
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
}
|
|
338
363
|
}
|
|
339
364
|
|
|
340
365
|
if (process.platform === 'win32') {
|
package/lib/staticServer.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Static Server
|
|
3
3
|
* @Date: 2023-12-03 18:02:24
|
|
4
4
|
* @LastEditors: chendq
|
|
5
|
-
* @LastEditTime:
|
|
5
|
+
* @LastEditTime: 2025-06-15 22:16:36
|
|
6
6
|
* @Author : chendq
|
|
7
7
|
*/
|
|
8
8
|
const express = require('express'), // 引入express
|
|
@@ -13,7 +13,6 @@ const { dateFormat, logger } = require('./utils');
|
|
|
13
13
|
const ifaces = os.networkInterfaces();
|
|
14
14
|
const log = logger(process.env.SILENT);
|
|
15
15
|
|
|
16
|
-
let done = false;
|
|
17
16
|
if (!process.env.PORT) {
|
|
18
17
|
portfinder.basePort = 8090;
|
|
19
18
|
portfinder.getPort(function (err, port) {
|
|
@@ -21,38 +20,40 @@ if (!process.env.PORT) {
|
|
|
21
20
|
throw err;
|
|
22
21
|
}
|
|
23
22
|
process.env.PORT = port;
|
|
24
|
-
|
|
23
|
+
startServer();
|
|
25
24
|
});
|
|
26
25
|
} else {
|
|
27
|
-
|
|
26
|
+
startServer();
|
|
28
27
|
}
|
|
29
|
-
// 将异步函数转换为同步代码代码执行
|
|
30
|
-
require('deasync').loopWhile(function () {
|
|
31
|
-
return !done;
|
|
32
|
-
});
|
|
33
|
-
const app = express();
|
|
34
28
|
|
|
35
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Start server
|
|
31
|
+
*/
|
|
32
|
+
function startServer() {
|
|
33
|
+
const app = express();
|
|
34
|
+
|
|
35
|
+
app.use(express.static(process.env.STATIC_DIRECTORY)); // 将dist目录下所有文件作为静态文件来管理
|
|
36
36
|
|
|
37
|
-
app.listen(Number.parseInt(process.env.PORT, 10), () => {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
37
|
+
app.listen(Number.parseInt(process.env.PORT, 10), () => {
|
|
38
|
+
console.info(
|
|
39
|
+
[
|
|
40
|
+
colors.yellow(`\nStarting up Static Server, serving `),
|
|
41
|
+
colors.cyan(process.env.STATIC_DIRECTORY),
|
|
42
|
+
colors.yellow(` ${dateFormat('YYYY-mm-dd HH:MM:SS', new Date())}`)
|
|
43
|
+
].join('')
|
|
44
|
+
);
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
46
|
+
console.info(colors.yellow(`\n Static Server available on:\n`));
|
|
47
|
+
console.info(' http://localhost:' + colors.green(process.env.PORT));
|
|
48
|
+
Object.keys(ifaces).forEach(function (dev) {
|
|
49
|
+
ifaces[dev].forEach(function (details) {
|
|
50
|
+
if (details.family === 'IPv4') {
|
|
51
|
+
console.info(' http://' + details.address + ':' + colors.green(process.env.PORT));
|
|
52
|
+
}
|
|
53
|
+
});
|
|
53
54
|
});
|
|
54
55
|
});
|
|
55
|
-
}
|
|
56
|
+
}
|
|
56
57
|
|
|
57
58
|
if (process.platform === 'win32') {
|
|
58
59
|
require('readline')
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mock-service-cli",
|
|
3
|
-
"version": "3.3.
|
|
4
|
-
"description": "🦅 Local mock server",
|
|
3
|
+
"version": "3.3.3",
|
|
4
|
+
"description": "🦅 Local mock server, Static server, SPA server, Http?s request proxy",
|
|
5
5
|
"main": "./lib/mockServer.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"mock-service-cli": "bin/mock-service-cli"
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
"start": "node ./bin/mock-service-cli",
|
|
11
11
|
"test:unit": "tap --reporter=spec test/*.test.js",
|
|
12
12
|
"test": "tap --cov --coverage-report=lcov --reporter=spec test/*.test.js ",
|
|
13
|
-
"coverage": "cat ./coverage/lcov.info | ./node_modules/.bin/coveralls -v",
|
|
14
13
|
"test-watch": "tap --reporter=spec --watch test/*.test.js",
|
|
15
14
|
"fix": "eslint --ext .js,.vue,.css --fix",
|
|
16
15
|
"prettier": "prettier -c --write \"**/*.{ts,js,jsx,css,less,scss,json}\"",
|
|
@@ -29,16 +28,13 @@
|
|
|
29
28
|
"node": ">=12"
|
|
30
29
|
},
|
|
31
30
|
"author": "chendq <deqiaochen@gmail.com>",
|
|
32
|
-
"packageManager": "yarn@1.22.19",
|
|
33
31
|
"keywords": [
|
|
34
|
-
"MIT-licensed",
|
|
35
32
|
"Static Server",
|
|
36
|
-
"
|
|
33
|
+
"Web Server",
|
|
34
|
+
"Mock Server CLI",
|
|
37
35
|
"MockServer",
|
|
38
36
|
"mock-service-cli",
|
|
39
|
-
"
|
|
40
|
-
"Socket server",
|
|
41
|
-
"socket.io"
|
|
37
|
+
"Socket server"
|
|
42
38
|
],
|
|
43
39
|
"repository": {
|
|
44
40
|
"type": "git",
|
|
@@ -49,7 +45,6 @@
|
|
|
49
45
|
"dependencies": {
|
|
50
46
|
"body-parser": "^1.20.2",
|
|
51
47
|
"colors": "^1.4.0",
|
|
52
|
-
"deasync": "^0.1.23",
|
|
53
48
|
"express": "^4.17.1",
|
|
54
49
|
"fs-extra": "^10.0.0",
|
|
55
50
|
"http-proxy-middleware": "^2.0.6",
|