crabatool 1.0.446 → 1.0.448
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/index.js +38 -4
- package/lib/config.js +5 -0
- package/package.json +1 -1
- package/res/client.js +12 -9
- package/run.js +5 -1
- package/tool/autoUpdate.js +497 -0
- package/tool/processManager.js +254 -0
- package/tool/start.js +110 -59
package/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
var config = require('./lib/config.js');
|
|
2
2
|
var start = require('./tool/start.js');
|
|
3
|
+
var autoUpdate = require('./tool/autoUpdate.js');
|
|
4
|
+
var processManager = require('./tool/processManager.js');
|
|
3
5
|
|
|
4
6
|
exports = module.exports;
|
|
5
7
|
exports.config = config;
|
|
@@ -15,25 +17,44 @@ exports.hashUtils = require('./tool/hash.js');
|
|
|
15
17
|
|
|
16
18
|
start.initArgs();
|
|
17
19
|
|
|
18
|
-
exports.run = function(options) {
|
|
20
|
+
exports.run = async function(options) {
|
|
19
21
|
console.log('当前环境:' + process.platform);
|
|
20
22
|
console.log('助手版本:' + config.Version);
|
|
21
23
|
Object.assign(config, options);
|
|
22
24
|
|
|
23
25
|
start.initArgs(); // 检查注册参数
|
|
24
26
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
// 先检查是否是命令行模式(非交互式)
|
|
28
|
+
const shouldContinueToInteractive = await checkFast(process.argv);
|
|
29
|
+
|
|
30
|
+
if (!shouldContinueToInteractive) {
|
|
31
|
+
// 命令行模式,已经执行完命令,直接返回
|
|
27
32
|
return;
|
|
28
33
|
}
|
|
29
34
|
|
|
35
|
+
// 只有在交互式模式下才进行分离式更新
|
|
36
|
+
if (!autoUpdate.shouldSkipUpdate()) {
|
|
37
|
+
// 执行分离式更新检查
|
|
38
|
+
const shouldContinue = await processManager.executeDetachedUpdate(
|
|
39
|
+
() => autoUpdate.checkAndUpdateDetached(false),
|
|
40
|
+
process.argv
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
// 如果不应该继续执行(已启动子进程),直接返回
|
|
44
|
+
if (!shouldContinue) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
} else {
|
|
48
|
+
console.log('⏭️ 跳过更新检查');
|
|
49
|
+
}
|
|
50
|
+
|
|
30
51
|
start.showLocalVersion(true);
|
|
31
52
|
|
|
32
53
|
// 2. 开发环境让开发选择功能
|
|
33
54
|
start.waitInput();
|
|
34
55
|
}
|
|
35
56
|
|
|
36
|
-
function checkFast(args) {
|
|
57
|
+
async function checkFast(args) {
|
|
37
58
|
// 1. 服务端构建环境,自动化,根据命令行参数,直接调用脚手架功能
|
|
38
59
|
if (args.length < 3) {
|
|
39
60
|
return true;
|
|
@@ -126,6 +147,19 @@ function checkFast(args) {
|
|
|
126
147
|
return false;
|
|
127
148
|
}
|
|
128
149
|
|
|
150
|
+
// 手动更新 crabaTool 工具本身
|
|
151
|
+
if (args.includes('-updateTool')) {
|
|
152
|
+
console.log('开始更新 crabaTool...');
|
|
153
|
+
const success = await autoUpdate.checkAndUpdate(false);
|
|
154
|
+
if (success) {
|
|
155
|
+
console.log('更新完成!');
|
|
156
|
+
} else {
|
|
157
|
+
console.log(`更新失败,请检查网络连接或手动执行:npm install crabatool --registry ${config.registry}`);
|
|
158
|
+
}
|
|
159
|
+
process.exit(success ? 0 : 1);
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
|
|
129
163
|
// 安装vscode环境
|
|
130
164
|
if (args.includes('-vscode')) {
|
|
131
165
|
start.installvscode();
|
package/lib/config.js
CHANGED
|
@@ -28,6 +28,10 @@ class Config {
|
|
|
28
28
|
|
|
29
29
|
// 文件监听与代码检测相关配置
|
|
30
30
|
this.enableFileWatcher = true; // 是否启用文件监听功能
|
|
31
|
+
|
|
32
|
+
// 分离式更新相关配置
|
|
33
|
+
this.enableDetachedUpdate = true; // 是否启用分离式更新
|
|
34
|
+
this.detachedUpdateDelay = 1500; // 子进程启动后父进程退出延迟(毫秒)
|
|
31
35
|
this.TempPath = path.join(os.tmpdir(), '_crabatemp');
|
|
32
36
|
this.ImageCache = path.join(this.TempPath, "_img");
|
|
33
37
|
this.JsonFile = path.join(this.TempPath, "craba.json");
|
|
@@ -36,6 +40,7 @@ class Config {
|
|
|
36
40
|
this.WebPath = path.join(this.CachePath, "www");
|
|
37
41
|
this.ServerPath = path.join(this.CachePath, "server");
|
|
38
42
|
this.CacheFileName = path.join(this.SaveFilePath, "craba.zip");
|
|
43
|
+
this.registry = 'http://172.17.0.236:4873';
|
|
39
44
|
}
|
|
40
45
|
}
|
|
41
46
|
|
package/package.json
CHANGED
package/res/client.js
CHANGED
|
@@ -56,12 +56,14 @@
|
|
|
56
56
|
$craba._mods.def = new Date().getTime(); // 重置版本号,防止浏览器缓存
|
|
57
57
|
|
|
58
58
|
var cssLinks = [];
|
|
59
|
-
var gspxLinks = [];
|
|
59
|
+
//var gspxLinks = [];
|
|
60
60
|
var jsLinks = [];
|
|
61
61
|
var loadedJs = $jsLoader._getLoadedScripts();
|
|
62
62
|
files.forEach(function(filePath) {
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
if ($craba.getModName) {
|
|
64
|
+
filePath = $craba.joinPath($craba.getModName(), filePath); // 增加模块名(如果有)
|
|
65
|
+
}
|
|
66
|
+
var index = loadedJs.findIndex(function(ls) {// 没有加载过的js,不需要刷新
|
|
65
67
|
return ls.includes(filePath);
|
|
66
68
|
});
|
|
67
69
|
if (index > -1) {
|
|
@@ -69,9 +71,10 @@
|
|
|
69
71
|
jsLinks.push(filePath);
|
|
70
72
|
} else if (filePath.includes('.css')) {
|
|
71
73
|
cssLinks.push(filePath);
|
|
72
|
-
} else if (filePath.includes('.gspx')) {
|
|
73
|
-
gspxLinks.push(filePath);
|
|
74
74
|
}
|
|
75
|
+
//else if (filePath.includes('.gspx')) {
|
|
76
|
+
//gspxLinks.push(filePath);
|
|
77
|
+
//}
|
|
75
78
|
});
|
|
76
79
|
|
|
77
80
|
// 1. 热更新css
|
|
@@ -189,9 +192,9 @@
|
|
|
189
192
|
</div>
|
|
190
193
|
<div style="padding: 10px;">
|
|
191
194
|
${allErrorFiles.map(function(file, index) {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
+
var isActive = file.filePath === currentFile.filePath;
|
|
196
|
+
var fileId = 'error-file-' + index;
|
|
197
|
+
return `
|
|
195
198
|
<div id="${fileId}" onclick="switchErrorFile('${file.filePath}')"
|
|
196
199
|
class="${isActive ? 'active-file' : 'inactive-file'}"
|
|
197
200
|
data-filepath="${file.filePath}"
|
|
@@ -210,7 +213,7 @@
|
|
|
210
213
|
<div style="font-size: 11px; margin-top: 4px;">${file.errors.length} 个错误</div>
|
|
211
214
|
</div>
|
|
212
215
|
`;
|
|
213
|
-
|
|
216
|
+
}).join('')}
|
|
214
217
|
</div>
|
|
215
218
|
</div>
|
|
216
219
|
`;
|
package/run.js
CHANGED
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
const { spawn } = require('child_process');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const ProgressBar = require('./progressbar.js');
|
|
6
|
+
const config = require('../lib/config.js');
|
|
7
|
+
|
|
8
|
+
class AutoUpdate {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.registry = config.registry;
|
|
11
|
+
this.packageName = 'crabatool';
|
|
12
|
+
this.isUpdating = false;
|
|
13
|
+
this.progressBar = null;
|
|
14
|
+
this.progressTimer = null;
|
|
15
|
+
this.currentProgress = 0;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 检查并执行自动更新
|
|
20
|
+
* @param {boolean} silent - 是否静默更新(不显示详细输出)
|
|
21
|
+
* @returns {Promise<boolean>} - 更新是否成功
|
|
22
|
+
*/
|
|
23
|
+
async checkAndUpdate(silent = false) {
|
|
24
|
+
if (this.isUpdating) {
|
|
25
|
+
if (!silent) {
|
|
26
|
+
console.log(chalk.yellow('⚠️ 更新正在进行中,请稍候...'));
|
|
27
|
+
}
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
this.isUpdating = true;
|
|
33
|
+
|
|
34
|
+
// 先快速检查是否需要更新
|
|
35
|
+
const needsUpdate = await this.quickCheckForUpdates(silent);
|
|
36
|
+
|
|
37
|
+
if (!needsUpdate) {
|
|
38
|
+
if (!silent) {
|
|
39
|
+
console.log(chalk.gray('📦 已是最新版本'));
|
|
40
|
+
}
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (!silent) {
|
|
45
|
+
console.log(chalk.blue('🔄 发现新版本,正在更新 crabaTool...'));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const updateResult = await this.performUpdate(silent);
|
|
49
|
+
|
|
50
|
+
if (updateResult.success) {
|
|
51
|
+
if (!silent) {
|
|
52
|
+
console.log(chalk.green('✅ crabaTool 更新完成'));
|
|
53
|
+
if (updateResult.updated) {
|
|
54
|
+
console.log(chalk.cyan(`📦 已更新到版本: ${updateResult.version || '最新版本'}`));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return true;
|
|
58
|
+
} else {
|
|
59
|
+
if (!silent) {
|
|
60
|
+
console.log(chalk.yellow('⚠️ 更新失败,继续使用当前版本'));
|
|
61
|
+
if (updateResult.error) {
|
|
62
|
+
console.log(chalk.gray(`错误详情: ${updateResult.error}`));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
} catch (error) {
|
|
68
|
+
if (!silent) {
|
|
69
|
+
console.log(chalk.yellow('⚠️ 自动更新遇到问题,继续使用当前版本'));
|
|
70
|
+
console.log(chalk.gray(`错误: ${error.message}`));
|
|
71
|
+
}
|
|
72
|
+
return false;
|
|
73
|
+
} finally {
|
|
74
|
+
this.isUpdating = false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 快速检查是否需要更新(不执行实际安装)
|
|
80
|
+
* @param {boolean} silent - 是否静默模式
|
|
81
|
+
* @returns {Promise<boolean>} - 是否需要更新
|
|
82
|
+
*/
|
|
83
|
+
async quickCheckForUpdates(silent = false) {
|
|
84
|
+
return new Promise((resolve) => {
|
|
85
|
+
const command = 'npm';
|
|
86
|
+
const args = ['view', this.packageName, 'version', '--registry', this.registry];
|
|
87
|
+
|
|
88
|
+
const npmProcess = spawn(command, args, {
|
|
89
|
+
stdio: 'pipe',
|
|
90
|
+
shell: true
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
let stdout = '';
|
|
94
|
+
let stderr = '';
|
|
95
|
+
|
|
96
|
+
npmProcess.stdout.on('data', (data) => {
|
|
97
|
+
stdout += data.toString();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
npmProcess.stderr.on('data', (data) => {
|
|
101
|
+
stderr += data.toString();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
npmProcess.on('close', (code) => {
|
|
105
|
+
if (code === 0) {
|
|
106
|
+
try {
|
|
107
|
+
const latestVersion = stdout.trim();
|
|
108
|
+
const currentVersion = this.getCurrentVersion();
|
|
109
|
+
|
|
110
|
+
if (!silent) {
|
|
111
|
+
console.log(chalk.gray(`当前版本: ${currentVersion}, 最新版本: ${latestVersion}`));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// 简单的版本比较
|
|
115
|
+
resolve(latestVersion !== currentVersion);
|
|
116
|
+
} catch (e) {
|
|
117
|
+
// 如果版本比较失败,默认执行更新
|
|
118
|
+
resolve(true);
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
// 如果检查失败,默认执行更新
|
|
122
|
+
resolve(true);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
npmProcess.on('error', () => {
|
|
127
|
+
// 如果命令执行失败,默认执行更新
|
|
128
|
+
resolve(true);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// 设置超时(10秒)
|
|
132
|
+
setTimeout(() => {
|
|
133
|
+
if (!npmProcess.killed) {
|
|
134
|
+
npmProcess.kill();
|
|
135
|
+
resolve(true); // 超时时默认执行更新
|
|
136
|
+
}
|
|
137
|
+
}, 10000);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* 获取当前安装的版本
|
|
143
|
+
* @returns {string} - 当前版本号
|
|
144
|
+
*/
|
|
145
|
+
getCurrentVersion() {
|
|
146
|
+
try {
|
|
147
|
+
const packageJsonPath = path.join(process.cwd(), 'node_modules', this.packageName, 'package.json');
|
|
148
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
149
|
+
const packageInfo = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
150
|
+
return packageInfo.version;
|
|
151
|
+
}
|
|
152
|
+
} catch (e) {
|
|
153
|
+
// 忽略错误
|
|
154
|
+
}
|
|
155
|
+
return 'unknown';
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* 创建并初始化进度条
|
|
160
|
+
*/
|
|
161
|
+
createProgressBar() {
|
|
162
|
+
if (this.progressBar) {
|
|
163
|
+
this.clearProgress();
|
|
164
|
+
}
|
|
165
|
+
this.progressBar = new ProgressBar('更新进度', 25);
|
|
166
|
+
this.currentProgress = 0;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* 更新进度显示
|
|
171
|
+
* @param {number} percent - 进度百分比 (0-100)
|
|
172
|
+
* @param {string} statusText - 状态文本
|
|
173
|
+
*/
|
|
174
|
+
updateProgress(percent, statusText = '') {
|
|
175
|
+
if (!this.progressBar) return;
|
|
176
|
+
|
|
177
|
+
this.currentProgress = Math.max(this.currentProgress, percent);
|
|
178
|
+
this.progressBar.render({
|
|
179
|
+
value: this.currentProgress,
|
|
180
|
+
total: 100,
|
|
181
|
+
text: statusText
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* 清理进度条显示
|
|
187
|
+
* @param {string} finalText - 最终显示的文本
|
|
188
|
+
*/
|
|
189
|
+
clearProgress(finalText = '') {
|
|
190
|
+
if (this.progressBar) {
|
|
191
|
+
this.progressBar.clear(finalText);
|
|
192
|
+
this.progressBar = null;
|
|
193
|
+
}
|
|
194
|
+
if (this.progressTimer) {
|
|
195
|
+
clearInterval(this.progressTimer);
|
|
196
|
+
this.progressTimer = null;
|
|
197
|
+
}
|
|
198
|
+
this.currentProgress = 0;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* 解析npm输出,识别安装阶段
|
|
203
|
+
* @param {string} output - npm输出内容
|
|
204
|
+
* @returns {object|null} - {phase: string, progress: number, text: string}
|
|
205
|
+
*/
|
|
206
|
+
parseNpmOutput(output) {
|
|
207
|
+
const npmPhases = {
|
|
208
|
+
'fetching': { progress: 35, text: '正在下载包...' },
|
|
209
|
+
'extract': { progress: 50, text: '正在解压...' },
|
|
210
|
+
'resolve': { progress: 65, text: '正在解析依赖...' },
|
|
211
|
+
'install': { progress: 78, text: '正在安装...' },
|
|
212
|
+
'link': { progress: 90, text: '正在链接...' },
|
|
213
|
+
'postinstall': { progress: 95, text: '正在完成安装...' },
|
|
214
|
+
'added': { progress: 100, text: '安装完成!' },
|
|
215
|
+
'updated': { progress: 100, text: '更新完成!' },
|
|
216
|
+
'changed': { progress: 100, text: '更改完成!' }
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
const lowerOutput = output.toLowerCase();
|
|
220
|
+
|
|
221
|
+
for (const [keyword, phaseInfo] of Object.entries(npmPhases)) {
|
|
222
|
+
if (lowerOutput.includes(keyword)) {
|
|
223
|
+
return {
|
|
224
|
+
phase: keyword,
|
|
225
|
+
progress: phaseInfo.progress,
|
|
226
|
+
text: phaseInfo.text
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* 启动基准进度定时器
|
|
236
|
+
* @param {number} maxProgress - 最大进度值
|
|
237
|
+
* @param {number} duration - 持续时间(毫秒)
|
|
238
|
+
*/
|
|
239
|
+
startProgressTimer(maxProgress = 80, duration = 120000) {
|
|
240
|
+
if (this.progressTimer) {
|
|
241
|
+
clearInterval(this.progressTimer);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const interval = 200; // 每200ms更新一次
|
|
245
|
+
const increment = (maxProgress / duration) * interval;
|
|
246
|
+
|
|
247
|
+
this.progressTimer = setInterval(() => {
|
|
248
|
+
if (this.currentProgress < maxProgress) {
|
|
249
|
+
this.updateProgress(this.currentProgress + increment, '正在更新中...');
|
|
250
|
+
}
|
|
251
|
+
}, interval);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* 执行实际的更新操作
|
|
256
|
+
* @param {boolean} silent - 是否静默模式
|
|
257
|
+
* @returns {Promise<{success: boolean, updated: boolean, version?: string, error?: string}>}
|
|
258
|
+
*/
|
|
259
|
+
performUpdate(silent = false) {
|
|
260
|
+
return new Promise((resolve) => {
|
|
261
|
+
const command = 'npm';
|
|
262
|
+
const args = ['install', this.packageName, '--registry', this.registry];
|
|
263
|
+
|
|
264
|
+
// 初始化进度条(非静默模式)
|
|
265
|
+
if (!silent) {
|
|
266
|
+
console.log(chalk.gray(`执行命令: ${command} ${args.join(' ')}`));
|
|
267
|
+
this.createProgressBar();
|
|
268
|
+
this.updateProgress(5, '准备更新...');
|
|
269
|
+
this.startProgressTimer(75, 120000); // 基准进度最高75%,持续2分钟
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const npmProcess = spawn(command, args, {
|
|
273
|
+
stdio: silent ? 'pipe' : ['pipe', 'pipe', 'pipe'],
|
|
274
|
+
shell: true
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
let stdout = '';
|
|
278
|
+
let stderr = '';
|
|
279
|
+
|
|
280
|
+
// 处理进程结束的函数
|
|
281
|
+
const handleProcessEnd = (code, isTimeout = false) => {
|
|
282
|
+
// 清理定时器和进度条
|
|
283
|
+
if (!silent) {
|
|
284
|
+
if (this.progressTimer) {
|
|
285
|
+
clearInterval(this.progressTimer);
|
|
286
|
+
this.progressTimer = null;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (code === 0) {
|
|
291
|
+
// 检查是否真的有更新
|
|
292
|
+
const wasUpdated = stdout.includes('added') || stdout.includes('updated') || stdout.includes('changed');
|
|
293
|
+
|
|
294
|
+
// 完成进度条显示
|
|
295
|
+
if (!silent) {
|
|
296
|
+
this.updateProgress(100, wasUpdated ? '更新完成!' : '已是最新版本');
|
|
297
|
+
setTimeout(() => this.clearProgress(), 1000);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// 尝试获取版本信息
|
|
301
|
+
let version = null;
|
|
302
|
+
try {
|
|
303
|
+
const packageJsonPath = path.join(process.cwd(), 'node_modules', this.packageName, 'package.json');
|
|
304
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
305
|
+
const packageInfo = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
306
|
+
version = packageInfo.version;
|
|
307
|
+
}
|
|
308
|
+
} catch (e) {
|
|
309
|
+
// 忽略版本获取错误
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
resolve({
|
|
313
|
+
success: true,
|
|
314
|
+
updated: wasUpdated,
|
|
315
|
+
version: version
|
|
316
|
+
});
|
|
317
|
+
} else {
|
|
318
|
+
const errorMessage = isTimeout ? '更新超时' : (stderr || `npm install 退出码: ${code}`);
|
|
319
|
+
|
|
320
|
+
// 错误状态进度条显示
|
|
321
|
+
if (!silent) {
|
|
322
|
+
this.updateProgress(this.currentProgress, `更新失败: ${errorMessage}`);
|
|
323
|
+
setTimeout(() => this.clearProgress(), 3000);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
resolve({
|
|
327
|
+
success: false,
|
|
328
|
+
updated: false,
|
|
329
|
+
error: errorMessage
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
if (npmProcess.stdout) {
|
|
335
|
+
npmProcess.stdout.on('data', (data) => {
|
|
336
|
+
stdout += data.toString();
|
|
337
|
+
const output = data.toString();
|
|
338
|
+
|
|
339
|
+
if (!silent) {
|
|
340
|
+
// 解析npm输出,更新智能进度
|
|
341
|
+
const npmPhase = this.parseNpmOutput(output);
|
|
342
|
+
if (npmPhase) {
|
|
343
|
+
this.updateProgress(npmPhase.progress, npmPhase.text);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// 显示关键的安装信息
|
|
347
|
+
if (output.includes('added') || output.includes('updated') || output.includes('changed')) {
|
|
348
|
+
process.stdout.write(chalk.gray(output));
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (npmProcess.stderr) {
|
|
355
|
+
npmProcess.stderr.on('data', (data) => {
|
|
356
|
+
stderr += data.toString();
|
|
357
|
+
const output = data.toString();
|
|
358
|
+
|
|
359
|
+
if (!silent) {
|
|
360
|
+
// 处理网络连接问题
|
|
361
|
+
if (output.toLowerCase().includes('network') || output.toLowerCase().includes('timeout')) {
|
|
362
|
+
this.updateProgress(this.currentProgress, '网络连接中...');
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// 只显示重要的错误信息,忽略警告
|
|
366
|
+
if (!output.includes('WARN') && !output.includes('deprecated')) {
|
|
367
|
+
process.stderr.write(chalk.yellow(output));
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
npmProcess.on('close', (code) => {
|
|
374
|
+
handleProcessEnd(code);
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
npmProcess.on('error', (error) => {
|
|
378
|
+
if (!silent) {
|
|
379
|
+
this.updateProgress(this.currentProgress, `进程错误: ${error.message}`);
|
|
380
|
+
setTimeout(() => this.clearProgress(), 3000);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
resolve({
|
|
384
|
+
success: false,
|
|
385
|
+
updated: false,
|
|
386
|
+
error: error.message
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
// 设置超时(2分钟)
|
|
391
|
+
setTimeout(() => {
|
|
392
|
+
if (!npmProcess.killed) {
|
|
393
|
+
npmProcess.kill();
|
|
394
|
+
handleProcessEnd(-1, true);
|
|
395
|
+
}
|
|
396
|
+
}, 2 * 60 * 1000);
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* 检查是否应该跳过自动更新
|
|
402
|
+
* @returns {boolean}
|
|
403
|
+
*/
|
|
404
|
+
shouldSkipUpdate() {
|
|
405
|
+
// 检查环境变量
|
|
406
|
+
if (process.env.CRABATOOL_SKIP_UPDATE === 'true') {
|
|
407
|
+
return true;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// 检查命令行参数
|
|
411
|
+
if (process.argv.includes('--skip-update')) {
|
|
412
|
+
return true;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// 在CI环境中跳过更新
|
|
416
|
+
if (process.env.CI === 'true' || process.env.CONTINUOUS_INTEGRATION === 'true') {
|
|
417
|
+
return true;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
return false;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* 分离式更新检查和执行
|
|
425
|
+
* 专门用于分离式子进程更新流程
|
|
426
|
+
* @param {boolean} silent - 是否静默模式
|
|
427
|
+
* @returns {Promise<{updated: boolean, success: boolean, version?: string}>}
|
|
428
|
+
*/
|
|
429
|
+
async checkAndUpdateDetached(silent = false) {
|
|
430
|
+
if (this.isUpdating) {
|
|
431
|
+
if (!silent) {
|
|
432
|
+
console.log(chalk.yellow('⚠️ 更新正在进行中,请稍候...'));
|
|
433
|
+
}
|
|
434
|
+
return { updated: false, success: false };
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
try {
|
|
438
|
+
this.isUpdating = true;
|
|
439
|
+
|
|
440
|
+
// 先快速检查是否需要更新
|
|
441
|
+
const needsUpdate = await this.quickCheckForUpdates(silent);
|
|
442
|
+
|
|
443
|
+
if (!needsUpdate) {
|
|
444
|
+
if (!silent) {
|
|
445
|
+
console.log(chalk.gray('📦 已是最新版本'));
|
|
446
|
+
}
|
|
447
|
+
return { updated: false, success: true };
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
if (!silent) {
|
|
451
|
+
console.log(chalk.blue('🔄 发现新版本,正在更新 crabaTool...'));
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
const updateResult = await this.performUpdate(silent);
|
|
455
|
+
|
|
456
|
+
if (updateResult.success && updateResult.updated) {
|
|
457
|
+
if (!silent) {
|
|
458
|
+
console.log(chalk.green('✅ crabaTool 更新完成'));
|
|
459
|
+
console.log(chalk.cyan(`📦 已更新到版本: ${updateResult.version || '最新版本'}`));
|
|
460
|
+
}
|
|
461
|
+
return {
|
|
462
|
+
updated: true,
|
|
463
|
+
success: true,
|
|
464
|
+
version: updateResult.version
|
|
465
|
+
};
|
|
466
|
+
} else {
|
|
467
|
+
if (!silent) {
|
|
468
|
+
console.log(chalk.yellow('⚠️ 更新失败或无实际更新'));
|
|
469
|
+
if (updateResult.error) {
|
|
470
|
+
console.log(chalk.gray(`错误详情: ${updateResult.error}`));
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
return {
|
|
474
|
+
updated: false,
|
|
475
|
+
success: updateResult.success,
|
|
476
|
+
error: updateResult.error
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
} catch (error) {
|
|
480
|
+
if (!silent) {
|
|
481
|
+
console.log(chalk.yellow('⚠️ 自动更新遇到问题'));
|
|
482
|
+
console.log(chalk.gray(`错误: ${error.message}`));
|
|
483
|
+
}
|
|
484
|
+
return {
|
|
485
|
+
updated: false,
|
|
486
|
+
success: false,
|
|
487
|
+
error: error.message
|
|
488
|
+
};
|
|
489
|
+
} finally {
|
|
490
|
+
this.isUpdating = false;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
module.exports = new AutoUpdate();
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/*
|
|
2
|
+
分离式子进程管理器
|
|
3
|
+
功能: 实现更新后的分离式子进程启动,避免更新过程中的进程冲突
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { spawn } = require('child_process');
|
|
7
|
+
const chalk = require('chalk');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const config = require('../lib/config.js');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 分离式进程管理器
|
|
14
|
+
* 用于处理更新后的子进程启动和父进程优雅退出
|
|
15
|
+
*/
|
|
16
|
+
class ProcessManager {
|
|
17
|
+
constructor() {
|
|
18
|
+
this.isChildProcess = false;
|
|
19
|
+
this.originalArgs = [];
|
|
20
|
+
this.childProcess = null;
|
|
21
|
+
|
|
22
|
+
// 检查是否是子进程
|
|
23
|
+
this.checkIfChildProcess();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 检查当前进程是否是子进程
|
|
28
|
+
*/
|
|
29
|
+
checkIfChildProcess() {
|
|
30
|
+
// 通过环境变量检查
|
|
31
|
+
this.isChildProcess = process.env.CRABATOOL_CHILD_PROCESS === 'true';
|
|
32
|
+
|
|
33
|
+
// 通过命令行参数检查
|
|
34
|
+
if (process.argv.includes('--child-process')) {
|
|
35
|
+
this.isChildProcess = true;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (this.isChildProcess) {
|
|
39
|
+
console.log(chalk.blue('🔄 子进程已启动,运行更新后的代码'));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 启动分离式子进程
|
|
45
|
+
* @param {Array} originalArgs - 原始命令行参数
|
|
46
|
+
* @param {Object} options - 启动选项
|
|
47
|
+
* @returns {Promise<boolean>} - 是否成功启动子进程
|
|
48
|
+
*/
|
|
49
|
+
async startDetachedChild(originalArgs = [], options = {}) {
|
|
50
|
+
try {
|
|
51
|
+
// 准备子进程参数
|
|
52
|
+
const childArgs = this.prepareChildArgs(originalArgs);
|
|
53
|
+
|
|
54
|
+
// 获取当前执行的脚本路径
|
|
55
|
+
const scriptPath = this.getScriptPath();
|
|
56
|
+
|
|
57
|
+
console.log(chalk.blue('🚀 启动分离式子进程...'));
|
|
58
|
+
console.log(chalk.gray(`执行命令: node ${scriptPath} ${childArgs.join(' ')}`));
|
|
59
|
+
|
|
60
|
+
// 启动子进程,不分离,让子进程接管终端
|
|
61
|
+
const child = spawn('node', [scriptPath, ...childArgs], {
|
|
62
|
+
detached: false, // 不分离
|
|
63
|
+
stdio: 'inherit', // 继承输入输出
|
|
64
|
+
env: {
|
|
65
|
+
...process.env,
|
|
66
|
+
CRABATOOL_CHILD_PROCESS: 'true'
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// 等待子进程结束,然后父进程也结束
|
|
71
|
+
child.on('close', (code) => {
|
|
72
|
+
process.exit(code || 0);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
child.on('error', (error) => {
|
|
76
|
+
console.error(chalk.red(`子进程启动失败: ${error.message}`));
|
|
77
|
+
process.exit(1);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// 等待一小段时间确保子进程启动
|
|
81
|
+
await this.delay(500);
|
|
82
|
+
|
|
83
|
+
return true;
|
|
84
|
+
} catch (error) {
|
|
85
|
+
console.log(chalk.red(`❌ 子进程启动失败: ${error.message}`));
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* 准备子进程参数
|
|
92
|
+
* @param {Array} originalArgs - 原始参数
|
|
93
|
+
* @returns {Array} - 处理后的参数
|
|
94
|
+
*/
|
|
95
|
+
prepareChildArgs(originalArgs) {
|
|
96
|
+
// 过滤掉 node 和脚本路径
|
|
97
|
+
const filteredArgs = originalArgs.slice(2);
|
|
98
|
+
|
|
99
|
+
// 添加子进程标识
|
|
100
|
+
if (!filteredArgs.includes('--child-process')) {
|
|
101
|
+
filteredArgs.push('--child-process');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return filteredArgs;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* 获取当前脚本路径
|
|
109
|
+
* @returns {string} - 脚本路径
|
|
110
|
+
*/
|
|
111
|
+
getScriptPath() {
|
|
112
|
+
// 尝试多种方式获取脚本路径
|
|
113
|
+
let scriptPath = process.argv[1];
|
|
114
|
+
|
|
115
|
+
// 如果是通过 npm 或其他方式启动,尝试找到实际的入口文件
|
|
116
|
+
if (!scriptPath || !fs.existsSync(scriptPath)) {
|
|
117
|
+
// 查找项目根目录的 index.js
|
|
118
|
+
const projectRoot = process.cwd();
|
|
119
|
+
const possiblePaths = [
|
|
120
|
+
path.join(projectRoot, 'index.js'),
|
|
121
|
+
path.join(projectRoot, 'app.js'),
|
|
122
|
+
path.join(projectRoot, 'main.js')
|
|
123
|
+
];
|
|
124
|
+
|
|
125
|
+
for (const possiblePath of possiblePaths) {
|
|
126
|
+
if (fs.existsSync(possiblePath)) {
|
|
127
|
+
scriptPath = possiblePath;
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return scriptPath;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* 优雅退出父进程
|
|
138
|
+
* @param {number} exitCode - 退出码
|
|
139
|
+
* @param {number} delay - 延迟时间(毫秒)
|
|
140
|
+
*/
|
|
141
|
+
async gracefulExit(exitCode = 0, delay = null) {
|
|
142
|
+
console.log(chalk.yellow('🔄 父进程准备退出...'));
|
|
143
|
+
|
|
144
|
+
// 使用配置的延迟时间或传入的延迟时间
|
|
145
|
+
const actualDelay = delay !== null ? delay : config.detachedUpdateDelay || 2000;
|
|
146
|
+
|
|
147
|
+
// 等待一段时间确保子进程稳定运行
|
|
148
|
+
await this.delay(actualDelay);
|
|
149
|
+
|
|
150
|
+
console.log(chalk.green('👋 父进程优雅退出'));
|
|
151
|
+
process.exit(exitCode);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* 延迟函数
|
|
156
|
+
* @param {number} ms - 延迟毫秒数
|
|
157
|
+
* @returns {Promise}
|
|
158
|
+
*/
|
|
159
|
+
delay(ms) {
|
|
160
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* 检查是否应该启动分离式更新流程
|
|
165
|
+
* @returns {boolean}
|
|
166
|
+
*/
|
|
167
|
+
shouldUseDetachedUpdate() {
|
|
168
|
+
// 如果已经是子进程,不再启动分离式更新
|
|
169
|
+
if (this.isChildProcess) {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// 检查配置文件
|
|
174
|
+
if (!config.enableDetachedUpdate) {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// 检查环境变量
|
|
179
|
+
if (process.env.CRABATOOL_DISABLE_DETACHED === 'true') {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// 检查命令行参数
|
|
184
|
+
if (process.argv.includes('--no-detached')) {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* 执行分离式更新流程
|
|
193
|
+
* @param {Function} updateFunction - 更新函数
|
|
194
|
+
* @param {Array} originalArgs - 原始参数
|
|
195
|
+
* @returns {Promise<boolean>} - 是否需要继续执行
|
|
196
|
+
*/
|
|
197
|
+
async executeDetachedUpdate(updateFunction, originalArgs = process.argv) {
|
|
198
|
+
// 如果是子进程,直接返回继续执行
|
|
199
|
+
if (this.isChildProcess) {
|
|
200
|
+
console.log(chalk.blue('📦 子进程运行中,跳过更新检查'));
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// 如果不应该使用分离式更新,返回继续执行
|
|
205
|
+
if (!this.shouldUseDetachedUpdate()) {
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
try {
|
|
210
|
+
console.log(chalk.blue('🔍 检查更新(分离式模式)...'));
|
|
211
|
+
|
|
212
|
+
// 执行更新检查
|
|
213
|
+
const updateResult = await updateFunction();
|
|
214
|
+
|
|
215
|
+
if (updateResult && updateResult.updated) {
|
|
216
|
+
console.log(chalk.green('✅ 更新完成,启动新进程...'));
|
|
217
|
+
|
|
218
|
+
// 启动分离式子进程
|
|
219
|
+
const childStarted = await this.startDetachedChild(originalArgs);
|
|
220
|
+
|
|
221
|
+
if (childStarted) {
|
|
222
|
+
// 子进程已启动,父进程不需要继续执行
|
|
223
|
+
// 子进程会接管所有后续操作
|
|
224
|
+
return false;
|
|
225
|
+
} else {
|
|
226
|
+
console.log(chalk.yellow('⚠️ 子进程启动失败,继续使用当前进程'));
|
|
227
|
+
return true; // 继续执行
|
|
228
|
+
}
|
|
229
|
+
} else {
|
|
230
|
+
console.log(chalk.gray('📦 无需更新或更新失败,继续执行'));
|
|
231
|
+
return true; // 继续执行
|
|
232
|
+
}
|
|
233
|
+
} catch (error) {
|
|
234
|
+
console.log(chalk.red(`❌ 分离式更新流程失败: ${error.message}`));
|
|
235
|
+
return true; // 继续执行
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* 获取进程状态信息
|
|
241
|
+
* @returns {Object} - 进程状态
|
|
242
|
+
*/
|
|
243
|
+
getStatus() {
|
|
244
|
+
return {
|
|
245
|
+
isChildProcess: this.isChildProcess,
|
|
246
|
+
pid: process.pid,
|
|
247
|
+
ppid: process.ppid,
|
|
248
|
+
shouldUseDetached: this.shouldUseDetachedUpdate(),
|
|
249
|
+
args: process.argv
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
module.exports = new ProcessManager();
|
package/tool/start.js
CHANGED
|
@@ -10,67 +10,101 @@ class Start {
|
|
|
10
10
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
waitInput() {
|
|
13
|
+
async waitInput() {
|
|
14
14
|
const rl = readline.createInterface({
|
|
15
15
|
input: process.stdin,
|
|
16
16
|
output: process.stdout
|
|
17
17
|
});
|
|
18
18
|
|
|
19
|
-
const question =
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
19
|
+
const question = (prompt) => {
|
|
20
|
+
return new Promise((resolve) => {
|
|
21
|
+
rl.question(prompt, resolve);
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
while (true) {
|
|
27
|
+
// 分离菜单内容和提示信息
|
|
28
|
+
var menuLines = [
|
|
29
|
+
"---------------------------------------------------------------------------------------------------------",
|
|
30
|
+
"1. 启动服务。\t\t\t\t主要包含:自动合并打包init.js和biz.js,查看本地平台的版本",
|
|
31
|
+
"2. 更新当前项目的平台,然后启动服务。\t主要包含:从<内网>拉取环境相关资源,再拷贝到webPath路径下覆盖老文件,替代手工操作,同时解决npm不稳定问题",
|
|
32
|
+
"3. 查询最新平台版本和日期。\t\t主要包含:从<内网>拉取最新平台版本,开发好及时更新补丁",
|
|
33
|
+
"4. 校验当前项目js和gspx语法。\t\t主要包含:对本地js语法做一次初步预检避免上线后才发现问题;比如:编码、let、const、=>、async、await、then、缺function",
|
|
34
|
+
"5. 打包当前项目前端所有js。\t\t主要包含:前端es6语法的打包和js的统一压缩处理",
|
|
35
|
+
"6. 安装或更新IDEA环境。\t\t\t主要包含:从<内网>拉取环境相关资源,包括智能语法、新增页面模板、更多新功能",
|
|
36
|
+
"7. 安装或更新vscode环境。\t\t主要包含:从<内网>拉取环境相关资源,包括智能语法、新增页面模板、ctrl+alt+n、alt+j快捷键",
|
|
37
|
+
"8. 新建Craba项目。\t\t\t主要包含:从<内网>拉取环境相关资源,根据模板创建新项目,避免手工拷贝文件",
|
|
38
|
+
"9. 更新 crabaTool 工具。\t\t\t主要包含:从内网更新 crabaTool 工具到最新版本",
|
|
39
|
+
"0. 结束 \t\t\t\t按Ctrl+C即可退出",
|
|
40
|
+
"#. 如果你对[nodejs版本的平台助手]有更多建议或想法可反馈给技术架构组改进哦,使用文档和介绍: http://crabadoc.ca.com/index.html?#~/Pages/Guide.gspx?id=1655080677276",
|
|
41
|
+
"---------------------------------------------------------------------------------------------------------"
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
// 先显示菜单
|
|
45
|
+
console.log(menuLines.join('\r\n'));
|
|
46
|
+
|
|
47
|
+
// 然后单独显示提示并等待输入
|
|
48
|
+
const num = await question('请输入对应功能的编号(数字):');
|
|
49
|
+
|
|
50
|
+
if (num == 1) {
|
|
51
|
+
rl.close();
|
|
52
|
+
start.runServer();
|
|
53
|
+
break;
|
|
54
|
+
} else if (num == 2) {
|
|
55
|
+
rl.close();
|
|
56
|
+
start.update();
|
|
57
|
+
break;
|
|
58
|
+
} else if (num == 3) {
|
|
59
|
+
start.showNewVersion();
|
|
60
|
+
// 继续循环,重新显示菜单
|
|
61
|
+
console.log('\r\n');
|
|
62
|
+
} else if (num == 4) {
|
|
63
|
+
rl.close();
|
|
64
|
+
start.checkAllJs();
|
|
65
|
+
break;
|
|
66
|
+
} else if (num == 5) {
|
|
67
|
+
rl.close();
|
|
68
|
+
start.compressAllJs();
|
|
69
|
+
break;
|
|
70
|
+
} else if (num == 6) {
|
|
71
|
+
rl.close();
|
|
72
|
+
start.installIDEA();
|
|
73
|
+
break;
|
|
74
|
+
} else if (num == 7) {
|
|
75
|
+
rl.close();
|
|
76
|
+
start.installvscode();
|
|
77
|
+
break;
|
|
78
|
+
} else if (num == 8) {
|
|
79
|
+
var options = {};
|
|
80
|
+
options.type = await question('选择项目类型:1. 普通craba纯前端项目; 2. ngp微前端项目;输入编号:');
|
|
81
|
+
if (options.type != 1 && options.type != 2) options.type = 1;
|
|
82
|
+
if (options.type == 2) {
|
|
83
|
+
options.modName = await question('请输入微服务的模块名称:');
|
|
84
|
+
if (!options.modName) options.modName = 'emptyname';
|
|
85
|
+
}
|
|
86
|
+
options.projectPath = await waitFolder(question);
|
|
87
|
+
|
|
88
|
+
rl.close();
|
|
89
|
+
start.createProject(options);
|
|
90
|
+
break;
|
|
91
|
+
} else if (num == 9) {
|
|
92
|
+
rl.close();
|
|
93
|
+
start.updateTool();
|
|
94
|
+
break;
|
|
95
|
+
} else if (num == 0) {
|
|
96
|
+
rl.close();
|
|
97
|
+
console.log('👋 程序已退出');
|
|
98
|
+
process.exit(0);
|
|
99
|
+
} else {
|
|
100
|
+
console.error('无效输入,请重新选择。\r\n');
|
|
101
|
+
// 继续循环,重新显示菜单
|
|
64
102
|
}
|
|
65
|
-
options.projectPath = await waitFolder(question);
|
|
66
|
-
|
|
67
|
-
rl.close(); // 关闭输入等待
|
|
68
|
-
start.createProject(options);
|
|
69
|
-
} else if (num != 9) {
|
|
70
|
-
console.error('无效输入,请继续。\r\n\r\n');
|
|
71
|
-
start.waitInput(); // 重复
|
|
72
103
|
}
|
|
73
|
-
}
|
|
104
|
+
} catch (error) {
|
|
105
|
+
console.error('菜单处理出错:', error);
|
|
106
|
+
rl.close();
|
|
107
|
+
}
|
|
74
108
|
}
|
|
75
109
|
|
|
76
110
|
initArgs() {
|
|
@@ -232,6 +266,28 @@ class Start {
|
|
|
232
266
|
utils.log('');
|
|
233
267
|
}
|
|
234
268
|
|
|
269
|
+
// 更新 crabaTool 工具本身
|
|
270
|
+
async updateTool() {
|
|
271
|
+
const autoUpdate = require('./autoUpdate.js');
|
|
272
|
+
console.log('开始更新 crabaTool 工具...');
|
|
273
|
+
try {
|
|
274
|
+
const success = await autoUpdate.checkAndUpdate(false);
|
|
275
|
+
if (success) {
|
|
276
|
+
console.log('✅ crabaTool 更新完成!');
|
|
277
|
+
console.log('💡 提示:重新启动 crabaTool 以使用最新版本');
|
|
278
|
+
} else {
|
|
279
|
+
console.log('⚠️ 更新失败,请检查网络连接或手动执行:');
|
|
280
|
+
console.log(`npm install crabatool --registry ${config.registry}`);
|
|
281
|
+
}
|
|
282
|
+
} catch (error) {
|
|
283
|
+
console.log('❌ 更新过程中出现错误:', error.message);
|
|
284
|
+
console.log(`💡 请手动执行:npm install crabatool --registry ${config.registry}`);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// 更新完成后返回主菜单
|
|
288
|
+
console.log('\n按任意ctrl+c键返回...');
|
|
289
|
+
}
|
|
290
|
+
|
|
235
291
|
|
|
236
292
|
// 安装或更新IDEA环境,主要包含:智能语法、新增页面模板
|
|
237
293
|
installIDEA() {
|
|
@@ -353,8 +409,3 @@ async function waitFolder(question) {
|
|
|
353
409
|
return projectPath;
|
|
354
410
|
}
|
|
355
411
|
|
|
356
|
-
function rlPromisify(fn) {
|
|
357
|
-
return async function(t) {
|
|
358
|
-
return new Promise(function(resolve) { fn(t, resolve) });
|
|
359
|
-
};
|
|
360
|
-
}
|