Node.js命令行工具开发
文章目录
- Node.js命令行工具开发
- 实例效果展示
- 初始化项目
- 基本结构
- 配置package.json
- 创建启动文件
- 创建入口文件及其它配置文件
- 1.lib/index.js 入口文件
- 2.lib/theme.js 封装打印带样式的文本到终端的函数
- 3.lib/utlis.js 复用模块封装
- 4.lib/create-cli.js cli创建逻辑
- 5.lib/create-uni.js uni项目模板创建
- 命令行工具测试
- 版本管理
- 发布npm
- 发布完成测试
- 各包的用途
- commander
- 核心功能
- 1. 定义命令和选项
- 1.自动生成的帮助信息
- 核心概念
- 1. 选项(Options)
- 2. 命令(Commands)
- 3. 子命令(Subcommands)
- 实际应用场景
- 1. 创建脚手架工具(如`create-react-app`)
- 2. 构建开发服务器
- chalk
- 核心功能
- 1. 改变文字颜色
- 2. 改变背景色
- 3. 添加文本样式
- 4. 链式调用
- 5.**256色和RGB支持**
- 实际应用场景
- 高级用法
- 1.自定义主题函数
- 2. 组合使用模板字符串
- ora
- 核心功能
- 1. 基本用法
- 2. 状态变化效果
- 3.**丰富的预设动画**:包含超过30种动画效果
- 4.**颜色支持**:与chalk完美配合
- 5.**速度控制**:调整动画刷新率
- 6.**多行文本支持**
- 实际应用场景
- 1. 文件下载进度
- 2. 安装依赖
- 高级用法
- 1. 自定义动画
- 2. 组合进度条
- 3. 动态更新文本
- inquirer
Node.js命令行工具开发
- 开发一个类似
npm create vite@latest
或npx xxx
这样的Node.js命令行工具
实例效果展示
- 模板提供了两个简单的列子
- 1.创建一个命令行工具模板项目,也就是当前这个项目的源代码
- 2.从远程拉取创建一个uniapp的模板项目
- 先全局安装
npm i -g rxm-node-cli
# 安装完成后
# 安装完成后 执行命令行工具,先不指定模板,会通过命令行交互选择模板
rxm-node-cli create project-name
# 或者
npx rxm-node-cli create project-name
# 指定模板
npx rxm-node-cli create project-name -t cli-template
npx rxm-node-cli create project-name -t uni-template
- npm地址:https://www.npmjs.com/package/rxm-node-cli
- 源码地址:https://gitee.com/public_12/node-cli-template
- 博文地址:https://blog.csdn.net/weixin_43376417/article/details/152220237?spm=1011.2415.3001.5331
初始化项目
mkdir rxm-node-cli-tool
cd rxm-node-cli-tool
npm init -y
基本结构
rxm-node-cli-tool/
├── bin/
│ └── cli.js # 命令行入口文件
├── lib/
│ └── index.js # 主要逻辑代码
├── package.json
└── README.md
配置package.json
- bin下的rxm-node-cli就是命令行工具名称
{"name": "rxm-node-cli","version": "1.0.3","bin": {"rxm-node-cli": "./bin/cli.js"},"scripts": {"dev": "node bin/cli.js create","upgrade": "standard-version"},"standard-version": {"message": "docs: %s [skip ci]"},"dependencies": {"chalk": "^4.1.0","commander": "^9.0.0","download-git-repo": "^3.0.2","fs-extra": "^11.3.2","inquirer": "^8.0.0"},"devDependencies": {"standard-version": "^9.5.0"}
}
创建启动文件
- 根目录下创建 bin/cli.js
#!/usr/bin/env nodeconst { program } = require('commander');
const pkg = require('../package.json');
program.name('rxm-node-cli').description('cli命令行开发模板');
program.version(pkg.version).command('create <project-name>').description('创建一个新项目').option('-t, --template <name>', '指定模板') // 子命令可以配置多个,多次调用option方法即可。得到的option对象会合并.action((name,options) => {require('../lib/index')({name,template:options.template});});program.parse(process.argv); // process.argv是启动命令行时传入的参数数组
创建入口文件及其它配置文件
1.lib/index.js 入口文件
// lib/index.js
const tool = require('./create-cli.js');
const uni = require('./create-uni')
const theme = require('./theme');
const inquirer = require('inquirer');module.exports = async function ({ name: projectName, template }) {if (!projectName) return theme.error('请输入项目名称')if (!template) {const { action } = await inquirer.prompt([{name: 'action',type: 'list',message: '请选择模板类型:',choices: [{ name: 'cli-template', value: 'cli-template' },{ name: 'uni-template', value: 'uni-template' }]}]);template = action}if (template === 'cli-template') return tool(projectName)if (template === 'uni-template') return uni(projectName)theme.error('模板不存在')
};
2.lib/theme.js 封装打印带样式的文本到终端的函数
// lib/theme.js
const chalk = require('chalk');module.exports = {error: text=>{console.log(chalk.red.bold(text))},success: text=>{console.log(chalk.green(text))},successBold: text=>{console.log(chalk.bold.green(text))},warning: text=>{console.log(chalk.yellow.underline(text))}
};
3.lib/utlis.js 复用模块封装
const inquirer = require('inquirer');
const fs = require('fs');
const pfs = require('fs-extra');
const theme = require('./theme');module.exports = {/*** 创建前*/async beforCreate(projectName) {const { action } = await inquirer.prompt([{name: 'action',type: 'list',message: '是否开始创建:',choices: [{ name: '是', value: 'yes' },{ name: '否', value: false }]}]);if (!action) returntheme.success(`创建项目: ${projectName}`)// 检查目录是否已存在if (fs.existsSync(projectName)) {// 交互式询问用户是否覆盖const { action } = await inquirer.prompt([{name: 'action',type: 'list',message: '目录已存在,请选择:',choices: [{ name: '覆盖', value: 'overwrite' },{ name: '取消', value: false }]}]);// action等于 choices选择的valueif (!action) return;if (action === 'overwrite') {console.log(`\n删除 ${projectName}...`);// 删除目录fs.rmSync(projectName, { recursive: true, force: true });}}// 创建项目目录fs.mkdirSync(projectName);return true},/*** initJson*/initJson(projectName) {const json = require('../package.json')json.name = projectNamejson.version = '1.0.0'console.log('创建package.json...')return pfs.outputFile(`${projectName}/package.json`,JSON.stringify(json, null, 2));}
}
4.lib/create-cli.js cli创建逻辑
const path = require('path');
const fs = require('fs');
const pfs = require('fs-extra');
const theme = require('./theme');
const utlis = require('./utlis');
const basePath = path.join(__dirname, '../')
// 拷贝目录
async function copyDirectorySync (src, projectName) {await pfs.ensureDir(projectName+'/'+src);const url = path.join(basePath, src)// 读取源目录内容const items = fs.readdirSync(url);for (const item of items) {pfs.outputFile(`${projectName}/${src}/${item}`,fs.readFileSync(path.join(url, item), 'utf-8'))}
}
module.exports = async function (projectName) {await utlis.beforCreate(projectName)await pfs.ensureDir(projectName);await utlis.initJson(projectName)await copyDirectorySync('lib', projectName)await copyDirectorySync('bin', projectName)// 拷贝 README.mdpfs.outputFile(`${projectName}/README.md`,fs.readFileSync(path.join(basePath, 'README.md'), 'utf-8'))theme.success('\n项目创建成功')theme.successBold(`cd ${projectName}`)theme.successBold(`npm install`)
}
5.lib/create-uni.js uni项目模板创建
// lib/create-uni.js
const path = require('path');
const fs = require('fs');
const theme = require('./theme');
const utlis = require('./utlis');
const { promisify } = require('util');
const download = promisify(require('download-git-repo'));
const ora = require('ora');
const chalk = require('chalk');module.exports = async function (projectName) {await utlis.beforCreate(projectName);const spinner = ora('正在创建项目...').start();try {spinner.text = '正在下载模板...';// 确保使用绝对路径const projectPath = path.resolve(process.cwd(), projectName);await download('direct:https://gitee.com/ren_jinming/uniapp-vue3-ts-uview-plus.git', projectPath, { clone: true });console.log('下载完成');// 修改package.jsonspinner.text = '正在修改package.json...';const packageJsonPath = path.join(projectPath, 'package.json');// 检查文件是否存在if (!fs.existsSync(packageJsonPath)) {throw new Error(`package.json not found at ${packageJsonPath}`);}// 读取并修改package.jsonconst packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));packageJson.name = projectName;// 写入修改后的内容fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');console.log('修改完成');spinner.succeed(chalk.green('项目创建成功!'));theme.successBold(`cd ${projectName}`);theme.successBold(`npm install`);console.log('使用Hbuild X打开项目')} catch (error) {console.error(error);spinner.warn('项目创建失败');}
}
命令行工具测试
- 本地测试
# cli-test作为创建项目的名称
npm run dev cli-test
- 将命令行工具链接到全局测试
# 安装
npm link
# 如果上面的命令报错可以换一个
npm link --force
# 查看支持哪些命令
rxm-node-cli
# 运行命令行 查询版本
rxm-node-cli -V
# 创建指定的模板
rxm-node-cli create text-project -t cli-template
rxm-node-cli create text-project -t uni-template
# 不指定模板,通过命令行选择
rxm-node-cli create text-project
# 或者用npx 的形式执行
npx rxm-node-cli create text-project
版本管理
- 安装 standard-version 用于版本管理:
npm install -D standard-version
- 更新 package.json 配置升级命令及提交msg
{"scripts": {"upgrade": "standard-version"},"standard-version":{"message": "docs: %s [skip ci]"},
}
- 推荐自动升级方案 会根据 Git 提交记录自动升级版本号,也可以手动设置版本
pnpm upgrade
# 或者
npx standard-version
# 它会读取git的提交信息 并将相关的记录写入到 CHANGELOG.md
发布npm
- 如果还没有npm账号可以先去注册一个 :https://www.npmjs.com/signup
- 登录npm
# 执行
npm login
# 在终端输入账号
Username: xxxx (回车)
# 输入密码
Password: xxxx (这里输入密码终端是没有交互反馈的,所以只要确认输入完直接回车)
# 输入邮箱会发送验证码
Email: (this IS public): xxx 回车
# 输入邮箱验证码
Enter one-time password: xxxx 回车
# 登录成功后显示 Logged in as renjinming on https://registry.npmjs.org/.# 模拟发布
npm pack --dry-run
# 执行后会打印出要上传的信息 如下:
Debugger attached.
npm notice
npm notice 📦 rxm-node-cli@1.0.4
npm notice === Tarball Contents ===
npm notice 503B CHANGELOG.md
npm notice 596B README.md
npm notice 622B bin/cli.js
npm notice 1.1kB lib/create-cli.js
npm notice 1.6kB lib/create-uni.js
npm notice 788B lib/index.js
npm notice 312B lib/theme.js
npm notice 1.6kB lib/utlis.js
npm notice 481B package.json
npm notice === Tarball Details ===
npm notice name: rxm-node-cli
npm notice version: 1.0.4
npm notice filename: rxm-node-cli-1.0.4.tgz
npm notice package size: 3.0 kB
npm notice unpacked size: 7.6 kB
npm notice shasum: 2efbb29c6d3731a2abb6333b386f39526b8d1e69
npm notice integrity: sha512-BrixVR69X/dJd[...]Brt8xuFQWYwnQ==
npm notice total files: 9
npm notice
rxm-node-cli-1.0.4.tgz
Waiting for the debugger to disconnect...
# 实际发布
npm publish
#如果版本号以存在,须要修改版本信息,不然提交会403 执行上面的升级命令在进行发布
➔ 若包名冲突或无权限,会报错。
- 发布403错误
如果发布403可能是包名已经存在。可以用下面的命令试试看有没有,如果存在可以修改package.json的name重新定义包名
npm view rxm-node-cli # 检查包名占用
发布完成测试
- 发布成功验证
# 查询包在npm上的所有版本
npm view rxm-node-cli versions
- 安装使用
npm i -g rxm-node-cli
# 安装完成后 执行命令行工具
rxm-node-cli create project-name
# 或者
npx rxm-node-cli create project-name
# 指定模板
npx rxm-node-cli create project-name -t cli-template
npx rxm-node-cli create project-name -t uni-template
各包的用途
commander
commander
是 Node.js 最流行的命令行工具开发框架,用于快速构建 CLI(Command Line Interface)应用程序。它可以实现如下功能:
- 解析命令行参数(选项、子命令等)
- 自动生成帮助信息(
--help
)- 提供用户友好的命令行交互体验
核心功能
1. 定义命令和选项
// app.js
const { program } = require('commander');program.name('rxm-node-cli') // 工具名称.description('一个示例CLI工具') // 描述.version('1.0.0'); // 版本号program.command('create <project-name>') // 定义子命令.description('创建新项目') // 命令描述.option('-t, --template <name>', '指定模板') // 命令选项 // 子命令可以配置多个,多次调用option方法即可。得到的option对象会合并.action((name, options) => { // 执行逻辑console.log(`创建项目 ${name},使用模板: ${options.template || '默认'}`);});program.parse(); // 必须调用// 运行 node app.js create my-project -t vue
// 输出 创建项目 my-project,使用模板: vue
1.自动生成的帮助信息
- 运行时会自动添加
-h, --help
选项:
node app.js --help
- 输出示例:
Usage: rxm-node-cli [options] [command]一个示例CLI工具Options:-V, --version 输出版本号-h, --help 显示帮助信息Commands:create <project-name> 创建新项目help [command] 显示命令帮助
核心概念
1. 选项(Options)
program.option('-d, --debug', '开启调试模式') // 布尔选项.option('-p, --port <number>', '端口号', 80) // 带默认值的选项.option('-c, --config <path>', '配置文件'); // 必填选项
2. 命令(Commands)
program.command('add <file>') // 必需参数.description('添加文件').action((file) => {console.log(`添加文件: ${file}`);});
3. 子命令(Subcommands)
program.command('server').description('服务器操作').command('start') // 子命令.action(() => {console.log('启动服务器');});
实际应用场景
1. 创建脚手架工具(如create-react-app
)
program.command('create <app-name>').description('创建新应用').action((name) => {// 下载模板、初始化项目等操作});
2. 构建开发服务器
program.command('start').option('-p, --port <number>', '端口号', 3000).action((options) => {startDevServer(options.port);});
chalk
chalk
是 Node.js 中最流行的终端字符串样式库,专门用于在命令行工具中输出彩色文本和丰富的格式(如加粗、下划线等)。它能让你的 CLI 工具拥有更美观、更专业的输出效果。
核心功能
1. 改变文字颜色
const chalk = require('chalk');console.log(chalk.red('错误信息')); // 红色文字
console.log(chalk.blue('提示信息')); // 蓝色文字
console.log(chalk.green('成功信息')); // 绿色文字
2. 改变背景色
console.log(chalk.bgRed.white('白字红底'));
console.log(chalk.bgGreen.black('黑字绿底'));
3. 添加文本样式
console.log(chalk.bold('加粗文本'));
console.log(chalk.underline('下划线文本'));
console.log(chalk.dim('暗淡文本'));
4. 链式调用
console.log(chalk.blue.bgRed.bold('蓝字红底加粗'));
5.256色和RGB支持
console.log(chalk.rgb(255, 136, 0).bold('橙色加粗'))
console.log(chalk.hex('#FF8800')('十六进制颜色'))
实际应用场景
- 可自定义进行封装,方便调用 ,如下
function showError(message) {console.log(chalk.red.bold('✖ 错误:'), chalk.red(message));
}
// 输出:✖ 错误: 文件不存在
function showSuccess(message) {console.log(chalk.green.bold('✓ 成功:'), message);
}
function showWarning(message) {console.log(chalk.yellow.bold('⚠ 警告:'), message);
}
- 创建彩色表格,虽然这种方式不太完美但是也勉强能看出是表格了
// 空格
const space = ' ';console.log(chalk.blue('名称') + space + chalk.green('状态') + space + chalk.yellow('进度')
);
console.log(chalk.blue('张三') + space +chalk.green('已支付') + space +chalk.yellow('100%')
)
console.log(chalk.blue('李四') + space +chalk.green('未支付') + space +chalk.yellow('0%')
)
高级用法
1.自定义主题函数
const theme = {error: text=>{console.log(chalk.red.bold(text))},success: text=>{console.log(chalk.green(text))},warning: text=>{console.log(chalk.yellow.underline(text))}
};
theme.error('错误信息');
theme.success('成功信息');
theme.warning('警告信息');
2. 组合使用模板字符串
const name = 'Alice';
console.log(chalk`{red 警告:} 用户 {blue.bold ${name}} 不存在`);
ora
ora
是 Node.js 中一个专门用于**命令行加载动画(Spinner)**的库,它可以在执行异步操作时显示旋转的加载指示器,为用户提供清晰的执行状态反馈。这个模块特别适合需要等待的操作(如文件下载、安装依赖等)。
核心功能
1. 基本用法
const ora = require('ora');const spinner = ora('正在加载...').start();// 模拟异步操作
setTimeout(() => {spinner.succeed('加载完成');
}, 2000);
2. 状态变化效果
方法 | 效果示例 | 使用场景 |
---|---|---|
.start() | ⠋ 正在加载… | 开始加载 |
.succeed(text) | ✓ 成功 | 操作成功 |
.fail(text) | ✖ 失败 | 操作失败 |
.warn(text) | ⚠ 警告 | 出现警告 |
.info(text) | ℹ 信息 | 显示信息 |
.stop() | 清除动画 | 手动停止 |
3.丰富的预设动画:包含超过30种动画效果
spinner.spinner = 'dots'; // 可选:dots, line, pulse等
4.颜色支持:与chalk完美配合
spinner.color = 'yellow'; // 改变颜色
5.速度控制:调整动画刷新率
spinner.interval = 100; // 毫秒(默认80)
6.多行文本支持
spinner.text = '第一行\n第二行';
实际应用场景
1. 文件下载进度
async function downloadFile() {const spinner = ora('下载文件中...').start();try {await download('https://example.com/file.zip');spinner.succeed('下载完成');} catch (err) {spinner.fail('下载失败');console.error(err);}
}
2. 安装依赖
async function installDependencies() {const spinner = ora('正在安装依赖...').start();try {await execa('npm', ['install']);spinner.succeed('依赖安装完成');} catch {spinner.fail('依赖安装失败');}
}
高级用法
1. 自定义动画
const ora = require('ora');const spinner = ora('正在加载...').start();
spinner.spinner = {interval: 80,frames: ['-', '+', '-']
};
// 模拟异步操作
setTimeout(() => {spinner.succeed('加载完成');// spinner.fail('加载完成');
}, 3000);
2. 组合进度条
const ora = require('ora');const spinner = ora({text: '处理中...',spinner: {frames: ['🕛', '🕒', '🕕', '🕘'],interval: 300}
}).start();
// 模拟异步操作
setTimeout(() => {spinner.succeed('加载完成');// spinner.fail('加载完成');
}, 3000);
3. 动态更新文本
const ora = require('ora');
const spinner = ora({text: '处理中...',spinner: {frames: ['🕛', '🕒', '🕕', '🕘'],interval: 300}
}).start();
let i = 1
const time = setInterval(() => {spinner.text = `已处理 ${i}/${100} 文件`;i++
}, 1000);
// 模拟异步操作
setTimeout(() => {spinner.succeed('加载完成');clearInterval(time);// spinner.fail('加载完成');
}, 5000);
inquirer
- 这是一个nodejs命令行交互工具,关于使用可查看我写的另外一篇文章
https://blog.csdn.net/weixin_43376417/article/details/135908186?spm=1011.2415.3001.5331
直接点击目录的 使用inquirer包来实现 查看
- 整理不易,大家方便的话可帮忙点点赞点点收藏啥的,拜托啦,