0搭建前准备环境

0.1创建项目

nodejs环境是必备的,这里不多说了,首先需要 npm init -y初始化一个项目。
然后创建一个index.js,整个项目结构如下
如图所示.
之后我们需要写的部分都在index.js中。

0.2配置项目

  • 这里注意下为了系统能识别为node脚本,需要在index首行声明如下

    #!/usr/bin/env node

  • 配置package.json
    在末尾加上这段配置

    “license”: “ISC”,
    “bin”: {
    “cli”: “index.js”
    },

    说明一下cli即为我们的命令,可以理解为vue-cli
    index则为我们需要执行的脚本

    0.3生成全局命令

    在index.js的目录下输入

    npm link

    这样就生成了全局变量,在任何地方只要输入cli就会执行index.js中的脚本。可以写一个console.log试试

    1命令包安装commander.js

    1.1commander说明

    commander是一个轻巧的nodejs模块,提供了用户命令行输入和参数解析强大功能
    可以在github上直接搜索这个项目传送门
    下载

    npm install commander -s

引用

const program = require(‘commander’);

1.2commander配置

直接复制Examples下的代码到index中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
program
.command('exec <cmd>')
.alias('ex')
.description('execute the given remote cmd')
.option("-e, --exec_mode <mode>", "Which exec mode to use")
.action(function(cmd, options){
console.log('exec "%s" using %s mode', cmd, options.exec_mode);
}).on('--help', function() {
console.log('');
console.log('Examples:');
console.log('');
console.log(' $ deploy exec sequential');
console.log(' $ deploy exec async');
});

这些就是一个命令全部内容
命令名称为:

.command(‘exec [env]’)

list代表命令名称 [env]代表参数

其中[]内的参数为可选参数
其中<>内的参数为必填参数

命令说明为:

.description(‘execute the given remote cmd’)

操作方法为:

.action(function(template, project){}

输入–help操作为:

.on(‘–help’, function() {})

其中 –help命令不需要额外配置,它会根据配置自动生成

1.3commander错误命令提示

错误配置代码如下:

1
2
3
4
5
program
.command('*')
.action(function(env){
console.log('deploying "%s"', env);
});

1.4commander配置版本号

版本号配置代码如下

1
2
program
.version('0.1.0') //调用-v或者 --version的时候输出该版本号

2下载包download-git-repo.js

2.1准备模板

这里我准备了三个模板供下载,在github上创建三个空项目,自动生成README.md出来如图所示:
如图所示.
创建项目这里就不多说了。

2.2下载download-git-repo.js

用npm下载

npm install download-git-repo -s

引用

const download = require(‘download-git-repo’)

传送门

1
2
3
download(downloadUrl,project,{clone:true},(err)=>{
console.log(err?'err':'success')
})
  • downloadUrl 为仓库地址路径
  • project 为创建项目的名称,即我本地想叫什么名
  • {clone:true} 进行克隆
  • err=>{}错误原因并在下载结束后执行方法

    2.3完成下载

    我们希望输入命令 cli vue new_vue 生成一个new_vue的项目并应用我们的vue_template模板,同理其他模板也用相应命令进行创建
  1. 首先我们创建一个对象来放我们的模板名称和路径和简介
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    templates={
    'vue':{
    url:"https://github.com/linjiad/vue_template.git",
    description:"vue模板",
    downloadUrl:"direct:https://github.com:linjiad/vue_template#master"
    },
    'react':{
    url:"https://github.com/linjiad/react_template.git",
    description:"react模板",
    downloadUrl:"direct:https://github.com:linjiad/react_template#master"
    },
    'angular':{
    url:"https://github.com/linjiad/angular_template.git",
    description:"angular模板",
    downloadUrl:"direct:https://github.com:linjiad/angular_template#master"
    }
    }

这里开始写program命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
program
.command('init <template> <project>') //命令
.description('初始化项目模板')
.action(function(template, project){
const {downloadUrl} = templates[template];
//根据模板名(template)下载对应模板,并命名为(project)
//第一个参数:仓库地址
//第二个参数:下载路径,下载到哪
download(downloadUrl,project,{clone:true},(err)=>{
if(err){
return console.log('下载失败:'+err);
} else{
return console.log('下载成功');
}
})
});

这样简单的模板下载就成功了

3交互handlebars和inquirer

3.1配置模板中的package.json

在模板中创建package.json,同时把需要客户填写的地方配置成可变的,代码如下

1
2
3
4
5
6
7
8
9
10
11
12
{
"name": "{{ name }}",
"version": "1.0.0",
"description": "{{ description }}",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "{{ author }}",
"license": "ISC"
}

  • 其中”name”: ““为用户输入的项目名
  • “description”: ““,为项目说明
  • “author”: “linjiad“,为作者姓名

    3.2下载handlebars和inquirer

  • handlebars:模板引擎传送门

    npm install handlebars -s

引用

const handlebars = require(‘handlebars’);

  • inquirer:向导传送门

    npm install inquirer -s

引用

const handlebars = require(‘inquirer’);

3.2修改program命令

我们的思路分为以下几部

  • 把项目下的 package.json 文件读取出来
  • 使用想到的方式采集用户输入的值
  • 使用模板引擎把用户输入的数据解析到package.json中
  • 解析完毕,把解析之后的结果重新写入package.json中
    在此之前我们要用的nodejs中的读写fs模块

    const fs = require(‘fs’);

修改后代码如下

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
program
.command('init <template> <project>') //命令
.description('初始化项目模板')
.action(function(template, project){
const {downloadUrl} = templates[template];
//根据模板名(template)下载对应模板,并命名为(project)
//第一个参数:仓库地址
//第二个参数:下载路径,下载到哪
download(downloadUrl,project,{clone:true},(err)=>{
if(err){
return console.log('下载失败:'+err);
} else{
// 把项目下的package.json读取
// 采取向导方式采集用户输入的值
// 使用模板引擎把用户输入的数据解析到package.json
// 解析之后的结果从新写入package.json
inquirer.prompt([
{
typ: 'input',
name: 'name',
message: '请输入项目名称'
},
{
typ: 'input',
name: 'description',
message: '请输入项目简介'
},
{
typ: 'input',
name: 'author',
message: '请输入作者姓名'
},
]).then((answers)=>{
const packagePath = `${project}/package.json`;
//把数据替换到package.json中
const packageContent = fs.readFileSync(packagePath,'utf8');
const packageResult = handlebars.compile(packageContent)(answers);
fs.writeFileSync(packagePath,packageResult)
console.log(logSymbols.success,chalk.green("初始化模板成功"))
})
}
})
});

这样整个cli脚本就更加人性化了

4美化脚本

4.1添加loading效果 ora.js

ora:loading美化传送门
下载

npm install ora -s

引用

var ora = require(‘ora’);

使用:

const spinner = ora(‘正在下载模板…’).start();
spinner.fail(‘下载失败’);//下载失败提示
spinner.succeed(‘下载成功’); //下载成功提示

4.2添加字体颜色 ora.js

chalk:字体美化传送门
下载

npm install chalk -s

引用

var chalk = require(‘chalk’);

使用:

chalk.red(“错误”)
chalk.green(“成功”)

4.3特殊符号 log-symbols.js

log-symbols:特殊符号,如对号错号传送门
下载

npm install log-symbols -s

引用

var logSymbols = require(‘log-symbols’);

使用:

console.log(logSymbols.erro);
console.log(logSymbols.success);

4.4修改program命令

修改后的代码如下(既全部代码):

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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#!/usr/bin/env node
//命令包
const program = require('commander');
//下载包
const download = require('download-git-repo')
//模板引擎
const handlebars = require('handlebars');
//向导
const inquirer = require('inquirer');
const fs = require('fs');
//loading美化
var ora = require('ora');
//字体美化
const chalk = require('chalk');
//符号
const logSymbols = require('log-symbols')
program
.version('0.1.0') //调用-v或者 --version的时候输出该版本号
const templates={
'vue':{
url:"https://github.com/linjiad/vue_template.git",
description:"vue模板",
downloadUrl:"direct:https://github.com:linjiad/vue_template#master"
},
'react':{
url:"https://github.com/linjiad/react_template.git",
description:"react模板",
downloadUrl:"direct:https://github.com:linjiad/react_template#master"
},
'angular':{
url:"https://github.com/linjiad/angular_template.git",
description:"angular模板",
downloadUrl:"direct:https://github.com:linjiad/angular_template#master"
}
}
program
.command('init <template> <project>') //命令
.description('初始化项目模板')
.action(function(template, project){
//下载之前做loading提示
const spinner = ora('正在下载模板...').start();
const {downloadUrl} = templates[template];
//根据模板名下载对应模板,并命名为project
//第一个参数:仓库地址
//第二个参数:下载路径,下载到哪
download(downloadUrl,project,{clone:true},(err)=>{
if(err){
spinner.fail('下载失败');//下载失败提示
return console.log(logSymbols.error,chalk.red(err))
}
spinner.succeed('下载成功'); //下载成功提示
// 把项目下的package.json读取
// 采取向导方式采集用户输入的值
// 使用模板引擎把用户输入的数据解析到package.json
// 解析之后的结果从新写入package.json
inquirer.prompt([
{
typ: 'input',
name: 'name',
message: '请输入项目名称'
},
{
typ: 'input',
name: 'description',
message: '请输入项目简介'
},
{
typ: 'input',
name: 'author',
message: '请输入作者姓名'
},
]).then((answers)=>{
const packagePath = `${project}/package.json`;
//把数据替换到package.json中
const packageContent = fs.readFileSync(packagePath,'utf8');
const packageResult = handlebars.compile(packageContent)(answers);
fs.writeFileSync(packagePath,packageResult)
console.log(logSymbols.success,chalk.green("初始化模板成功"))
})
})
});

program
.command('list') //命令
.description('查看所有可用模板')
.action(()=>{
for(const key in templates){
console.log(`${key}:${templates[key].description}.`)
}
});

program
.command('setup [env]') //命令
.description('run setup commands for all envs')
.option("-s, --setup_mode [mode]", "Which setup mode to use")
.action(function(env, options){
var mode = options.setup_mode || "normal";
env = env || 'all';
console.log('setup for %s env(s) with %s mode', env, mode);
});

program
.command('exec <cmd>')
.alias('ex')
.description('execute the given remote cmd')
.option("-e, --exec_mode <mode>", "Which exec mode to use")
.action(function(cmd, options){
console.log('exec "%s" using %s mode', cmd, options.exec_mode);
}).on('--help', function() {
console.log('');
console.log('Examples:');
console.log('');
console.log(' $ deploy exec sequential');
console.log(' $ deploy exec async');
});

program
.command('*') //其他的命令
.action(function(env){
console.log('不存在 "%s"命令', env);
});

program.parse(process.argv);

5npm发布包

  1. 打开npm官网
  2. 注册一个npm账号
  3. 在npm官网上搜索是否有重名
    4.讲package.json中的那么修改为发布到npm上的名

    和本地项目名称没有关系

5.打开控制台,执行npm login,在控制台输入登录
6.登陆成功后,在项目下执行npm publish进行发布
7 在其他电脑上就可以安装成全局变量了

这样一个脚手架就搭建完毕了

最后更新: 2018年11月25日 17:24

原始链接: http://linjiad.github.io/2018/11/11/构建cli脚手架/

× 请我吃糖~
打赏二维码