目前在市面上存在很多脚手架,如:create-raect-app、vue-cli。我们可以通过一行简单的命令,就能创建一个基本的项目工程,大大的提高了开发效率。本文通过编写一个清理node_modules的Node程序,介绍编写CLI工具一般流程。
常用组件
- commander CLI常用开发框架
- chalk 用于给终端输出内容添加样式
- clear 清空终端
- configstore 一个轻量级的键值对存储方案,可以将内容保存在json文件中
- figlet 用于生成ASCII字符的艺术字
- inquirer (vue-cli在用)处理终端输入,除此之外,prompts、enquirer也常用于处理输入内容
- log-symbols 提供用户友好的文本提示
- node-fetch 发送请求
- ora 在处理耗时任务时非常有用
- clui 进度条
- shelljs 运行shell命令
- blessed-contrib 命令行可视化组件
项目运行
package.json
"scripts": {
"debug": "nodemon --no-stdin index.js",
"start": "node index.js"
},
现在可以通过yarn debug
将项目运行在开发模式,--no-stdin
参数用来禁止展示输入的内容,原因在于nodemon对交互式命令支持不是很好,例如输入密码时会展示明文。
项目结构
lib/
inquirer.js
ui.js
util.js
index.js
配置bin
为了避免每次执行脚本时必须加上node
,可以在index.js
顶部中增加如内容
#!/usr/bin/env node
package.json
"bin": "./index.js",
# or
"bin": {
"node-cli": "./index.js"
},
bin
指定命令的名字和命令入口文件,接收键值对或者字符串(如果是字符串,则命令的名字和name
属性相同)。然后在项目根目录执行npm link
或者npm install -g
,就可以在任意目录下输入node-cli
运行程序了。
卸载node-cli
# 在任意文件夹
npm uninstall -g node-cli
# 在项目根目录
npm unlink
终端输入
inquirer.prompt
用于获取用户的输入,返回一个对象
inquirer.js
const inquirer = require('inquirer')
const { checkDir } = require('./util')
const logSymbols = require('log-symbols')
module.exports = {
selectToRemove: async (choices) => {
return inquirer.prompt([
{
name: 'option',
type: 'checkbox',
message: 'Choose to remove:\n',
choices
}
])
},
mainOptions: async (choices) => {
return inquirer.prompt([
{
name: 'option',
type: 'list',
message: 'Enter or choose a folder:\n',
choices: [
{
name: 'Manually input a folder',
value: -1
},
new inquirer.Separator('----PICK ONE FOLDER----'),
...choices
]
}
])
},
askFolderName: async () => {
return inquirer.prompt([
{
name: 'option',
type: 'input',
message: 'Input a folder path:',
validate: (value) => (checkDir(value) ? true : logSymbols.warning + ' Please enter a valid folder path')
}
])
}
}
数据持久存储
ConfigStore
和redis类似,采用键值对的存储方式
const ConfigStore = require('configstore')
const configStore = new ConfigStore('node-cleaner')
const saveRecentDir = (path) => {
// configStore.clear('DIRS')
const dirs = configStore.get('DIRS') || []
path = fixPath(path)
const dirname = path.substring(path.lastIndexOf('/') + 1)
const index = dirs.findIndex((dir) => dir.path === path)
if (index > -1) {
dirs[index].count++
} else {
dirs.push({
path,
name: dirname,
count: 1
})
}
configStore.set('DIRS', dirs)
// console.log(configStore.get('DIRS'))
}
自定义样式
lib/ui.js
const chalk = require('chalk')
const figlet = require('figlet')
const boxen = require('boxen')
module.exports = {
title: () => {
const boxenOptions = {
padding: 1,
borderStyle: 'classic',
borderColor: 'green',
align: 'center'
}
console.log(`${chalk.blue('Node Cleaner v1.0.0 Powered by alfalfaw')}`)
console.log(`${boxen(chalk.white('Node Cleaner') + '\n' + chalk.yellow('A node tool to clean your computer.'), boxenOptions)}\n`)
}
}
源码及使用说明
源码发布在Github
全局安装
npm install -g
查看帮助
cleaner help
清理指定目录
cleaner c '/absolute/path'
手动输入目录
cleaner start