# 命令行工具开发小记CLI
TIP
英语:command-line interface 缩写:CLI
# 初始化
npm init -y
1
- 在当前目录下初始化
package.json
文件
# 准备文件
#目录结构
└─bin
└─index.js
└─package.json
1
2
3
4
2
3
4
- 在当前项目增加
bin
文件夹在bin
中新建index.js
文件
// /bin/index.js
#!/usr/bin/env node
console.log('husky are you scared')
1
2
3
2
3
- index.js文件第一行一定要声名使用那个执行器来执行这里写node
- deno现在也发布了1.0.0版本,说不定之后就用deno了
- 需要在
package.json
中加入bin
字段
{
"name": "chrome-plugin-basic-cli",
"version": "1.0.0",
"description": "",
"main": "index.js",
"bin": {
"basic-chrome-plugin": "./bin/index.js"
},
"keywords": [],
"author": "",
"license": "ISC"
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# link 到全局
- 在当前项目目录下执行连接到全局
npm link
1
- 在当前目录在连接一下,方便调试,如果后期发到
npm
上了,可以直接npm install <package-name>
即可 - 开发阶段可以这样安装到本项目中
npm link chrome-plugin-basic-cli # 这里是自己定义的名字,就是package中的bin对象的属性名
1
TIP
注意这里npm link xxx 是package.json中的name字段,同样npm install 也是 当在命令行执行命令的时候才用bin中定义的名字来执行
# 简化命令
- 可以看到为了语义化我们的命令行工具,名字很长这里我们给他取一个简短的名字
- 在
bin
对象下再新建一个属性,和basic-chrome-plugin
指定的文件是同一个
{
"name": "chrome-plugin-basic-cli",
"version": "1.0.0",
"description": "",
"main": "index.js",
"bin": {
"basic-chrome-plugin": "./bin/index.js",
"chromeCli": "./bin/index.js"
},
"keywords": [],
"author": "",
"license": "ISC"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 命令行参数
# 用process.argv获取命令行参数
- 获取命令行的参数
process.argv // 可以获取到我们输入的参数
1
- 举例:chromeCli -v
- process.argv会返回一个数组
- 数组第一个元素是node执行器的位置
- 第二个元素是当前命令的位置
- 第三个参数才是我们输入的参数,一次往后都是我们输入的参数,输入多个参数用空格隔开
chromeCli list init name ....
1
# 使用commander工具包
- 首先来安装一下commander
yarn add commander
1
# 完成-V --version 的参数功能
// /bin/index.js
const { program } = require('commander')
const package = require('./package.json')
program.version(package.version)
program.parse(process.argv)
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
- 此时在命令行中
chromeCli -V
或chromeCli --version
就会打印package.json
中的version
# 定义 init 命令
- 这里我们以初始化项目模板功能进行演示,首先需要介绍几个API
- command 命令名称
<require>
代表必填参数[option]
代表可选参数
- description 命令描述
- action 执行该命令的方法
// /bin/index.js
program
.command('init <template> <project-name>') // 定义init 命令和两个必填参数
.description('init chrome plugin project') // 描述
.action(function (template, projectName) { // 获取参数 并根据所输入的参数完成相应逻辑
console.log('%s -- %s', template, projectName)
})
program.parse(process.argv) // 解析参数 必须要执行 一定要放在所有命令的最后执行
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 定义 list 命令
const templates = {
'basic': {
repoUrl: 'https://github.com/huskyAreYouScared/husky-tools',
description: '基础的chrome plugin 需要的文件模板'
}
}
program
.command('list')
.action(()=>{
for (let key in templates) {
console.log(`${key} ${templates[key].description}`);
}
})
program.parse(process.argv)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- 在命令行输入
chromeCli list
就会打印templates
中的模板信息
# 下载github上的模板
- 在下载之前首先要在
GitHub
上面新建仓库,这个步骤就不在这里记录了 - 将
repo
的地址放在templates
中即可 - 这里我们要借助
download-git-repo
的第三方包来下载模板
yarn add download-git-repo
1
- 需要在之前的templates对象中新增downloadUrl,这个属性是传给download-git-repo的第一个参数
- downloadUrl的路径应该遵循这样的规范
https://github.com:账户/repositry#分支
1
const templates = {
'basic': {
repoUrl: 'https://github.com/huskyAreYouScared/husky-tools',
downloadUrl: 'https://github.com:huskyAreYouScared/husky-tools#master',
description: '基础的chrome plugin 需要的文件模板'
}
}
program
.command('init <template> <project-name>')
.description('init chrome plugin project')
.action(function (template, projectName) {
downloadGitRepo(templates[template].downloadUrl, projectName, { clone : true }, (err)=>{
if (err) {
console.log(err);
} else {
console.log('success');
}
})
})
program.parse(process.argv)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- 此时执行 chromeCli init basic demo就会将github上面的模板下载下来
# 命令行交互
# 需要借助 inquirer
- 首先来安装 inquirer
yarn add inquirer
1
- inquirer 插件提供了很多的交互方式,这里已最常见的多选,输入,确认为例
const { program } = require('commander')
const inquirer = require('inquirer')
program
.command('create')
.action(()=>{
inquirer
.prompt([
{
type: 'confirm',
name: 'isDefaultImg',
message: 'default image is husky image'
},
{
type: 'checkbox',
name: 'type',
choices: ['cat', 'dog', 'birde'],
message: 'default image is husky image'
},
{
type: 'input',
name: 'name',
choices: ['cat', 'dog', 'birde'],
message: 'default image is husky image'
}
])
.then(answers => {
console.log(answers);
})
.catch(error => {
if (error.isTtyError) {
// Prompt couldn't be rendered in the current environment
} else {
// Something else when wrong
}
});
})
program.parse(process.argv);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
- prompt 用户执行完操作后返回一个promise对象,将用户的选择结果已对象的方式进行返回(answers)
? default image is husky image Yes
? default image is husky image cat
? default image is husky image twohaha
{ isDefaultImg: true, type: [ 'cat' ], name: 'twohaha' }
1
2
3
4
2
3
4
# 命令行交互优化
# loading效果
- 这里需要ora插件来帮忙
- 使用也很简单
const ora = require('ora')
let spinner = ora('正在初始化...')
spinner.start()
setTimeout(()=>{
// spinner.fail() 下载失败提示
spinner.succeed() 下载成功提示
},2000)
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 终端字符串样式
- github地址chalk
yarn add chalk
1
const chalk = require('chalk')
const ora = require('ora')
let loading = chalk.cyan('正在初始化...') // 加载过程中是蓝色
let spinner = ora(loading)
spinner.start()
setTimeout(()=>{
spinner.succeed(chalk.green('正在初始化...')) // 成功设置字符为绿色
},2000)
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
- 还有很多的样式详情可以去这里传送门
# 日志图标 log-symbols
yarn add log-symbols
1
- 一共有四种类型
const logSymbols = require('log-symbols');
console.log(logSymbols.success, 'Finished successfully!');
1
2
3
2
3
← tapable原理学习 React全家桶 →