用commander.js構建自己的腳手架工具


隨着前端技術的發展,工程化逐漸成為了一種趨勢。但在實際開發時,搭建項目是一件很繁瑣的事情,尤其是在對一個框架的用法還不熟悉的時候。於是很多框架都自帶一套腳手架工具,在初始化前端項目的時候就可以不用自己從頭搭建,只要在命令行輸入初始化命令即可。

那么,如果想自行開發出這樣一個命令行工具來初始化自定義項目,該怎么做呢?研究的過程中,偶然間發現了 commander.js 這個模塊,可以幫助命令行工具的開發。於是邊研究邊整理了這篇筆記。

一、commander.js的基本用法
1. 安裝

mkdir commander-example && cd commander-example
npm install commander --save

 


2. 使用

新建一個bin目錄,然后在該目錄下新建一個test.js文件,文件內容:

// 引入依賴
var program = require('commander');
 
// 定義版本和參數選項
program
  .version('0.1.0', '-v, --version')
  .option('-i, --init', 'init something')
  .option('-g, --generate', 'generate something')
  .option('-r, --remove', 'remove something');
 
// 必須在.parse()之前,因為node的emit()是即時的
program.on('--help', function(){
 console.log('  Examples:');
  console.log('');
  console.log('    this is an example');
  console.log('');
});
 
program.parse(process.argv);
 
if(program.init) {
  console.log('init something')
}
 
if(program.generate) {
  console.log('generate something')
}
 
if(program.remove) {
  console.log('remove something')
}

然后在命令行里輸入測試:

node bin\test --help

得到如下結果:

Usage: test [options]
 
  Options:
 
    -v, --version   output the version number
    -i, --init      init something
    -g, --generate  generate something
    -r, --remove    remove something
    -h, --help      output usage information
  Examples:
 

3. API解析

· version

作用:定義命令程序的版本號
用法示例:.version('0.0.1', '-v, --version')
參數解析:
① 版本號<必須>

② 自定義標志<可省略>:默認為 -V 和 --version

· option

作用:用於定義命令選項
用法示例:.option('-n, --name<path>', 'name description', 'default name')
參數解析:
① 自定義標志<必須>:分為長短標識,中間用逗號、豎線或者空格分割;標志后面可跟必須參數或可選參數,前者用 <> 包含,后者用 [] 包含
② 選項描述<省略不報錯>:在使用 --help 命令時顯示標志描述

③ 默認值<可省略>

· command

作用:添加命令名稱
用法示例:.command('rmdir <dir> [otherDirs...]', 'install description', opts)
參數解析:
① 命令名稱<必須>:命令后面可跟用 <> 或 [] 包含的參數;命令的最后一個參數可以是可變的,像實例中那樣在數組后面加入 ... 標志;在命令后面傳入的參數會被傳入到 action 的回調函數以及 program.args 數組中
② 命令描述<可省略>:如果存在,且沒有顯示調用action(fn),就會啟動子命令程序,否則會報錯

③ 配置選項<可省略>:可配置noHelp、isDefault等

· description

作用:定義命令的描述

用法示例:.description('rmdir desc')

· action

作用:定義命令的回調函數

用法示例:.action(fn)

· parse

作用:用於解析process.argv,設置options以及觸發commands
用法示例:.parse(process.argv)

二、使用commander.js開發本地模塊init-commander-tool
1. 新建目錄如下:

init-commander-tool
  |-bin 
    |-init-project.js
  |-lib 
    |-install.js
  |-templates

2. 運行 npm init 來初始化項目,項目名稱設置為init-commander-tool,並安裝依賴包:

```

"devDependencies": {
    "chalk": "^2.4.1",
    "commander": "^2.15.1",
    "fs-extra": "^6.0.1",
    "path": "^0.12.7",
    "through2": "^2.0.3",
    "vinyl-fs": "^3.0.3",
    "which": "^1.3.1"
  }

```
npm init
npm install

3. init-porject.js中代碼如下

 
// 指定腳本的執行程序
#! /usr/bin/env node
 
// 引入依賴
var program = require('commander');
var vfs = require('vinyl-fs');
var through = require('through2');
const chalk = require('chalk');
const fs = require('fs-extra');
const path = require('path');
 
// 定義版本號以及命令選項
program
  .version('1.0.0')
  .option('-i --init [name]', 'init a project', 'myFirstProject')
    
program.parse(process.argv);
 
if(program.init) {
  // 獲取將要構建的項目根目錄
  var projectPath = path.resolve(program.init);
  // 獲取將要構建的的項目名稱
  var projectName = path.basename(projectPath);
    
  console.log(`Start to init a project in ${chalk.green(projectPath)}`);
 
  // 根據將要構建的項目名稱創建文件夾
  fs.ensureDirSync(projectName);
 
  // 獲取本地模塊下的demo1目錄
  var cwd = path.join(__dirname, '../templates/demo1');
  
  // 從demo1目錄中讀取除node_modules目錄下的所有文件並篩選處理
  vfs.src(['**/*', '!node_modules/**/*'], {cwd: cwd, dot: true})
  .pipe(through.obj(function(file, enc, callback){
    if(!file.stat.isFile()) {
      return callback();
  }
        
  this.push(file);
    return callback();
  }))
   // 將從demo1目錄下讀取的文件流寫入到之前創建的文件夾中
  .pipe(vfs.dest(projectPath))
  .on('end', function() {
    console.log('Installing packages...')
    
    // 將node工作目錄更改成構建的項目根目錄下
    process.chdir(projectPath);
 
    // 執行安裝命令
    require('../lib/install');
  })
  .resume();
}

· #! /usr/bin/env node

指定腳本的執行程序,這里是node。也可以用!/usr/bin/node,但如果用戶將node安裝在非默認路徑下,會找不到node。所以最好選擇用env(包含環境變量)來查找node安裝目錄。

 

· vinyl-fs:

Vinyl用於描述文件的元數據對象;該模塊主要暴露了兩個方法src和dest,它們各自返回數據流;不同的是前者提供Vinyl對象,后者使用Vinyl對象;簡單的說就是一個讀取文件,另一個往磁盤寫文件

param@src:src(globs[, options])

第一個參數為字符串或字符串數組,表明文件位置。如果是數組,則會按照從前到后的順序來執行,但帶有!(非)符號的路徑應該放在后面。第二個參數為選項對象,查看具體配置

param@dest:dest(folder[, options])

第一個參數為文件夾路徑或函數,如果是后者,則它會被用於處理每一個Vinyl文件對象,且該函數必須返回一個文件夾路徑。第二個參數為選項對象,查看具體配置。該方法返回文件對象流,並將他們寫入磁盤以及傳遞到管道下游,所以你可以繼續在管道中進行操作。如果文件擁有symlink屬性,就會創建一個符號鏈接。

 

· through2:

through2主要是對node中streams.Transform的簡單封裝,讓其使用起來更加簡單。具體用法可查看through2**

4. install.js中代碼如下:

 
// 引入依賴
var which = require('which');
const chalk = require('chalk');
 
var childProcess = require('child_process');
 
// 開啟子進程來執行npm install命令
function runCmd(cmd, args, fn) {
  args = args || [];
  var runner = childProcess.spawn(cmd, args, {
    stdio: 'inherit'    
  });
    
  runner.on('close', function(code) {
    if(fn) {
      fn(code);
    }
  })
}
 
// 查找系統中用於安裝依賴包的命令
function findNpm() {
  var npms = ['tnpm', 'cnpm', 'npm'];
  for(var i = 0; i < npms.length; i++) {
    try {
      // 查找環境變量下指定的可執行文件的第一個實例
      which.sync(npms[i]);
      console.log('use npm: ' + npms[i]);
      return npms[i]
    }catch(e) {     
    }
  }
  throw new Error(chalk.red('please install npm'));
}
 
var npm = findNpm();
runCmd(which.sync(npm), ['install'], function() {
  console.log(npm + ' install end');
})

三、構建項目demo
此demo用於初始化項目副本,因此可以根據自己的需要構建。我們可以利用一些腳手架工具來初始化項目,也可以自己一步步搭建。

將搭建的項目復制進templates目錄下(node_modules下的文件及文件夾可以不用復制),並重命名為demo1;然后在init-commander-tool目錄下運行 node bin\init-project --help 測試所有命令是否能正常顯示。

四、全局使用
在package.json里面添加bin字段:

// bin項用來指定各個內部命令對應的可執行文件的位置
"bin": {
    "initP": "./bin/init-project"
  },

然后在init-commander-tool目錄下運行 npm link,將本地模塊鏈接到全局環境下,這樣就可以在任何地方使用initP命令了。
在命令行中鍵入:initP --help,出現以下內容則代表可以正常使用。

Usage: init-project [options]
 
  Options:
 
    -V, --version     output the version number
    -i --init [name]  init a project (default: myFirstProject)
    -h, --help        output usage information

最后在需要初始化項目的地方運行 initP --init myProject 即可,項目名稱可以自己定義。

 




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM