generator-mico-cli 0.1.0 → 0.1.2
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/bin/mico.js +248 -54
- package/lib/utils.js +44 -0
- package/package.json +3 -1
package/bin/mico.js
CHANGED
|
@@ -1,81 +1,275 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
const { spawn } = require('node:child_process');
|
|
4
|
+
const { spawn, spawnSync } = require('node:child_process');
|
|
5
5
|
const fs = require('node:fs');
|
|
6
6
|
const path = require('node:path');
|
|
7
|
+
const readline = require('node:readline');
|
|
7
8
|
|
|
9
|
+
const rootDir = path.resolve(__dirname, '..');
|
|
10
|
+
const pkg = JSON.parse(
|
|
11
|
+
fs.readFileSync(path.join(rootDir, 'package.json'), 'utf8')
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 打印帮助信息
|
|
16
|
+
*/
|
|
8
17
|
function printHelp() {
|
|
9
|
-
console.log(
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
console.log(`
|
|
19
|
+
Usage: mico <command> [options]
|
|
20
|
+
|
|
21
|
+
Commands:
|
|
22
|
+
create <generator> Run a generator (e.g., mico create subapp-react)
|
|
23
|
+
update Update mico-cli to the latest version
|
|
24
|
+
|
|
25
|
+
Options:
|
|
26
|
+
--help, -h Show this help message
|
|
27
|
+
--version, -v Show version number
|
|
28
|
+
--no-update-check Skip update check
|
|
29
|
+
|
|
30
|
+
Examples:
|
|
31
|
+
mico create subapp-react
|
|
32
|
+
mico create app
|
|
33
|
+
mico update
|
|
34
|
+
|
|
35
|
+
Notes:
|
|
36
|
+
Requires Yeoman CLI (yo) installed globally.
|
|
37
|
+
If a local generator exists at generators/<name>, it will be used.
|
|
38
|
+
`);
|
|
19
39
|
}
|
|
20
40
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
41
|
+
/**
|
|
42
|
+
* 打印版本信息
|
|
43
|
+
*/
|
|
44
|
+
function printVersion() {
|
|
45
|
+
console.log(`mico-cli v${pkg.version}`);
|
|
46
|
+
}
|
|
27
47
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
48
|
+
/**
|
|
49
|
+
* 检查是否有新版本
|
|
50
|
+
* @returns {Promise<{current: string, latest: string, hasUpdate: boolean} | null>}
|
|
51
|
+
*/
|
|
52
|
+
async function checkForUpdate() {
|
|
53
|
+
try {
|
|
54
|
+
const updateNotifier = (await import('update-notifier')).default;
|
|
55
|
+
const notifier = updateNotifier({
|
|
56
|
+
pkg,
|
|
57
|
+
updateCheckInterval: 0 // 每次都检查
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// 等待检查完成
|
|
61
|
+
await notifier.fetchInfo();
|
|
62
|
+
|
|
63
|
+
if (notifier.update && notifier.update.latest !== pkg.version) {
|
|
64
|
+
return {
|
|
65
|
+
current: pkg.version,
|
|
66
|
+
latest: notifier.update.latest,
|
|
67
|
+
hasUpdate: true
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return { current: pkg.version, latest: pkg.version, hasUpdate: false };
|
|
71
|
+
} catch {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
31
74
|
}
|
|
32
75
|
|
|
33
|
-
|
|
76
|
+
/**
|
|
77
|
+
* 询问用户是否更新
|
|
78
|
+
* @returns {Promise<boolean>}
|
|
79
|
+
*/
|
|
80
|
+
function askForUpdate(current, latest) {
|
|
81
|
+
return new Promise((resolve) => {
|
|
82
|
+
const rl = readline.createInterface({
|
|
83
|
+
input: process.stdin,
|
|
84
|
+
output: process.stdout
|
|
85
|
+
});
|
|
34
86
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
87
|
+
console.log('');
|
|
88
|
+
console.log(` 📦 New version available: ${current} → \x1b[32m${latest}\x1b[0m`);
|
|
89
|
+
rl.question(' Do you want to update now? (Y/n) ', (answer) => {
|
|
90
|
+
rl.close();
|
|
91
|
+
const normalized = answer.trim().toLowerCase();
|
|
92
|
+
resolve(normalized === '' || normalized === 'y' || normalized === 'yes');
|
|
93
|
+
});
|
|
94
|
+
});
|
|
38
95
|
}
|
|
39
96
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
);
|
|
97
|
+
/**
|
|
98
|
+
* 执行更新
|
|
99
|
+
* @returns {boolean} 是否更新成功
|
|
100
|
+
*/
|
|
101
|
+
function performUpdate() {
|
|
102
|
+
console.log('');
|
|
103
|
+
console.log(' ⏳ Updating mico-cli...');
|
|
104
|
+
|
|
105
|
+
// 检测包管理器
|
|
106
|
+
const npmUserAgent = process.env.npm_config_user_agent || '';
|
|
107
|
+
let pm = 'npm';
|
|
108
|
+
if (npmUserAgent.includes('pnpm')) {
|
|
109
|
+
pm = 'pnpm';
|
|
110
|
+
} else if (npmUserAgent.includes('yarn')) {
|
|
111
|
+
pm = 'yarn';
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// 执行全局安装
|
|
115
|
+
const installCmd = pm === 'yarn' ? 'yarn' : pm;
|
|
116
|
+
const installArgs =
|
|
117
|
+
pm === 'yarn'
|
|
118
|
+
? ['global', 'add', pkg.name]
|
|
119
|
+
: ['install', '-g', pkg.name];
|
|
120
|
+
|
|
121
|
+
const result = spawnSync(installCmd, installArgs, {
|
|
122
|
+
stdio: 'inherit',
|
|
123
|
+
shell: process.platform === 'win32'
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
if (result.status === 0) {
|
|
127
|
+
console.log('');
|
|
128
|
+
console.log(' ✅ Update successful! Please re-run your command.');
|
|
129
|
+
return true;
|
|
130
|
+
} else {
|
|
131
|
+
console.log('');
|
|
132
|
+
console.log(' ❌ Update failed. Please try manually:');
|
|
133
|
+
console.log(` ${installCmd} ${installArgs.join(' ')}`);
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
47
137
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
138
|
+
/**
|
|
139
|
+
* 运行 Yeoman 生成器
|
|
140
|
+
*/
|
|
141
|
+
function runGenerator(generator, rest, passthroughArgs) {
|
|
142
|
+
const localGeneratorEntry = path.join(
|
|
143
|
+
rootDir,
|
|
144
|
+
'generators',
|
|
145
|
+
generator,
|
|
146
|
+
'index.js'
|
|
52
147
|
);
|
|
148
|
+
|
|
53
149
|
const pkgName = typeof pkg.name === 'string' ? pkg.name : '';
|
|
54
|
-
localNamespace =
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
150
|
+
const localNamespace =
|
|
151
|
+
pkgName.replace(/^generator-/, '') || pkgName || 'generator';
|
|
152
|
+
|
|
153
|
+
const yoGenerator = fs.existsSync(localGeneratorEntry)
|
|
154
|
+
? `${localNamespace}:${generator}`
|
|
155
|
+
: generator;
|
|
156
|
+
|
|
157
|
+
const yoArgs = [yoGenerator, ...rest];
|
|
158
|
+
|
|
159
|
+
if (passthroughArgs.length > 0) {
|
|
160
|
+
yoArgs.push('--', ...passthroughArgs);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const child = spawn('yo', yoArgs, { stdio: 'inherit' });
|
|
58
164
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
165
|
+
child.on('error', (error) => {
|
|
166
|
+
if (error && error.code === 'ENOENT') {
|
|
167
|
+
console.error('Cannot find "yo". Install it with: npm install -g yo');
|
|
168
|
+
} else {
|
|
169
|
+
console.error(error);
|
|
170
|
+
}
|
|
171
|
+
process.exit(1);
|
|
172
|
+
});
|
|
63
173
|
|
|
64
|
-
|
|
65
|
-
|
|
174
|
+
child.on('exit', (code) => {
|
|
175
|
+
process.exit(typeof code === 'number' ? code : 1);
|
|
176
|
+
});
|
|
66
177
|
}
|
|
67
178
|
|
|
68
|
-
|
|
179
|
+
/**
|
|
180
|
+
* 主函数
|
|
181
|
+
*/
|
|
182
|
+
async function main() {
|
|
183
|
+
const args = process.argv.slice(2);
|
|
69
184
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
185
|
+
// 解析参数
|
|
186
|
+
const doubleDashIndex = args.indexOf('--');
|
|
187
|
+
const passthroughArgs =
|
|
188
|
+
doubleDashIndex === -1 ? [] : args.slice(doubleDashIndex + 1);
|
|
189
|
+
const mainArgs =
|
|
190
|
+
doubleDashIndex === -1 ? args : args.slice(0, doubleDashIndex);
|
|
191
|
+
|
|
192
|
+
// 检查特殊标志
|
|
193
|
+
const hasHelp = mainArgs.includes('--help') || mainArgs.includes('-h');
|
|
194
|
+
const hasVersion = mainArgs.includes('--version') || mainArgs.includes('-v');
|
|
195
|
+
const skipUpdateCheck = mainArgs.includes('--no-update-check');
|
|
196
|
+
|
|
197
|
+
// 过滤掉标志参数
|
|
198
|
+
const filteredArgs = mainArgs.filter(
|
|
199
|
+
(arg) =>
|
|
200
|
+
arg !== '--help' &&
|
|
201
|
+
arg !== '-h' &&
|
|
202
|
+
arg !== '--version' &&
|
|
203
|
+
arg !== '-v' &&
|
|
204
|
+
arg !== '--no-update-check'
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
// 处理 --version
|
|
208
|
+
if (hasVersion) {
|
|
209
|
+
printVersion();
|
|
210
|
+
process.exit(0);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// 处理 --help 或无参数
|
|
214
|
+
if (hasHelp || filteredArgs.length === 0) {
|
|
215
|
+
printHelp();
|
|
216
|
+
process.exit(0);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const [command, ...rest] = filteredArgs;
|
|
220
|
+
|
|
221
|
+
// 处理 update 命令
|
|
222
|
+
if (command === 'update') {
|
|
223
|
+
const updateInfo = await checkForUpdate();
|
|
224
|
+
if (updateInfo && updateInfo.hasUpdate) {
|
|
225
|
+
performUpdate();
|
|
226
|
+
} else if (updateInfo) {
|
|
227
|
+
console.log(` ✅ You are using the latest version (v${updateInfo.current})`);
|
|
228
|
+
} else {
|
|
229
|
+
console.log(' ⚠️ Could not check for updates');
|
|
230
|
+
}
|
|
231
|
+
process.exit(0);
|
|
75
232
|
}
|
|
233
|
+
|
|
234
|
+
// 处理 create 命令
|
|
235
|
+
if (command === 'create') {
|
|
236
|
+
const generator = rest[0];
|
|
237
|
+
if (!generator) {
|
|
238
|
+
printHelp();
|
|
239
|
+
process.exit(1);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// 检查更新(除非跳过)
|
|
243
|
+
if (!skipUpdateCheck) {
|
|
244
|
+
const updateInfo = await checkForUpdate();
|
|
245
|
+
if (updateInfo && updateInfo.hasUpdate) {
|
|
246
|
+
const shouldUpdate = await askForUpdate(
|
|
247
|
+
updateInfo.current,
|
|
248
|
+
updateInfo.latest
|
|
249
|
+
);
|
|
250
|
+
if (shouldUpdate) {
|
|
251
|
+
const success = performUpdate();
|
|
252
|
+
if (success) {
|
|
253
|
+
process.exit(0);
|
|
254
|
+
}
|
|
255
|
+
// 更新失败则继续执行
|
|
256
|
+
}
|
|
257
|
+
console.log('');
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// 运行生成器
|
|
262
|
+
runGenerator(generator, rest.slice(1), passthroughArgs);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// 未知命令
|
|
267
|
+
console.error(`Unknown command: ${command}`);
|
|
268
|
+
printHelp();
|
|
76
269
|
process.exit(1);
|
|
77
|
-
}
|
|
270
|
+
}
|
|
78
271
|
|
|
79
|
-
|
|
80
|
-
|
|
272
|
+
main().catch((err) => {
|
|
273
|
+
console.error(err);
|
|
274
|
+
process.exit(1);
|
|
81
275
|
});
|
package/lib/utils.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 转换为 kebab-case
|
|
5
|
+
* @param {string} input
|
|
6
|
+
* @returns {string}
|
|
7
|
+
*/
|
|
8
|
+
function toKebab(input) {
|
|
9
|
+
return String(input)
|
|
10
|
+
.trim()
|
|
11
|
+
.replace(/[\s_]+/g, '-')
|
|
12
|
+
.replace(/[^a-zA-Z0-9-]/g, '-')
|
|
13
|
+
.replace(/-+/g, '-')
|
|
14
|
+
.replace(/^-|-$/g, '')
|
|
15
|
+
.toLowerCase();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 转换为 PascalCase
|
|
20
|
+
* @param {string} input
|
|
21
|
+
* @returns {string}
|
|
22
|
+
*/
|
|
23
|
+
function toPascal(input) {
|
|
24
|
+
return toKebab(input)
|
|
25
|
+
.split('-')
|
|
26
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
27
|
+
.join('');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 转换为 camelCase
|
|
32
|
+
* @param {string} input
|
|
33
|
+
* @returns {string}
|
|
34
|
+
*/
|
|
35
|
+
function toCamel(input) {
|
|
36
|
+
const pascal = toPascal(input);
|
|
37
|
+
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = {
|
|
41
|
+
toKebab,
|
|
42
|
+
toPascal,
|
|
43
|
+
toCamel
|
|
44
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "generator-mico-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Yeoman generator for Mico CLI projects",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"yeoman-generator",
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
},
|
|
15
15
|
"files": [
|
|
16
16
|
"bin",
|
|
17
|
+
"lib",
|
|
17
18
|
"generators"
|
|
18
19
|
],
|
|
19
20
|
"scripts": {
|
|
@@ -23,6 +24,7 @@
|
|
|
23
24
|
"node": ">=18"
|
|
24
25
|
},
|
|
25
26
|
"dependencies": {
|
|
27
|
+
"update-notifier": "^7.3.1",
|
|
26
28
|
"yeoman-generator": "^5.9.0"
|
|
27
29
|
}
|
|
28
30
|
}
|