@xubill/xx-cli
v2.0.0
Published
个人工具集
Readme
xx-cli
个人开发工具集,支持插件系统,提供常用开发工具功能。
安装与使用
# 全局安装 xx-cli
npm i @xubill/xx-cli -g
# 添加插件
xx p add xxxx.js
# 使用命令
xx <command> [options]版本记录
v1.0.0
- 初始化项目,搭建基本框架
- 实现插件系统核心功能
- 添加基础命令(help, version, history)
- 修复原生插件和用户插件区别
- 壳功能移动到原生插件目录
v2.0.0
- 支持新格式命令注册,兼容旧格式插件
- 更新插件创建模板,采用配置对象格式并包含类结构实现
框架自带命令
xx-cli 框架自带以下核心命令(位于 core 模块中):
基础命令
| 命令 | 别名 | 描述 |
|------|------|------|
| help | - | 显示帮助信息 |
| version | v | 显示版本信息 |
| history | h | 查看命令历史记录 |
插件管理命令
| 命令 | 别名 | 描述 |
|------|------|------|
| plugin list | p ls | 列出所有插件 |
| plugin enable <plugin> | p en <plugin> | 启用插件 |
| plugin disable <plugin> | p dis <plugin> | 禁用插件 |
| plugin add <url> | p add <url> | 添加外部自定义插件 |
| plugin remove <plugin> | p rm <plugin> | 删除指定插件 |
| plugin create <name> | p cr <name> | 创建指定名称的插件模板 |
| plugin config <plugin> [key] [value] | p c <plugin> [key] [value] | 查看和设置插件配置 |
配置管理命令
| 命令 | 别名 | 描述 |
|------|------|------|
| config-manager export [target] | conf export [target] | 导出所有插件配置到指定目录 |
| config-manager import [source] | conf import [source] | 从指定目录导入配置并覆盖 |
| config-manager list | conf list | 列出所有插件的配置文件位置和状态 |
项目结构
xx-cli 项目采用模块化设计,主要包含以下核心目录:
核心模块目录 (/Users/xub/xx-cli/lib/core)
核心模块目录包含 xx-cli 的核心功能实现,负责初始化系统、注册命令、管理插件等。
文件结构
lib/core/
├── index.js # 核心模块,负责初始化系统、注册命令、管理插件等
└── base-plugin.js # 插件基础类,提供标准化的插件接口和通用功能核心文件说明
- index.js:核心模块,负责初始化系统、注册命令、管理插件等。它包含了插件加载、命令注册、钩子管理等核心功能。
- base-plugin.js:插件基础类,提供标准化的插件接口和通用功能。所有插件应该继承此类以确保一致性,它提供了命令注册、配置管理、用户界面等通用功能。
插件目录 (/Users/xub/xx-cli/lib/plugins)
插件目录包含 xx-cli 的内置插件,这些插件提供了各种功能,如配置管理、历史记录管理、插件管理等。
文件结构
lib/plugins/
├── config-manager.js # 配置管理插件,用于管理所有插件的配置文件
├── history.js # 历史记录插件,用于查看命令历史记录
└── plugin-manager.js # 插件管理插件,用于管理所有插件内置插件说明
- config-manager.js:配置管理插件,用于管理所有插件的配置文件。它提供了导出、导入、列出配置等功能。
- history.js:历史记录插件,用于查看命令历史记录。它提供了查看、清除、搜索历史记录等功能。
- plugin-manager.js:插件管理插件,用于管理所有插件。它提供了列出、启用、禁用、添加、删除、创建插件等功能。
插件开发
本部分将详细介绍如何在 xx-cli 中创建、开发和部署插件。
创建插件
使用 plugin create 命令
xx-cli 提供了 plugin create 命令,可以快速生成插件模板。这是推荐的创建插件的方式。
基本用法
# 创建插件到插件目录(默认使用新格式,包含类结构实现)
xx plugin create <plugin-name>
# 或者使用别名
xx p create <plugin-name>
# 或者使用更短的别名
xx p cr <plugin-name>插件模板格式
从 v2.0.0 版本开始,plugin create 命令生成的插件模板采用统一的格式:
- 配置对象格式:使用配置对象定义插件的基本信息和命令
- 包含类结构实现:核心功能通过类结构实现,提高代码模块化和可维护性
- 自动生成命令名称:根据插件名称自动生成驼峰命名的命令名称
- 标准的用户提示:使用标准化的用户提示和错误处理
这种格式结合了配置对象的简洁性和类结构的模块化优势,是推荐的插件开发方式。
示例
# 创建一个名为 `hello-world` 的插件
xx plugin create hello-world
# 输出:
# 创建插件模板...
# 插件模板已成功创建到:/Users/xub/.xx-cli/plugins/hello-world.js
# 提示:请编辑插件文件,实现具体功能
# 测试方法:运行 "node bin/cli.js <插件命令>"
# 已自动打开 vscode 编辑插件文件手动创建插件
如果需要更灵活地控制插件结构,也可以手动创建插件文件。
- 在
lib/plugins目录下创建一个新的 JavaScript 文件,例如my-plugin.js - 实现插件类,包含必要的方法
插件结构
插件格式
xx-cli 支持两种插件格式:
- 旧格式(类结构格式):继承自
BasePlugin类的插件实现 - 新格式(配置对象格式):使用配置对象定义的插件实现(推荐)
新格式(配置对象格式)插件结构
使用 plugin create 命令生成的默认插件模板包含以下结构(v2.0.0 版本更新):
/**
* hello-world 插件
* 用于实现 hello-world 相关功能
*
* 命令说明:
* - helloWorld [options]:hello-world 相关功能
* - 示例:helloWorld
*
* 功能说明:
* - 实现 hello-world 相关功能
*
* 用户体验:
* - 使用标准化的用户提示
* - 提供清晰的错误提示
* - 支持命令行参数
*/
/**
* hello-world 插件类
* 实现 hello-world 相关核心功能
*/
class HelloWorldPlugin {
constructor() {
this.pluginName = 'hello-world';
}
/**
* 执行 hello-world 命令
* @param {Array} args - 命令参数
* @param {Object} options - 命令选项
* @param {Object} helper - 插件帮助器
*/
async execute(args, options, helper) {
helper.showInfo('hello-world 命令执行成功!');
if (options && options.verbose) {
helper.showInfo('详细信息:');
helper.showInfo('- 命令名称: helloWorld');
helper.showInfo('- 执行时间: ' + new Date().toLocaleString());
}
}
}
// 创建插件实例
const pluginInstance = new HelloWorldPlugin();
module.exports = {
name: "helloWorld",
alias: "h",
description: "hello-world 相关功能",
options: [
{
name: "-v, --verbose",
description: "显示详细信息",
},
{
name: "-h, --help",
description: "显示帮助信息",
},
],
action: async (args, options, helper) => {
await pluginInstance.execute(args, options, helper);
},
};旧格式(类结构格式)插件结构
使用 plugin create --format old 命令生成的插件模板包含以下结构:
/**
* hello-world 插件
* 用于实现 hello-world 相关功能
*
* 命令说明:
* - helloWorld [options]:hello-world 相关功能
* - 示例:helloWorld
*
* 功能说明:
* - 实现 hello-world 相关功能
*
* 用户体验:
* - 使用标准化的用户提示
* - 提供清晰的错误提示
* - 支持命令行参数
*/
const BasePlugin = require('../core/base-plugin');
class HelloWorldPlugin extends BasePlugin {
constructor(options = {}) {
super(options);
this.pluginName = 'hello-world';
}
/**
* 注册命令
* @param {Object} program - Commander.js 实例
*/
registerCommands(program) {
program.command('helloWorld')
.description('hello-world 相关功能')
.option('-v, --verbose', '显示详细信息')
.option('-h, --help', '显示帮助信息')
.action((options) => this.helloWorldCommand(options));
}
/**
* hello-world 命令
* @param {Object} options - 命令选项
*/
helloWorldCommand(options) {
console.log('hello-world 命令执行成功!');
if (options && options.verbose) {
console.log('详细信息:');
console.log('- 命令名称:', 'helloWorld');
console.log('- 执行时间:', new Date().toLocaleString());
}
}
}
module.exports = HelloWorldPlugin;命令注册
新格式(配置对象格式)命令注册
对于新格式的插件,命令注册通过配置对象的 options 和 action 属性实现:
module.exports = {
name: 'hello-world',
alias: 'hw',
description: 'hello-world 相关功能',
options: [
{
name: '-v, --verbose',
description: '显示详细信息'
},
{
name: '-n, --name <name>',
description: '指定名称'
}
],
action: (args, options, cmd) => {
const helper = createPluginsHelper('hello-world');
helloWorldCommand(args.alias, options, helper);
}
};旧格式(类结构格式)命令注册
对于旧格式的插件,命令注册通过实现 registerCommands 方法实现:
registerCommands(program) {
program.command('helloWorld [alias]')
.alias('hw')
.description('hello-world 相关功能')
.option('-v, --verbose', '显示详细信息')
.option('-n, --name <name>', '指定名称')
.action((alias, options) => this.helloWorldCommand(alias, options));
}命令选项
可以为命令添加各种选项,以增强命令的灵活性。
常用选项类型
布尔选项:不需要参数的选项
.option('-v, --verbose', '显示详细信息')值选项:需要参数的选项
.option('-n, --name <name>', '指定名称')数组选项:可以接收多个值的选项
.option('-a, --array <items...>', '指定数组参数')带默认值的选项:
.option('-p, --port <port>', '指定端口', '3000')带验证器的选项:
.option('-p, --port <port>', '指定端口', parseInt)
插件配置
插件可以使用配置文件来存储和管理配置信息。
配置文件结构
配置文件通常存储在用户主目录的 .xx 文件夹中,例如:
~/.xx/
└── hello-world/
└── config.json读取配置
const fs = require('fs');
const path = require('path');
// 获取用户主目录
const homeDir = process.env.HOME || process.env.USERPROFILE;
const xxDir = path.join(homeDir, '.xx-cli');
const pluginConfigDir = path.join(xxDir, this.pluginName);
const configFile = path.join(pluginConfigDir, 'config.json');
// 读取配置
let config = {};
if (fs.existsSync(configFile)) {
config = JSON.parse(fs.readFileSync(configFile, 'utf8'));
}写入配置
const fs = require('fs');
const path = require('path');
// 确保配置目录存在
fs.ensureDirSync(pluginConfigDir);
// 写入配置
fs.writeFileSync(configFile, JSON.stringify(config, null, 2));插件工具
新格式(配置对象格式)插件工具
对于新格式的插件,使用 plugins-helper 提供的功能来替代 BasePlugin 类的方法:
导入 plugins-helper
const { createPluginsHelper } = require('../utils/plugins-helper');核心功能
plugins-helper 提供了以下核心功能:
1. 用户界面
helper.showInfo(text):显示信息helper.showSuccess(text):显示成功信息helper.showWarning(text):显示警告信息helper.showError(text):显示错误信息helper.startLoading(text):显示加载状态helper.stopLoading(text):停止加载状态
2. 配置管理
可以使用 fs-extra 和 path 模块来实现配置管理:
const fs = require('fs-extra');
const path = require('path');
// 确保配置目录存在
function ensureConfigDir(pluginName) {
const homeDir = process.env.HOME || process.env.USERPROFILE;
const xxDir = path.join(homeDir, '.xx-cli');
const pluginConfigDir = path.join(xxDir, pluginName);
fs.ensureDirSync(pluginConfigDir);
return pluginConfigDir;
}
// 加载配置
function loadConfig(pluginName) {
const configDir = ensureConfigDir(pluginName);
const configFile = path.join(configDir, 'config.json');
if (fs.existsSync(configFile)) {
return JSON.parse(fs.readFileSync(configFile, 'utf8'));
}
return {};
}
// 保存配置
function saveConfig(pluginName, config) {
const configDir = ensureConfigDir(pluginName);
const configFile = path.join(configDir, 'config.json');
fs.writeFileSync(configFile, JSON.stringify(config, null, 2));
}使用示例
const { createPluginsHelper } = require('../utils/plugins-helper');
function myCommand(options, helper) {
try {
helper.showInfo('执行操作...');
// 执行操作
setTimeout(() => {
helper.showSuccess('操作成功');
if (options && options.verbose) {
helper.showInfo('详细信息:');
helper.showInfo('- 执行时间:', new Date().toLocaleString());
}
}, 1000);
} catch (error) {
helper.showError(`执行命令失败: ${error.message}`);
}
}
module.exports = {
name: 'my-plugin',
description: 'My plugin command',
options: [
{
name: '-v, --verbose',
description: '显示详细信息'
}
],
action: (args, options, cmd) => {
const helper = createPluginsHelper('my-plugin');
myCommand(options, helper);
}
};旧格式(类结构格式)基础插件类
对于旧格式的插件,使用 BasePlugin 基类:
基础插件类路径
BasePlugin 类位于 /Users/xub/xx-cli/lib/core/base-plugin.js,插件可以通过以下方式导入:
const BasePlugin = require('../core/base-plugin');核心功能
BasePlugin 类提供了以下核心功能:
1. 命令注册
registerCommands(program):注册命令到 Commander.js 实例,子类必须实现此方法
2. 插件初始化
init():插件初始化逻辑,子类可以覆盖getInfo():获取插件信息
3. 用户界面
startLoading(text):显示加载状态stopLoading(text):停止加载状态showError(text):显示错误信息startProgressBar(text, total):启动进度条updateProgressBar(current, text):更新进度条stopProgressBar(text):停止进度条showSuccess(text):显示成功信息showWarning(text):显示警告信息showInfo(text):显示信息
4. 配置管理
loadConfig(configName, cliOptions):加载配置saveConfig(config, configName, isGlobal):保存配置
5. 错误处理
handleError(error, message):处理错误
6. 异步操作
executeAsync(fn, loadingText, successText, errorText):执行异步操作并显示加载状态
7. 钩子系统
registerHook(event, callback):注册钩子triggerHook(event, data):触发钩子hasHook(event):检查是否注册了指定钩子
8. 剪贴板操作
copyToClipboard(text, successMessage):复制文本到剪贴板
使用示例
const BasePlugin = require('../core/base-plugin');
class MyPlugin extends BasePlugin {
constructor(options = {}) {
super(options);
this.pluginName = 'my-plugin';
}
registerCommands(program) {
program.command('mycommand')
.description('My plugin command')
.action(() => this.myCommand());
}
async myCommand() {
try {
await this.executeAsync(
async () => {
// 执行异步操作
await new Promise(resolve => setTimeout(resolve, 1000));
return '操作结果';
},
'执行操作...',
'操作成功',
'操作失败'
);
} catch (error) {
this.handleError(error, '执行命令失败');
}
}
}
module.exports = MyPlugin;插件生命周期
插件的生命周期包括:
- 初始化:插件被加载时
- 命令注册:调用
registerCommands方法 - 命令执行:用户运行插件命令时
- 清理:命令执行完成后
测试插件
本地测试
创建插件:
xx plugin create <plugin-name>编辑插件文件,实现具体功能
运行插件命令:
# 直接运行 node bin/cli.js <plugin-command> # 或者使用 xx 命令(如果已全局安装) xx <plugin-command>
测试命令选项
# 测试布尔选项
xx <plugin-command> --verbose
# 测试值选项
xx <plugin-command> --name John
# 测试组合选项
xx <plugin-command> --verbose --name John部署插件
发布到插件目录
- 确保插件文件位于
lib/plugins目录 - 运行
xx plugin list命令,确认插件已被识别 - 插件会自动加载并注册到命令系统中
分享插件
如果要与他人分享插件,可以:
- 将插件文件发送给他人
- 他人将插件文件放入其
lib/plugins目录 - 运行
xx plugin list命令,确认插件已被识别
从外部添加插件
可以使用 plugin add 命令从 URL 或本地文件添加插件:
# 从 URL 添加插件
xx plugin add https://example.com/my-plugin.js
# 从本地文件添加插件
xx plugin add /path/to/my-plugin.js