# 手写一个自己的cli
初始化
npm init -y
1
创建一个bin文件夹,在文件夹里创建一个文件 mycli,没有后缀 mycli
#!/usr/bin/env node
console.log('我是第一个cli');
1
2
2
在package.json里添加一个bin
{
...
"bin": {
mycli: "bin/mycli"
}
...
}
1
2
3
4
5
6
7
2
3
4
5
6
7
在本地做一个软链,将这个包放在本地全局安装包的地方
npm link
1
在命令行里输入 mycli ,会输出 "我是第一个cli"
但是这个效果就很普通了 安装两个模块 生成asc码的包
npm install figlet --save-dev
1
做渐变色的包
npm install @darkobits/lolcatjs --save-dev
1
#!/usr/bin/env node
const figlet = require('figlet');
const Printer = require('@darkobits/lolcatjs');
const txt = figlet.textSync('MY CLI') + "\n" + "我的脚手架";
console.log(Printer.default.fromString(txt));
1
2
3
4
5
2
3
4
5
输入mycli 可以看到控制台输出了渐变色的 MY CLI
添加查看版本,输入 mycli 输出 版本号(刚刚输入的'MY CLI')
#!/usr/bin/env node
const figlet = require('figlet');
const Printer = require('@darkobits/lolcatjs');
// 变色
const txt = figlet.textSync('MY CLI');
// 一个写cli很重要的库, 可以跟用户交互
const program = require('commander');
program.version(Printer.default.fromString(txt), "-v,--version");
program.parse(process.argv);
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
接下来要跟用户进行交互
const bindHandler = {
init(params) {
console.log(params)
}
}
// 跟用户交互
program.usage("<cmd> [env]")
.arguments('<cmd> [env]')
.action(function (cmd, otherParams) {
// 输出用户输入的内容 cmd 是用户输入的第一个参数 otherParams 是第二个参数
const handler = bindHandler[cmd];
if (handler) {
handler(otherParams);
} else {
console.log('暂未实现' + cmd)
}
})
program.parse(process.argv);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
输入 mycli init xxx 输出 xxx
安装 inquirer 进行与用户的交互
npm install inquirer --save-dev
1
const inquirer = require('inquirer');
const bindHandler = {
init() {
inquirer.prompt([
{
type: 'list',
name: 'jskind',
message: '请问使用哪种编程语言',
choices: ['✔ es6', '✔ typescript']
}
])
.then(answers => {
});
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
安装 chalk
npm install chalk --save-dev
1
继续和用户交互
const chalk = require('chalk');
// 跟用户交互
program.usage("<cmd> [env]")
.arguments('<cmd> [env]')
.action(function (cmd, otherParams) {
// 输出用户输入的内容 cmd 是用户输入的第一个参数 otherParams 是第二个参数
const handler = bindHandler[cmd];
if (handler) {
handler(otherParams);
} else {
console.log(chalk.yellow("非常遗憾") + "【" + chalk.red(cmd) + "】" + chalk.yellow("暂未实现"))
}
})
program.parse(process.argv);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
当用户输入了没有预设的指令时
mycli xxx
1
在我们使用ts过程中经常需要将后台数据转成接口,所以这里添加一个自动转json的东西。
npm install json2ts --save-dev
1
在 bindHandler 中添加这个方法
const bindHandler = {
json2ts(url) {
// 拿到后面的参数去请求后台接口地址
// 这里模拟一下数据
// 实际用的时候可以在指令后直接加后台地址
const jsonContent = {
code:1,
info: {
message: '请求成功',
data: [
{
num:1,
title:"第一条数据"
}
]
}
};
let result = json2ts.convert(JSON.stringify(jsonContent));
console.log(result);
// 拿到 interface 之后就可以把它打到该去的地方了
}
}
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
执行命令,后面可以加参数指向后台地址来获取接口 mycli json2ts http://xxxx.json
mycli json2ts
1
添加让用户等待时的loading 安装 ora
npm install ora --save-dev
1
当用户做完选择后进行loading状态
const ora = require('ora');
const bindHandler = {
json2ts(url) {
console.log('接口地址', url);
// 假装接到了数据
const jsonContent = {
code:1,
info: {
message: '请求成功',
data: [
{
num:1,
title:"第一条数据"
}
]
}
};
let result = json2ts.convert(JSON.stringify(jsonContent));
console.log(result);
const spinner = ora("正在帮爷生成代码中,请稍后。。。");
spinner.start();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
接下来要进行最重要的部分了。根据用户输入的需要创建生成项目。 分5步:
- git准备好一个能容纳百川的项目
- git 下载那个包
- shelljs 根据用户选择对你下载的包进行修改删除
- 在用户的桌面创建最终的项目
- 引导开发使用
这里需要使用 shelljs 来进行操作。还需要使用 download-git-repo 来拉取git上的项目
npm install shelljs download-git-repo --save-dev
1
// 获取用户全路径的包
// const userHome = require('user-home');
// 也可以直接用shelljs来取
const shell = require('shelljs');
// console.log(shell.pwd().stdout)
// 拉取git上的项目
const download = require('download-git-repo');
// 项目地址 地址前面要加上 direct: ,我也不知道为啥,反正不加会报错 ╮(╯▽╰)╭
const templateUrl = "direct:https://github.com/xxx/xxx.git";
const bindHandler = {
init() {
inquirer.prompt([
{
type: 'text',
name: 'dirname',
message: '请输入文件夹的名称'
},
{
type: 'list',
name: 'jskind',
message: '请问使用哪种编程语言',
choices: ['✔ es6', '✔ typescript']
}
])
.then(answers => {
// 1.git准备好一个能容纳百川的项目
// 2.git 下载那个包
// 3.shelljs 根据用户选择对你下载的包进行修改删除
// 4.在用户的桌面创建最终的项目
// 5.引导开发使用
const _dirname = answers.dirname;
if (_dirname) {
const spinner = ora("正在下载模板,请稍后。。。");
spinner.start();
const _pwd = shell.pwd().stdout;
const _projectPath = `${_pwd}/${_dirname}`;
shell.cd(_pwd);
shell.rm('-rf', _projectPath);
shell.mkdir(_dirname);
download(templateUrl, _projectPath, { clone: true }, err => {
spinner.stop();
if (err) {
console.log(chalk.red('mycli启动异常'), err);
} else {
// 在package.json 中查找到要替换的名字,将用户输入的文件夹名替换上去
shell.sed("-i", "要替换的名字",_dirname,_projectPath+"/package.json");
}
})
}
});
}
}
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
到此一个基本的cli就完成了┓( ´∀` )┏
还需要进行很多完善,比如根据用户不同的输入,来判断使用什么模板,替换不同的东西之类的吧啦吧啦。
贴一下完整代码,
#!/usr/bin/env node
const figlet = require('figlet');/* *********************** 生成asc码的包*/
const Printer = require('@darkobits/lolcatjs');/* ********* 做渐变色的包*/
const program = require('commander');/* ******************* 和用户交互的包*/
const inquirer = require('inquirer');/* ******************* 和用户交互的包*/
const chalk = require('chalk');/* ************************* 说话变颜色的包*/
const json2ts = require('json2ts');/* ********************* 把json转换成ts的包*/
const ora = require('ora');/* ***************************** 添加loading等待的包*/
// 获取用户全路径的包
// const userHome = require('user-home');
// 也可以直接用shelljs来取
const shell = require('shelljs');/* *********************** 能调用shell的包*/
// console.log(shell.pwd().stdout)
const download = require('download-git-repo');/* ********** 从git上拉项目的包*/
// git上模板的地址
const templateUrl = "direct:https://github.com/xxx/xxx.git";
// 版本号
const txt = figlet.textSync('MY CLI v1.0.0');
// 添加版本号
program.version(Printer.default.fromString(txt), "-v,--version");
// 根据用户输入不同的初始化指令做不同的骚操作
const bindHandler = {
init() {
// 与用户交互的问题
inquirer.prompt([
{
type: 'text',
name: 'dirname',
message: '请输入文件夹的名称'
},
{
type: 'list',
name: 'jskind',
message: '请问使用哪种编程语言',
choices: ['✔ es6', '✔ typescript']
}
])
.then(answers => {
// 1.git准备好一个能容纳百川的项目
// 2.git 下载那个包
// 3.shelljs 根据用户选择对你下载的包进行修改删除
// 4.在用户的桌面创建最终的项目
// 5.引导开发使用
const _dirname = answers.dirname;
if (_dirname) {
const spinner = ora("正在下载模板,请稍后。。。");
spinner.start();
const _pwd = shell.pwd().stdout;
const _projectPath = `${_pwd}/${_dirname}`;
shell.cd(_pwd);
shell.rm('-rf', _projectPath);
shell.mkdir(_dirname);
download(templateUrl, _projectPath, { clone: true }, err => {
spinner.stop();
if (err) {
console.log(chalk.red('mycli启动异常'), err);
} else {
// 在package.json 中查找到要替换的名字,将用户输入的文件夹名替换上去
shell.sed("-i", "要替换的名字",_dirname,_projectPath+"/package.json");
}
})
}
});
},
json2ts(url) {
console.log('接口地址', url);
// 假装接到了数据
const jsonContent = {
code: 1,
info: {
message: '请求成功',
data: [
{
num: 1,
title: "第一条数据"
}
]
}
};
let result = json2ts.convert(JSON.stringify(jsonContent));
console.log(result);
const spinner = ora("正在帮爷生成代码中,请稍后。。。");
spinner.start();
}
}
// 跟用户交互
program.usage("<cmd> [env]")
.arguments('<cmd> [env]')
.action(function (cmd, otherParams) {
// 输出用户输入的内容 cmd 是用户输入的第一个参数 otherParams 是第二个参数
const handler = bindHandler[cmd];
if (handler) {
handler(otherParams);
} else {
console.log(chalk.yellow("非常遗憾") + "【" + chalk.red(cmd) + "】" + chalk.yellow("暂未实现"))
}
})
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
← 从0到大论前端持续集成 构建工具 →