@xubill/xx-cli 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/bin/cli.js +87 -0
- package/lib/core/base-plugin.js +292 -0
- package/lib/core/index.js +583 -0
- package/lib/utils/config-manager.js +185 -0
- package/lib/utils/history-manager.js +118 -0
- package/lib/utils/logger.js +28 -0
- package/lib/utils/plugin-config.js +158 -0
- package/lib/utils/transformers/getInput.js +76 -0
- package/lib/utils/transformers/index.js +43 -0
- package/lib/utils/transformers/renderLists.js +94 -0
- package/lib/utils/transformers/sfcMaterial.js +113 -0
- package/lib/utils/transformers/transformFilter.js +95 -0
- package/lib/utils/transformers/transformList.js +120 -0
- package/lib/utils/transformers/transformMock.js +43 -0
- package/lib/utils/transformers/transformRoutes.js +36 -0
- package/lib/utils/transformers/transformTable.js +81 -0
- package/lib/utils/transformers/transformTemplate.js +129 -0
- package/lib/utils/validator.js +96 -0
- package/package.json +62 -0
- package/readme.md +5 -0
package/bin/cli.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* xx CLI 工具入口文件
|
|
5
|
+
* 全局命令:xx
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
(async () => {
|
|
9
|
+
const { program } = require("commander");
|
|
10
|
+
// 直接导入原始核心模块
|
|
11
|
+
const core = require("../lib/core");
|
|
12
|
+
const figlet = require("figlet");
|
|
13
|
+
const chalk = require("chalk").default;
|
|
14
|
+
const updateNotifier = require("update-notifier");
|
|
15
|
+
const pkg = require("../package.json");
|
|
16
|
+
|
|
17
|
+
// // 检查版本更新
|
|
18
|
+
// const notifier = updateNotifier({
|
|
19
|
+
// pkg,
|
|
20
|
+
// updateCheckInterval: 1000 * 60 * 60 * 24 // 1天检查一次
|
|
21
|
+
// });
|
|
22
|
+
|
|
23
|
+
// // 显示更新通知
|
|
24
|
+
// notifier.notify();
|
|
25
|
+
|
|
26
|
+
// 初始化核心功能
|
|
27
|
+
await core.init();
|
|
28
|
+
|
|
29
|
+
// 定义 CLI 命令
|
|
30
|
+
program
|
|
31
|
+
.name("xx")
|
|
32
|
+
.version(pkg.version)
|
|
33
|
+
.description("个人开发工具集,支持插件系统");
|
|
34
|
+
|
|
35
|
+
// 监听 --help 指令,加上额外的提示
|
|
36
|
+
program.on("--help", async function () {
|
|
37
|
+
// 美化logo
|
|
38
|
+
console.log(
|
|
39
|
+
chalk.green("\r\n" +
|
|
40
|
+
figlet.textSync("xx", {
|
|
41
|
+
font: "3D-ASCII",
|
|
42
|
+
horizontalLayout: "default",
|
|
43
|
+
verticalLayout: "default",
|
|
44
|
+
whitespaceBreak: true,
|
|
45
|
+
})
|
|
46
|
+
)
|
|
47
|
+
);
|
|
48
|
+
// 前后两个空行调整格式,更舒适
|
|
49
|
+
console.log();
|
|
50
|
+
console.log("示例:");
|
|
51
|
+
console.log(" $ xx help");
|
|
52
|
+
console.log(" $ xx version");
|
|
53
|
+
console.log();
|
|
54
|
+
console.log();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// 注册核心命令
|
|
58
|
+
core.registerCommands(program);
|
|
59
|
+
|
|
60
|
+
// 全局错误处理
|
|
61
|
+
process.on('uncaughtException', (error) => {
|
|
62
|
+
console.error(chalk.red('错误:'), error.message);
|
|
63
|
+
console.error(chalk.yellow('提示:'), '请检查命令参数是否正确,或使用 --help 查看帮助信息');
|
|
64
|
+
process.exit(1);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
process.on('unhandledRejection', (error) => {
|
|
68
|
+
console.error(chalk.red('错误:'), error.message);
|
|
69
|
+
console.error(chalk.yellow('提示:'), '请检查命令参数是否正确,或使用 --help 查看帮助信息');
|
|
70
|
+
process.exit(1);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// 记录命令历史
|
|
74
|
+
const commandLine = process.argv.slice(2).join(' ');
|
|
75
|
+
if (commandLine) {
|
|
76
|
+
const historyManager = require('../lib/utils/history-manager');
|
|
77
|
+
historyManager.addHistory(commandLine);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 解析命令行参数
|
|
81
|
+
program.parse(process.argv);
|
|
82
|
+
|
|
83
|
+
// 注册自动补全
|
|
84
|
+
if (program.autocomplete) {
|
|
85
|
+
program.autocomplete();
|
|
86
|
+
}
|
|
87
|
+
})();
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 插件基础类
|
|
3
|
+
* 提供标准化的插件接口和通用功能
|
|
4
|
+
* 所有插件应该继承此类以确保一致性
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const ConfigManager = require('../utils/config-manager');
|
|
8
|
+
const chalk = require('chalk').default;
|
|
9
|
+
const ora = require('ora').default;;
|
|
10
|
+
const clipboardy = require('clipboardy').default;
|
|
11
|
+
|
|
12
|
+
class BasePlugin {
|
|
13
|
+
/**
|
|
14
|
+
* 构造函数
|
|
15
|
+
* @param {Object} options - 插件选项
|
|
16
|
+
*/
|
|
17
|
+
constructor(options = {}) {
|
|
18
|
+
this.pluginName = this.constructor.name.replace('Plugin', '').toLowerCase();
|
|
19
|
+
this.homeDir = process.env.HOME || process.env.USERPROFILE;
|
|
20
|
+
this.configManager = new ConfigManager(this.pluginName, options);
|
|
21
|
+
this.spinner = null;
|
|
22
|
+
this.progressTotal = 0;
|
|
23
|
+
this.progressCurrent = 0;
|
|
24
|
+
this.hooks = {};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 注册命令
|
|
29
|
+
* @param {Object} program - Commander.js 实例
|
|
30
|
+
* @abstract
|
|
31
|
+
*/
|
|
32
|
+
registerCommands(program) {
|
|
33
|
+
throw new Error('子类必须实现 registerCommands 方法');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 初始化插件
|
|
38
|
+
* @returns {Promise<void>}
|
|
39
|
+
*/
|
|
40
|
+
async init() {
|
|
41
|
+
// 插件初始化逻辑,子类可以覆盖
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 获取插件信息
|
|
46
|
+
* @returns {Object} 插件信息对象
|
|
47
|
+
*/
|
|
48
|
+
getInfo() {
|
|
49
|
+
return {
|
|
50
|
+
name: this.pluginName,
|
|
51
|
+
version: '1.0.0',
|
|
52
|
+
description: '插件描述'
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 显示加载状态
|
|
58
|
+
* @param {string} text - 加载文本
|
|
59
|
+
*/
|
|
60
|
+
startLoading(text) {
|
|
61
|
+
this.spinner = ora(text).start();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 停止加载状态
|
|
66
|
+
* @param {string} text - 成功文本
|
|
67
|
+
*/
|
|
68
|
+
stopLoading(text) {
|
|
69
|
+
if (this.spinner) {
|
|
70
|
+
this.spinner.succeed(text);
|
|
71
|
+
this.spinner = null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* 显示错误状态
|
|
77
|
+
* @param {string} text - 错误文本
|
|
78
|
+
*/
|
|
79
|
+
showError(text) {
|
|
80
|
+
if (this.spinner) {
|
|
81
|
+
this.spinner.fail(text);
|
|
82
|
+
this.spinner = null;
|
|
83
|
+
} else {
|
|
84
|
+
console.error(chalk.red(`❌ ${text}`));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* 启动进度条
|
|
90
|
+
* @param {string} text - 进度条文本
|
|
91
|
+
* @param {number} total - 总进度值
|
|
92
|
+
*/
|
|
93
|
+
startProgressBar(text, total = 100) {
|
|
94
|
+
this.spinner = ora({
|
|
95
|
+
text,
|
|
96
|
+
spinner: 'arc',
|
|
97
|
+
color: 'cyan'
|
|
98
|
+
}).start();
|
|
99
|
+
this.progressTotal = total;
|
|
100
|
+
this.progressCurrent = 0;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* 更新进度条
|
|
105
|
+
* @param {number} current - 当前进度值
|
|
106
|
+
* @param {string} text - 进度条文本
|
|
107
|
+
*/
|
|
108
|
+
updateProgressBar(current, text) {
|
|
109
|
+
if (this.spinner && this.progressTotal > 0) {
|
|
110
|
+
this.progressCurrent = current;
|
|
111
|
+
const percentage = Math.min(Math.round((current / this.progressTotal) * 100), 100);
|
|
112
|
+
this.spinner.text = `${text || ''} ${percentage}%`;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* 停止进度条
|
|
118
|
+
* @param {string} text - 完成文本
|
|
119
|
+
*/
|
|
120
|
+
stopProgressBar(text) {
|
|
121
|
+
if (this.spinner) {
|
|
122
|
+
this.spinner.succeed(text);
|
|
123
|
+
this.spinner = null;
|
|
124
|
+
this.progressTotal = 0;
|
|
125
|
+
this.progressCurrent = 0;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* 显示成功信息
|
|
131
|
+
* @param {string} text - 成功文本
|
|
132
|
+
*/
|
|
133
|
+
showSuccess(text) {
|
|
134
|
+
console.log(chalk.green(`✅ ${text}`));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* 显示警告信息
|
|
139
|
+
* @param {string} text - 警告文本
|
|
140
|
+
*/
|
|
141
|
+
showWarning(text) {
|
|
142
|
+
console.log(chalk.yellow(`⚠️ ${text}`));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* 显示信息
|
|
147
|
+
* @param {string} text - 信息文本
|
|
148
|
+
*/
|
|
149
|
+
showInfo(text) {
|
|
150
|
+
console.log(chalk.blue(`${text}`));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* 加载配置
|
|
155
|
+
* @param {string} configName - 配置名称
|
|
156
|
+
* @param {Object} cliOptions - 命令行选项
|
|
157
|
+
* @returns {Object} 合并后的配置对象
|
|
158
|
+
*/
|
|
159
|
+
loadConfig(configName = 'config', cliOptions = {}) {
|
|
160
|
+
return this.configManager.loadConfig(configName, cliOptions);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* 保存配置
|
|
165
|
+
* @param {Object} config - 配置对象
|
|
166
|
+
* @param {string} configName - 配置名称
|
|
167
|
+
* @param {boolean} isGlobal - 是否保存到全局配置
|
|
168
|
+
*/
|
|
169
|
+
saveConfig(config, configName = 'config', isGlobal = true) {
|
|
170
|
+
this.configManager.saveConfig(config, configName, isGlobal);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* 验证配置
|
|
175
|
+
* @param {Object} config - 配置对象
|
|
176
|
+
* @param {Object} schema - 配置 schema
|
|
177
|
+
* @returns {Object} 验证结果
|
|
178
|
+
*/
|
|
179
|
+
validateConfig(config, schema) {
|
|
180
|
+
return this.configManager.validateConfig(config, schema);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* 错误处理
|
|
185
|
+
* @param {Error} error - 错误对象
|
|
186
|
+
* @param {string} message - 错误消息
|
|
187
|
+
*/
|
|
188
|
+
handleError(error, message = '操作失败') {
|
|
189
|
+
this.showError(`${message}: ${error.message}`);
|
|
190
|
+
// 可以添加错误日志记录等逻辑
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* 执行异步操作
|
|
195
|
+
* @param {Function} fn - 异步函数
|
|
196
|
+
* @param {string} loadingText - 加载文本
|
|
197
|
+
* @param {string} successText - 成功文本
|
|
198
|
+
* @param {string} errorText - 错误文本
|
|
199
|
+
* @returns {Promise<any>}
|
|
200
|
+
*/
|
|
201
|
+
async executeAsync(fn, loadingText, successText, errorText) {
|
|
202
|
+
try {
|
|
203
|
+
this.startLoading(loadingText);
|
|
204
|
+
const result = await fn();
|
|
205
|
+
this.stopLoading(successText);
|
|
206
|
+
return result;
|
|
207
|
+
} catch (error) {
|
|
208
|
+
this.handleError(error, errorText);
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* 注册钩子
|
|
215
|
+
* @param {string} event - 事件名称
|
|
216
|
+
* @param {Function} callback - 回调函数
|
|
217
|
+
*/
|
|
218
|
+
registerHook(event, callback) {
|
|
219
|
+
if (!this.hooks) {
|
|
220
|
+
this.hooks = {};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (!this.hooks[event]) {
|
|
224
|
+
this.hooks[event] = [];
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
this.hooks[event].push(callback);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* 触发钩子
|
|
232
|
+
* @param {string} event - 事件名称
|
|
233
|
+
* @param {*} data - 事件数据
|
|
234
|
+
* @returns {Promise<Array>} 钩子执行结果
|
|
235
|
+
*/
|
|
236
|
+
async triggerHook(event, data) {
|
|
237
|
+
if (!this.hooks || !this.hooks[event]) {
|
|
238
|
+
return [];
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const results = [];
|
|
242
|
+
for (const callback of this.hooks[event]) {
|
|
243
|
+
try {
|
|
244
|
+
const result = await callback(data);
|
|
245
|
+
results.push(result);
|
|
246
|
+
} catch (error) {
|
|
247
|
+
this.handleError(error, `执行钩子 ${event} 时出错`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return results;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* 检查是否注册了指定钩子
|
|
256
|
+
* @param {string} event - 事件名称
|
|
257
|
+
* @returns {boolean} 是否注册了钩子
|
|
258
|
+
*/
|
|
259
|
+
hasHook(event) {
|
|
260
|
+
return this.hooks && this.hooks[event] && this.hooks[event].length > 0;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* 复制文本到剪贴板
|
|
265
|
+
* @param {string} text - 要复制的文本
|
|
266
|
+
* @param {string} successMessage - 成功消息
|
|
267
|
+
* @returns {Promise<boolean>} 是否复制成功
|
|
268
|
+
*/
|
|
269
|
+
async copyToClipboard(text, successMessage) {
|
|
270
|
+
try {
|
|
271
|
+
// 尝试不同的 clipboardy API 调用方式
|
|
272
|
+
if (clipboardy.default && typeof clipboardy.default.write === 'function') {
|
|
273
|
+
await clipboardy.default.write(text);
|
|
274
|
+
} else if (typeof clipboardy.write === 'function') {
|
|
275
|
+
await clipboardy.write(text);
|
|
276
|
+
} else if (typeof clipboardy.writeSync === 'function') {
|
|
277
|
+
clipboardy.writeSync(text);
|
|
278
|
+
} else {
|
|
279
|
+
throw new Error('clipboardy API 不可用');
|
|
280
|
+
}
|
|
281
|
+
if (successMessage) {
|
|
282
|
+
this.showSuccess(successMessage);
|
|
283
|
+
}
|
|
284
|
+
return true;
|
|
285
|
+
} catch (error) {
|
|
286
|
+
this.showWarning(`复制到剪贴板失败: ${error.message}`);
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
module.exports = BasePlugin;
|