@lppx/nlearn 1.1.0
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/dist/scripts/sync-repo.js +100 -0
- package/dist/src/cli/cli.js +9 -0
- package/dist/src/demo/commander/01-/345/237/272/347/241/200/346/246/202/345/277/265.js +174 -0
- package/dist/src/demo/commander/02-/345/221/275/344/273/244/347/263/273/347/273/237.js +260 -0
- package/dist/src/demo/commander/03-/351/253/230/347/272/247/347/211/271/346/200/247.js +285 -0
- package/dist/src/demo/commander/04-/345/256/236/346/210/230/346/241/210/344/276/213.js +408 -0
- package/dist/src/demo/esm/01-/345/237/272/347/241/200/346/246/202/345/277/265.js +226 -0
- package/dist/src/demo/esm/02-/345/257/274/345/207/272/350/257/255/346/263/225.js +281 -0
- package/dist/src/demo/esm/03-/345/257/274/345/205/245/350/257/255/346/263/225.js +366 -0
- package/dist/src/demo/esm/04-/345/256/236/346/210/230/346/241/210/344/276/213.js +509 -0
- package/dist/src/demo/inquirer/01-/345/237/272/347/241/200/346/246/202/345/277/265.js +135 -0
- package/dist/src/demo/inquirer/02-/351/200/211/346/213/251/347/261/273/345/236/213.js +143 -0
- package/dist/src/demo/inquirer/03-/351/253/230/347/272/247/347/211/271/346/200/247.js +211 -0
- package/dist/src/demo/inquirer/04-/345/256/236/346/210/230/346/241/210/344/276/213.js +343 -0
- package/dist/src/demo/yargs/01-/345/237/272/347/241/200/346/246/202/345/277/265.js +142 -0
- package/dist/src/demo/yargs/02-/345/221/275/344/273/244/347/263/273/347/273/237.js +211 -0
- package/dist/src/demo/yargs/03-/351/253/230/347/272/247/347/211/271/346/200/247.js +205 -0
- package/dist/src/demo/yargs/04-/345/256/236/346/210/230/346/241/210/344/276/213.js +276 -0
- package/package.json +39 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Commander.js 高级特性
|
|
4
|
+
* ======================
|
|
5
|
+
* 本文件涵盖:自定义事件处理、错误处理、异步命令、配置文件、环境变量
|
|
6
|
+
* 适用版本:commander ^12.0.0
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
const commander_1 = require("commander");
|
|
10
|
+
// #region 示例1: 异步命令处理
|
|
11
|
+
async function demoAsyncCommands() {
|
|
12
|
+
const program = new commander_1.Command();
|
|
13
|
+
program
|
|
14
|
+
.name('async-cli')
|
|
15
|
+
.description('异步命令示例')
|
|
16
|
+
.version('1.0.0');
|
|
17
|
+
// 异步命令:模拟文件下载
|
|
18
|
+
program
|
|
19
|
+
.command('download <url>')
|
|
20
|
+
.description('下载文件')
|
|
21
|
+
.option('-o, --output <path>', '输出路径', './downloads')
|
|
22
|
+
.action(async (url, options) => {
|
|
23
|
+
console.log('\n示例1: 开始下载...');
|
|
24
|
+
console.log('URL:', url);
|
|
25
|
+
console.log('输出路径:', options.output);
|
|
26
|
+
// 模拟异步操作
|
|
27
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
28
|
+
console.log('下载完成!');
|
|
29
|
+
});
|
|
30
|
+
// 异步命令:模拟数据库查询
|
|
31
|
+
program
|
|
32
|
+
.command('query <table>')
|
|
33
|
+
.description('查询数据库表')
|
|
34
|
+
.option('-l, --limit <number>', '限制结果数量', '10')
|
|
35
|
+
.action(async (table, options) => {
|
|
36
|
+
console.log('\n示例1: 查询数据库...');
|
|
37
|
+
console.log('表名:', table);
|
|
38
|
+
console.log('限制:', options.limit);
|
|
39
|
+
// 模拟异步查询
|
|
40
|
+
await new Promise(resolve => setTimeout(resolve, 800));
|
|
41
|
+
console.log('查询结果: [模拟数据]');
|
|
42
|
+
});
|
|
43
|
+
await program.parseAsync(process.argv);
|
|
44
|
+
console.log('\n提示: 尝试运行 ts-node src/demo/commander/03-高级特性.ts 1 download https://example.com/file.zip');
|
|
45
|
+
}
|
|
46
|
+
// #endregion
|
|
47
|
+
// #region 示例2: 全局错误处理
|
|
48
|
+
function demoErrorHandling() {
|
|
49
|
+
const program = new commander_1.Command();
|
|
50
|
+
program
|
|
51
|
+
.name('error-handler')
|
|
52
|
+
.description('错误处理示例')
|
|
53
|
+
.version('1.0.0')
|
|
54
|
+
.configureOutput({
|
|
55
|
+
// 自定义错误输出
|
|
56
|
+
outputError: (str, write) => {
|
|
57
|
+
write(`[错误] ${str}`);
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
.exitOverride(); // 阻止自动退出,便于错误处理
|
|
61
|
+
program
|
|
62
|
+
.command('divide <a> <b>')
|
|
63
|
+
.description('除法运算')
|
|
64
|
+
.action((a, b) => {
|
|
65
|
+
const numA = parseFloat(a);
|
|
66
|
+
const numB = parseFloat(b);
|
|
67
|
+
if (isNaN(numA) || isNaN(numB)) {
|
|
68
|
+
throw new Error('参数必须是数字');
|
|
69
|
+
}
|
|
70
|
+
if (numB === 0) {
|
|
71
|
+
throw new Error('除数不能为零');
|
|
72
|
+
}
|
|
73
|
+
console.log('\n示例2: 计算结果:', numA / numB);
|
|
74
|
+
});
|
|
75
|
+
try {
|
|
76
|
+
program.parse(process.argv);
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
console.error('\n捕获到错误:', err.message);
|
|
80
|
+
console.log('\n提示: 尝试运行 ts-node src/demo/commander/03-高级特性.ts 2 divide 10 0');
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// #endregion
|
|
84
|
+
// #region 示例3: 命令执行前后的钩子
|
|
85
|
+
function demoCommandHooks() {
|
|
86
|
+
const program = new commander_1.Command();
|
|
87
|
+
program
|
|
88
|
+
.name('hook-demo')
|
|
89
|
+
.description('命令钩子示例')
|
|
90
|
+
.version('1.0.0');
|
|
91
|
+
// 全局前置钩子
|
|
92
|
+
program.hook('preAction', (thisCommand) => {
|
|
93
|
+
console.log('\n[全局前置钩子] 准备执行命令:', thisCommand.name());
|
|
94
|
+
console.log('[全局前置钩子] 开始时间:', new Date().toISOString());
|
|
95
|
+
});
|
|
96
|
+
// 全局后置钩子
|
|
97
|
+
program.hook('postAction', (thisCommand) => {
|
|
98
|
+
console.log('[全局后置钩子] 命令执行完成:', thisCommand.name());
|
|
99
|
+
console.log('[全局后置钩子] 结束时间:', new Date().toISOString());
|
|
100
|
+
});
|
|
101
|
+
program
|
|
102
|
+
.command('process <file>')
|
|
103
|
+
.description('处理文件')
|
|
104
|
+
.hook('preAction', () => {
|
|
105
|
+
console.log('[命令前置钩子] 验证文件权限...');
|
|
106
|
+
})
|
|
107
|
+
.hook('postAction', () => {
|
|
108
|
+
console.log('[命令后置钩子] 清理临时文件...');
|
|
109
|
+
})
|
|
110
|
+
.action((file) => {
|
|
111
|
+
console.log('\n示例3: 正在处理文件:', file);
|
|
112
|
+
});
|
|
113
|
+
program.parse(process.argv);
|
|
114
|
+
console.log('\n提示: 尝试运行 ts-node src/demo/commander/03-高级特性.ts 3 process data.txt');
|
|
115
|
+
}
|
|
116
|
+
// #endregion
|
|
117
|
+
// #region 示例4: 从配置文件读取选项
|
|
118
|
+
function demoConfigFileLoading() {
|
|
119
|
+
const program = new commander_1.Command();
|
|
120
|
+
// 模拟配置文件内容
|
|
121
|
+
const mockConfig = {
|
|
122
|
+
host: 'localhost',
|
|
123
|
+
port: 3000,
|
|
124
|
+
debug: true,
|
|
125
|
+
database: {
|
|
126
|
+
name: 'mydb',
|
|
127
|
+
user: 'admin'
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
program
|
|
131
|
+
.name('config-loader')
|
|
132
|
+
.description('配置文件加载示例')
|
|
133
|
+
.version('1.0.0');
|
|
134
|
+
program
|
|
135
|
+
.command('start')
|
|
136
|
+
.description('启动服务')
|
|
137
|
+
.option('-c, --config <path>', '配置文件路径')
|
|
138
|
+
.option('-h, --host <host>', '主机地址')
|
|
139
|
+
.option('-p, --port <port>', '端口号')
|
|
140
|
+
.action((options) => {
|
|
141
|
+
console.log('\n示例4: 加载配置...');
|
|
142
|
+
// 优先级:命令行参数 > 配置文件 > 默认值
|
|
143
|
+
let config = { ...mockConfig };
|
|
144
|
+
if (options.config) {
|
|
145
|
+
console.log('从配置文件加载:', options.config);
|
|
146
|
+
// 实际应用中这里会读取真实文件
|
|
147
|
+
// config = JSON.parse(fs.readFileSync(options.config, 'utf-8'));
|
|
148
|
+
}
|
|
149
|
+
// 命令行参数覆盖配置文件
|
|
150
|
+
const finalConfig = {
|
|
151
|
+
host: options.host || config.host,
|
|
152
|
+
port: options.port || config.port,
|
|
153
|
+
debug: config.debug
|
|
154
|
+
};
|
|
155
|
+
console.log('最终配置:', finalConfig);
|
|
156
|
+
});
|
|
157
|
+
program.parse(process.argv);
|
|
158
|
+
console.log('\n提示: 尝试运行 ts-node src/demo/commander/03-高级特性.ts 4 start -h 0.0.0.0 -p 8080');
|
|
159
|
+
}
|
|
160
|
+
// #endregion
|
|
161
|
+
// #region 示例5: 环境变量集成
|
|
162
|
+
function demoEnvironmentVariables() {
|
|
163
|
+
const program = new commander_1.Command();
|
|
164
|
+
program
|
|
165
|
+
.name('env-demo')
|
|
166
|
+
.description('环境变量集成示例')
|
|
167
|
+
.version('1.0.0');
|
|
168
|
+
program
|
|
169
|
+
.command('deploy')
|
|
170
|
+
.description('部署应用')
|
|
171
|
+
.option('-e, --env <environment>', '部署环境', process.env.NODE_ENV || 'development')
|
|
172
|
+
.option('--api-key <key>', 'API 密钥', process.env.API_KEY)
|
|
173
|
+
.option('--region <region>', '部署区域', process.env.AWS_REGION || 'us-east-1')
|
|
174
|
+
.action((options) => {
|
|
175
|
+
console.log('\n示例5: 部署配置');
|
|
176
|
+
console.log('环境:', options.env);
|
|
177
|
+
console.log('API 密钥:', options.apiKey ? '***已设置***' : '未设置');
|
|
178
|
+
console.log('区域:', options.region);
|
|
179
|
+
// 验证必需的环境变量
|
|
180
|
+
if (!options.apiKey) {
|
|
181
|
+
console.error('\n错误: 缺少 API 密钥');
|
|
182
|
+
console.log('请设置环境变量 API_KEY 或使用 --api-key 选项');
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
program.parse(process.argv);
|
|
186
|
+
console.log('\n提示: 尝试运行 API_KEY=secret123 ts-node src/demo/commander/03-高级特性.ts 5 deploy -e production');
|
|
187
|
+
}
|
|
188
|
+
// #endregion
|
|
189
|
+
// #region 示例6: 自定义帮助和版本显示
|
|
190
|
+
function demoCustomOutput() {
|
|
191
|
+
const program = new commander_1.Command();
|
|
192
|
+
program
|
|
193
|
+
.name('custom-output')
|
|
194
|
+
.description('自定义输出示例')
|
|
195
|
+
.version('1.0.0')
|
|
196
|
+
.configureOutput({
|
|
197
|
+
// 自定义标准输出
|
|
198
|
+
writeOut: (str) => {
|
|
199
|
+
process.stdout.write(`[输出] ${str}`);
|
|
200
|
+
},
|
|
201
|
+
// 自定义错误输出
|
|
202
|
+
writeErr: (str) => {
|
|
203
|
+
process.stdout.write(`[错误] ${str}`);
|
|
204
|
+
}
|
|
205
|
+
})
|
|
206
|
+
.configureHelp({
|
|
207
|
+
// 自定义帮助信息格式
|
|
208
|
+
sortSubcommands: true,
|
|
209
|
+
sortOptions: true,
|
|
210
|
+
subcommandTerm: (cmd) => `${cmd.name()} ${cmd.usage()}`,
|
|
211
|
+
})
|
|
212
|
+
.addHelpText('beforeAll', `
|
|
213
|
+
╔════════════════════════════════════════╗
|
|
214
|
+
║ 自定义 CLI 工具 v1.0.0 ║
|
|
215
|
+
╚════════════════════════════════════════╝
|
|
216
|
+
`)
|
|
217
|
+
.addHelpText('afterAll', `
|
|
218
|
+
示例:
|
|
219
|
+
$ custom-output process --input data.txt
|
|
220
|
+
$ custom-output analyze --verbose
|
|
221
|
+
|
|
222
|
+
文档: https://example.com/docs
|
|
223
|
+
`);
|
|
224
|
+
program
|
|
225
|
+
.command('process')
|
|
226
|
+
.description('处理数据')
|
|
227
|
+
.option('-i, --input <file>', '输入文件')
|
|
228
|
+
.action((options) => {
|
|
229
|
+
console.log('\n示例6: 处理数据');
|
|
230
|
+
console.log('输入文件:', options.input);
|
|
231
|
+
});
|
|
232
|
+
program
|
|
233
|
+
.command('analyze')
|
|
234
|
+
.description('分析数据')
|
|
235
|
+
.option('-v, --verbose', '详细输出')
|
|
236
|
+
.action((options) => {
|
|
237
|
+
console.log('\n示例6: 分析数据');
|
|
238
|
+
console.log('详细模式:', options.verbose || false);
|
|
239
|
+
});
|
|
240
|
+
program.parse(process.argv);
|
|
241
|
+
console.log('\n提示: 尝试运行 ts-node src/demo/commander/03-高级特性.ts 6 --help');
|
|
242
|
+
}
|
|
243
|
+
// #endregion
|
|
244
|
+
if (require.main === module) {
|
|
245
|
+
const exampleNumber = process.env.EXAMPLE || process.argv[2];
|
|
246
|
+
// console.log('='.repeat(60));
|
|
247
|
+
// console.log('Commander.js 高级特性示例');
|
|
248
|
+
// console.log('='.repeat(60));
|
|
249
|
+
switch (exampleNumber) {
|
|
250
|
+
case '1':
|
|
251
|
+
process.argv.splice(2, 1);
|
|
252
|
+
demoAsyncCommands();
|
|
253
|
+
break;
|
|
254
|
+
case '2':
|
|
255
|
+
process.argv.splice(2, 1);
|
|
256
|
+
demoErrorHandling();
|
|
257
|
+
break;
|
|
258
|
+
case '3':
|
|
259
|
+
process.argv.splice(2, 1);
|
|
260
|
+
demoCommandHooks();
|
|
261
|
+
break;
|
|
262
|
+
case '4':
|
|
263
|
+
process.argv.splice(2, 1);
|
|
264
|
+
demoConfigFileLoading();
|
|
265
|
+
break;
|
|
266
|
+
case '5':
|
|
267
|
+
process.argv.splice(2, 1);
|
|
268
|
+
demoEnvironmentVariables();
|
|
269
|
+
break;
|
|
270
|
+
case '6':
|
|
271
|
+
process.argv.splice(2, 1);
|
|
272
|
+
demoCustomOutput();
|
|
273
|
+
break;
|
|
274
|
+
default:
|
|
275
|
+
console.log('\n请指定要运行的示例编号 (1-6)');
|
|
276
|
+
console.log('用法: ts-node src/demo/commander/03-高级特性.ts <示例编号> [命令] [选项]');
|
|
277
|
+
console.log('\n可用示例:');
|
|
278
|
+
console.log(' 1 - 异步命令处理');
|
|
279
|
+
console.log(' 2 - 全局错误处理');
|
|
280
|
+
console.log(' 3 - 命令执行前后的钩子');
|
|
281
|
+
console.log(' 4 - 从配置文件读取选项');
|
|
282
|
+
console.log(' 5 - 环境变量集成');
|
|
283
|
+
console.log(' 6 - 自定义帮助和版本显示');
|
|
284
|
+
}
|
|
285
|
+
}
|
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Commander.js 实战案例
|
|
4
|
+
* ======================
|
|
5
|
+
* 本文件涵盖:完整的 CLI 工具实现案例
|
|
6
|
+
* 包括:文件管理工具、项目脚手架、任务管理器
|
|
7
|
+
* 适用版本:commander ^12.0.0
|
|
8
|
+
*/
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
21
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
22
|
+
}) : function(o, v) {
|
|
23
|
+
o["default"] = v;
|
|
24
|
+
});
|
|
25
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
26
|
+
var ownKeys = function(o) {
|
|
27
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
28
|
+
var ar = [];
|
|
29
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
30
|
+
return ar;
|
|
31
|
+
};
|
|
32
|
+
return ownKeys(o);
|
|
33
|
+
};
|
|
34
|
+
return function (mod) {
|
|
35
|
+
if (mod && mod.__esModule) return mod;
|
|
36
|
+
var result = {};
|
|
37
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
38
|
+
__setModuleDefault(result, mod);
|
|
39
|
+
return result;
|
|
40
|
+
};
|
|
41
|
+
})();
|
|
42
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
const commander_1 = require("commander");
|
|
44
|
+
const fs = __importStar(require("fs"));
|
|
45
|
+
const path = __importStar(require("path"));
|
|
46
|
+
// #region 示例1: 简单的文件管理 CLI
|
|
47
|
+
function demoFileManagerCLI() {
|
|
48
|
+
const program = new commander_1.Command();
|
|
49
|
+
program
|
|
50
|
+
.name('fman')
|
|
51
|
+
.description('简单的文件管理工具')
|
|
52
|
+
.version('1.0.0');
|
|
53
|
+
// 列出文件
|
|
54
|
+
program
|
|
55
|
+
.command('list [dir]')
|
|
56
|
+
.alias('ls')
|
|
57
|
+
.description('列出目录内容')
|
|
58
|
+
.option('-a, --all', '显示隐藏文件')
|
|
59
|
+
.action((dir = '.', options) => {
|
|
60
|
+
console.log('\n示例1: 列出文件');
|
|
61
|
+
console.log('目录:', path.resolve(dir));
|
|
62
|
+
try {
|
|
63
|
+
const files = fs.readdirSync(dir);
|
|
64
|
+
const filtered = options.all ? files : files.filter(f => !f.startsWith('.'));
|
|
65
|
+
console.log('\n文件列表:');
|
|
66
|
+
filtered.forEach(file => {
|
|
67
|
+
const stats = fs.statSync(path.join(dir, file));
|
|
68
|
+
const type = stats.isDirectory() ? '[目录]' : '[文件]';
|
|
69
|
+
console.log(` ${type} ${file}`);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
console.error('错误:', err.message);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
// 创建目录
|
|
77
|
+
program
|
|
78
|
+
.command('mkdir <dirs...>')
|
|
79
|
+
.description('创建目录')
|
|
80
|
+
.option('-p, --parents', '创建父目录')
|
|
81
|
+
.action((dirs, options) => {
|
|
82
|
+
console.log('\n示例1: 创建目录');
|
|
83
|
+
dirs.forEach((dir) => {
|
|
84
|
+
try {
|
|
85
|
+
if (options.parents) {
|
|
86
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
fs.mkdirSync(dir);
|
|
90
|
+
}
|
|
91
|
+
console.log('✓ 已创建:', dir);
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
console.error('✗ 失败:', dir, '-', err.message);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
// 删除文件
|
|
99
|
+
program
|
|
100
|
+
.command('remove <files...>')
|
|
101
|
+
.alias('rm')
|
|
102
|
+
.description('删除文件或目录')
|
|
103
|
+
.option('-r, --recursive', '递归删除目录')
|
|
104
|
+
.option('-f, --force', '强制删除')
|
|
105
|
+
.action((files, options) => {
|
|
106
|
+
console.log('\n示例1: 删除文件');
|
|
107
|
+
files.forEach((file) => {
|
|
108
|
+
try {
|
|
109
|
+
const stats = fs.statSync(file);
|
|
110
|
+
if (stats.isDirectory()) {
|
|
111
|
+
if (options.recursive) {
|
|
112
|
+
fs.rmSync(file, { recursive: true, force: options.force });
|
|
113
|
+
console.log('✓ 已删除目录:', file);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
console.error('✗ 是目录,需要 -r 选项:', file);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
fs.unlinkSync(file);
|
|
121
|
+
console.log('✓ 已删除文件:', file);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
if (!options.force) {
|
|
126
|
+
console.error('✗ 失败:', file, '-', err.message);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
program.parse(process.argv);
|
|
132
|
+
console.log('\n提示: 尝试运行 ts-node src/demo/commander/04-实战案例.ts 1 ls . -a');
|
|
133
|
+
}
|
|
134
|
+
// #endregion
|
|
135
|
+
// #region 示例2: 项目脚手架工具
|
|
136
|
+
function demoProjectScaffolder() {
|
|
137
|
+
const program = new commander_1.Command();
|
|
138
|
+
const templates = {
|
|
139
|
+
'node-basic': {
|
|
140
|
+
files: ['index.js', 'package.json', 'README.md'],
|
|
141
|
+
description: '基础 Node.js 项目'
|
|
142
|
+
},
|
|
143
|
+
'express-api': {
|
|
144
|
+
files: ['server.js', 'routes/', 'models/', 'package.json'],
|
|
145
|
+
description: 'Express API 项目'
|
|
146
|
+
},
|
|
147
|
+
'react-app': {
|
|
148
|
+
files: ['src/', 'public/', 'package.json', 'README.md'],
|
|
149
|
+
description: 'React 应用'
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
program
|
|
153
|
+
.name('create-project')
|
|
154
|
+
.description('项目脚手架工具')
|
|
155
|
+
.version('1.0.0');
|
|
156
|
+
// 创建项目
|
|
157
|
+
program
|
|
158
|
+
.command('init <name>')
|
|
159
|
+
.description('初始化新项目')
|
|
160
|
+
.option('-t, --template <template>', '项目模板', 'node-basic')
|
|
161
|
+
.option('--git', '初始化 Git 仓库')
|
|
162
|
+
.option('--install', '自动安装依赖')
|
|
163
|
+
.action((name, options) => {
|
|
164
|
+
console.log('\n示例2: 创建项目');
|
|
165
|
+
console.log('项目名称:', name);
|
|
166
|
+
console.log('模板:', options.template);
|
|
167
|
+
const template = templates[options.template];
|
|
168
|
+
if (!template) {
|
|
169
|
+
console.error('错误: 未知模板');
|
|
170
|
+
console.log('可用模板:', Object.keys(templates).join(', '));
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
console.log('\n创建文件结构:');
|
|
174
|
+
template.files.forEach(file => {
|
|
175
|
+
console.log(` ✓ ${file}`);
|
|
176
|
+
});
|
|
177
|
+
if (options.git) {
|
|
178
|
+
console.log('\n初始化 Git 仓库...');
|
|
179
|
+
console.log(' ✓ .git/');
|
|
180
|
+
console.log(' ✓ .gitignore');
|
|
181
|
+
}
|
|
182
|
+
if (options.install) {
|
|
183
|
+
console.log('\n安装依赖...');
|
|
184
|
+
console.log(' ✓ npm install');
|
|
185
|
+
}
|
|
186
|
+
console.log('\n项目创建完成!');
|
|
187
|
+
console.log(`\n下一步:\n cd ${name}\n npm start`);
|
|
188
|
+
});
|
|
189
|
+
// 列出模板
|
|
190
|
+
program
|
|
191
|
+
.command('list')
|
|
192
|
+
.description('列出可用模板')
|
|
193
|
+
.action(() => {
|
|
194
|
+
console.log('\n示例2: 可用模板\n');
|
|
195
|
+
Object.entries(templates).forEach(([name, info]) => {
|
|
196
|
+
console.log(`${name}`);
|
|
197
|
+
console.log(` ${info.description}`);
|
|
198
|
+
console.log(` 文件: ${info.files.join(', ')}\n`);
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
program.parse(process.argv);
|
|
202
|
+
console.log('\n提示: 尝试运行 ts-node src/demo/commander/04-实战案例.ts 2 init my-app -t express-api --git');
|
|
203
|
+
}
|
|
204
|
+
// #endregion
|
|
205
|
+
// #region 示例3: 任务管理 CLI
|
|
206
|
+
function demoTaskManagerCLI() {
|
|
207
|
+
const program = new commander_1.Command();
|
|
208
|
+
// 模拟任务数据库
|
|
209
|
+
let tasks = [
|
|
210
|
+
{ id: 1, title: '学习 Commander.js', done: false, priority: 'high' },
|
|
211
|
+
{ id: 2, title: '编写示例代码', done: true, priority: 'medium' },
|
|
212
|
+
{ id: 3, title: '测试 CLI 工具', done: false, priority: 'low' }
|
|
213
|
+
];
|
|
214
|
+
program
|
|
215
|
+
.name('todo')
|
|
216
|
+
.description('任务管理工具')
|
|
217
|
+
.version('1.0.0');
|
|
218
|
+
// 添加任务
|
|
219
|
+
program
|
|
220
|
+
.command('add <title>')
|
|
221
|
+
.description('添加新任务')
|
|
222
|
+
.option('-p, --priority <level>', '优先级 (high/medium/low)', 'medium')
|
|
223
|
+
.action((title, options) => {
|
|
224
|
+
const newTask = {
|
|
225
|
+
id: tasks.length + 1,
|
|
226
|
+
title,
|
|
227
|
+
done: false,
|
|
228
|
+
priority: options.priority
|
|
229
|
+
};
|
|
230
|
+
tasks.push(newTask);
|
|
231
|
+
console.log('\n示例3: 任务已添加');
|
|
232
|
+
console.log(`ID: ${newTask.id} | ${newTask.title} [${newTask.priority}]`);
|
|
233
|
+
});
|
|
234
|
+
// 列出任务
|
|
235
|
+
program
|
|
236
|
+
.command('list')
|
|
237
|
+
.alias('ls')
|
|
238
|
+
.description('列出所有任务')
|
|
239
|
+
.option('-a, --all', '显示所有任务')
|
|
240
|
+
.option('-d, --done', '只显示已完成')
|
|
241
|
+
.option('-p, --pending', '只显示未完成')
|
|
242
|
+
.action((options) => {
|
|
243
|
+
console.log('\n示例3: 任务列表\n');
|
|
244
|
+
let filtered = tasks;
|
|
245
|
+
if (options.done) {
|
|
246
|
+
filtered = tasks.filter(t => t.done);
|
|
247
|
+
}
|
|
248
|
+
else if (options.pending) {
|
|
249
|
+
filtered = tasks.filter(t => !t.done);
|
|
250
|
+
}
|
|
251
|
+
if (filtered.length === 0) {
|
|
252
|
+
console.log('没有任务');
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
filtered.forEach(task => {
|
|
256
|
+
const status = task.done ? '✓' : '○';
|
|
257
|
+
const priority = `[${task.priority}]`;
|
|
258
|
+
console.log(`${status} ${task.id}. ${task.title} ${priority}`);
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
// 完成任务
|
|
262
|
+
program
|
|
263
|
+
.command('done <id>')
|
|
264
|
+
.description('标记任务为已完成')
|
|
265
|
+
.action((id) => {
|
|
266
|
+
const task = tasks.find(t => t.id === parseInt(id));
|
|
267
|
+
if (!task) {
|
|
268
|
+
console.error('\n错误: 任务不存在');
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
task.done = true;
|
|
272
|
+
console.log('\n示例3: 任务已完成');
|
|
273
|
+
console.log(`✓ ${task.id}. ${task.title}`);
|
|
274
|
+
});
|
|
275
|
+
// 删除任务
|
|
276
|
+
program
|
|
277
|
+
.command('delete <id>')
|
|
278
|
+
.alias('rm')
|
|
279
|
+
.description('删除任务')
|
|
280
|
+
.action((id) => {
|
|
281
|
+
const index = tasks.findIndex(t => t.id === parseInt(id));
|
|
282
|
+
if (index === -1) {
|
|
283
|
+
console.error('\n错误: 任务不存在');
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
const deleted = tasks.splice(index, 1)[0];
|
|
287
|
+
console.log('\n示例3: 任务已删除');
|
|
288
|
+
console.log(`✗ ${deleted.id}. ${deleted.title}`);
|
|
289
|
+
});
|
|
290
|
+
program.parse(process.argv);
|
|
291
|
+
console.log('\n提示: 尝试运行 ts-node src/demo/commander/04-实战案例.ts 3 list');
|
|
292
|
+
}
|
|
293
|
+
// #endregion
|
|
294
|
+
// #region 示例4: 完整的部署工具
|
|
295
|
+
async function demoDeploymentTool() {
|
|
296
|
+
const program = new commander_1.Command();
|
|
297
|
+
program
|
|
298
|
+
.name('deployer')
|
|
299
|
+
.description('应用部署工具')
|
|
300
|
+
.version('1.0.0');
|
|
301
|
+
// 部署命令
|
|
302
|
+
program
|
|
303
|
+
.command('deploy')
|
|
304
|
+
.description('部署应用')
|
|
305
|
+
.argument('<environment>', '部署环境 (dev/staging/prod)')
|
|
306
|
+
.option('-b, --branch <branch>', 'Git 分支', 'main')
|
|
307
|
+
.option('--skip-tests', '跳过测试')
|
|
308
|
+
.option('--skip-build', '跳过构建')
|
|
309
|
+
.option('-y, --yes', '跳过确认')
|
|
310
|
+
.action(async (environment, options) => {
|
|
311
|
+
console.log('\n示例4: 开始部署流程');
|
|
312
|
+
console.log('='.repeat(50));
|
|
313
|
+
const steps = [
|
|
314
|
+
{ name: '检查环境', skip: false },
|
|
315
|
+
{ name: '拉取代码', skip: false },
|
|
316
|
+
{ name: '运行测试', skip: options.skipTests },
|
|
317
|
+
{ name: '构建应用', skip: options.skipBuild },
|
|
318
|
+
{ name: '上传文件', skip: false },
|
|
319
|
+
{ name: '重启服务', skip: false }
|
|
320
|
+
];
|
|
321
|
+
console.log(`\n环境: ${environment}`);
|
|
322
|
+
console.log(`分支: ${options.branch}`);
|
|
323
|
+
if (!options.yes) {
|
|
324
|
+
console.log('\n部署步骤:');
|
|
325
|
+
steps.forEach((step, i) => {
|
|
326
|
+
const status = step.skip ? '[跳过]' : '[执行]';
|
|
327
|
+
console.log(` ${i + 1}. ${step.name} ${status}`);
|
|
328
|
+
});
|
|
329
|
+
console.log('\n(实际应用中这里会等待用户确认)');
|
|
330
|
+
}
|
|
331
|
+
console.log('\n执行部署...');
|
|
332
|
+
for (const step of steps) {
|
|
333
|
+
if (step.skip)
|
|
334
|
+
continue;
|
|
335
|
+
console.log(`\n▶ ${step.name}...`);
|
|
336
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
337
|
+
console.log(` ✓ 完成`);
|
|
338
|
+
}
|
|
339
|
+
console.log('\n' + '='.repeat(50));
|
|
340
|
+
console.log('部署成功!');
|
|
341
|
+
console.log(`应用已部署到 ${environment} 环境`);
|
|
342
|
+
});
|
|
343
|
+
// 回滚命令
|
|
344
|
+
program
|
|
345
|
+
.command('rollback')
|
|
346
|
+
.description('回滚到上一个版本')
|
|
347
|
+
.argument('<environment>', '环境')
|
|
348
|
+
.option('-v, --version <version>', '指定版本')
|
|
349
|
+
.action(async (environment, options) => {
|
|
350
|
+
console.log('\n示例4: 回滚部署');
|
|
351
|
+
console.log('环境:', environment);
|
|
352
|
+
console.log('版本:', options.version || '上一个版本');
|
|
353
|
+
console.log('\n执行回滚...');
|
|
354
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
355
|
+
console.log('✓ 回滚完成');
|
|
356
|
+
});
|
|
357
|
+
// 查看状态
|
|
358
|
+
program
|
|
359
|
+
.command('status')
|
|
360
|
+
.description('查看部署状态')
|
|
361
|
+
.argument('[environment]', '环境(可选)')
|
|
362
|
+
.action((environment) => {
|
|
363
|
+
console.log('\n示例4: 部署状态\n');
|
|
364
|
+
const envs = environment ? [environment] : ['dev', 'staging', 'prod'];
|
|
365
|
+
envs.forEach(env => {
|
|
366
|
+
console.log(`${env}:`);
|
|
367
|
+
console.log(` 版本: v1.2.3`);
|
|
368
|
+
console.log(` 状态: 运行中`);
|
|
369
|
+
console.log(` 更新时间: 2024-01-15 10:30:00\n`);
|
|
370
|
+
});
|
|
371
|
+
});
|
|
372
|
+
await program.parseAsync(process.argv);
|
|
373
|
+
console.log('\n提示: 尝试运行 ts-node src/demo/commander/04-实战案例.ts 4 deploy staging -b develop');
|
|
374
|
+
}
|
|
375
|
+
// #endregion
|
|
376
|
+
if (require.main === module) {
|
|
377
|
+
const exampleNumber = process.env.EXAMPLE || process.argv[2];
|
|
378
|
+
console.log('='.repeat(60));
|
|
379
|
+
console.log('Commander.js 实战案例');
|
|
380
|
+
console.log('='.repeat(60));
|
|
381
|
+
switch (exampleNumber) {
|
|
382
|
+
case '1':
|
|
383
|
+
process.argv.splice(2, 1);
|
|
384
|
+
demoFileManagerCLI();
|
|
385
|
+
break;
|
|
386
|
+
case '2':
|
|
387
|
+
process.argv.splice(2, 1);
|
|
388
|
+
demoProjectScaffolder();
|
|
389
|
+
break;
|
|
390
|
+
case '3':
|
|
391
|
+
process.argv.splice(2, 1);
|
|
392
|
+
demoTaskManagerCLI();
|
|
393
|
+
break;
|
|
394
|
+
case '4':
|
|
395
|
+
process.argv.splice(2, 1);
|
|
396
|
+
demoDeploymentTool();
|
|
397
|
+
break;
|
|
398
|
+
default:
|
|
399
|
+
console.log('\n请指定要运行的示例编号 (1-4)');
|
|
400
|
+
console.log('用法: ts-node src/demo/commander/04-实战案例.ts <示例编号> [命令] [选项]');
|
|
401
|
+
console.log('\n可用示例:');
|
|
402
|
+
console.log(' 1 - 文件管理 CLI (fman)');
|
|
403
|
+
console.log(' 2 - 项目脚手架工具 (create-project)');
|
|
404
|
+
console.log(' 3 - 任务管理 CLI (todo)');
|
|
405
|
+
console.log(' 4 - 应用部署工具 (deployer)');
|
|
406
|
+
console.log('\n每个示例都是一个完整的 CLI 工具,包含多个子命令');
|
|
407
|
+
}
|
|
408
|
+
}
|