最近因为需要给其他同事提供一些方便开发的工具,就写了一个基于node的CLI,下面给大家分享下怎么自己去动手写一个基于node的CLI。
原理
当前端工程安装到全局目录下之后,可以直接通过别名来执行package.json当中bin下面的js文件,我们就是通过编写这个js文件来实现相应命令。
{
"name": "testnode-cli",
"version": "1.0.0",
"description": "CLI测试",
"main": "index.js",
"bin": {
"testnode": "./bin/index.js"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git@github.com:yangzq007/testnode-cli.git"
},
"author": "yangzq007@126.com",
"license": "ISC",
"dependencies": {
"commander": "^2.15.1"
}
}
上面所展示的示例当中testnode
字段是别名,命令头也是由这里决定的,执行的入口是./bin/
下的index.js文件
编写
1.创建一个CLI项目文件夹,并在项目目录当中创建一个如上的package.json文件(可以使用npm init
命令来创建)
2.然后在该文件夹目录下运行npm install
命令初始化该工程
3.添加package.json文件当中bin字段对应的js文件
4.编写js脚本文件实现命令(文件内容如下所示)
5.使用命令npm install -g
将当前项目安装到全局
6.愉快测试和使用自己编写的命令
如下是一个简单的js脚本内容,编写安装完毕后,当你在终端输入testnode
时,就会输出对应的log
#!/usr/bin/env node
function show () {
console.log("testnode-cli is comming!");
}
show();
添加参数
我们虽然可以在show方法中添加我们的处理逻辑,但是我们的脚本只能执行这样一个命令,怎么给它加上参数执行多条命令呢,比如testnode show
、testnode delete
、testnode add
等来执行不同的操作,那我们需要给它添加参数处理的逻辑
如下是一个简单的参数处理的逻辑
#!/usr/bin/env node
var params = {};
process.argv.slice(2).forEach( function (item) {
switch (item) {
case "show":
config.show = true;
break;
case "delete":
config.delete = true;
break;
case "add":
config.add = true;
break;
case "-l":
config.log = true;
break;
}
});
function show () {
console.log("testnode-cli is comming!");
}
function delete () {
console.log("do some delete");
config.log && console.log("delete logging");
}
function add () {
console.log("do some add");
config.log && console.log("add logging");
}
config.show && show();
config.delete && delete();
config.add && add();
或者我们可以使用commander插件来帮我们处理参数,这样效率会更高,commander在开头的package.json文件当中已经引入,实现大致如下
#!/usr/bin/env node
function show () {
console.log("testnode-cli is comming!");
}
function delete () {
console.log("do some delete");
}
function add (name)) {
console.log(`do some add, name:${name}`);
}
var program = require('commander');
program
.version(require('../package.json').version);
program
.command('show') //命令
.description('展示相关描述') //命令描述
.action(function() {
show(); //命令动作
}).on('--help',function() {
console.log('展示相关信息'); //选项
});
program
.command('delete')
.description('删除相关文件')
.action(function() {
delete();
});
program
.command('add [name]') //带参数的处理
.description('添加相关文件')
.action(function(name) {
add(name);
});
program.parse(process.argv);
commander更详细的用法介绍参考https://github.com/tj/commander.js
命令实现
上面的操作已经基本实现了一个CLI的结构,我们剩下要做的就是实现命令的具体内容了,我们可以通过一些现有的工具来方便实现我们的操作。
//我们可以使用execSync来创建一个阻塞node的事件循环的shell客户端来执行我们常用的shell命令
const execSync = require('child_process').execSync;
execSync('ls');
脚手架实现
我们常用的前端脚手架的实现思路一般是在远端创建一个模板工程,在创建我们自己的工程时将远端的模板工程克隆到本地,然后通过CLI实现对模板工程相应关键信息的修改来转换成对应的开发工程。
为了方便实现替换,我们可能需要在模板当中对一些关键信息使用特殊样式的字符替换,例如用&{projectName}&来占位项目名称,这样在替换时就方便检索和替换,避免出现误操作。
相关的文件处理和文字处理可能还需要特殊的工具,这里不再详细描述。
解释器声明
你可能注意到脚本文件的头一行有个声明,这个是表示用node来执行这个文件,如果没有这句声明,就会用默认的文本编辑器打开相应的js脚本文件。
windows
#! node
linux/unix/mac
#!/usr/bin/env node
#!/usr/bin node和#!/usr/bin/env node的区别
这个在unix类的操作系统才有意义。#!/usr/bin node是告诉操作系统执行这个脚本的时候,调用/usr/bin下的node解释器,#!/usr/bin/env node这种用法是为了防止操作系统用户没有将node装在默认的/usr/bin路径里。当系统看到这一行的时候,首先会到env设置里查找node的安装路径,再调用对应路径下的解释器程序完成操作。