傳統的前端項目初始流程通常是這樣:javascript
能夠看出,傳統的初始化步驟,花費的時間並很多。並且,人工操做的狀況下,總有改漏的狀況出現。這個缺點有時很致命。
甚至有馬大哈,沒有更新項目倉庫地址,致使提交代碼到舊倉庫,這就很尷尬了。。。
基於這些狀況,編寫命令行工具(CLI)的目的就很明確:html
如下是新的流程示意圖:前端
如下是自動化流程圖:java
從流程圖能夠得出兩個重要的信息:node
命令行工具的角色,是負責將兩個信息進行融合,提供一個交互平臺給用戶。git
配置信息的得到,須要靠和用戶進行交互。因爲程序員通常是用終端輸入命令進行項目操做。因此,這裏選擇了兩個工具進行支撐。程序員
借鑑Ruby commander理念實現的命令行執行補全解決方案github
commander
能夠接收命令行傳入的參數shell
例子:npm
npg-cli --help
♫ ♫♬♪♫ npm-package-cli ♫ ♫♬♪♫
Usage: npg-cli [options]
Options:
-V, --version output the version number
-h, --help output usage information
run testcli and edit the setting.
複製代碼
經常使用交互式命令行用戶界面的集合。
inquirer
用詢問式的語句,與用戶進行交互,接收參數
例子:
npg-cli
♫ ♫♬♪♫ npm-package-cli ♫ ♫♬♪♫
Follow the prompts to complete the project configuration.
? project name test
? version 1.0.0
? description
複製代碼
前端的JavaScript 模板引擎,好比ejs,jade等。能夠根據傳入的參數,對模板標籤進行替換,最終生成html。
若是把全部項目文件,無論文件後綴名,都當作是ejs模板,則能夠在文件內容中使用ejs語法。
再根據配置信息進行替換,最終生成新文件。
其實,業界依據這個想法,已經有成熟的工具產生。
mem-fs
是對文件進行讀取,存入內存中。
mem-fs-editor
是對內存中的文件信息,使用ejs語法進行編譯。最後調用commit
方法輸出最終文件。
提示信息,除了console.log
,還可使用色彩更豐富的chalk
。
這樣,能夠輸出更直觀、友好的提示。
文件操做,有業界成熟的shelljs
。
利用shelljs
,能夠在項目中簡化如下步驟:
shelljs.copySync
同步方式生成。shelljs.mkdir
進行建立如下按我作的開源項目——npm-package-cli
的創做過程進行分拆、講解。
新建項目文件夾npm-package-cli
,並在該文件夾下運行npm init
,生成package.json
。
項目結構以下:
npm-package-cli
|-- package.json
複製代碼
這裏要生成的全局指令是npg-cli
。
新建文件夾bin
,並在文件夾下新建名稱爲cli
的shell腳本文件(注意:不能有後綴名)。
cli
shell腳本文件內容以下:
#!/usr/bin/env node
console.log('hello world');
複製代碼
其中,#!/usr/bin/env node
是告訴編譯器,以node
的方式,運行代碼。
並在package.json
加入如下內容:
"bin": {
"npg-cli": "bin/cli"
}
複製代碼
此時,項目結構以下:
npm-package-cli
|-- bin
|-- cli
|-- package.json
複製代碼
連接指令有兩種方式:
npm link
npm install -g
兩種方式,都須要在npm-package-cli
文件夾下運行,才能生效。
做用是把npg-cli
指令,指向全局的bin
文件下,實現軟鏈。
在任意文件夾下運行命令:
npg-cli
# 輸出
hello world
複製代碼
到這裏,一個基本的指令就算完成了,接下來是指令的工做內容細化。
Creation
的做用是整合全部操做,並提供接口給指令文件cli
。
Creation
的結構以下:
class Creation{
constructor(){
// code
}
do(){
// code
}
// other function
}
複製代碼
其中do
方法暴露給腳本文件cli
調用。
Creation
類放在src/index.js
中。
此時,項目結構以下:
npm-package-cli
|-- bin
|-- cli
|-- src
|-- index.js
|-- package.json
複製代碼
cli
文件#!/usr/bin/env node
const Creator = require('../src/index.js');
const project = new Creator();
project.do();
複製代碼
這樣,只要實現好do
方法,就能夠完成npg-cli
指令的運行了。
實現npg-cli --help
,須要藉助上文提到的工具commander
。
新建src/command.js
文件,文件內容以下:
const commander = require('commander');
const chalk = require('chalk');
const packageJson = require('../package.json');
const log = console.log;
function initCommand(){
commander.version(packageJson.version)
.on('--help', ()=>{
log(chalk.green(' run testcli and edit the setting.'));
})
.parse(process.argv);
}
module.exports = initCommand;
複製代碼
此時,項目結構以下:
npm-package-cli
|-- bin
|-- cli
|-- src
|-- command.js
|-- index.js
|-- package.json
複製代碼
而後在Creation.do
方法內執行initCommand()
便可生效。
// src/index.js Creation
const initCommand = require('./command');
class Creation{
// other code
do(){
initCommand();
}
}
複製代碼
此時,運行npg-cli --help
指令,就能夠看到:
Usage: npg-cli [options]
Options:
-V, --version output the version number
-h, --help output usage information
run testcli and edit the setting.
複製代碼
要獲取用戶輸入的信息,須要藉助工具inquirer
。
新建src/setting.js
文件,文件內容以下:
const inquirer = require('inquirer');
const fse = require('fs-extra');
function initSetting(){
let prompt = [
{
type: 'input',
name: 'projectName',
message: 'project name',
validate(input){
if(!input){
return 'project name is required.'
}
if(fse.existsSync(input)){
return 'project name of folder is exist.'
}
return true;
}
},
// other prompt
];
return inquirer.prompt(prompt);
}
module.exports = initSetting;
複製代碼
此時,項目結構以下:
npm-package-cli
|-- bin
|-- cli
|-- src
|-- command.js
|-- index.js
|-- setting.js
|-- package.json
複製代碼
而後在Creation.do
方法內執行initSetting()
便可生效。
// src/index.js Creation
const initCommand = require('./command');
const initSetting = require('./setting');
class Creation{
// other code
do(){
initCommand();
initSetting().then(setting => {
// 用戶輸入完成後,會獲得所有輸入信息的json數據 setting
});
}
}
複製代碼
這裏,inquirer.prompt
方法裝載好要收集的問題後,返回的是Promise
對象。收集完成以後,要在then
方法內拿到配置信息,以便進行下一步模板替換的操做。
模板文件替換,要用到工具mem-fs
和mem-fs-editor
。
文件操做,要用到工具shelljs
。
新建src/output.js
文件,文件內容以下(刪除了部分代碼,如下只是示例,完整項目看最後分享連接):
const chalk = require('chalk');
const fse = require('fs-extra');
const path = require('path');
const log = console.log;
function output(creation){
return new Promise((resolve, reject)=>{
// 拿到配置信息
const setting = creation._setting;
const {
projectName
} = setting;
// 獲取當前命令行執行環境所在文件夾
const cwd = process.cwd();
// 初始化文件夾path
const projectPath = path.join(cwd, projectName);
const projectResolve = getProjectResolve(projectPath);
// 新建項目文件夾
fse.mkdirSync(projectPath);
// copy文件夾
creation.copy('src', projectResolve('src'));
// 根據配置信息,替換文件內容
creation.copyTpl('package.json', projectResolve('package.json'), setting);
// 將內存中的文件,輸出到硬盤上
creation._mfs.commit(() => {
resolve();
});
});
}
module.exports = output;
複製代碼
output
方法的做用:
mem-fs-editor
的copyTpl
方法)這裏最重要的一步,是調用mem-fs-editor
的方法後,要執行mem-fs-editor
的commit
方法,輸出內存中的文件到硬盤上。
在Creation.do
方法中,調用output
方法便可輸出新項目文件。 打開src/index.js
文件,文件內容增長以下方法:
// src/index.js Creation
const initCommand = require('./command');
const initSetting = require('./setting');
const output = require('./output');
class Creation{
// other code
do(){
initCommand();
initSetting().then(setting => {
// 用戶輸入完成後,會獲得所有輸入信息的json數據 setting
this._setting = Object.assign({}, this._setting, setting);
// 輸出文件
output(this).then(res => {
// 項目輸出完成
});
});
}
}
複製代碼
自動初始化一個項目的流程不外乎如下三點:
命令行工具,是對這三點的有效整合,串連成一個規範的流程。
命令行工具中,使用的第三方工具包,都須要用--save
的方式安裝。
體如今package.json
的表現是dependencies
字段:
"dependencies": {
"chalk": "^2.4.2",
"commander": "^3.0.0",
"fs-extra": "^8.1.0",
"inquirer": "^6.5.0",
"mem-fs": "^1.1.3",
"mem-fs-editor": "^6.0.0",
"shelljs": "^0.8.3"
},
複製代碼
這樣,其餘用戶在安裝你發佈的CLI工具時,纔會自動安裝這些依賴。
.gitignore
文件npm官方是默認去除.gitignore文件的,無論你用任何方式聲明.gitignore
文件須要publish
。
解決方式是:將.gitignore
更名稱,好比改成gitignore
。當使用CLI工具時,再將文件名改回來。
例子:
creation.copy('gitignore', projectResolve('.gitignore'));
複製代碼
我創做的npm-package-cli
,是專門用於生成我的npm package
項目的CLI工具。
生成的項目,囊括如下功能點:
coverage
CHANGELOG.md
CLI工具安裝方式:
npm install -g npm-package-cli
複製代碼
開源倉庫地址:github.com/wall-wxk/np…
若是對你有所幫助,麻煩給個Star,你的確定是我前進的動力~
喜歡我文章的朋友,能夠經過如下方式關注我: