最近接手任务——使用nodejs开发一个公司内部使用的cli工具,简而言之就是输入一行命令快速搭建好项目结构,也能够经过不一样的命令引入不一样的文件。html
首先要基于node环境,而后咱们须要知道cli是什么?cli是command-line interface的缩写,即命令行工具,经常使用的vue-cli
, create-react-app
,express-generator
等都是cli工具。vue
建立一个exercise-cli
目录,并使用cmd进入该目录:node
mkdir exercise-cli && cd exercise-cli 复制代码
在该目录下新建index.js
:react
//index.js console.log('谢邀,人在美国,刚下飞机。'); 复制代码
使用node运行index.js:webpack
使用npm init
建立package.json
,一路回车,固然你也能够配置相关信息,有兴趣可本身选择:git
package.json
文件:
{ "name": "exercise-cli", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" } 复制代码
如今在package.json
中添加字段bin
,用来存放一个可执行的文件,咱们此处的可执行文件就是index.js,所以配置以下:github
"bin":{ "exercise-cli":"./index.js" }, 复制代码
此时咱们配置exercise-cli
命令来执行index.js
文件,须要在index.js
文件头部添加#!/usr/bin/env node
, 让系统本身去找node的执行程序。至于这玩意具体什么,百度出这么个东西,可自行参考。web
//index.js #!/usr/bin/env node console.log('谢邀,人在美国,刚下飞机。'); 复制代码
而后在cmd输入npn link
或npm install -g
将当前项目安装到全局环境,这样就能够直接使用exercise-cli
来运行文件了:vue-cli
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "exercise":"exercise-cli" } 复制代码
命令行输入npm run exercise
,一样输出了index.js
里的内容,联想起vue-cli
的npm run dev
、npm run build
等会不会如有所思呢?express
有点样子了,接下来让咱们看看它是如何生成项目模板的,一个思路是用一个templates文件夹保存项目模板,而后经过fs.mkdir()来建立项目目录,最后把文件从templates文件夹拷贝到项目中去。本地templates目录以下图所示:
模拟场景:将本地templates中的vue.min.js
拷贝到新生成的项目模板中。在新生成的项目模板中新建public目录,该目录下新建js目录,将vue.min.js
经过copyTemplate()
方法从templates里拷贝到新建的js目录下面:
//index.js #!/usr/bin/env node var fs = require('fs'); var path = require('path'); // 复制文件 function copyTemplate (from, to) { from = path.join(__dirname, 'templates', from); console.log(from); write(to, fs.readFileSync(from, 'utf-8')) } function write (path, str, mode) { fs.writeFileSync(path, str) } // 新建目录 function mkdir (path, fn) { fs.mkdir(path, function (err) { fn && fn() }) } var PATH = "."; mkdir(PATH+'/public',function(){ mkdir(PATH + '/public/js',function () { copyTemplate("/js/vue.min.js", PATH + '/public/js/vue.min.js'); }) }) 复制代码
用cmd打开任意文件夹输入exercise-cli
,该文件夹下会public\js\vue.min.js
:
咱们学会了拷贝文件,那么如何拷贝整个文件夹呢,例如我想将templates下的整个js目录所有拷贝到新生成的项目模板中,又该如何?有需求就有方案,咱们能够遍历整个文件夹,对遍历到的path进行判断,若是是文件则直接拷贝,若是是文件夹则递归:
//index.js // 复制目录 var copy=function(src,dst){ let paths = fs.readdirSync(src); //同步读取当前目录(只能读取绝对路径,相对路径没法获取) paths.forEach(function(path){ var _src=src+'/'+path; var _dst=dst+'/'+path; fs.stat(_src,function(err,stats){ //stats 该对象 包含文件属性 if(err)throw err; if(stats.isFile()){ //若是是个文件则拷贝 let readable=fs.createReadStream(_src);//建立读取流 let writable=fs.createWriteStream(_dst);//建立写入流 readable.pipe(writable); }else if(stats.isDirectory()){ //是目录则 递归 checkDirectory(_src,_dst,copy); } }); }); } var checkDirectory=function(src,dst,callback){ fs.access(dst, fs.constants.F_OK, (err) => { if(err){ fs.mkdirSync(dst); callback(src,dst); }else{ callback(src,dst); } }); }; mkdir(PATH+'/public',function(){ mkdir(PATH + '/public/js',function () { checkDirectory('C:/Users/Administrator/Desktop/vue-3.0/nodeTest/exercise/templates/js',PATH+'/public/js',copy); }) }) 复制代码
依然在找一个文件夹打开cmd输入exercise-cli
,该文件夹下会生成public目录,该目录下面会生成templates下的整个js文件:
日常咱们使用命令行工具时都会用到参数,如webpack -p
, express -e
等,在此咱们为exercise-cli
配置-l
,当使用exercise-cli -l
时,添加layerJS。
咱们可使用process.argv
来获取命令行参数,process.argv
是一个参数数组,第一项为node.exe的绝对路径,第二项为执行该js的绝对路径,使用process.argv.slice(2)
便可获取输入的参数数组。
//index.js
console.log(process.argv);
复制代码
var config = {}; process.argv.slice(2).forEach(item=>{ if(item=="-l"){ config.layer = true; } }) var PATH = "."; mkdir(PATH+'/public',function(){ mkdir(PATH + '/public/js',function () { // copyTemplate("/js/vue.min.js", PATH + '/public/js/vue.min.js'); checkDirectory('C:/Users/Administrator/Desktop/vue-3.0/nodeTest/exercise/templates/js',PATH+'/public/js',copy); if(config.layer){ checkDirectory('C:/Users/Administrator/Desktop/exercise-cli/templates/layer',PATH+'/public/js',copy); //此处注意layerJS存放在templates中的路径。 } }) }) console.log('拷贝成功'); 复制代码
在任意文件夹打开cmd输入exercise-cli -l
,执行成功,js目录中多出了layerJS目录:
其实node中有一款工具包能够快速开发命令行工具,它就是commander.js。
首先全局安装一下:
npm install commander -g
复制代码
看个例子:
var program = require('commander'); program .version('1.0.0','-v, --version') .command('check [checkname]') .alias('c') .description('yo yo check now') .option('-a, --name [moduleName]', '模块名称') .action((checkname,option) => { console.log('指令 install 后面跟的参数值 checkname: ' + checkname); console.log(option); // 得到了参数,能够在这里作响应的业务处理 }) //自定义帮助信息 .on('--help', function() { console.log(' 下面我随便说两句:') console.log('') console.log('$ 人有多大胆,母猪多大产,i love xx') console.log('$ 广阔天地,大有所为,呱~') }) program.parse(process.argv) 复制代码
命令行执行:
看到这,多多少少对如何编写命令行工具备个大致的认知了,光说不练嘴把式,自我实践:用commander.js完成上个段落3.接收命令行参数
中的例子。
分割线(如下深刻和浅出部分于2019.4.30 更)
建立脚手架的时候咱们会发现不少脚手架都须要咱们和命令行频繁交互,就像咱们开始使用npm init
的时候同样,那么是如何实现和命令行交互的呢?此时inquirer.js闪亮登场。
//命令行安装 npm install inquirer //index.js引入 var inquirer = require('inquirer'); 复制代码
var inquirer = require('inquirer'); inquirer.prompt([/* Pass your questions in here */]).then(function (answers) { // Use user feedback for... whatever!! }) 复制代码
在.action
的回调函数里输入如下内容:
// 得到了参数,能够在这里作响应的业务处理 var prompList = [ { type:'input', message:'姓名', name:'name' },{ type:'input', message:'手机号', name:'phone', validate:val=>{ if(val.match(/\d{11}/g)){ return true } return '请输入11位数字' } },{ type:'confirm', message:'是否参加本次考核?', name:'assess', prefix:'前缀' },{ type:'confirm', message:'是否赞成本次考核须知?', name:'notice', suffix:'后缀', when:answers=>{ return answers.assess } },{ type:'list', message:'欢迎来到本次考核,请选择学历:', name:'eductionBg', choices:[ "大专", "本科", "本科以上" ], filter:val=>{//将选择的内容后面加学历 return val+'学历' } },{ type:'rawlist', message:'请选择你爱玩的游戏:', name:'game', choices:[ "LOL", "DOTA", "PUBG" ] },{ type:'expand', message:'请选择你喜欢的水果:', name:'fruit', choices: [ { key: "a", name: "Apple", value: "apple" }, { key: "O", name: "Orange", value: "orange" }, { key: "p", name: "Pear", value: "pear" } ] },{ type:'checkbox', message:'请选择你喜欢的颜色:', name:'color', choices:[ { name: "red" }, new inquirer.Separator(), // 添加分隔符 { name: "blur", checked: true // 默认选中 }, { name: "green" }, new inquirer.Separator("--- 分隔符 ---"), // 自定义分隔符 { name: "yellow" } ] },{ type:'password', message:'请输入你的游戏密码:', name:'pwd' } ] inquirer.prompt(prompList).then(answers=>{ console.log(answers); }) 复制代码
命令行交互以下:
最后咱们引入chalk这个美化命令行的模块,它具备轻量级、高性能、学习成本低等特色。继续在以上例子中引入chalk进行输出:
//命令行安装 npm install chalk //index.js引入 var chalk = require('chalk'); 复制代码
在inquirer里打印以下:
inquirer.prompt(prompList).then(answers=>{ console.log(answers); console.log(chalk.green('考核完成'))//字体绿色 console.log(chalk.blue('你最棒了'))//字体蓝色 console.log(chalk.blue.bgRed('五一放假喽')) //支持设置背景 console.log(chalk.blue(answers)) }) 复制代码
命令行最终显示以下:
想让别人来安装你的cli工具,你须要把它发布到npm上,先在npm官网创个帐号(注意须要邮件验证),在命令行输入npm adduser
,依次填上你注册的username、password、email。接着输入npm publish
便可:
npm install -g exercise-cli
或
npm install exercise-cli
安装一下你的cli感觉它的魅力吧。
代码已上传至个人GitHub,欢迎Fork。