最近接手任務——使用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
這是node的基本用法,那麼如何使用自定義命令行輸出這句話呢?使用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);
複製代碼
經過遍歷參數數組來檢查命令中輸入了哪些參數。若是輸入了預設的參數,就爲config對象添加對應的屬性,在生成文件時根據config判斷是否將模板文件拷貝到項目中。
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。