wbw-cli-tools-lib 1.0.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/README.md +291 -0
- package/dist/esm/index.d.ts +738 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/package.json +57 -0
package/README.md
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
# wbw-cli-tools-lib
|
|
2
|
+
|
|
3
|
+
CLI 工具库 - 封装 commander、@inquirer/prompts、ora、chalk、figlet、cli-cursor,提供链式 API、工厂模式、事件系统与插件机制。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install wbw-cli-tools-lib
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 快速开始
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { CliTools } from 'wbw-cli-tools-lib';
|
|
15
|
+
|
|
16
|
+
const cli = CliTools.create({
|
|
17
|
+
commandName: 'my-cli',
|
|
18
|
+
commandDescription: '我的CLI工具',
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
cli
|
|
22
|
+
.printBanner('My App')
|
|
23
|
+
.addOption('-n, --name <name>', '你的名字', 'World')
|
|
24
|
+
.addOption('-d, --debug', '调试模式')
|
|
25
|
+
.addOption('-p, --port <port>', '端口号', 3000)
|
|
26
|
+
.onAction(async (opts) => {
|
|
27
|
+
if (opts.debug) {
|
|
28
|
+
cli.spinnerStart('加载中...');
|
|
29
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
30
|
+
cli.spinnerSucceed('完成!');
|
|
31
|
+
}
|
|
32
|
+
cli.success(`你好, ${opts.name}! 端口: ${opts.port}`);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
cli.parse();
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## 核心特性
|
|
39
|
+
|
|
40
|
+
### 工厂模式
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
const cli = CliTools.create({
|
|
44
|
+
commandName: 'my-cli',
|
|
45
|
+
commandDescription: '描述',
|
|
46
|
+
color: true,
|
|
47
|
+
cursor: true,
|
|
48
|
+
logger: myLogger,
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 链式 API
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
cli
|
|
56
|
+
.setName('my-cli')
|
|
57
|
+
.setVersion('1.0.0')
|
|
58
|
+
.addOption('-d, --debug', '调试模式')
|
|
59
|
+
.success('操作成功')
|
|
60
|
+
.info('提示信息')
|
|
61
|
+
.warn('警告信息')
|
|
62
|
+
.error('错误信息');
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 事件系统
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
// 监听事件
|
|
69
|
+
cli.on('spinner:start', ({ text }) => console.log(`开始: ${text}`));
|
|
70
|
+
cli.on('spinner:stop', () => console.log('停止'));
|
|
71
|
+
cli.on('prompt:cancel', ({ type }) => console.log(`取消: ${type}`));
|
|
72
|
+
cli.on('error', ({ error, context }) => console.error(context, error));
|
|
73
|
+
|
|
74
|
+
// 取消监听
|
|
75
|
+
cli.off('spinner:start', handler);
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**可用事件:**
|
|
79
|
+
|
|
80
|
+
| 事件 | 数据 | 说明 |
|
|
81
|
+
|------|------|------|
|
|
82
|
+
| `init` | `void` | 实例创建后触发 |
|
|
83
|
+
| `parse:before` | `void` | 命令解析前触发 |
|
|
84
|
+
| `parse:after` | `void` | 命令解析后触发 |
|
|
85
|
+
| `spinner:start` | `{ text }` | Spinner 启动 |
|
|
86
|
+
| `spinner:stop` | `void` | Spinner 停止 |
|
|
87
|
+
| `spinner:succeed` | `{ text? }` | Spinner 成功 |
|
|
88
|
+
| `spinner:fail` | `{ text? }` | Spinner 失败 |
|
|
89
|
+
| `prompt:before` | `{ type, message }` | Prompt 开始前 |
|
|
90
|
+
| `prompt:after` | `{ type, result }` | Prompt 结束后 |
|
|
91
|
+
| `prompt:cancel` | `{ type }` | Prompt 被取消 |
|
|
92
|
+
| `plugin:load` | `{ name }` | 插件加载 |
|
|
93
|
+
| `error` | `{ error, context? }` | 错误发生 |
|
|
94
|
+
|
|
95
|
+
### 插件系统
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
const myPlugin: CliToolsPlugin = {
|
|
99
|
+
name: 'my-plugin',
|
|
100
|
+
version: '1.0.0',
|
|
101
|
+
install(cli) {
|
|
102
|
+
cli.on('init', () => console.log('Plugin loaded'));
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// 注册插件
|
|
107
|
+
await cli.use(myPlugin);
|
|
108
|
+
|
|
109
|
+
// 获取已加载插件
|
|
110
|
+
console.log(cli.getPlugins()); // ['my-plugin']
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### 配置文件加载
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
// .myclirc
|
|
117
|
+
{ "theme": "dark", "verbose": true }
|
|
118
|
+
|
|
119
|
+
// 加载配置
|
|
120
|
+
const { config, source } = await cli.loadConfig({
|
|
121
|
+
sources: ['.myclirc', 'package.json#myCli'],
|
|
122
|
+
defaults: { theme: 'light', verbose: false },
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// 获取配置值
|
|
126
|
+
const theme = cli.getConfigValue<string>('theme', 'light');
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### 自定义日志器
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
const myLogger: Logger = {
|
|
133
|
+
level: 'info', // 只显示 info 及以上级别
|
|
134
|
+
log: (...args) => console.log('[LOG]', ...args),
|
|
135
|
+
error: (...args) => console.error('[ERR]', ...args),
|
|
136
|
+
warn: (...args) => console.warn('[WARN]', ...args),
|
|
137
|
+
info: (...args) => console.info('[INFO]', ...args),
|
|
138
|
+
debug: (...args) => console.debug('[DEBUG]', ...args),
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const cli = CliTools.create({ logger: myLogger });
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### 错误处理
|
|
145
|
+
|
|
146
|
+
所有 prompt 方法在用户取消时返回 `null`(而非抛出异常):
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
const name = await cli.promptInput('请输入名字:');
|
|
150
|
+
if (name === null) {
|
|
151
|
+
cli.warn('已取消');
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const lang = await cli.promptSelect('选择语言:', ['TS', 'JS']);
|
|
156
|
+
if (lang === null) return;
|
|
157
|
+
|
|
158
|
+
const pwd = await cli.promptPassword('密码:', { minLength: 6 });
|
|
159
|
+
if (pwd === null) return;
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### 泛型 ChoiceOption
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
// 使用对象选项
|
|
166
|
+
await cli.promptSelect('选择:', [
|
|
167
|
+
{ value: 'ts', name: 'TypeScript' },
|
|
168
|
+
{ value: 'js', name: 'JavaScript' },
|
|
169
|
+
]);
|
|
170
|
+
|
|
171
|
+
// 简写(字符串)
|
|
172
|
+
await cli.promptSelect('选择:', ['TypeScript', 'JavaScript']);
|
|
173
|
+
|
|
174
|
+
// 带描述和禁用状态
|
|
175
|
+
await cli.promptSelect('选择:', [
|
|
176
|
+
{ value: 'ts', name: 'TypeScript', description: '强类型' },
|
|
177
|
+
{ value: 'legacy', name: 'Legacy', disabled: '已废弃' },
|
|
178
|
+
]);
|
|
179
|
+
|
|
180
|
+
// checkbox 默认选中
|
|
181
|
+
await cli.promptCheckbox('选择功能:', [
|
|
182
|
+
{ value: 'eslint', name: 'ESLint', checked: true },
|
|
183
|
+
{ value: 'prettier', name: 'Prettier' },
|
|
184
|
+
]);
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## API 列表
|
|
188
|
+
|
|
189
|
+
### 工厂方法
|
|
190
|
+
|
|
191
|
+
| 方法 | 说明 |
|
|
192
|
+
|------|------|
|
|
193
|
+
| `CliTools.create(options?)` | 创建实例 |
|
|
194
|
+
|
|
195
|
+
### Commander
|
|
196
|
+
|
|
197
|
+
| 方法 | 说明 |
|
|
198
|
+
|------|------|
|
|
199
|
+
| `setName(name)` | 设置命令名称 |
|
|
200
|
+
| `setVersion(version)` | 设置版本号 |
|
|
201
|
+
| `setDescription(desc)` | 设置描述 |
|
|
202
|
+
| `addOption(flags, desc, default?)` | 添加选项 |
|
|
203
|
+
| `addArgument(name, desc)` | 添加参数 |
|
|
204
|
+
| `addCommand(name, desc, action)` | 注册子命令 |
|
|
205
|
+
| `onAction(callback)` | 设置主回调 |
|
|
206
|
+
| `parse(argv?)` | 解析参数 |
|
|
207
|
+
| `parseAsync(argv?)` | 异步解析 |
|
|
208
|
+
| `getProgram()` | 获取 Commander 实例 |
|
|
209
|
+
|
|
210
|
+
### Inquirer Prompts
|
|
211
|
+
|
|
212
|
+
| 方法 | 返回值 | 说明 |
|
|
213
|
+
|------|--------|------|
|
|
214
|
+
| `promptInput(msg, opts?)` | `string \| null` | 文本输入 |
|
|
215
|
+
| `promptConfirm(msg, default?)` | `boolean \| null` | 确认提示 |
|
|
216
|
+
| `promptSelect(msg, choices)` | `string \| null` | 列表选择 |
|
|
217
|
+
| `promptCheckbox(msg, choices)` | `string[] \| null` | 多选 |
|
|
218
|
+
| `promptPassword(msg, opts?)` | `string \| null` | 密码输入 |
|
|
219
|
+
|
|
220
|
+
### Spinner
|
|
221
|
+
|
|
222
|
+
| 方法 | 说明 |
|
|
223
|
+
|------|------|
|
|
224
|
+
| `spinnerStart(text, opts?)` | 启动 Spinner |
|
|
225
|
+
| `spinnerStop()` | 停止 |
|
|
226
|
+
| `spinnerSucceed(text?)` | 成功状态 |
|
|
227
|
+
| `spinnerFail(text?)` | 失败状态 |
|
|
228
|
+
| `spinnerWarn(text?)` | 警告状态 |
|
|
229
|
+
| `spinnerInfo(text?)` | 信息状态 |
|
|
230
|
+
| `spinnerPersist(symbol?, text?)` | 持久化 |
|
|
231
|
+
| `spinnerText(text)` | 更新文本 |
|
|
232
|
+
| `getSpinner()` | 获取实例 |
|
|
233
|
+
|
|
234
|
+
### 颜色输出
|
|
235
|
+
|
|
236
|
+
| 方法 | 说明 |
|
|
237
|
+
|------|------|
|
|
238
|
+
| `logRed/Green/Yellow/Blue/Cyan/Magenta/White/Gray(text)` | 彩色输出 |
|
|
239
|
+
| `logBold(text)` | 加粗 |
|
|
240
|
+
| `logUnderline(text)` | 下划线 |
|
|
241
|
+
| `success(text)` | ✔ 成功 |
|
|
242
|
+
| `error(text)` | ✖ 错误 |
|
|
243
|
+
| `warn(text)` | ⚠ 警告 |
|
|
244
|
+
| `info(text)` | ℹ 信息 |
|
|
245
|
+
|
|
246
|
+
### ASCII Art
|
|
247
|
+
|
|
248
|
+
| 方法 | 说明 |
|
|
249
|
+
|------|------|
|
|
250
|
+
| `printBanner(text, font?)` | 生成并输出 |
|
|
251
|
+
| `banner(text, font?)` | 生成(不输出) |
|
|
252
|
+
| `bannerAsync(text, font?)` | 异步生成 |
|
|
253
|
+
| `getFonts()` | 获取字体列表 |
|
|
254
|
+
|
|
255
|
+
### 光标控制
|
|
256
|
+
|
|
257
|
+
| 方法 | 说明 |
|
|
258
|
+
|------|------|
|
|
259
|
+
| `cursorShow()` | 显示 |
|
|
260
|
+
| `cursorHide()` | 隐藏 |
|
|
261
|
+
| `cursorToggle(force?)` | 切换 |
|
|
262
|
+
|
|
263
|
+
### 日志工具
|
|
264
|
+
|
|
265
|
+
| 方法 | 说明 |
|
|
266
|
+
|------|------|
|
|
267
|
+
| `print(text)` | 输出日志 |
|
|
268
|
+
| `logWithPrefix(prefix, text)` | 带前缀日志 |
|
|
269
|
+
| `newline()` | 空行 |
|
|
270
|
+
| `divider(char?, length?)` | 分隔线 |
|
|
271
|
+
|
|
272
|
+
### 事件与插件
|
|
273
|
+
|
|
274
|
+
| 方法 | 说明 |
|
|
275
|
+
|------|------|
|
|
276
|
+
| `on(event, handler)` | 监听事件 |
|
|
277
|
+
| `off(event, handler)` | 取消监听 |
|
|
278
|
+
| `use(plugin)` | 注册插件 |
|
|
279
|
+
| `getPlugins()` | 获取插件列表 |
|
|
280
|
+
|
|
281
|
+
### 配置
|
|
282
|
+
|
|
283
|
+
| 方法 | 说明 |
|
|
284
|
+
|------|------|
|
|
285
|
+
| `loadConfig(options)` | 加载配置文件 |
|
|
286
|
+
| `getConfig()` | 获取配置 |
|
|
287
|
+
| `getConfigValue<T>(key, default?)` | 获取配置值 |
|
|
288
|
+
|
|
289
|
+
## 许可证
|
|
290
|
+
|
|
291
|
+
GPLv3.0-only
|
|
@@ -0,0 +1,738 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { Options, Ora } from "ora";
|
|
3
|
+
//#region src/types.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* CLI 工具配置选项
|
|
6
|
+
* @public
|
|
7
|
+
*/
|
|
8
|
+
interface CliToolsOptions {
|
|
9
|
+
/**
|
|
10
|
+
* 是否启用彩色输出(默认:true)
|
|
11
|
+
*/
|
|
12
|
+
color?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* 是否启用光标控制(默认:true)
|
|
15
|
+
*/
|
|
16
|
+
cursor?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* 默认命令名称
|
|
19
|
+
*/
|
|
20
|
+
commandName?: string;
|
|
21
|
+
/**
|
|
22
|
+
* 默认命令描述
|
|
23
|
+
*/
|
|
24
|
+
commandDescription?: string;
|
|
25
|
+
/**
|
|
26
|
+
* 自定义日志器(默认:console)
|
|
27
|
+
*/
|
|
28
|
+
logger?: Logger;
|
|
29
|
+
/**
|
|
30
|
+
* 已注册的插件列表
|
|
31
|
+
*/
|
|
32
|
+
plugins?: CliToolsPlugin[];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* 日志级别
|
|
36
|
+
* @public
|
|
37
|
+
*/
|
|
38
|
+
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
39
|
+
/**
|
|
40
|
+
* 自定义日志器接口
|
|
41
|
+
* @public
|
|
42
|
+
*/
|
|
43
|
+
interface Logger {
|
|
44
|
+
/** 当前日志级别(低于此级别的日志将被忽略) */
|
|
45
|
+
level?: LogLevel;
|
|
46
|
+
log: (...args: unknown[]) => void;
|
|
47
|
+
error?: (...args: unknown[]) => void;
|
|
48
|
+
warn?: (...args: unknown[]) => void;
|
|
49
|
+
info?: (...args: unknown[]) => void;
|
|
50
|
+
debug?: (...args: unknown[]) => void;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* 事件映射表
|
|
54
|
+
* @public
|
|
55
|
+
*/
|
|
56
|
+
interface CliToolsEventMap {
|
|
57
|
+
/** 实例创建后触发 */
|
|
58
|
+
'init': void;
|
|
59
|
+
/** 命令解析前触发 */
|
|
60
|
+
'parse:before': void;
|
|
61
|
+
/** 命令解析后触发 */
|
|
62
|
+
'parse:after': void;
|
|
63
|
+
/** Spinner 启动时触发 */
|
|
64
|
+
'spinner:start': {
|
|
65
|
+
text: string;
|
|
66
|
+
};
|
|
67
|
+
/** Spinner 停止时触发 */
|
|
68
|
+
'spinner:stop': void;
|
|
69
|
+
/** Spinner 成功时触发 */
|
|
70
|
+
'spinner:succeed': {
|
|
71
|
+
text?: string;
|
|
72
|
+
};
|
|
73
|
+
/** Spinner 失败时触发 */
|
|
74
|
+
'spinner:fail': {
|
|
75
|
+
text?: string;
|
|
76
|
+
};
|
|
77
|
+
/** Prompt 开始前触发 */
|
|
78
|
+
'prompt:before': {
|
|
79
|
+
type: string;
|
|
80
|
+
message: string;
|
|
81
|
+
};
|
|
82
|
+
/** Prompt 结束后触发 */
|
|
83
|
+
'prompt:after': {
|
|
84
|
+
type: string;
|
|
85
|
+
result: unknown;
|
|
86
|
+
};
|
|
87
|
+
/** Prompt 被取消时触发 */
|
|
88
|
+
'prompt:cancel': {
|
|
89
|
+
type: string;
|
|
90
|
+
};
|
|
91
|
+
/** 插件加载时触发 */
|
|
92
|
+
'plugin:load': {
|
|
93
|
+
name: string;
|
|
94
|
+
};
|
|
95
|
+
/** 错误发生时触发 */
|
|
96
|
+
'error': {
|
|
97
|
+
error: Error;
|
|
98
|
+
context?: string;
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* 事件回调函数类型
|
|
103
|
+
* @public
|
|
104
|
+
*/
|
|
105
|
+
type CliToolsEventHandler<T = void> = (data: T) => void | Promise<void>;
|
|
106
|
+
/**
|
|
107
|
+
* 插件接口
|
|
108
|
+
* @public
|
|
109
|
+
*/
|
|
110
|
+
interface CliToolsPlugin {
|
|
111
|
+
/** 插件名称 */
|
|
112
|
+
name: string;
|
|
113
|
+
/** 插件版本 */
|
|
114
|
+
version?: string;
|
|
115
|
+
/**
|
|
116
|
+
* 安装插件
|
|
117
|
+
* @param cli - CliTools 实例
|
|
118
|
+
*/
|
|
119
|
+
install(cli: unknown): void | Promise<void>;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* 列表选项(用于 select/checkbox 提示)
|
|
123
|
+
* @public
|
|
124
|
+
*/
|
|
125
|
+
interface ChoiceOption<T = string> {
|
|
126
|
+
/** 选项值 */
|
|
127
|
+
value: T;
|
|
128
|
+
/** 选项显示名称 */
|
|
129
|
+
name: string;
|
|
130
|
+
/** 选项描述 */
|
|
131
|
+
description?: string;
|
|
132
|
+
/** 是否禁用 */
|
|
133
|
+
disabled?: boolean | string;
|
|
134
|
+
/** 是否默认选中(仅 checkbox) */
|
|
135
|
+
checked?: boolean;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* promptPassword 选项
|
|
139
|
+
* @public
|
|
140
|
+
*/
|
|
141
|
+
interface PromptPasswordOptions {
|
|
142
|
+
/** 遮罩字符(默认 '*') */
|
|
143
|
+
mask?: string | boolean;
|
|
144
|
+
/** 最小长度(0 = 不限制) */
|
|
145
|
+
minLength?: number;
|
|
146
|
+
/** 验证函数 */
|
|
147
|
+
validate?: (value: string) => boolean | string | Promise<string | boolean>;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* promptInput 选项
|
|
151
|
+
* @public
|
|
152
|
+
*/
|
|
153
|
+
interface PromptInputOptions {
|
|
154
|
+
/** 默认值 */
|
|
155
|
+
defaultValue?: string;
|
|
156
|
+
/** 验证函数 */
|
|
157
|
+
validate?: (value: string) => boolean | string | Promise<string | boolean>;
|
|
158
|
+
/** 是否必填 */
|
|
159
|
+
required?: boolean;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* 配置文件加载选项
|
|
163
|
+
* @public
|
|
164
|
+
*/
|
|
165
|
+
interface LoadConfigOptions {
|
|
166
|
+
/** 配置文件路径列表(按优先级排序) */
|
|
167
|
+
sources?: string[];
|
|
168
|
+
/** 默认配置 */
|
|
169
|
+
defaults?: Record<string, unknown>;
|
|
170
|
+
/** 是否自动合并 defaults(默认:true) */
|
|
171
|
+
mergeDefaults?: boolean;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* 已解析的配置结果
|
|
175
|
+
* @public
|
|
176
|
+
*/
|
|
177
|
+
interface ResolvedConfig {
|
|
178
|
+
/** 配置来源文件路径 */
|
|
179
|
+
source?: string;
|
|
180
|
+
/** 配置内容 */
|
|
181
|
+
config: Record<string, unknown>;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Option 类型安全定义
|
|
185
|
+
* @public
|
|
186
|
+
*/
|
|
187
|
+
type OptionValue = string | boolean | number;
|
|
188
|
+
//#endregion
|
|
189
|
+
//#region src/cli-tools.d.ts
|
|
190
|
+
/**
|
|
191
|
+
* CLI 工具库主类
|
|
192
|
+
*
|
|
193
|
+
* 封装 commander、@inquirer/prompts、ora、chalk、figlet、cli-cursor,
|
|
194
|
+
* 提供链式 API、工厂模式、事件系统与插件机制。
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* ```ts
|
|
198
|
+
* const cli = CliTools.create({
|
|
199
|
+
* commandName: 'my-cli',
|
|
200
|
+
* commandDescription: '我的CLI工具',
|
|
201
|
+
* });
|
|
202
|
+
*
|
|
203
|
+
* cli
|
|
204
|
+
* .printBanner('My App')
|
|
205
|
+
* .addOption('-d, --debug', '调试模式')
|
|
206
|
+
* .onAction(async (opts, cmd) => {
|
|
207
|
+
* if (opts.debug) cli.success('调试已开启');
|
|
208
|
+
* });
|
|
209
|
+
*
|
|
210
|
+
* cli.parse();
|
|
211
|
+
* ```
|
|
212
|
+
*
|
|
213
|
+
* @public
|
|
214
|
+
*/
|
|
215
|
+
declare class CliTools {
|
|
216
|
+
private _program;
|
|
217
|
+
private _spinner;
|
|
218
|
+
private _options;
|
|
219
|
+
private _cursorHidden;
|
|
220
|
+
private _exitHandlerRegistered;
|
|
221
|
+
private _plugins;
|
|
222
|
+
private _eventHandlers;
|
|
223
|
+
private _config;
|
|
224
|
+
private constructor();
|
|
225
|
+
/**
|
|
226
|
+
* 工厂方法:创建 CliTools 实例
|
|
227
|
+
*
|
|
228
|
+
* @param options - 配置选项
|
|
229
|
+
* @returns CliTools 实例
|
|
230
|
+
*
|
|
231
|
+
* @example
|
|
232
|
+
* ```ts
|
|
233
|
+
* const cli = CliTools.create({
|
|
234
|
+
* commandName: 'my-cli',
|
|
235
|
+
* commandDescription: '我的CLI工具',
|
|
236
|
+
* logger: myLogger,
|
|
237
|
+
* plugins: [myPlugin],
|
|
238
|
+
* });
|
|
239
|
+
* ```
|
|
240
|
+
*/
|
|
241
|
+
static create(options?: CliToolsOptions): CliTools;
|
|
242
|
+
/**
|
|
243
|
+
* 监听事件
|
|
244
|
+
*
|
|
245
|
+
* @param event - 事件名称
|
|
246
|
+
* @param handler - 事件处理器
|
|
247
|
+
* @returns 当前实例(支持链式调用)
|
|
248
|
+
*
|
|
249
|
+
* @example
|
|
250
|
+
* ```ts
|
|
251
|
+
* cli.on('spinner:stop', () => console.log('Spinner 已停止'));
|
|
252
|
+
* cli.on('error', ({ error }) => console.error(error));
|
|
253
|
+
* ```
|
|
254
|
+
*/
|
|
255
|
+
on<K extends keyof CliToolsEventMap>(event: K, handler: CliToolsEventHandler<CliToolsEventMap[K]>): this;
|
|
256
|
+
/**
|
|
257
|
+
* 取消监听事件
|
|
258
|
+
*
|
|
259
|
+
* @param event - 事件名称
|
|
260
|
+
* @param handler - 要移除的事件处理器
|
|
261
|
+
* @returns 当前实例
|
|
262
|
+
*/
|
|
263
|
+
off<K extends keyof CliToolsEventMap>(event: K, handler: CliToolsEventHandler<CliToolsEventMap[K]>): this;
|
|
264
|
+
/**
|
|
265
|
+
* 触发事件(仅内部使用)
|
|
266
|
+
*
|
|
267
|
+
* @private
|
|
268
|
+
*/
|
|
269
|
+
private _emit;
|
|
270
|
+
/**
|
|
271
|
+
* 注册插件
|
|
272
|
+
*
|
|
273
|
+
* @param plugin - 插件实例
|
|
274
|
+
* @returns 当前实例
|
|
275
|
+
*
|
|
276
|
+
* @example
|
|
277
|
+
* ```ts
|
|
278
|
+
* const myPlugin: CliToolsPlugin = {
|
|
279
|
+
* name: 'my-plugin',
|
|
280
|
+
* install(cli) {
|
|
281
|
+
* cli.on('init', () => console.log('Plugin loaded'));
|
|
282
|
+
* },
|
|
283
|
+
* };
|
|
284
|
+
* cli.use(myPlugin);
|
|
285
|
+
* ```
|
|
286
|
+
*/
|
|
287
|
+
use(plugin: CliToolsPlugin): Promise<this>;
|
|
288
|
+
/**
|
|
289
|
+
* 获取已加载的插件列表
|
|
290
|
+
*
|
|
291
|
+
* @returns 插件名称数组
|
|
292
|
+
*/
|
|
293
|
+
getPlugins(): string[];
|
|
294
|
+
/**
|
|
295
|
+
* 加载配置文件
|
|
296
|
+
*
|
|
297
|
+
* 支持 JSON 格式的配置文件。按 sources 顺序查找,优先使用第一个找到的文件。
|
|
298
|
+
*
|
|
299
|
+
* @param options - 配置加载选项
|
|
300
|
+
* @returns 解析后的配置结果,未找到文件则返回 defaults
|
|
301
|
+
*
|
|
302
|
+
* @example
|
|
303
|
+
* ```ts
|
|
304
|
+
* const { config } = await cli.loadConfig({
|
|
305
|
+
* sources: ['.myclirc', 'package.json#myCli'],
|
|
306
|
+
* defaults: { theme: 'dark', verbose: false },
|
|
307
|
+
* });
|
|
308
|
+
* console.log(config.theme); // 'dark'
|
|
309
|
+
* ```
|
|
310
|
+
*/
|
|
311
|
+
loadConfig(options?: LoadConfigOptions): Promise<ResolvedConfig>;
|
|
312
|
+
/**
|
|
313
|
+
* 获取已加载的配置
|
|
314
|
+
*
|
|
315
|
+
* @returns 当前配置对象
|
|
316
|
+
*/
|
|
317
|
+
getConfig(): Record<string, unknown>;
|
|
318
|
+
/**
|
|
319
|
+
* 获取配置值(支持类型断言)
|
|
320
|
+
*
|
|
321
|
+
* @param key - 配置键名
|
|
322
|
+
* @param defaultValue - 默认值
|
|
323
|
+
* @returns 配置值
|
|
324
|
+
*
|
|
325
|
+
* @example
|
|
326
|
+
* ```ts
|
|
327
|
+
* const theme = cli.getConfigValue<string>('theme', 'light');
|
|
328
|
+
* ```
|
|
329
|
+
*/
|
|
330
|
+
getConfigValue<T>(key: string, defaultValue?: T): T;
|
|
331
|
+
/**
|
|
332
|
+
* 设置命令名称
|
|
333
|
+
*
|
|
334
|
+
* @param name - 命令名称
|
|
335
|
+
* @returns 当前实例
|
|
336
|
+
*/
|
|
337
|
+
setName(name: string): this;
|
|
338
|
+
/**
|
|
339
|
+
* 设置命令版本号
|
|
340
|
+
*
|
|
341
|
+
* @param version - 版本号字符串
|
|
342
|
+
* @returns 当前实例
|
|
343
|
+
*/
|
|
344
|
+
setVersion(version: string): this;
|
|
345
|
+
/**
|
|
346
|
+
* 设置命令描述
|
|
347
|
+
*
|
|
348
|
+
* @param description - 描述文本
|
|
349
|
+
* @returns 当前实例
|
|
350
|
+
*/
|
|
351
|
+
setDescription(description: string): this;
|
|
352
|
+
/**
|
|
353
|
+
* 添加命令行选项
|
|
354
|
+
*
|
|
355
|
+
* @param flags - 选项标志(如 `'-d, --debug'`)
|
|
356
|
+
* @param description - 选项描述
|
|
357
|
+
* @param defaultValue - 默认值(仅支持 string | boolean | number)
|
|
358
|
+
* @returns 当前实例
|
|
359
|
+
*
|
|
360
|
+
* @example
|
|
361
|
+
* ```ts
|
|
362
|
+
* cli.addOption('-n, --name <name>', '你的名字', 'World');
|
|
363
|
+
* cli.addOption('-d, --debug', '调试模式');
|
|
364
|
+
* cli.addOption('-p, --port <port>', '端口号', 3000);
|
|
365
|
+
* ```
|
|
366
|
+
*/
|
|
367
|
+
addOption(flags: string, description: string, defaultValue?: OptionValue): this;
|
|
368
|
+
/**
|
|
369
|
+
* 添加命令行参数
|
|
370
|
+
*
|
|
371
|
+
* @param name - 参数名称(如 `'<file>'` 或 `'[file]'`)
|
|
372
|
+
* @param description - 参数描述
|
|
373
|
+
* @returns 当前实例
|
|
374
|
+
*/
|
|
375
|
+
addArgument(name: string, description: string): this;
|
|
376
|
+
/**
|
|
377
|
+
* 注册子命令
|
|
378
|
+
*
|
|
379
|
+
* @param name - 子命令名称
|
|
380
|
+
* @param description - 子命令描述
|
|
381
|
+
* @param action - 子命令执行回调
|
|
382
|
+
* @returns 当前实例
|
|
383
|
+
*
|
|
384
|
+
* @example
|
|
385
|
+
* ```ts
|
|
386
|
+
* cli.addCommand('init', '初始化项目', async (options, cmd) => {
|
|
387
|
+
* console.log('项目已初始化');
|
|
388
|
+
* });
|
|
389
|
+
* ```
|
|
390
|
+
*/
|
|
391
|
+
addCommand(name: string, description: string, action: (...args: unknown[]) => void | Promise<void>): this;
|
|
392
|
+
/**
|
|
393
|
+
* 设置主命令的执行回调
|
|
394
|
+
*
|
|
395
|
+
* @param action - 执行回调,参数为解析后的 options 和 Command 实例
|
|
396
|
+
* @returns 当前实例
|
|
397
|
+
*/
|
|
398
|
+
onAction(action: (options: Record<string, unknown>, command: Command) => void | Promise<void>): this;
|
|
399
|
+
/**
|
|
400
|
+
* 解析命令行参数
|
|
401
|
+
*
|
|
402
|
+
* @param argv - 参数数组(默认 process.argv)
|
|
403
|
+
* @returns 当前实例
|
|
404
|
+
*/
|
|
405
|
+
parse(argv?: string[]): this;
|
|
406
|
+
/**
|
|
407
|
+
* 解析命令行参数并返回 Command 实例
|
|
408
|
+
*
|
|
409
|
+
* @param argv - 参数数组(默认 process.argv)
|
|
410
|
+
* @returns Commander Command 实例
|
|
411
|
+
*/
|
|
412
|
+
parseAsync(argv?: string[]): Promise<Command>;
|
|
413
|
+
/**
|
|
414
|
+
* 获取底层 Commander Command 实例,用于高级定制
|
|
415
|
+
*
|
|
416
|
+
* @returns Commander Command 实例
|
|
417
|
+
*/
|
|
418
|
+
getProgram(): Command;
|
|
419
|
+
/**
|
|
420
|
+
* 文本输入提示
|
|
421
|
+
*
|
|
422
|
+
* @param message - 提示消息
|
|
423
|
+
* @param options - 额外配置
|
|
424
|
+
* @returns 用户输入的文本,用户取消返回 null
|
|
425
|
+
*
|
|
426
|
+
* @example
|
|
427
|
+
* ```ts
|
|
428
|
+
* const name = await cli.promptInput('请输入你的名字:', { defaultValue: 'wbw' });
|
|
429
|
+
* if (name === null) cli.warn('已取消');
|
|
430
|
+
* ```
|
|
431
|
+
*/
|
|
432
|
+
promptInput(message: string, options?: PromptInputOptions): Promise<string | null>;
|
|
433
|
+
/**
|
|
434
|
+
* 确认提示(是/否)
|
|
435
|
+
*
|
|
436
|
+
* @param message - 提示消息
|
|
437
|
+
* @param defaultValue - 默认值
|
|
438
|
+
* @returns 用户选择的结果,用户取消返回 null
|
|
439
|
+
*
|
|
440
|
+
* @example
|
|
441
|
+
* ```ts
|
|
442
|
+
* const ok = await cli.promptConfirm('确认继续?');
|
|
443
|
+
* if (ok === null) cli.warn('已取消');
|
|
444
|
+
* ```
|
|
445
|
+
*/
|
|
446
|
+
promptConfirm(message: string, defaultValue?: boolean): Promise<boolean | null>;
|
|
447
|
+
/**
|
|
448
|
+
* 列表选择提示
|
|
449
|
+
*
|
|
450
|
+
* @param message - 提示消息
|
|
451
|
+
* @param choices - 选项数组
|
|
452
|
+
* @returns 用户选择的值,用户取消返回 null
|
|
453
|
+
*
|
|
454
|
+
* @example
|
|
455
|
+
* ```ts
|
|
456
|
+
* const lang = await cli.promptSelect('选择语言:', ['TypeScript', 'JavaScript']);
|
|
457
|
+
* if (lang === null) return;
|
|
458
|
+
* cli.success(`你选择了: ${lang}`);
|
|
459
|
+
* ```
|
|
460
|
+
*/
|
|
461
|
+
promptSelect(message: string, choices: (string | ChoiceOption)[]): Promise<string | null>;
|
|
462
|
+
/**
|
|
463
|
+
* 多选提示(复选框)
|
|
464
|
+
*
|
|
465
|
+
* @param message - 提示消息
|
|
466
|
+
* @param choices - 选项数组
|
|
467
|
+
* @returns 用户选择的值数组,用户取消返回 null
|
|
468
|
+
*
|
|
469
|
+
* @example
|
|
470
|
+
* ```ts
|
|
471
|
+
* const features = await cli.promptCheckbox('选择功能:', ['ESLint', 'Prettier']);
|
|
472
|
+
* if (features === null) return;
|
|
473
|
+
* cli.success(`已选择: ${features.join(', ')}`);
|
|
474
|
+
* ```
|
|
475
|
+
*/
|
|
476
|
+
promptCheckbox(message: string, choices: (string | ChoiceOption)[]): Promise<string[] | null>;
|
|
477
|
+
/**
|
|
478
|
+
* 密码输入提示(内容隐藏)
|
|
479
|
+
*
|
|
480
|
+
* @param message - 提示消息
|
|
481
|
+
* @param options - 密码选项(mask、minLength、validate)
|
|
482
|
+
* @returns 用户输入的密码文本,用户取消返回 null
|
|
483
|
+
*
|
|
484
|
+
* @example
|
|
485
|
+
* ```ts
|
|
486
|
+
* const pwd = await cli.promptPassword('请输入密码:', { minLength: 6 });
|
|
487
|
+
* if (pwd === null) return;
|
|
488
|
+
* cli.success(`密码长度: ${pwd.length}`);
|
|
489
|
+
* ```
|
|
490
|
+
*/
|
|
491
|
+
promptPassword(message: string, options?: PromptPasswordOptions): Promise<string | null>;
|
|
492
|
+
/**
|
|
493
|
+
* 创建并启动 Spinner
|
|
494
|
+
*
|
|
495
|
+
* @param text - 显示文本
|
|
496
|
+
* @param options - ora 配置选项
|
|
497
|
+
* @returns 当前实例
|
|
498
|
+
*
|
|
499
|
+
* @example
|
|
500
|
+
* ```ts
|
|
501
|
+
* cli.spinnerStart('加载中...').spinnerSucceed('完成!');
|
|
502
|
+
* ```
|
|
503
|
+
*/
|
|
504
|
+
spinnerStart(text: string, options?: Options): this;
|
|
505
|
+
/**
|
|
506
|
+
* 停止 Spinner
|
|
507
|
+
*
|
|
508
|
+
* @returns 当前实例
|
|
509
|
+
*/
|
|
510
|
+
spinnerStop(): this;
|
|
511
|
+
/**
|
|
512
|
+
* Spinner 成功状态(✔)
|
|
513
|
+
*
|
|
514
|
+
* @param text - 成功消息
|
|
515
|
+
* @returns 当前实例
|
|
516
|
+
*/
|
|
517
|
+
spinnerSucceed(text?: string): this;
|
|
518
|
+
/**
|
|
519
|
+
* Spinner 失败状态(✖)
|
|
520
|
+
*
|
|
521
|
+
* @param text - 失败消息
|
|
522
|
+
* @returns 当前实例
|
|
523
|
+
*/
|
|
524
|
+
spinnerFail(text?: string): this;
|
|
525
|
+
/**
|
|
526
|
+
* Spinner 警告状态(⚠)
|
|
527
|
+
*
|
|
528
|
+
* @param text - 警告消息
|
|
529
|
+
* @returns 当前实例
|
|
530
|
+
*/
|
|
531
|
+
spinnerWarn(text?: string): this;
|
|
532
|
+
/**
|
|
533
|
+
* Spinner 信息状态(ℹ)
|
|
534
|
+
*
|
|
535
|
+
* @param text - 信息消息
|
|
536
|
+
* @returns 当前实例
|
|
537
|
+
*/
|
|
538
|
+
spinnerInfo(text?: string): this;
|
|
539
|
+
/**
|
|
540
|
+
* 持久化 Spinner(停止并保留文本)
|
|
541
|
+
*
|
|
542
|
+
* @param symbol - 替代符号
|
|
543
|
+
* @param text - 保留的文本
|
|
544
|
+
* @returns 当前实例
|
|
545
|
+
*/
|
|
546
|
+
spinnerPersist(symbol?: string, text?: string): this;
|
|
547
|
+
/**
|
|
548
|
+
* 更新 Spinner 文本
|
|
549
|
+
*
|
|
550
|
+
* @param text - 新文本
|
|
551
|
+
* @returns 当前实例
|
|
552
|
+
*/
|
|
553
|
+
spinnerText(text: string): this;
|
|
554
|
+
/**
|
|
555
|
+
* 获取当前 Spinner 实例(用于高级操作)
|
|
556
|
+
*
|
|
557
|
+
* @returns 当前 Spinner 实例,若未创建则返回 null
|
|
558
|
+
*/
|
|
559
|
+
getSpinner(): Ora | null;
|
|
560
|
+
/** @private */
|
|
561
|
+
private _out;
|
|
562
|
+
/** @private */
|
|
563
|
+
private _logWithLevel;
|
|
564
|
+
/** @private */
|
|
565
|
+
private _logDebug;
|
|
566
|
+
logRed(text: string): this;
|
|
567
|
+
logGreen(text: string): this;
|
|
568
|
+
logYellow(text: string): this;
|
|
569
|
+
logBlue(text: string): this;
|
|
570
|
+
logCyan(text: string): this;
|
|
571
|
+
logMagenta(text: string): this;
|
|
572
|
+
logWhite(text: string): this;
|
|
573
|
+
logGray(text: string): this;
|
|
574
|
+
logBold(text: string): this;
|
|
575
|
+
logUnderline(text: string): this;
|
|
576
|
+
/**
|
|
577
|
+
* 输出成功信息(绿色 + ✔)
|
|
578
|
+
*
|
|
579
|
+
* @param text - 文本内容
|
|
580
|
+
* @returns 当前实例
|
|
581
|
+
*
|
|
582
|
+
* @example
|
|
583
|
+
* ```ts
|
|
584
|
+
* cli.success('操作成功!');
|
|
585
|
+
* ```
|
|
586
|
+
*/
|
|
587
|
+
success(text: string): this;
|
|
588
|
+
/**
|
|
589
|
+
* 输出错误信息(红色 + ✖)
|
|
590
|
+
*
|
|
591
|
+
* @param text - 文本内容
|
|
592
|
+
* @returns 当前实例
|
|
593
|
+
*
|
|
594
|
+
* @example
|
|
595
|
+
* ```ts
|
|
596
|
+
* cli.error('操作失败!');
|
|
597
|
+
* ```
|
|
598
|
+
*/
|
|
599
|
+
error(text: string): this;
|
|
600
|
+
/**
|
|
601
|
+
* 输出警告信息(黄色 + ⚠)
|
|
602
|
+
*
|
|
603
|
+
* @param text - 文本内容
|
|
604
|
+
* @returns 当前实例
|
|
605
|
+
*
|
|
606
|
+
* @example
|
|
607
|
+
* ```ts
|
|
608
|
+
* cli.warn('请注意!');
|
|
609
|
+
* ```
|
|
610
|
+
*/
|
|
611
|
+
warn(text: string): this;
|
|
612
|
+
/**
|
|
613
|
+
* 输出提示信息(蓝色 + ℹ)
|
|
614
|
+
*
|
|
615
|
+
* @param text - 文本内容
|
|
616
|
+
* @returns 当前实例
|
|
617
|
+
*
|
|
618
|
+
* @example
|
|
619
|
+
* ```ts
|
|
620
|
+
* cli.info('提示信息');
|
|
621
|
+
* ```
|
|
622
|
+
*/
|
|
623
|
+
info(text: string): this;
|
|
624
|
+
/**
|
|
625
|
+
* 生成并输出 ASCII Art 文本
|
|
626
|
+
*
|
|
627
|
+
* @param text - 要转换的文本
|
|
628
|
+
* @param font - figlet 字体名称(默认 'Standard')
|
|
629
|
+
* @returns 当前实例
|
|
630
|
+
*
|
|
631
|
+
* @example
|
|
632
|
+
* ```ts
|
|
633
|
+
* cli.printBanner('Hello');
|
|
634
|
+
* cli.printBanner('Big', 'Big');
|
|
635
|
+
* ```
|
|
636
|
+
*/
|
|
637
|
+
printBanner(text: string, font?: string): this;
|
|
638
|
+
/**
|
|
639
|
+
* 生成 ASCII Art 文本(不输出)
|
|
640
|
+
*
|
|
641
|
+
* @param text - 要转换的文本
|
|
642
|
+
* @param font - figlet 字体名称(默认 'Standard')
|
|
643
|
+
* @returns 生成的 ASCII Art 文本
|
|
644
|
+
*
|
|
645
|
+
* @example
|
|
646
|
+
* ```ts
|
|
647
|
+
* const art = cli.banner('Hello');
|
|
648
|
+
* fs.writeFileSync('banner.txt', art);
|
|
649
|
+
* ```
|
|
650
|
+
*/
|
|
651
|
+
banner(text: string, font?: string): string;
|
|
652
|
+
/**
|
|
653
|
+
* 异步生成 ASCII Art 文本
|
|
654
|
+
*
|
|
655
|
+
* @param text - 要转换的文本
|
|
656
|
+
* @param font - figlet 字体名称(默认 'Standard')
|
|
657
|
+
* @returns Promise,解析为 ASCII Art 文本
|
|
658
|
+
*/
|
|
659
|
+
bannerAsync(text: string, font?: string): Promise<string>;
|
|
660
|
+
/**
|
|
661
|
+
* 获取所有可用的 figlet 字体列表
|
|
662
|
+
*
|
|
663
|
+
* @returns 字体名称数组
|
|
664
|
+
*/
|
|
665
|
+
getFonts(): string[];
|
|
666
|
+
/** @private */
|
|
667
|
+
private _hideCursor;
|
|
668
|
+
/** @private */
|
|
669
|
+
private _showCursor;
|
|
670
|
+
/** @private */
|
|
671
|
+
private _registerExitHandler;
|
|
672
|
+
/**
|
|
673
|
+
* 显示终端光标
|
|
674
|
+
*
|
|
675
|
+
* @returns 当前实例
|
|
676
|
+
*/
|
|
677
|
+
cursorShow(): this;
|
|
678
|
+
/**
|
|
679
|
+
* 隐藏终端光标
|
|
680
|
+
*
|
|
681
|
+
* @returns 当前实例
|
|
682
|
+
*/
|
|
683
|
+
cursorHide(): this;
|
|
684
|
+
/**
|
|
685
|
+
* 切换光标可见性
|
|
686
|
+
*
|
|
687
|
+
* @param force - true 显示,false 隐藏,undefined 切换
|
|
688
|
+
* @returns 当前实例
|
|
689
|
+
*/
|
|
690
|
+
cursorToggle(force?: boolean): this;
|
|
691
|
+
/**
|
|
692
|
+
* 输出普通日志
|
|
693
|
+
*
|
|
694
|
+
* @param text - 文本内容
|
|
695
|
+
* @returns 当前实例
|
|
696
|
+
*
|
|
697
|
+
* @example
|
|
698
|
+
* ```ts
|
|
699
|
+
* cli.print('Hello World');
|
|
700
|
+
* ```
|
|
701
|
+
*/
|
|
702
|
+
print(text: string): this;
|
|
703
|
+
/**
|
|
704
|
+
* 输出带前缀的日志
|
|
705
|
+
*
|
|
706
|
+
* @param prefix - 前缀文本
|
|
707
|
+
* @param text - 日志内容
|
|
708
|
+
* @returns 当前实例
|
|
709
|
+
*
|
|
710
|
+
* @example
|
|
711
|
+
* ```ts
|
|
712
|
+
* cli.logWithPrefix('[INFO]', '任务完成');
|
|
713
|
+
* ```
|
|
714
|
+
*/
|
|
715
|
+
logWithPrefix(prefix: string, text: string): this;
|
|
716
|
+
/**
|
|
717
|
+
* 输出空行
|
|
718
|
+
*
|
|
719
|
+
* @returns 当前实例
|
|
720
|
+
*/
|
|
721
|
+
newline(): this;
|
|
722
|
+
/**
|
|
723
|
+
* 输出分隔线
|
|
724
|
+
*
|
|
725
|
+
* @param char - 分隔线字符
|
|
726
|
+
* @param length - 分隔线长度
|
|
727
|
+
* @returns 当前实例
|
|
728
|
+
*
|
|
729
|
+
* @example
|
|
730
|
+
* ```ts
|
|
731
|
+
* cli.divider('=', 50);
|
|
732
|
+
* ```
|
|
733
|
+
*/
|
|
734
|
+
divider(char?: string, length?: number): this;
|
|
735
|
+
}
|
|
736
|
+
//#endregion
|
|
737
|
+
export { type ChoiceOption, CliTools, type CliToolsEventHandler, type CliToolsEventMap, type CliToolsOptions, type CliToolsPlugin, type LoadConfigOptions, type LogLevel, type Logger, type OptionValue, type PromptInputOptions, type PromptPasswordOptions, type ResolvedConfig };
|
|
738
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{Command as e}from"commander";import t from"ora";import n from"cli-cursor";import r from"chalk";import i from"figlet";import a from"@inquirer/input";import o from"@inquirer/confirm";import s from"@inquirer/select";import c from"@inquirer/checkbox";import l from"@inquirer/password";import{existsSync as u,readFileSync as d}from"node:fs";import{resolve as f}from"node:path";const p={debug:0,info:1,warn:2,error:3};var m=class m{constructor(t={}){if(this._spinner=null,this._cursorHidden=!1,this._exitHandlerRegistered=!1,this._plugins=[],this._eventHandlers=new Map,this._config={},this._options={color:t.color??!0,cursor:t.cursor??!0,commandName:t.commandName??`cli-tools`,commandDescription:t.commandDescription??`CLI 工具库`,logger:t.logger??console},this._program=new e,this._program.name(this._options.commandName),this._program.description(this._options.commandDescription),this._options.color||(r.level=0),this._options.cursor||this._hideCursor(),t.plugins)for(let e of t.plugins)this.use(e);this._emit(`init`)}static create(e){return new m(e)}on(e,t){return this._eventHandlers.has(e)||this._eventHandlers.set(e,new Set),this._eventHandlers.get(e).add(t),this}off(e,t){return this._eventHandlers.get(e)?.delete(t),this}_emit(e,t){let n=this._eventHandlers.get(e);if(n)for(let r of n)try{r(t)}catch(t){this._options.logger.error?.(`[Event:${e}] Handler error:`,t)}}async use(e){return this._plugins.some(t=>t.name===e.name)?(this._logDebug(`Plugin "${e.name}" already loaded, skipping`),this):(this._plugins.push(e),this._emit(`plugin:load`,{name:e.name}),await e.install(this),this._logDebug(`Plugin "${e.name}" loaded`),this)}getPlugins(){return this._plugins.map(e=>e.name)}async loadConfig(e={}){let{sources:t=[],defaults:n={},mergeDefaults:r=!0}=e;for(let e of t){let[t,i]=e.split(`#`),a=f(t);if(u(a))try{let t=d(a,`utf-8`),o=JSON.parse(t);i&&typeof o==`object`&&o&&(o=o[i]??{});let s=r?{...n,...o}:o;return this._config=s,this._logDebug(`Config loaded from ${e}`),{source:e,config:s}}catch(t){this._options.logger.error?.(`Failed to load config from ${e}:`,t)}}return this._config={...n},{config:{...n}}}getConfig(){return{...this._config}}getConfigValue(e,t){let n=this._config[e];return n===void 0?t:n}setName(e){return this._program.name(e),this}setVersion(e){return this._program.version(e),this}setDescription(e){return this._program.description(e),this}addOption(e,t,n){return typeof n==`number`?this._program.option(e,t,e=>Number(e),n):this._program.option(e,t,n),this}addArgument(e,t){return this._program.argument(e,t),this}addCommand(e,t,n){return this._program.command(e).description(t).action(n),this}onAction(e){return this._program.action((...t)=>{let n=t[1],r=n.opts();return this._emit(`parse:after`),e(r,n)}),this}parse(e){return this._emit(`parse:before`),this._program.parse(e),this}parseAsync(e){return this._emit(`parse:before`),this._program.parseAsync(e)}getProgram(){return this._program}async promptInput(e,t){this._emit(`prompt:before`,{type:`input`,message:e});try{let n=await a({message:e,default:t?.defaultValue,validate:t?.validate,required:t?.required});return this._emit(`prompt:after`,{type:`input`,result:n}),n}catch(e){return this._emit(`prompt:cancel`,{type:`input`}),this._emit(`error`,{error:e,context:`promptInput`}),null}}async promptConfirm(e,t){this._emit(`prompt:before`,{type:`confirm`,message:e});try{let n=await o({message:e,default:t});return this._emit(`prompt:after`,{type:`confirm`,result:n}),n}catch(e){return this._emit(`prompt:cancel`,{type:`confirm`}),this._emit(`error`,{error:e,context:`promptConfirm`}),null}}async promptSelect(e,t){this._emit(`prompt:before`,{type:`select`,message:e});try{let n=await s({message:e,choices:t.map(e=>typeof e==`string`?{name:e,value:e}:{name:e.name,value:e.value})});return this._emit(`prompt:after`,{type:`select`,result:n}),n}catch(e){return this._emit(`prompt:cancel`,{type:`select`}),this._emit(`error`,{error:e,context:`promptSelect`}),null}}async promptCheckbox(e,t){this._emit(`prompt:before`,{type:`checkbox`,message:e});try{let n=await c({message:e,choices:t.map(e=>typeof e==`string`?{name:e,value:e,checked:!1}:{name:e.name,value:e.value,checked:e.checked??!1})});return this._emit(`prompt:after`,{type:`checkbox`,result:n}),n}catch(e){return this._emit(`prompt:cancel`,{type:`checkbox`}),this._emit(`error`,{error:e,context:`promptCheckbox`}),null}}async promptPassword(e,t){this._emit(`prompt:before`,{type:`password`,message:e});try{let{mask:n,minLength:r,validate:i}=t??{},a=await l({message:e,mask:n,validate:async e=>r&&r>0&&e.length<r?`密码长度不能少于 ${r} 个字符`:!i||i(e)});return this._emit(`prompt:after`,{type:`password`,result:a}),a}catch(e){return this._emit(`prompt:cancel`,{type:`password`}),this._emit(`error`,{error:e,context:`promptPassword`}),null}}spinnerStart(e,n){return this._emit(`spinner:start`,{text:e}),this._spinner=t({text:e,...n}).start(),this}spinnerStop(){return this._spinner?.stop(),this._spinner=null,this._emit(`spinner:stop`),this}spinnerSucceed(e){return this._spinner?.succeed(e),this._spinner=null,this._emit(`spinner:succeed`,{text:e}),this}spinnerFail(e){return this._spinner?.fail(e),this._spinner=null,this._emit(`spinner:fail`,{text:e}),this}spinnerWarn(e){return this._spinner?.warn(e),this._spinner=null,this._emit(`spinner:stop`),this}spinnerInfo(e){return this._spinner?.info(e),this._spinner=null,this._emit(`spinner:stop`),this}spinnerPersist(e,t){return this._spinner?.stopAndPersist({symbol:e,text:t}),this._spinner=null,this._emit(`spinner:stop`),this}spinnerText(e){return this._spinner&&(this._spinner.text=e),this}getSpinner(){return this._spinner}_out(e){this._options.logger.log(e)}_logWithLevel(e,t){let n=this._options.logger.level??`debug`;p[e]<p[n]||(e===`error`?(this._options.logger.error??this._options.logger.log)(t):e===`warn`?(this._options.logger.warn??this._options.logger.log)(t):e===`info`?(this._options.logger.info??this._options.logger.log)(t):(this._options.logger.debug??this._options.logger.log)(t))}_logDebug(e){this._logWithLevel(`debug`,e)}logRed(e){return this._out(r.red(e)),this}logGreen(e){return this._out(r.green(e)),this}logYellow(e){return this._out(r.yellow(e)),this}logBlue(e){return this._out(r.blue(e)),this}logCyan(e){return this._out(r.cyan(e)),this}logMagenta(e){return this._out(r.magenta(e)),this}logWhite(e){return this._out(r.white(e)),this}logGray(e){return this._out(r.gray(e)),this}logBold(e){return this._out(r.bold(e)),this}logUnderline(e){return this._out(r.underline(e)),this}success(e){return this._out(r.green(`✔ ${e}`)),this}error(e){return this._out(r.red(`✖ ${e}`)),this}warn(e){return this._out(r.yellow(`⚠ ${e}`)),this}info(e){return this._out(r.blue(`ℹ ${e}`)),this}printBanner(e,t){let n=i.textSync(e,{font:t});return this._out(r.cyan(n)),this}banner(e,t){return i.textSync(e,{font:t})}bannerAsync(e,t){return i.text(e,{font:t})}getFonts(){return i.fontsSync()}_hideCursor(){n.hide(),this._cursorHidden=!0,this._registerExitHandler()}_showCursor(){this._cursorHidden&&=(n.show(),!1)}_registerExitHandler(){if(this._exitHandlerRegistered)return;this._exitHandlerRegistered=!0;let e=()=>{this._showCursor(),this._spinner?.isSpinning&&this._spinner.stop()};process.on(`exit`,e),process.on(`SIGINT`,()=>{e(),process.exit(130)}),process.on(`SIGTERM`,()=>{e(),process.exit(143)})}cursorShow(){return this._showCursor(),this}cursorHide(){return this._hideCursor(),this}cursorToggle(e){return e===!0?this._showCursor():e===!1?this._hideCursor():this._cursorHidden?this._showCursor():this._hideCursor(),this}print(e){return this._out(e),this}logWithPrefix(e,t){return this._out(`${r.gray(e)} ${t}`),this}newline(){return this._out(``),this}divider(e=`─`,t=40){return this._out(r.gray(e.repeat(t))),this}};export{m as CliTools};
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/cli-tools.ts"],"sourcesContent":["import { Command } from 'commander';\nimport ora, { type Ora, type Options as OraOptions } from 'ora';\nimport cliCursor from 'cli-cursor';\nimport chalk from 'chalk';\nimport figlet from 'figlet';\nimport input from '@inquirer/input';\nimport confirm from '@inquirer/confirm';\nimport select from '@inquirer/select';\nimport checkbox from '@inquirer/checkbox';\nimport password from '@inquirer/password';\nimport { readFileSync, existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport type {\n\tCliToolsOptions,\n\tLogger,\n\tLogLevel,\n\tCliToolsPlugin,\n\tCliToolsEventMap,\n\tCliToolsEventHandler,\n\tChoiceOption,\n\tPromptInputOptions,\n\tPromptPasswordOptions,\n\tLoadConfigOptions,\n\tResolvedConfig,\n\tOptionValue,\n} from './types.js';\n\nconst LOG_LEVEL_PRIORITY: Record<LogLevel, number> = {\n\tdebug: 0,\n\tinfo: 1,\n\twarn: 2,\n\terror: 3,\n};\n\n/**\n * CLI 工具库主类\n *\n * 封装 commander、@inquirer/prompts、ora、chalk、figlet、cli-cursor,\n * 提供链式 API、工厂模式、事件系统与插件机制。\n *\n * @example\n * ```ts\n * const cli = CliTools.create({\n * commandName: 'my-cli',\n * commandDescription: '我的CLI工具',\n * });\n *\n * cli\n * .printBanner('My App')\n * .addOption('-d, --debug', '调试模式')\n * .onAction(async (opts, cmd) => {\n * if (opts.debug) cli.success('调试已开启');\n * });\n *\n * cli.parse();\n * ```\n *\n * @public\n */\nexport class CliTools {\n\tprivate _program: Command;\n\tprivate _spinner: Ora | null = null;\n\tprivate _options: Required<Omit<CliToolsOptions, 'logger' | 'plugins'>> & { logger: Logger };\n\tprivate _cursorHidden = false;\n\tprivate _exitHandlerRegistered = false;\n\tprivate _plugins: CliToolsPlugin[] = [];\n\tprivate _eventHandlers = new Map<string, Set<CliToolsEventHandler>>();\n\tprivate _config: Record<string, unknown> = {};\n\n\tprivate constructor(options: CliToolsOptions = {}) {\n\t\tthis._options = {\n\t\t\tcolor: options.color ?? true,\n\t\t\tcursor: options.cursor ?? true,\n\t\t\tcommandName: options.commandName ?? 'cli-tools',\n\t\t\tcommandDescription: options.commandDescription ?? 'CLI 工具库',\n\t\t\tlogger: options.logger ?? console,\n\t\t};\n\n\t\tthis._program = new Command();\n\t\tthis._program.name(this._options.commandName);\n\t\tthis._program.description(this._options.commandDescription);\n\n\t\tif (!this._options.color) {\n\t\t\tchalk.level = 0;\n\t\t}\n\t\tif (!this._options.cursor) {\n\t\t\tthis._hideCursor();\n\t\t}\n\n\t\tif (options.plugins) {\n\t\t\tfor (const plugin of options.plugins) {\n\t\t\t\tthis.use(plugin);\n\t\t\t}\n\t\t}\n\n\t\tthis._emit('init');\n\t}\n\n\t// ═══════════════════════════════════════════\n\t// 工厂模式\n\t// ═══════════════════════════════════════════\n\n\t/**\n\t * 工厂方法:创建 CliTools 实例\n\t *\n\t * @param options - 配置选项\n\t * @returns CliTools 实例\n\t *\n\t * @example\n\t * ```ts\n\t * const cli = CliTools.create({\n\t * commandName: 'my-cli',\n\t * commandDescription: '我的CLI工具',\n\t * logger: myLogger,\n\t * plugins: [myPlugin],\n\t * });\n\t * ```\n\t */\n\tstatic create(options?: CliToolsOptions): CliTools {\n\t\treturn new CliTools(options);\n\t}\n\n\t// ═══════════════════════════════════════════\n\t// 事件系统\n\t// ═══════════════════════════════════════════\n\n\t/**\n\t * 监听事件\n\t *\n\t * @param event - 事件名称\n\t * @param handler - 事件处理器\n\t * @returns 当前实例(支持链式调用)\n\t *\n\t * @example\n\t * ```ts\n\t * cli.on('spinner:stop', () => console.log('Spinner 已停止'));\n\t * cli.on('error', ({ error }) => console.error(error));\n\t * ```\n\t */\n\ton<K extends keyof CliToolsEventMap>(event: K, handler: CliToolsEventHandler<CliToolsEventMap[K]>): this {\n\t\tif (!this._eventHandlers.has(event)) {\n\t\t\tthis._eventHandlers.set(event, new Set());\n\t\t}\n\t\tthis._eventHandlers.get(event)!.add(handler as CliToolsEventHandler);\n\t\treturn this;\n\t}\n\n\t/**\n\t * 取消监听事件\n\t *\n\t * @param event - 事件名称\n\t * @param handler - 要移除的事件处理器\n\t * @returns 当前实例\n\t */\n\toff<K extends keyof CliToolsEventMap>(event: K, handler: CliToolsEventHandler<CliToolsEventMap[K]>): this {\n\t\tthis._eventHandlers.get(event)?.delete(handler as CliToolsEventHandler);\n\t\treturn this;\n\t}\n\n\t/**\n\t * 触发事件(仅内部使用)\n\t *\n\t * @private\n\t */\n\tprivate _emit<K extends keyof CliToolsEventMap>(event: K, data?: CliToolsEventMap[K]): void {\n\t\tconst handlers = this._eventHandlers.get(event);\n\t\tif (!handlers) return;\n\t\tfor (const handler of handlers) {\n\t\t\ttry {\n\t\t\t\thandler(data as never);\n\t\t\t} catch (e) {\n\t\t\t\tthis._options.logger.error?.(`[Event:${event}] Handler error:`, e);\n\t\t\t}\n\t\t}\n\t}\n\n\t// ═══════════════════════════════════════════\n\t// 插件系统\n\t// ═══════════════════════════════════════════\n\n\t/**\n\t * 注册插件\n\t *\n\t * @param plugin - 插件实例\n\t * @returns 当前实例\n\t *\n\t * @example\n\t * ```ts\n\t * const myPlugin: CliToolsPlugin = {\n\t * name: 'my-plugin',\n\t * install(cli) {\n\t * cli.on('init', () => console.log('Plugin loaded'));\n\t * },\n\t * };\n\t * cli.use(myPlugin);\n\t * ```\n\t */\n\tasync use(plugin: CliToolsPlugin): Promise<this> {\n\t\tif (this._plugins.some(p => p.name === plugin.name)) {\n\t\t\tthis._logDebug(`Plugin \"${plugin.name}\" already loaded, skipping`);\n\t\t\treturn this;\n\t\t}\n\t\tthis._plugins.push(plugin);\n\t\tthis._emit('plugin:load', { name: plugin.name });\n\t\tawait plugin.install(this);\n\t\tthis._logDebug(`Plugin \"${plugin.name}\" loaded`);\n\t\treturn this;\n\t}\n\n\t/**\n\t * 获取已加载的插件列表\n\t *\n\t * @returns 插件名称数组\n\t */\n\tgetPlugins(): string[] {\n\t\treturn this._plugins.map(p => p.name);\n\t}\n\n\t// ═══════════════════════════════════════════\n\t// 配置文件加载\n\t// ═══════════════════════════════════════════\n\n\t/**\n\t * 加载配置文件\n\t *\n\t * 支持 JSON 格式的配置文件。按 sources 顺序查找,优先使用第一个找到的文件。\n\t *\n\t * @param options - 配置加载选项\n\t * @returns 解析后的配置结果,未找到文件则返回 defaults\n\t *\n\t * @example\n\t * ```ts\n\t * const { config } = await cli.loadConfig({\n\t * sources: ['.myclirc', 'package.json#myCli'],\n\t * defaults: { theme: 'dark', verbose: false },\n\t * });\n\t * console.log(config.theme); // 'dark'\n\t * ```\n\t */\n\tasync loadConfig(options: LoadConfigOptions = {}): Promise<ResolvedConfig> {\n\t\tconst { sources = [], defaults = {}, mergeDefaults = true } = options;\n\n\t\tfor (const source of sources) {\n\t\t\tconst [filePath, jsonKey] = source.split('#');\n\t\t\tconst fullPath = resolve(filePath);\n\n\t\t\tif (!existsSync(fullPath)) continue;\n\n\t\t\ttry {\n\t\t\t\tconst raw = readFileSync(fullPath, 'utf-8');\n\t\t\t\tlet parsed: Record<string, unknown> = JSON.parse(raw);\n\n\t\t\t\tif (jsonKey && typeof parsed === 'object' && parsed !== null) {\n\t\t\t\t\tparsed = (parsed[jsonKey] as Record<string, unknown>) ?? {};\n\t\t\t\t}\n\n\t\t\t\tconst config = mergeDefaults ? { ...defaults, ...parsed } : parsed;\n\t\t\t\tthis._config = config;\n\t\t\t\tthis._logDebug(`Config loaded from ${source}`);\n\t\t\t\treturn { source, config };\n\t\t\t} catch (e) {\n\t\t\t\tthis._options.logger.error?.(`Failed to load config from ${source}:`, e);\n\t\t\t}\n\t\t}\n\n\t\tthis._config = { ...defaults };\n\t\treturn { config: { ...defaults } };\n\t}\n\n\t/**\n\t * 获取已加载的配置\n\t *\n\t * @returns 当前配置对象\n\t */\n\tgetConfig(): Record<string, unknown> {\n\t\treturn { ...this._config };\n\t}\n\n\t/**\n\t * 获取配置值(支持类型断言)\n\t *\n\t * @param key - 配置键名\n\t * @param defaultValue - 默认值\n\t * @returns 配置值\n\t *\n\t * @example\n\t * ```ts\n\t * const theme = cli.getConfigValue<string>('theme', 'light');\n\t * ```\n\t */\n\tgetConfigValue<T>(key: string, defaultValue?: T): T {\n\t\tconst value = this._config[key];\n\t\treturn (value !== undefined ? value : defaultValue) as T;\n\t}\n\n\t// ═══════════════════════════════════════════\n\t// Commander 封装(链式)\n\t// ═══════════════════════════════════════════\n\n\t/**\n\t * 设置命令名称\n\t *\n\t * @param name - 命令名称\n\t * @returns 当前实例\n\t */\n\tsetName(name: string): this {\n\t\tthis._program.name(name);\n\t\treturn this;\n\t}\n\n\t/**\n\t * 设置命令版本号\n\t *\n\t * @param version - 版本号字符串\n\t * @returns 当前实例\n\t */\n\tsetVersion(version: string): this {\n\t\tthis._program.version(version);\n\t\treturn this;\n\t}\n\n\t/**\n\t * 设置命令描述\n\t *\n\t * @param description - 描述文本\n\t * @returns 当前实例\n\t */\n\tsetDescription(description: string): this {\n\t\tthis._program.description(description);\n\t\treturn this;\n\t}\n\n\t/**\n\t * 添加命令行选项\n\t *\n\t * @param flags - 选项标志(如 `'-d, --debug'`)\n\t * @param description - 选项描述\n\t * @param defaultValue - 默认值(仅支持 string | boolean | number)\n\t * @returns 当前实例\n\t *\n\t * @example\n\t * ```ts\n\t * cli.addOption('-n, --name <name>', '你的名字', 'World');\n\t * cli.addOption('-d, --debug', '调试模式');\n\t * cli.addOption('-p, --port <port>', '端口号', 3000);\n\t * ```\n\t */\n\taddOption(flags: string, description: string, defaultValue?: OptionValue): this {\n\t\tif (typeof defaultValue === 'number') {\n\t\t\tthis._program.option(flags, description, (val: string) => Number(val), defaultValue);\n\t\t} else {\n\t\t\tthis._program.option(flags, description, defaultValue);\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * 添加命令行参数\n\t *\n\t * @param name - 参数名称(如 `'<file>'` 或 `'[file]'`)\n\t * @param description - 参数描述\n\t * @returns 当前实例\n\t */\n\taddArgument(name: string, description: string): this {\n\t\tthis._program.argument(name, description);\n\t\treturn this;\n\t}\n\n\t/**\n\t * 注册子命令\n\t *\n\t * @param name - 子命令名称\n\t * @param description - 子命令描述\n\t * @param action - 子命令执行回调\n\t * @returns 当前实例\n\t *\n\t * @example\n\t * ```ts\n\t * cli.addCommand('init', '初始化项目', async (options, cmd) => {\n\t * console.log('项目已初始化');\n\t * });\n\t * ```\n\t */\n\taddCommand(name: string, description: string, action: (...args: unknown[]) => void | Promise<void>): this {\n\t\tthis._program.command(name).description(description).action(action as (...a: unknown[]) => void);\n\t\treturn this;\n\t}\n\n\t/**\n\t * 设置主命令的执行回调\n\t *\n\t * @param action - 执行回调,参数为解析后的 options 和 Command 实例\n\t * @returns 当前实例\n\t */\n\tonAction(action: (options: Record<string, unknown>, command: Command) => void | Promise<void>): this {\n\t\tthis._program.action((...args: [string[], Command]) => {\n\t\t\tconst command = args[1];\n\t\t\tconst opts = command.opts();\n\t\t\tthis._emit('parse:after');\n\t\t\treturn action(opts as Record<string, unknown>, command);\n\t\t});\n\t\treturn this;\n\t}\n\n\t/**\n\t * 解析命令行参数\n\t *\n\t * @param argv - 参数数组(默认 process.argv)\n\t * @returns 当前实例\n\t */\n\tparse(argv?: string[]): this {\n\t\tthis._emit('parse:before');\n\t\tthis._program.parse(argv);\n\t\treturn this;\n\t}\n\n\t/**\n\t * 解析命令行参数并返回 Command 实例\n\t *\n\t * @param argv - 参数数组(默认 process.argv)\n\t * @returns Commander Command 实例\n\t */\n\tparseAsync(argv?: string[]): Promise<Command> {\n\t\tthis._emit('parse:before');\n\t\treturn this._program.parseAsync(argv);\n\t}\n\n\t/**\n\t * 获取底层 Commander Command 实例,用于高级定制\n\t *\n\t * @returns Commander Command 实例\n\t */\n\tgetProgram(): Command {\n\t\treturn this._program;\n\t}\n\n\t// ═══════════════════════════════════════════\n\t// Inquirer Prompts 封装(带错误处理)\n\t// ═══════════════════════════════════════════\n\n\t/**\n\t * 文本输入提示\n\t *\n\t * @param message - 提示消息\n\t * @param options - 额外配置\n\t * @returns 用户输入的文本,用户取消返回 null\n\t *\n\t * @example\n\t * ```ts\n\t * const name = await cli.promptInput('请输入你的名字:', { defaultValue: 'wbw' });\n\t * if (name === null) cli.warn('已取消');\n\t * ```\n\t */\n\tasync promptInput(message: string, options?: PromptInputOptions): Promise<string | null> {\n\t\tthis._emit('prompt:before', { type: 'input', message });\n\t\ttry {\n\t\t\tconst result = await input({\n\t\t\t\tmessage,\n\t\t\t\tdefault: options?.defaultValue,\n\t\t\t\tvalidate: options?.validate,\n\t\t\t\trequired: options?.required,\n\t\t\t});\n\t\t\tthis._emit('prompt:after', { type: 'input', result });\n\t\t\treturn result;\n\t\t} catch (e) {\n\t\t\tthis._emit('prompt:cancel', { type: 'input' });\n\t\t\tthis._emit('error', { error: e as Error, context: 'promptInput' });\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * 确认提示(是/否)\n\t *\n\t * @param message - 提示消息\n\t * @param defaultValue - 默认值\n\t * @returns 用户选择的结果,用户取消返回 null\n\t *\n\t * @example\n\t * ```ts\n\t * const ok = await cli.promptConfirm('确认继续?');\n\t * if (ok === null) cli.warn('已取消');\n\t * ```\n\t */\n\tasync promptConfirm(message: string, defaultValue?: boolean): Promise<boolean | null> {\n\t\tthis._emit('prompt:before', { type: 'confirm', message });\n\t\ttry {\n\t\t\tconst result = await confirm({ message, default: defaultValue });\n\t\t\tthis._emit('prompt:after', { type: 'confirm', result });\n\t\t\treturn result;\n\t\t} catch (e) {\n\t\t\tthis._emit('prompt:cancel', { type: 'confirm' });\n\t\t\tthis._emit('error', { error: e as Error, context: 'promptConfirm' });\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * 列表选择提示\n\t *\n\t * @param message - 提示消息\n\t * @param choices - 选项数组\n\t * @returns 用户选择的值,用户取消返回 null\n\t *\n\t * @example\n\t * ```ts\n\t * const lang = await cli.promptSelect('选择语言:', ['TypeScript', 'JavaScript']);\n\t * if (lang === null) return;\n\t * cli.success(`你选择了: ${lang}`);\n\t * ```\n\t */\n\tasync promptSelect(message: string, choices: (string | ChoiceOption)[]): Promise<string | null> {\n\t\tthis._emit('prompt:before', { type: 'select', message });\n\t\ttry {\n\t\t\tconst formattedChoices = choices.map(c =>\n\t\t\t\ttypeof c === 'string' ? { name: c, value: c } : { name: c.name, value: c.value }\n\t\t\t);\n\t\t\tconst result = await select({\n\t\t\t\tmessage,\n\t\t\t\tchoices: formattedChoices as never,\n\t\t\t});\n\t\t\tthis._emit('prompt:after', { type: 'select', result });\n\t\t\treturn result as string;\n\t\t} catch (e) {\n\t\t\tthis._emit('prompt:cancel', { type: 'select' });\n\t\t\tthis._emit('error', { error: e as Error, context: 'promptSelect' });\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * 多选提示(复选框)\n\t *\n\t * @param message - 提示消息\n\t * @param choices - 选项数组\n\t * @returns 用户选择的值数组,用户取消返回 null\n\t *\n\t * @example\n\t * ```ts\n\t * const features = await cli.promptCheckbox('选择功能:', ['ESLint', 'Prettier']);\n\t * if (features === null) return;\n\t * cli.success(`已选择: ${features.join(', ')}`);\n\t * ```\n\t */\n\tasync promptCheckbox(message: string, choices: (string | ChoiceOption)[]): Promise<string[] | null> {\n\t\tthis._emit('prompt:before', { type: 'checkbox', message });\n\t\ttry {\n\t\t\tconst formattedChoices = choices.map(c =>\n\t\t\t\ttypeof c === 'string'\n\t\t\t\t\t? { name: c, value: c, checked: false }\n\t\t\t\t\t: { name: c.name, value: c.value, checked: c.checked ?? false }\n\t\t\t);\n\t\t\tconst result = await checkbox({\n\t\t\t\tmessage,\n\t\t\t\tchoices: formattedChoices as never,\n\t\t\t});\n\t\t\tthis._emit('prompt:after', { type: 'checkbox', result });\n\t\t\treturn result as string[];\n\t\t} catch (e) {\n\t\t\tthis._emit('prompt:cancel', { type: 'checkbox' });\n\t\t\tthis._emit('error', { error: e as Error, context: 'promptCheckbox' });\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * 密码输入提示(内容隐藏)\n\t *\n\t * @param message - 提示消息\n\t * @param options - 密码选项(mask、minLength、validate)\n\t * @returns 用户输入的密码文本,用户取消返回 null\n\t *\n\t * @example\n\t * ```ts\n\t * const pwd = await cli.promptPassword('请输入密码:', { minLength: 6 });\n\t * if (pwd === null) return;\n\t * cli.success(`密码长度: ${pwd.length}`);\n\t * ```\n\t */\n\tasync promptPassword(message: string, options?: PromptPasswordOptions): Promise<string | null> {\n\t\tthis._emit('prompt:before', { type: 'password', message });\n\t\ttry {\n\t\t\tconst { mask, minLength, validate } = options ?? {};\n\n\t\t\tconst combinedValidate = async (value: string): Promise<boolean | string> => {\n\t\t\t\tif (minLength && minLength > 0 && value.length < minLength) {\n\t\t\t\t\treturn `密码长度不能少于 ${minLength} 个字符`;\n\t\t\t\t}\n\t\t\t\tif (validate) {\n\t\t\t\t\treturn validate(value);\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t};\n\n\t\t\tconst result = await password({\n\t\t\t\tmessage,\n\t\t\t\tmask,\n\t\t\t\tvalidate: combinedValidate,\n\t\t\t});\n\t\t\tthis._emit('prompt:after', { type: 'password', result });\n\t\t\treturn result;\n\t\t} catch (e) {\n\t\t\tthis._emit('prompt:cancel', { type: 'password' });\n\t\t\tthis._emit('error', { error: e as Error, context: 'promptPassword' });\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t// ═══════════════════════════════════════════\n\t// Ora Spinner 封装(链式)\n\t// ═══════════════════════════════════════════\n\n\t/**\n\t * 创建并启动 Spinner\n\t *\n\t * @param text - 显示文本\n\t * @param options - ora 配置选项\n\t * @returns 当前实例\n\t *\n\t * @example\n\t * ```ts\n\t * cli.spinnerStart('加载中...').spinnerSucceed('完成!');\n\t * ```\n\t */\n\tspinnerStart(text: string, options?: OraOptions): this {\n\t\tthis._emit('spinner:start', { text });\n\t\tthis._spinner = ora({ text, ...options }).start();\n\t\treturn this;\n\t}\n\n\t/**\n\t * 停止 Spinner\n\t *\n\t * @returns 当前实例\n\t */\n\tspinnerStop(): this {\n\t\tthis._spinner?.stop();\n\t\tthis._spinner = null;\n\t\tthis._emit('spinner:stop');\n\t\treturn this;\n\t}\n\n\t/**\n\t * Spinner 成功状态(✔)\n\t *\n\t * @param text - 成功消息\n\t * @returns 当前实例\n\t */\n\tspinnerSucceed(text?: string): this {\n\t\tthis._spinner?.succeed(text);\n\t\tthis._spinner = null;\n\t\tthis._emit('spinner:succeed', { text });\n\t\treturn this;\n\t}\n\n\t/**\n\t * Spinner 失败状态(✖)\n\t *\n\t * @param text - 失败消息\n\t * @returns 当前实例\n\t */\n\tspinnerFail(text?: string): this {\n\t\tthis._spinner?.fail(text);\n\t\tthis._spinner = null;\n\t\tthis._emit('spinner:fail', { text });\n\t\treturn this;\n\t}\n\n\t/**\n\t * Spinner 警告状态(⚠)\n\t *\n\t * @param text - 警告消息\n\t * @returns 当前实例\n\t */\n\tspinnerWarn(text?: string): this {\n\t\tthis._spinner?.warn(text);\n\t\tthis._spinner = null;\n\t\tthis._emit('spinner:stop');\n\t\treturn this;\n\t}\n\n\t/**\n\t * Spinner 信息状态(ℹ)\n\t *\n\t * @param text - 信息消息\n\t * @returns 当前实例\n\t */\n\tspinnerInfo(text?: string): this {\n\t\tthis._spinner?.info(text);\n\t\tthis._spinner = null;\n\t\tthis._emit('spinner:stop');\n\t\treturn this;\n\t}\n\n\t/**\n\t * 持久化 Spinner(停止并保留文本)\n\t *\n\t * @param symbol - 替代符号\n\t * @param text - 保留的文本\n\t * @returns 当前实例\n\t */\n\tspinnerPersist(symbol?: string, text?: string): this {\n\t\tthis._spinner?.stopAndPersist({ symbol, text });\n\t\tthis._spinner = null;\n\t\tthis._emit('spinner:stop');\n\t\treturn this;\n\t}\n\n\t/**\n\t * 更新 Spinner 文本\n\t *\n\t * @param text - 新文本\n\t * @returns 当前实例\n\t */\n\tspinnerText(text: string): this {\n\t\tif (this._spinner) {\n\t\t\tthis._spinner.text = text;\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * 获取当前 Spinner 实例(用于高级操作)\n\t *\n\t * @returns 当前 Spinner 实例,若未创建则返回 null\n\t */\n\tgetSpinner(): Ora | null {\n\t\treturn this._spinner;\n\t}\n\n\t// ═══════════════════════════════════════════\n\t// Chalk 颜色工具(链式)\n\t// ═══════════════════════════════════════════\n\n\t/** @private */\n\tprivate _out(text: string): void {\n\t\tthis._options.logger.log(text);\n\t}\n\n\t/** @private */\n\tprivate _logWithLevel(level: LogLevel, text: string): void {\n\t\tconst currentLevel = this._options.logger.level ?? 'debug';\n\t\tif (LOG_LEVEL_PRIORITY[level] < LOG_LEVEL_PRIORITY[currentLevel]) return;\n\n\t\tif (level === 'error') {\n\t\t\t(this._options.logger.error ?? this._options.logger.log)(text);\n\t\t} else if (level === 'warn') {\n\t\t\t(this._options.logger.warn ?? this._options.logger.log)(text);\n\t\t} else if (level === 'info') {\n\t\t\t(this._options.logger.info ?? this._options.logger.log)(text);\n\t\t} else {\n\t\t\t(this._options.logger.debug ?? this._options.logger.log)(text);\n\t\t}\n\t}\n\n\t/** @private */\n\tprivate _logDebug(text: string): void {\n\t\tthis._logWithLevel('debug', text);\n\t}\n\n\tlogRed(text: string): this { this._out(chalk.red(text)); return this; }\n\tlogGreen(text: string): this { this._out(chalk.green(text)); return this; }\n\tlogYellow(text: string): this { this._out(chalk.yellow(text)); return this; }\n\tlogBlue(text: string): this { this._out(chalk.blue(text)); return this; }\n\tlogCyan(text: string): this { this._out(chalk.cyan(text)); return this; }\n\tlogMagenta(text: string): this { this._out(chalk.magenta(text)); return this; }\n\tlogWhite(text: string): this { this._out(chalk.white(text)); return this; }\n\tlogGray(text: string): this { this._out(chalk.gray(text)); return this; }\n\tlogBold(text: string): this { this._out(chalk.bold(text)); return this; }\n\tlogUnderline(text: string): this { this._out(chalk.underline(text)); return this; }\n\n\t/**\n\t * 输出成功信息(绿色 + ✔)\n\t *\n\t * @param text - 文本内容\n\t * @returns 当前实例\n\t *\n\t * @example\n\t * ```ts\n\t * cli.success('操作成功!');\n\t * ```\n\t */\n\tsuccess(text: string): this {\n\t\tthis._out(chalk.green(`✔ ${text}`));\n\t\treturn this;\n\t}\n\n\t/**\n\t * 输出错误信息(红色 + ✖)\n\t *\n\t * @param text - 文本内容\n\t * @returns 当前实例\n\t *\n\t * @example\n\t * ```ts\n\t * cli.error('操作失败!');\n\t * ```\n\t */\n\terror(text: string): this {\n\t\tthis._out(chalk.red(`✖ ${text}`));\n\t\treturn this;\n\t}\n\n\t/**\n\t * 输出警告信息(黄色 + ⚠)\n\t *\n\t * @param text - 文本内容\n\t * @returns 当前实例\n\t *\n\t * @example\n\t * ```ts\n\t * cli.warn('请注意!');\n\t * ```\n\t */\n\twarn(text: string): this {\n\t\tthis._out(chalk.yellow(`⚠ ${text}`));\n\t\treturn this;\n\t}\n\n\t/**\n\t * 输出提示信息(蓝色 + ℹ)\n\t *\n\t * @param text - 文本内容\n\t * @returns 当前实例\n\t *\n\t * @example\n\t * ```ts\n\t * cli.info('提示信息');\n\t * ```\n\t */\n\tinfo(text: string): this {\n\t\tthis._out(chalk.blue(`ℹ ${text}`));\n\t\treturn this;\n\t}\n\n\t// ═══════════════════════════════════════════\n\t// Figlet ASCII Art 封装\n\t// ═══════════════════════════════════════════\n\n\t/**\n\t * 生成并输出 ASCII Art 文本\n\t *\n\t * @param text - 要转换的文本\n\t * @param font - figlet 字体名称(默认 'Standard')\n\t * @returns 当前实例\n\t *\n\t * @example\n\t * ```ts\n\t * cli.printBanner('Hello');\n\t * cli.printBanner('Big', 'Big');\n\t * ```\n\t */\n\tprintBanner(text: string, font?: string): this {\n\t\tconst art = figlet.textSync(text, { font: font as figlet.Fonts });\n\t\tthis._out(chalk.cyan(art));\n\t\treturn this;\n\t}\n\n\t/**\n\t * 生成 ASCII Art 文本(不输出)\n\t *\n\t * @param text - 要转换的文本\n\t * @param font - figlet 字体名称(默认 'Standard')\n\t * @returns 生成的 ASCII Art 文本\n\t *\n\t * @example\n\t * ```ts\n\t * const art = cli.banner('Hello');\n\t * fs.writeFileSync('banner.txt', art);\n\t * ```\n\t */\n\tbanner(text: string, font?: string): string {\n\t\treturn figlet.textSync(text, { font: font as figlet.Fonts });\n\t}\n\n\t/**\n\t * 异步生成 ASCII Art 文本\n\t *\n\t * @param text - 要转换的文本\n\t * @param font - figlet 字体名称(默认 'Standard')\n\t * @returns Promise,解析为 ASCII Art 文本\n\t */\n\tbannerAsync(text: string, font?: string): Promise<string> {\n\t\treturn figlet.text(text, { font: font as figlet.Fonts });\n\t}\n\n\t/**\n\t * 获取所有可用的 figlet 字体列表\n\t *\n\t * @returns 字体名称数组\n\t */\n\tgetFonts(): string[] {\n\t\treturn figlet.fontsSync();\n\t}\n\n\t// ═══════════════════════════════════════════\n\t// 光标控制\n\t// ═══════════════════════════════════════════\n\n\t/** @private */\n\tprivate _hideCursor(): void {\n\t\tcliCursor.hide();\n\t\tthis._cursorHidden = true;\n\t\tthis._registerExitHandler();\n\t}\n\n\t/** @private */\n\tprivate _showCursor(): void {\n\t\tif (this._cursorHidden) {\n\t\t\tcliCursor.show();\n\t\t\tthis._cursorHidden = false;\n\t\t}\n\t}\n\n\t/** @private */\n\tprivate _registerExitHandler(): void {\n\t\tif (this._exitHandlerRegistered) return;\n\t\tthis._exitHandlerRegistered = true;\n\n\t\tconst cleanup = (): void => {\n\t\t\tthis._showCursor();\n\t\t\tif (this._spinner?.isSpinning) {\n\t\t\t\tthis._spinner.stop();\n\t\t\t}\n\t\t};\n\n\t\tprocess.on('exit', cleanup);\n\t\tprocess.on('SIGINT', () => { cleanup(); process.exit(130); });\n\t\tprocess.on('SIGTERM', () => { cleanup(); process.exit(143); });\n\t}\n\n\t/**\n\t * 显示终端光标\n\t *\n\t * @returns 当前实例\n\t */\n\tcursorShow(): this {\n\t\tthis._showCursor();\n\t\treturn this;\n\t}\n\n\t/**\n\t * 隐藏终端光标\n\t *\n\t * @returns 当前实例\n\t */\n\tcursorHide(): this {\n\t\tthis._hideCursor();\n\t\treturn this;\n\t}\n\n\t/**\n\t * 切换光标可见性\n\t *\n\t * @param force - true 显示,false 隐藏,undefined 切换\n\t * @returns 当前实例\n\t */\n\tcursorToggle(force?: boolean): this {\n\t\tif (force === true) {\n\t\t\tthis._showCursor();\n\t\t} else if (force === false) {\n\t\t\tthis._hideCursor();\n\t\t} else {\n\t\t\tif (this._cursorHidden) {\n\t\t\t\tthis._showCursor();\n\t\t\t} else {\n\t\t\t\tthis._hideCursor();\n\t\t\t}\n\t\t}\n\t\treturn this;\n\t}\n\n\t// ═══════════════════════════════════════════\n\t// 通用日志\n\t// ═══════════════════════════════════════════\n\n\t/**\n\t * 输出普通日志\n\t *\n\t * @param text - 文本内容\n\t * @returns 当前实例\n\t *\n\t * @example\n\t * ```ts\n\t * cli.print('Hello World');\n\t * ```\n\t */\n\tprint(text: string): this {\n\t\tthis._out(text);\n\t\treturn this;\n\t}\n\n\t/**\n\t * 输出带前缀的日志\n\t *\n\t * @param prefix - 前缀文本\n\t * @param text - 日志内容\n\t * @returns 当前实例\n\t *\n\t * @example\n\t * ```ts\n\t * cli.logWithPrefix('[INFO]', '任务完成');\n\t * ```\n\t */\n\tlogWithPrefix(prefix: string, text: string): this {\n\t\tthis._out(`${chalk.gray(prefix)} ${text}`);\n\t\treturn this;\n\t}\n\n\t/**\n\t * 输出空行\n\t *\n\t * @returns 当前实例\n\t */\n\tnewline(): this {\n\t\tthis._out('');\n\t\treturn this;\n\t}\n\n\t/**\n\t * 输出分隔线\n\t *\n\t * @param char - 分隔线字符\n\t * @param length - 分隔线长度\n\t * @returns 当前实例\n\t *\n\t * @example\n\t * ```ts\n\t * cli.divider('=', 50);\n\t * ```\n\t */\n\tdivider(char = '─', length = 40): this {\n\t\tthis._out(chalk.gray(char.repeat(length)));\n\t\treturn this;\n\t}\n}\n"],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require("commander"),l=require("ora");l=s(l,1);let u=require("cli-cursor");u=s(u,1);let d=require("chalk");d=s(d,1);let f=require("figlet");f=s(f,1);let p=require("@inquirer/input");p=s(p,1);let m=require("@inquirer/confirm");m=s(m,1);let h=require("@inquirer/select");h=s(h,1);let g=require("@inquirer/checkbox");g=s(g,1);let _=require("@inquirer/password");_=s(_,1);let v=require("node:fs"),y=require("node:path");const b={debug:0,info:1,warn:2,error:3};var x=class e{constructor(e={}){if(this._spinner=null,this._cursorHidden=!1,this._exitHandlerRegistered=!1,this._plugins=[],this._eventHandlers=new Map,this._config={},this._options={color:e.color??!0,cursor:e.cursor??!0,commandName:e.commandName??`cli-tools`,commandDescription:e.commandDescription??`CLI 工具库`,logger:e.logger??console},this._program=new c.Command,this._program.name(this._options.commandName),this._program.description(this._options.commandDescription),this._options.color||(d.default.level=0),this._options.cursor||this._hideCursor(),e.plugins)for(let t of e.plugins)this.use(t);this._emit(`init`)}static create(t){return new e(t)}on(e,t){return this._eventHandlers.has(e)||this._eventHandlers.set(e,new Set),this._eventHandlers.get(e).add(t),this}off(e,t){return this._eventHandlers.get(e)?.delete(t),this}_emit(e,t){let n=this._eventHandlers.get(e);if(n)for(let r of n)try{r(t)}catch(t){this._options.logger.error?.(`[Event:${e}] Handler error:`,t)}}async use(e){return this._plugins.some(t=>t.name===e.name)?(this._logDebug(`Plugin "${e.name}" already loaded, skipping`),this):(this._plugins.push(e),this._emit(`plugin:load`,{name:e.name}),await e.install(this),this._logDebug(`Plugin "${e.name}" loaded`),this)}getPlugins(){return this._plugins.map(e=>e.name)}async loadConfig(e={}){let{sources:t=[],defaults:n={},mergeDefaults:r=!0}=e;for(let e of t){let[t,i]=e.split(`#`),a=(0,y.resolve)(t);if((0,v.existsSync)(a))try{let t=(0,v.readFileSync)(a,`utf-8`),o=JSON.parse(t);i&&typeof o==`object`&&o&&(o=o[i]??{});let s=r?{...n,...o}:o;return this._config=s,this._logDebug(`Config loaded from ${e}`),{source:e,config:s}}catch(t){this._options.logger.error?.(`Failed to load config from ${e}:`,t)}}return this._config={...n},{config:{...n}}}getConfig(){return{...this._config}}getConfigValue(e,t){let n=this._config[e];return n===void 0?t:n}setName(e){return this._program.name(e),this}setVersion(e){return this._program.version(e),this}setDescription(e){return this._program.description(e),this}addOption(e,t,n){return typeof n==`number`?this._program.option(e,t,e=>Number(e),n):this._program.option(e,t,n),this}addArgument(e,t){return this._program.argument(e,t),this}addCommand(e,t,n){return this._program.command(e).description(t).action(n),this}onAction(e){return this._program.action((...t)=>{let n=t[1],r=n.opts();return this._emit(`parse:after`),e(r,n)}),this}parse(e){return this._emit(`parse:before`),this._program.parse(e),this}parseAsync(e){return this._emit(`parse:before`),this._program.parseAsync(e)}getProgram(){return this._program}async promptInput(e,t){this._emit(`prompt:before`,{type:`input`,message:e});try{let n=await(0,p.default)({message:e,default:t?.defaultValue,validate:t?.validate,required:t?.required});return this._emit(`prompt:after`,{type:`input`,result:n}),n}catch(e){return this._emit(`prompt:cancel`,{type:`input`}),this._emit(`error`,{error:e,context:`promptInput`}),null}}async promptConfirm(e,t){this._emit(`prompt:before`,{type:`confirm`,message:e});try{let n=await(0,m.default)({message:e,default:t});return this._emit(`prompt:after`,{type:`confirm`,result:n}),n}catch(e){return this._emit(`prompt:cancel`,{type:`confirm`}),this._emit(`error`,{error:e,context:`promptConfirm`}),null}}async promptSelect(e,t){this._emit(`prompt:before`,{type:`select`,message:e});try{let n=await(0,h.default)({message:e,choices:t.map(e=>typeof e==`string`?{name:e,value:e}:{name:e.name,value:e.value})});return this._emit(`prompt:after`,{type:`select`,result:n}),n}catch(e){return this._emit(`prompt:cancel`,{type:`select`}),this._emit(`error`,{error:e,context:`promptSelect`}),null}}async promptCheckbox(e,t){this._emit(`prompt:before`,{type:`checkbox`,message:e});try{let n=await(0,g.default)({message:e,choices:t.map(e=>typeof e==`string`?{name:e,value:e,checked:!1}:{name:e.name,value:e.value,checked:e.checked??!1})});return this._emit(`prompt:after`,{type:`checkbox`,result:n}),n}catch(e){return this._emit(`prompt:cancel`,{type:`checkbox`}),this._emit(`error`,{error:e,context:`promptCheckbox`}),null}}async promptPassword(e,t){this._emit(`prompt:before`,{type:`password`,message:e});try{let{mask:n,minLength:r,validate:i}=t??{},a=await(0,_.default)({message:e,mask:n,validate:async e=>r&&r>0&&e.length<r?`密码长度不能少于 ${r} 个字符`:!i||i(e)});return this._emit(`prompt:after`,{type:`password`,result:a}),a}catch(e){return this._emit(`prompt:cancel`,{type:`password`}),this._emit(`error`,{error:e,context:`promptPassword`}),null}}spinnerStart(e,t){return this._emit(`spinner:start`,{text:e}),this._spinner=(0,l.default)({text:e,...t}).start(),this}spinnerStop(){return this._spinner?.stop(),this._spinner=null,this._emit(`spinner:stop`),this}spinnerSucceed(e){return this._spinner?.succeed(e),this._spinner=null,this._emit(`spinner:succeed`,{text:e}),this}spinnerFail(e){return this._spinner?.fail(e),this._spinner=null,this._emit(`spinner:fail`,{text:e}),this}spinnerWarn(e){return this._spinner?.warn(e),this._spinner=null,this._emit(`spinner:stop`),this}spinnerInfo(e){return this._spinner?.info(e),this._spinner=null,this._emit(`spinner:stop`),this}spinnerPersist(e,t){return this._spinner?.stopAndPersist({symbol:e,text:t}),this._spinner=null,this._emit(`spinner:stop`),this}spinnerText(e){return this._spinner&&(this._spinner.text=e),this}getSpinner(){return this._spinner}_out(e){this._options.logger.log(e)}_logWithLevel(e,t){let n=this._options.logger.level??`debug`;b[e]<b[n]||(e===`error`?(this._options.logger.error??this._options.logger.log)(t):e===`warn`?(this._options.logger.warn??this._options.logger.log)(t):e===`info`?(this._options.logger.info??this._options.logger.log)(t):(this._options.logger.debug??this._options.logger.log)(t))}_logDebug(e){this._logWithLevel(`debug`,e)}logRed(e){return this._out(d.default.red(e)),this}logGreen(e){return this._out(d.default.green(e)),this}logYellow(e){return this._out(d.default.yellow(e)),this}logBlue(e){return this._out(d.default.blue(e)),this}logCyan(e){return this._out(d.default.cyan(e)),this}logMagenta(e){return this._out(d.default.magenta(e)),this}logWhite(e){return this._out(d.default.white(e)),this}logGray(e){return this._out(d.default.gray(e)),this}logBold(e){return this._out(d.default.bold(e)),this}logUnderline(e){return this._out(d.default.underline(e)),this}success(e){return this._out(d.default.green(`✔ ${e}`)),this}error(e){return this._out(d.default.red(`✖ ${e}`)),this}warn(e){return this._out(d.default.yellow(`⚠ ${e}`)),this}info(e){return this._out(d.default.blue(`ℹ ${e}`)),this}printBanner(e,t){let n=f.default.textSync(e,{font:t});return this._out(d.default.cyan(n)),this}banner(e,t){return f.default.textSync(e,{font:t})}bannerAsync(e,t){return f.default.text(e,{font:t})}getFonts(){return f.default.fontsSync()}_hideCursor(){u.default.hide(),this._cursorHidden=!0,this._registerExitHandler()}_showCursor(){this._cursorHidden&&=(u.default.show(),!1)}_registerExitHandler(){if(this._exitHandlerRegistered)return;this._exitHandlerRegistered=!0;let e=()=>{this._showCursor(),this._spinner?.isSpinning&&this._spinner.stop()};process.on(`exit`,e),process.on(`SIGINT`,()=>{e(),process.exit(130)}),process.on(`SIGTERM`,()=>{e(),process.exit(143)})}cursorShow(){return this._showCursor(),this}cursorHide(){return this._hideCursor(),this}cursorToggle(e){return e===!0?this._showCursor():e===!1?this._hideCursor():this._cursorHidden?this._showCursor():this._hideCursor(),this}print(e){return this._out(e),this}logWithPrefix(e,t){return this._out(`${d.default.gray(e)} ${t}`),this}newline(){return this._out(``),this}divider(e=`─`,t=40){return this._out(d.default.gray(e.repeat(t))),this}};exports.CliTools=x;
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["Command"],"sources":["../src/cli-tools.ts"],"sourcesContent":["import { Command } from 'commander';\nimport ora, { type Ora, type Options as OraOptions } from 'ora';\nimport cliCursor from 'cli-cursor';\nimport chalk from 'chalk';\nimport figlet from 'figlet';\nimport input from '@inquirer/input';\nimport confirm from '@inquirer/confirm';\nimport select from '@inquirer/select';\nimport checkbox from '@inquirer/checkbox';\nimport password from '@inquirer/password';\nimport { readFileSync, existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport type {\n\tCliToolsOptions,\n\tLogger,\n\tLogLevel,\n\tCliToolsPlugin,\n\tCliToolsEventMap,\n\tCliToolsEventHandler,\n\tChoiceOption,\n\tPromptInputOptions,\n\tPromptPasswordOptions,\n\tLoadConfigOptions,\n\tResolvedConfig,\n\tOptionValue,\n} from './types.js';\n\nconst LOG_LEVEL_PRIORITY: Record<LogLevel, number> = {\n\tdebug: 0,\n\tinfo: 1,\n\twarn: 2,\n\terror: 3,\n};\n\n/**\n * CLI 工具库主类\n *\n * 封装 commander、@inquirer/prompts、ora、chalk、figlet、cli-cursor,\n * 提供链式 API、工厂模式、事件系统与插件机制。\n *\n * @example\n * ```ts\n * const cli = CliTools.create({\n * commandName: 'my-cli',\n * commandDescription: '我的CLI工具',\n * });\n *\n * cli\n * .printBanner('My App')\n * .addOption('-d, --debug', '调试模式')\n * .onAction(async (opts, cmd) => {\n * if (opts.debug) cli.success('调试已开启');\n * });\n *\n * cli.parse();\n * ```\n *\n * @public\n */\nexport class CliTools {\n\tprivate _program: Command;\n\tprivate _spinner: Ora | null = null;\n\tprivate _options: Required<Omit<CliToolsOptions, 'logger' | 'plugins'>> & { logger: Logger };\n\tprivate _cursorHidden = false;\n\tprivate _exitHandlerRegistered = false;\n\tprivate _plugins: CliToolsPlugin[] = [];\n\tprivate _eventHandlers = new Map<string, Set<CliToolsEventHandler>>();\n\tprivate _config: Record<string, unknown> = {};\n\n\tprivate constructor(options: CliToolsOptions = {}) {\n\t\tthis._options = {\n\t\t\tcolor: options.color ?? true,\n\t\t\tcursor: options.cursor ?? true,\n\t\t\tcommandName: options.commandName ?? 'cli-tools',\n\t\t\tcommandDescription: options.commandDescription ?? 'CLI 工具库',\n\t\t\tlogger: options.logger ?? console,\n\t\t};\n\n\t\tthis._program = new Command();\n\t\tthis._program.name(this._options.commandName);\n\t\tthis._program.description(this._options.commandDescription);\n\n\t\tif (!this._options.color) {\n\t\t\tchalk.level = 0;\n\t\t}\n\t\tif (!this._options.cursor) {\n\t\t\tthis._hideCursor();\n\t\t}\n\n\t\tif (options.plugins) {\n\t\t\tfor (const plugin of options.plugins) {\n\t\t\t\tthis.use(plugin);\n\t\t\t}\n\t\t}\n\n\t\tthis._emit('init');\n\t}\n\n\t// ═══════════════════════════════════════════\n\t// 工厂模式\n\t// ═══════════════════════════════════════════\n\n\t/**\n\t * 工厂方法:创建 CliTools 实例\n\t *\n\t * @param options - 配置选项\n\t * @returns CliTools 实例\n\t *\n\t * @example\n\t * ```ts\n\t * const cli = CliTools.create({\n\t * commandName: 'my-cli',\n\t * commandDescription: '我的CLI工具',\n\t * logger: myLogger,\n\t * plugins: [myPlugin],\n\t * });\n\t * ```\n\t */\n\tstatic create(options?: CliToolsOptions): CliTools {\n\t\treturn new CliTools(options);\n\t}\n\n\t// ═══════════════════════════════════════════\n\t// 事件系统\n\t// ═══════════════════════════════════════════\n\n\t/**\n\t * 监听事件\n\t *\n\t * @param event - 事件名称\n\t * @param handler - 事件处理器\n\t * @returns 当前实例(支持链式调用)\n\t *\n\t * @example\n\t * ```ts\n\t * cli.on('spinner:stop', () => console.log('Spinner 已停止'));\n\t * cli.on('error', ({ error }) => console.error(error));\n\t * ```\n\t */\n\ton<K extends keyof CliToolsEventMap>(event: K, handler: CliToolsEventHandler<CliToolsEventMap[K]>): this {\n\t\tif (!this._eventHandlers.has(event)) {\n\t\t\tthis._eventHandlers.set(event, new Set());\n\t\t}\n\t\tthis._eventHandlers.get(event)!.add(handler as CliToolsEventHandler);\n\t\treturn this;\n\t}\n\n\t/**\n\t * 取消监听事件\n\t *\n\t * @param event - 事件名称\n\t * @param handler - 要移除的事件处理器\n\t * @returns 当前实例\n\t */\n\toff<K extends keyof CliToolsEventMap>(event: K, handler: CliToolsEventHandler<CliToolsEventMap[K]>): this {\n\t\tthis._eventHandlers.get(event)?.delete(handler as CliToolsEventHandler);\n\t\treturn this;\n\t}\n\n\t/**\n\t * 触发事件(仅内部使用)\n\t *\n\t * @private\n\t */\n\tprivate _emit<K extends keyof CliToolsEventMap>(event: K, data?: CliToolsEventMap[K]): void {\n\t\tconst handlers = this._eventHandlers.get(event);\n\t\tif (!handlers) return;\n\t\tfor (const handler of handlers) {\n\t\t\ttry {\n\t\t\t\thandler(data as never);\n\t\t\t} catch (e) {\n\t\t\t\tthis._options.logger.error?.(`[Event:${event}] Handler error:`, e);\n\t\t\t}\n\t\t}\n\t}\n\n\t// ═══════════════════════════════════════════\n\t// 插件系统\n\t// ═══════════════════════════════════════════\n\n\t/**\n\t * 注册插件\n\t *\n\t * @param plugin - 插件实例\n\t * @returns 当前实例\n\t *\n\t * @example\n\t * ```ts\n\t * const myPlugin: CliToolsPlugin = {\n\t * name: 'my-plugin',\n\t * install(cli) {\n\t * cli.on('init', () => console.log('Plugin loaded'));\n\t * },\n\t * };\n\t * cli.use(myPlugin);\n\t * ```\n\t */\n\tasync use(plugin: CliToolsPlugin): Promise<this> {\n\t\tif (this._plugins.some(p => p.name === plugin.name)) {\n\t\t\tthis._logDebug(`Plugin \"${plugin.name}\" already loaded, skipping`);\n\t\t\treturn this;\n\t\t}\n\t\tthis._plugins.push(plugin);\n\t\tthis._emit('plugin:load', { name: plugin.name });\n\t\tawait plugin.install(this);\n\t\tthis._logDebug(`Plugin \"${plugin.name}\" loaded`);\n\t\treturn this;\n\t}\n\n\t/**\n\t * 获取已加载的插件列表\n\t *\n\t * @returns 插件名称数组\n\t */\n\tgetPlugins(): string[] {\n\t\treturn this._plugins.map(p => p.name);\n\t}\n\n\t// ═══════════════════════════════════════════\n\t// 配置文件加载\n\t// ═══════════════════════════════════════════\n\n\t/**\n\t * 加载配置文件\n\t *\n\t * 支持 JSON 格式的配置文件。按 sources 顺序查找,优先使用第一个找到的文件。\n\t *\n\t * @param options - 配置加载选项\n\t * @returns 解析后的配置结果,未找到文件则返回 defaults\n\t *\n\t * @example\n\t * ```ts\n\t * const { config } = await cli.loadConfig({\n\t * sources: ['.myclirc', 'package.json#myCli'],\n\t * defaults: { theme: 'dark', verbose: false },\n\t * });\n\t * console.log(config.theme); // 'dark'\n\t * ```\n\t */\n\tasync loadConfig(options: LoadConfigOptions = {}): Promise<ResolvedConfig> {\n\t\tconst { sources = [], defaults = {}, mergeDefaults = true } = options;\n\n\t\tfor (const source of sources) {\n\t\t\tconst [filePath, jsonKey] = source.split('#');\n\t\t\tconst fullPath = resolve(filePath);\n\n\t\t\tif (!existsSync(fullPath)) continue;\n\n\t\t\ttry {\n\t\t\t\tconst raw = readFileSync(fullPath, 'utf-8');\n\t\t\t\tlet parsed: Record<string, unknown> = JSON.parse(raw);\n\n\t\t\t\tif (jsonKey && typeof parsed === 'object' && parsed !== null) {\n\t\t\t\t\tparsed = (parsed[jsonKey] as Record<string, unknown>) ?? {};\n\t\t\t\t}\n\n\t\t\t\tconst config = mergeDefaults ? { ...defaults, ...parsed } : parsed;\n\t\t\t\tthis._config = config;\n\t\t\t\tthis._logDebug(`Config loaded from ${source}`);\n\t\t\t\treturn { source, config };\n\t\t\t} catch (e) {\n\t\t\t\tthis._options.logger.error?.(`Failed to load config from ${source}:`, e);\n\t\t\t}\n\t\t}\n\n\t\tthis._config = { ...defaults };\n\t\treturn { config: { ...defaults } };\n\t}\n\n\t/**\n\t * 获取已加载的配置\n\t *\n\t * @returns 当前配置对象\n\t */\n\tgetConfig(): Record<string, unknown> {\n\t\treturn { ...this._config };\n\t}\n\n\t/**\n\t * 获取配置值(支持类型断言)\n\t *\n\t * @param key - 配置键名\n\t * @param defaultValue - 默认值\n\t * @returns 配置值\n\t *\n\t * @example\n\t * ```ts\n\t * const theme = cli.getConfigValue<string>('theme', 'light');\n\t * ```\n\t */\n\tgetConfigValue<T>(key: string, defaultValue?: T): T {\n\t\tconst value = this._config[key];\n\t\treturn (value !== undefined ? value : defaultValue) as T;\n\t}\n\n\t// ═══════════════════════════════════════════\n\t// Commander 封装(链式)\n\t// ═══════════════════════════════════════════\n\n\t/**\n\t * 设置命令名称\n\t *\n\t * @param name - 命令名称\n\t * @returns 当前实例\n\t */\n\tsetName(name: string): this {\n\t\tthis._program.name(name);\n\t\treturn this;\n\t}\n\n\t/**\n\t * 设置命令版本号\n\t *\n\t * @param version - 版本号字符串\n\t * @returns 当前实例\n\t */\n\tsetVersion(version: string): this {\n\t\tthis._program.version(version);\n\t\treturn this;\n\t}\n\n\t/**\n\t * 设置命令描述\n\t *\n\t * @param description - 描述文本\n\t * @returns 当前实例\n\t */\n\tsetDescription(description: string): this {\n\t\tthis._program.description(description);\n\t\treturn this;\n\t}\n\n\t/**\n\t * 添加命令行选项\n\t *\n\t * @param flags - 选项标志(如 `'-d, --debug'`)\n\t * @param description - 选项描述\n\t * @param defaultValue - 默认值(仅支持 string | boolean | number)\n\t * @returns 当前实例\n\t *\n\t * @example\n\t * ```ts\n\t * cli.addOption('-n, --name <name>', '你的名字', 'World');\n\t * cli.addOption('-d, --debug', '调试模式');\n\t * cli.addOption('-p, --port <port>', '端口号', 3000);\n\t * ```\n\t */\n\taddOption(flags: string, description: string, defaultValue?: OptionValue): this {\n\t\tif (typeof defaultValue === 'number') {\n\t\t\tthis._program.option(flags, description, (val: string) => Number(val), defaultValue);\n\t\t} else {\n\t\t\tthis._program.option(flags, description, defaultValue);\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * 添加命令行参数\n\t *\n\t * @param name - 参数名称(如 `'<file>'` 或 `'[file]'`)\n\t * @param description - 参数描述\n\t * @returns 当前实例\n\t */\n\taddArgument(name: string, description: string): this {\n\t\tthis._program.argument(name, description);\n\t\treturn this;\n\t}\n\n\t/**\n\t * 注册子命令\n\t *\n\t * @param name - 子命令名称\n\t * @param description - 子命令描述\n\t * @param action - 子命令执行回调\n\t * @returns 当前实例\n\t *\n\t * @example\n\t * ```ts\n\t * cli.addCommand('init', '初始化项目', async (options, cmd) => {\n\t * console.log('项目已初始化');\n\t * });\n\t * ```\n\t */\n\taddCommand(name: string, description: string, action: (...args: unknown[]) => void | Promise<void>): this {\n\t\tthis._program.command(name).description(description).action(action as (...a: unknown[]) => void);\n\t\treturn this;\n\t}\n\n\t/**\n\t * 设置主命令的执行回调\n\t *\n\t * @param action - 执行回调,参数为解析后的 options 和 Command 实例\n\t * @returns 当前实例\n\t */\n\tonAction(action: (options: Record<string, unknown>, command: Command) => void | Promise<void>): this {\n\t\tthis._program.action((...args: [string[], Command]) => {\n\t\t\tconst command = args[1];\n\t\t\tconst opts = command.opts();\n\t\t\tthis._emit('parse:after');\n\t\t\treturn action(opts as Record<string, unknown>, command);\n\t\t});\n\t\treturn this;\n\t}\n\n\t/**\n\t * 解析命令行参数\n\t *\n\t * @param argv - 参数数组(默认 process.argv)\n\t * @returns 当前实例\n\t */\n\tparse(argv?: string[]): this {\n\t\tthis._emit('parse:before');\n\t\tthis._program.parse(argv);\n\t\treturn this;\n\t}\n\n\t/**\n\t * 解析命令行参数并返回 Command 实例\n\t *\n\t * @param argv - 参数数组(默认 process.argv)\n\t * @returns Commander Command 实例\n\t */\n\tparseAsync(argv?: string[]): Promise<Command> {\n\t\tthis._emit('parse:before');\n\t\treturn this._program.parseAsync(argv);\n\t}\n\n\t/**\n\t * 获取底层 Commander Command 实例,用于高级定制\n\t *\n\t * @returns Commander Command 实例\n\t */\n\tgetProgram(): Command {\n\t\treturn this._program;\n\t}\n\n\t// ═══════════════════════════════════════════\n\t// Inquirer Prompts 封装(带错误处理)\n\t// ═══════════════════════════════════════════\n\n\t/**\n\t * 文本输入提示\n\t *\n\t * @param message - 提示消息\n\t * @param options - 额外配置\n\t * @returns 用户输入的文本,用户取消返回 null\n\t *\n\t * @example\n\t * ```ts\n\t * const name = await cli.promptInput('请输入你的名字:', { defaultValue: 'wbw' });\n\t * if (name === null) cli.warn('已取消');\n\t * ```\n\t */\n\tasync promptInput(message: string, options?: PromptInputOptions): Promise<string | null> {\n\t\tthis._emit('prompt:before', { type: 'input', message });\n\t\ttry {\n\t\t\tconst result = await input({\n\t\t\t\tmessage,\n\t\t\t\tdefault: options?.defaultValue,\n\t\t\t\tvalidate: options?.validate,\n\t\t\t\trequired: options?.required,\n\t\t\t});\n\t\t\tthis._emit('prompt:after', { type: 'input', result });\n\t\t\treturn result;\n\t\t} catch (e) {\n\t\t\tthis._emit('prompt:cancel', { type: 'input' });\n\t\t\tthis._emit('error', { error: e as Error, context: 'promptInput' });\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * 确认提示(是/否)\n\t *\n\t * @param message - 提示消息\n\t * @param defaultValue - 默认值\n\t * @returns 用户选择的结果,用户取消返回 null\n\t *\n\t * @example\n\t * ```ts\n\t * const ok = await cli.promptConfirm('确认继续?');\n\t * if (ok === null) cli.warn('已取消');\n\t * ```\n\t */\n\tasync promptConfirm(message: string, defaultValue?: boolean): Promise<boolean | null> {\n\t\tthis._emit('prompt:before', { type: 'confirm', message });\n\t\ttry {\n\t\t\tconst result = await confirm({ message, default: defaultValue });\n\t\t\tthis._emit('prompt:after', { type: 'confirm', result });\n\t\t\treturn result;\n\t\t} catch (e) {\n\t\t\tthis._emit('prompt:cancel', { type: 'confirm' });\n\t\t\tthis._emit('error', { error: e as Error, context: 'promptConfirm' });\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * 列表选择提示\n\t *\n\t * @param message - 提示消息\n\t * @param choices - 选项数组\n\t * @returns 用户选择的值,用户取消返回 null\n\t *\n\t * @example\n\t * ```ts\n\t * const lang = await cli.promptSelect('选择语言:', ['TypeScript', 'JavaScript']);\n\t * if (lang === null) return;\n\t * cli.success(`你选择了: ${lang}`);\n\t * ```\n\t */\n\tasync promptSelect(message: string, choices: (string | ChoiceOption)[]): Promise<string | null> {\n\t\tthis._emit('prompt:before', { type: 'select', message });\n\t\ttry {\n\t\t\tconst formattedChoices = choices.map(c =>\n\t\t\t\ttypeof c === 'string' ? { name: c, value: c } : { name: c.name, value: c.value }\n\t\t\t);\n\t\t\tconst result = await select({\n\t\t\t\tmessage,\n\t\t\t\tchoices: formattedChoices as never,\n\t\t\t});\n\t\t\tthis._emit('prompt:after', { type: 'select', result });\n\t\t\treturn result as string;\n\t\t} catch (e) {\n\t\t\tthis._emit('prompt:cancel', { type: 'select' });\n\t\t\tthis._emit('error', { error: e as Error, context: 'promptSelect' });\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * 多选提示(复选框)\n\t *\n\t * @param message - 提示消息\n\t * @param choices - 选项数组\n\t * @returns 用户选择的值数组,用户取消返回 null\n\t *\n\t * @example\n\t * ```ts\n\t * const features = await cli.promptCheckbox('选择功能:', ['ESLint', 'Prettier']);\n\t * if (features === null) return;\n\t * cli.success(`已选择: ${features.join(', ')}`);\n\t * ```\n\t */\n\tasync promptCheckbox(message: string, choices: (string | ChoiceOption)[]): Promise<string[] | null> {\n\t\tthis._emit('prompt:before', { type: 'checkbox', message });\n\t\ttry {\n\t\t\tconst formattedChoices = choices.map(c =>\n\t\t\t\ttypeof c === 'string'\n\t\t\t\t\t? { name: c, value: c, checked: false }\n\t\t\t\t\t: { name: c.name, value: c.value, checked: c.checked ?? false }\n\t\t\t);\n\t\t\tconst result = await checkbox({\n\t\t\t\tmessage,\n\t\t\t\tchoices: formattedChoices as never,\n\t\t\t});\n\t\t\tthis._emit('prompt:after', { type: 'checkbox', result });\n\t\t\treturn result as string[];\n\t\t} catch (e) {\n\t\t\tthis._emit('prompt:cancel', { type: 'checkbox' });\n\t\t\tthis._emit('error', { error: e as Error, context: 'promptCheckbox' });\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * 密码输入提示(内容隐藏)\n\t *\n\t * @param message - 提示消息\n\t * @param options - 密码选项(mask、minLength、validate)\n\t * @returns 用户输入的密码文本,用户取消返回 null\n\t *\n\t * @example\n\t * ```ts\n\t * const pwd = await cli.promptPassword('请输入密码:', { minLength: 6 });\n\t * if (pwd === null) return;\n\t * cli.success(`密码长度: ${pwd.length}`);\n\t * ```\n\t */\n\tasync promptPassword(message: string, options?: PromptPasswordOptions): Promise<string | null> {\n\t\tthis._emit('prompt:before', { type: 'password', message });\n\t\ttry {\n\t\t\tconst { mask, minLength, validate } = options ?? {};\n\n\t\t\tconst combinedValidate = async (value: string): Promise<boolean | string> => {\n\t\t\t\tif (minLength && minLength > 0 && value.length < minLength) {\n\t\t\t\t\treturn `密码长度不能少于 ${minLength} 个字符`;\n\t\t\t\t}\n\t\t\t\tif (validate) {\n\t\t\t\t\treturn validate(value);\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t};\n\n\t\t\tconst result = await password({\n\t\t\t\tmessage,\n\t\t\t\tmask,\n\t\t\t\tvalidate: combinedValidate,\n\t\t\t});\n\t\t\tthis._emit('prompt:after', { type: 'password', result });\n\t\t\treturn result;\n\t\t} catch (e) {\n\t\t\tthis._emit('prompt:cancel', { type: 'password' });\n\t\t\tthis._emit('error', { error: e as Error, context: 'promptPassword' });\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t// ═══════════════════════════════════════════\n\t// Ora Spinner 封装(链式)\n\t// ═══════════════════════════════════════════\n\n\t/**\n\t * 创建并启动 Spinner\n\t *\n\t * @param text - 显示文本\n\t * @param options - ora 配置选项\n\t * @returns 当前实例\n\t *\n\t * @example\n\t * ```ts\n\t * cli.spinnerStart('加载中...').spinnerSucceed('完成!');\n\t * ```\n\t */\n\tspinnerStart(text: string, options?: OraOptions): this {\n\t\tthis._emit('spinner:start', { text });\n\t\tthis._spinner = ora({ text, ...options }).start();\n\t\treturn this;\n\t}\n\n\t/**\n\t * 停止 Spinner\n\t *\n\t * @returns 当前实例\n\t */\n\tspinnerStop(): this {\n\t\tthis._spinner?.stop();\n\t\tthis._spinner = null;\n\t\tthis._emit('spinner:stop');\n\t\treturn this;\n\t}\n\n\t/**\n\t * Spinner 成功状态(✔)\n\t *\n\t * @param text - 成功消息\n\t * @returns 当前实例\n\t */\n\tspinnerSucceed(text?: string): this {\n\t\tthis._spinner?.succeed(text);\n\t\tthis._spinner = null;\n\t\tthis._emit('spinner:succeed', { text });\n\t\treturn this;\n\t}\n\n\t/**\n\t * Spinner 失败状态(✖)\n\t *\n\t * @param text - 失败消息\n\t * @returns 当前实例\n\t */\n\tspinnerFail(text?: string): this {\n\t\tthis._spinner?.fail(text);\n\t\tthis._spinner = null;\n\t\tthis._emit('spinner:fail', { text });\n\t\treturn this;\n\t}\n\n\t/**\n\t * Spinner 警告状态(⚠)\n\t *\n\t * @param text - 警告消息\n\t * @returns 当前实例\n\t */\n\tspinnerWarn(text?: string): this {\n\t\tthis._spinner?.warn(text);\n\t\tthis._spinner = null;\n\t\tthis._emit('spinner:stop');\n\t\treturn this;\n\t}\n\n\t/**\n\t * Spinner 信息状态(ℹ)\n\t *\n\t * @param text - 信息消息\n\t * @returns 当前实例\n\t */\n\tspinnerInfo(text?: string): this {\n\t\tthis._spinner?.info(text);\n\t\tthis._spinner = null;\n\t\tthis._emit('spinner:stop');\n\t\treturn this;\n\t}\n\n\t/**\n\t * 持久化 Spinner(停止并保留文本)\n\t *\n\t * @param symbol - 替代符号\n\t * @param text - 保留的文本\n\t * @returns 当前实例\n\t */\n\tspinnerPersist(symbol?: string, text?: string): this {\n\t\tthis._spinner?.stopAndPersist({ symbol, text });\n\t\tthis._spinner = null;\n\t\tthis._emit('spinner:stop');\n\t\treturn this;\n\t}\n\n\t/**\n\t * 更新 Spinner 文本\n\t *\n\t * @param text - 新文本\n\t * @returns 当前实例\n\t */\n\tspinnerText(text: string): this {\n\t\tif (this._spinner) {\n\t\t\tthis._spinner.text = text;\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * 获取当前 Spinner 实例(用于高级操作)\n\t *\n\t * @returns 当前 Spinner 实例,若未创建则返回 null\n\t */\n\tgetSpinner(): Ora | null {\n\t\treturn this._spinner;\n\t}\n\n\t// ═══════════════════════════════════════════\n\t// Chalk 颜色工具(链式)\n\t// ═══════════════════════════════════════════\n\n\t/** @private */\n\tprivate _out(text: string): void {\n\t\tthis._options.logger.log(text);\n\t}\n\n\t/** @private */\n\tprivate _logWithLevel(level: LogLevel, text: string): void {\n\t\tconst currentLevel = this._options.logger.level ?? 'debug';\n\t\tif (LOG_LEVEL_PRIORITY[level] < LOG_LEVEL_PRIORITY[currentLevel]) return;\n\n\t\tif (level === 'error') {\n\t\t\t(this._options.logger.error ?? this._options.logger.log)(text);\n\t\t} else if (level === 'warn') {\n\t\t\t(this._options.logger.warn ?? this._options.logger.log)(text);\n\t\t} else if (level === 'info') {\n\t\t\t(this._options.logger.info ?? this._options.logger.log)(text);\n\t\t} else {\n\t\t\t(this._options.logger.debug ?? this._options.logger.log)(text);\n\t\t}\n\t}\n\n\t/** @private */\n\tprivate _logDebug(text: string): void {\n\t\tthis._logWithLevel('debug', text);\n\t}\n\n\tlogRed(text: string): this { this._out(chalk.red(text)); return this; }\n\tlogGreen(text: string): this { this._out(chalk.green(text)); return this; }\n\tlogYellow(text: string): this { this._out(chalk.yellow(text)); return this; }\n\tlogBlue(text: string): this { this._out(chalk.blue(text)); return this; }\n\tlogCyan(text: string): this { this._out(chalk.cyan(text)); return this; }\n\tlogMagenta(text: string): this { this._out(chalk.magenta(text)); return this; }\n\tlogWhite(text: string): this { this._out(chalk.white(text)); return this; }\n\tlogGray(text: string): this { this._out(chalk.gray(text)); return this; }\n\tlogBold(text: string): this { this._out(chalk.bold(text)); return this; }\n\tlogUnderline(text: string): this { this._out(chalk.underline(text)); return this; }\n\n\t/**\n\t * 输出成功信息(绿色 + ✔)\n\t *\n\t * @param text - 文本内容\n\t * @returns 当前实例\n\t *\n\t * @example\n\t * ```ts\n\t * cli.success('操作成功!');\n\t * ```\n\t */\n\tsuccess(text: string): this {\n\t\tthis._out(chalk.green(`✔ ${text}`));\n\t\treturn this;\n\t}\n\n\t/**\n\t * 输出错误信息(红色 + ✖)\n\t *\n\t * @param text - 文本内容\n\t * @returns 当前实例\n\t *\n\t * @example\n\t * ```ts\n\t * cli.error('操作失败!');\n\t * ```\n\t */\n\terror(text: string): this {\n\t\tthis._out(chalk.red(`✖ ${text}`));\n\t\treturn this;\n\t}\n\n\t/**\n\t * 输出警告信息(黄色 + ⚠)\n\t *\n\t * @param text - 文本内容\n\t * @returns 当前实例\n\t *\n\t * @example\n\t * ```ts\n\t * cli.warn('请注意!');\n\t * ```\n\t */\n\twarn(text: string): this {\n\t\tthis._out(chalk.yellow(`⚠ ${text}`));\n\t\treturn this;\n\t}\n\n\t/**\n\t * 输出提示信息(蓝色 + ℹ)\n\t *\n\t * @param text - 文本内容\n\t * @returns 当前实例\n\t *\n\t * @example\n\t * ```ts\n\t * cli.info('提示信息');\n\t * ```\n\t */\n\tinfo(text: string): this {\n\t\tthis._out(chalk.blue(`ℹ ${text}`));\n\t\treturn this;\n\t}\n\n\t// ═══════════════════════════════════════════\n\t// Figlet ASCII Art 封装\n\t// ═══════════════════════════════════════════\n\n\t/**\n\t * 生成并输出 ASCII Art 文本\n\t *\n\t * @param text - 要转换的文本\n\t * @param font - figlet 字体名称(默认 'Standard')\n\t * @returns 当前实例\n\t *\n\t * @example\n\t * ```ts\n\t * cli.printBanner('Hello');\n\t * cli.printBanner('Big', 'Big');\n\t * ```\n\t */\n\tprintBanner(text: string, font?: string): this {\n\t\tconst art = figlet.textSync(text, { font: font as figlet.Fonts });\n\t\tthis._out(chalk.cyan(art));\n\t\treturn this;\n\t}\n\n\t/**\n\t * 生成 ASCII Art 文本(不输出)\n\t *\n\t * @param text - 要转换的文本\n\t * @param font - figlet 字体名称(默认 'Standard')\n\t * @returns 生成的 ASCII Art 文本\n\t *\n\t * @example\n\t * ```ts\n\t * const art = cli.banner('Hello');\n\t * fs.writeFileSync('banner.txt', art);\n\t * ```\n\t */\n\tbanner(text: string, font?: string): string {\n\t\treturn figlet.textSync(text, { font: font as figlet.Fonts });\n\t}\n\n\t/**\n\t * 异步生成 ASCII Art 文本\n\t *\n\t * @param text - 要转换的文本\n\t * @param font - figlet 字体名称(默认 'Standard')\n\t * @returns Promise,解析为 ASCII Art 文本\n\t */\n\tbannerAsync(text: string, font?: string): Promise<string> {\n\t\treturn figlet.text(text, { font: font as figlet.Fonts });\n\t}\n\n\t/**\n\t * 获取所有可用的 figlet 字体列表\n\t *\n\t * @returns 字体名称数组\n\t */\n\tgetFonts(): string[] {\n\t\treturn figlet.fontsSync();\n\t}\n\n\t// ═══════════════════════════════════════════\n\t// 光标控制\n\t// ═══════════════════════════════════════════\n\n\t/** @private */\n\tprivate _hideCursor(): void {\n\t\tcliCursor.hide();\n\t\tthis._cursorHidden = true;\n\t\tthis._registerExitHandler();\n\t}\n\n\t/** @private */\n\tprivate _showCursor(): void {\n\t\tif (this._cursorHidden) {\n\t\t\tcliCursor.show();\n\t\t\tthis._cursorHidden = false;\n\t\t}\n\t}\n\n\t/** @private */\n\tprivate _registerExitHandler(): void {\n\t\tif (this._exitHandlerRegistered) return;\n\t\tthis._exitHandlerRegistered = true;\n\n\t\tconst cleanup = (): void => {\n\t\t\tthis._showCursor();\n\t\t\tif (this._spinner?.isSpinning) {\n\t\t\t\tthis._spinner.stop();\n\t\t\t}\n\t\t};\n\n\t\tprocess.on('exit', cleanup);\n\t\tprocess.on('SIGINT', () => { cleanup(); process.exit(130); });\n\t\tprocess.on('SIGTERM', () => { cleanup(); process.exit(143); });\n\t}\n\n\t/**\n\t * 显示终端光标\n\t *\n\t * @returns 当前实例\n\t */\n\tcursorShow(): this {\n\t\tthis._showCursor();\n\t\treturn this;\n\t}\n\n\t/**\n\t * 隐藏终端光标\n\t *\n\t * @returns 当前实例\n\t */\n\tcursorHide(): this {\n\t\tthis._hideCursor();\n\t\treturn this;\n\t}\n\n\t/**\n\t * 切换光标可见性\n\t *\n\t * @param force - true 显示,false 隐藏,undefined 切换\n\t * @returns 当前实例\n\t */\n\tcursorToggle(force?: boolean): this {\n\t\tif (force === true) {\n\t\t\tthis._showCursor();\n\t\t} else if (force === false) {\n\t\t\tthis._hideCursor();\n\t\t} else {\n\t\t\tif (this._cursorHidden) {\n\t\t\t\tthis._showCursor();\n\t\t\t} else {\n\t\t\t\tthis._hideCursor();\n\t\t\t}\n\t\t}\n\t\treturn this;\n\t}\n\n\t// ═══════════════════════════════════════════\n\t// 通用日志\n\t// ═══════════════════════════════════════════\n\n\t/**\n\t * 输出普通日志\n\t *\n\t * @param text - 文本内容\n\t * @returns 当前实例\n\t *\n\t * @example\n\t * ```ts\n\t * cli.print('Hello World');\n\t * ```\n\t */\n\tprint(text: string): this {\n\t\tthis._out(text);\n\t\treturn this;\n\t}\n\n\t/**\n\t * 输出带前缀的日志\n\t *\n\t * @param prefix - 前缀文本\n\t * @param text - 日志内容\n\t * @returns 当前实例\n\t *\n\t * @example\n\t * ```ts\n\t * cli.logWithPrefix('[INFO]', '任务完成');\n\t * ```\n\t */\n\tlogWithPrefix(prefix: string, text: string): this {\n\t\tthis._out(`${chalk.gray(prefix)} ${text}`);\n\t\treturn this;\n\t}\n\n\t/**\n\t * 输出空行\n\t *\n\t * @returns 当前实例\n\t */\n\tnewline(): this {\n\t\tthis._out('');\n\t\treturn this;\n\t}\n\n\t/**\n\t * 输出分隔线\n\t *\n\t * @param char - 分隔线字符\n\t * @param length - 分隔线长度\n\t * @returns 当前实例\n\t *\n\t * @example\n\t * ```ts\n\t * cli.divider('=', 50);\n\t * ```\n\t */\n\tdivider(char = '─', length = 40): this {\n\t\tthis._out(chalk.gray(char.repeat(length)));\n\t\treturn this;\n\t}\n}\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "wbw-cli-tools-lib",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI工具库 - 封装inquirer/commander/ora/chalk/figlet等",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/esm/index.js",
|
|
8
|
+
"types": "./dist/esm/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": {
|
|
12
|
+
"types": "./dist/esm/index.d.ts",
|
|
13
|
+
"default": "./dist/esm/index.js"
|
|
14
|
+
},
|
|
15
|
+
"require": {
|
|
16
|
+
"types": "./dist/esm/index.d.ts",
|
|
17
|
+
"default": "./dist/index.js"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"author": {
|
|
25
|
+
"name": "wbw121124"
|
|
26
|
+
},
|
|
27
|
+
"license": "GPLv3.0-only",
|
|
28
|
+
"private": false,
|
|
29
|
+
"sideEffects": false,
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=18.0.0"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"typecheck": "tsc --noEmit",
|
|
35
|
+
"build": "npm run typecheck && rolldown -c",
|
|
36
|
+
"clean": "rimraf dist",
|
|
37
|
+
"doc": "rimraf docs && typedoc",
|
|
38
|
+
"dev": "npm run doc && npm run build"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@inquirer/prompts": "^7.0.0",
|
|
42
|
+
"commander": "^13.0.0",
|
|
43
|
+
"ora": "^8.0.0",
|
|
44
|
+
"cli-cursor": "^4.0.0",
|
|
45
|
+
"chalk": "^5.4.1",
|
|
46
|
+
"figlet": "^1.8.0"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/figlet": "^1.7.0",
|
|
50
|
+
"@types/node": "^22.0.0",
|
|
51
|
+
"rolldown": "^1.2.0",
|
|
52
|
+
"rolldown-plugin-dts": "^0.27.14",
|
|
53
|
+
"typedoc": "^0.28.20",
|
|
54
|
+
"typescript": "^5.7.0",
|
|
55
|
+
"rimraf": "^6.0.0"
|
|
56
|
+
}
|
|
57
|
+
}
|