完整的腳手架代碼前端
隨着NodeJs的崛起,現代前端工程已經變得愈來愈複雜。前端框架如雨後春筍。從Backbone,Ember,Knockout,Spine,Batman框架的崛起到Angular,React,Vue平分天下,咱們經歷太多代碼風格的變革。vue
不可避免學習新的框架,複雜的環境又讓初學者很難入手。就拿React來講,咱們要使用Babel,Webpack等模塊輔助編譯,咱們還須要Redux,Immutable,Mocha,Jest,Antd或者其餘第三方包(如eslint,lodash,moment,debug,uuid,request,co,koa等)輔助業務需求。node
目錄結構愈來愈複雜,爲了讓每一個項目使用的統一的技術和目錄結構,各大模塊紛紛推出了本身的腳手架工具,好比redux-cli,create-react-app,vue-cli等。如今就讓咱們以any-cli爲例,編寫本身的腳手架工具吧。react
yoeman
搭建項目須要提供yoeman-generator
。yoeman-generator
本質上就是一個具有完整文件結構的模板,用戶須要手動地把這些模板下載到本地,而後yoeman
就會根據這些模板自動生成各類不一樣的項目。git
vue-cli提供了至關豐富的選項和設定功能,可是其本質也是從遠程倉庫把不一樣的模版拉取到本地,而並不是是什麼「本地生成」的黑科技。程序員
這樣看來,思路也就有了——首先創建不一樣的模板,而後腳手架根據用戶的指令引用模板生成實際項目。github
模板既能夠內置在腳手架當中,也能夠部署在遠程倉庫。vue-cli
內置在腳手架中,使用node file
操做來把模板克隆到本地。npm
優勢是不用新建倉庫保存模板。尤爲是有多項目模板時候,好比init pc
與init mobile
分別生成兩個不一樣項目,咱們只須要一個倉庫保存腳手架便可。編程
第二個優勢:不管腳手架仍是模板變動,只須要提交一次。
部署在遠程倉庫,使用git clone
把項目克隆到本地。
優勢是每次模板有代碼變動時,無需讓用戶本地升級腳手架。
若是模板內置在腳手架裏,每次模板變動,由於是在同一倉庫,因此用戶須要升級腳手架。而部署在遠程倉庫則不一樣,咱們只須要git clone便可獲取最新模板。
按照標準慣例,先看下總體流程(github不支持流程圖flow類型展現,好搓,害的我又把流程代碼改爲這種挫樣式)
添加模板->輸入模板名->是否有重名模板?-添加成功:給出提示
刪除模板->輸入模板名->是否有模板?-刪除成功:給出提示
模板列表->列出全部模板
初始化模板-輸入模板名-是否有模板?-輸入模塊名-克隆遠程倉庫到本地模塊-切換分支:給出提示複製代碼
命令都位於腳手架中,而執行命令的地方經常在模板項目中。
好比腳手架路徑是/usr/local/lib/node_modules/any-cli/lib/
,執行全局腳手架命令路徑是/Users/admin/test/
。
想要腳手架代碼想讀腳手架目錄下的a.js
文件寫入模板項目(執行全局腳手架命令位置)b.js
中,那麼readFile的路徑爲path.resolve(__dirname,'a.js')
,writeFile路徑爲path.resolve(process.cwd(),'b.js')
。就這麼簡單。
許多npm模塊有可執行文件但願被安裝到全局系統路徑。
須要在package.json中提供一個bin字段,它是一個命令名稱到文件路徑的映射。以下:
"bin": {
"any": "./bin/any"
},複製代碼
這樣會把any命令和本地可執行文件./bin/any創建映射。也就是說,當你在命令行執行any命令時,會執行./bin/any可執行文件。
全局安裝,npm將會使用符號連接把這些文件連接到/usr/local/bin目錄下,系統的二進制命令所有在這裏。
若是是本地安裝,會連接到./node_modules/.bin目錄下。只有當前目錄運行any命令時,纔會生效。
若是你只有一個可執行文件,那麼它的名字應該和包名相同,此時只須要提供這個文件路徑(字符串),好比:
{
"name": "any-cli",
"version": "1.2.5",
"bin": "./bin/any"
}複製代碼
等同於:
{
"name": "any-cli",
"version": "1.2.5",
"bin": {
"any-cli": "./bin/any"
}
}複製代碼
本地目錄連接到全局模塊
npm link能夠把本地目錄連接到全局模塊下。
對於開發模塊者而言,這算是最有價值的命令了。好比咱們開發any-cli
模塊時,須要在命令行中使用any
來測試咱們的代碼(開發中沒有發佈,也就沒法全局安裝模塊)。不要擔憂,使用npm link一切變得很是容易。
好比咱們any-cli
項目package.json裏,有一條命令以下:
"bin": {
"any": "./bin/any"
},複製代碼
命令行中使用npm link
$ npm link複製代碼
獲得如下結果
/usr/local/bin/any -> /usr/local/lib/node_modules/any-cli/bin/any
/usr/local/lib/node_modules/any-cli -> /Users/lihongji/work/any-cli複製代碼
分別進入/usr/local/bin與/usr/local/lib/node_modules目錄下查看。咱們發現裏面分別多了any
可執行文件與any-cli
目錄。
這樣,每次本地倉庫有改動時,全局命令也隨之更新。咱們就能夠邊開發邊測試了。
本地目錄引用全局模塊
若是你還有其餘模塊any-cli-test
依賴於any-cli
模塊,你可使用以下命令把全局any
連接到當前模塊下。
$ cd ~/work/any-cli-test
$ npm link any-cli # 把全局模式的模塊連接到本地複製代碼
npm link test 命令會去/usr/local/lib/node_modules
目錄下查找 any-cli的模塊,找到這個模塊後把/usr/local/lib/node_modules/any-cli
的目錄連接到當前any-cli-test
下的./node_modules/any-cli
目錄上。
如今任何 test 模塊上的改動都會直接映射到 test-example 上來。
node7.6.0開始支持async,如何保證用戶本地安裝node7.6.0以上版本呢複製代碼
engine
你能夠在本地安裝node特定版本:
"engines": { "install-node": "7.6.0" }複製代碼
安裝後,在本地node_modules/.bin目錄下會多一個node可執行文件。本地的任何node命令都會使用這個版本的node可執行文件。
你能夠指定工做的node的版本:
{ "engines" : { "node" : ">=0.10.3 <0.12" }="" }<="" code="">
複製代碼
而且,像dependensies
同樣,若是你不指定版本或者指定「*」做爲版本,那麼全部版本的node均可以。
若是指定一個engines
字段,那麼npm
會須要node
在裏面,若是「engines」被省略,npm會假定它在node上工做。
你也能夠用engines
字段來指定哪個npm
版本能更好地初始化你的程序,如:
{ "engines" : { "npm" : "~1.0.20" } }複製代碼
除非用戶設置engine-strict
標記,這個字段只是建議值。
engineStrict
若是你肯定你的模塊必定不會運行在你指定版本以外的node
或者npm
上,你能夠在package.json
文件中設置engineStrict:true
。它會重寫用戶的engine-strict
設置。
這幾個包分別用來處理提交執行腳本,全局配置文件管理與命令行處理。會在代碼中一一講解。
建立any-cli
項目
work$mkdir any-cli
work$cd any-cli
any-cli$git init && npm init複製代碼
package.json內容
{
"name": "any-cli",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"pub": "npm version patch && npm publish",
"pre-commit": "eslint src"
},
"author": "antgod",
"devDependencies": {
"eslint": "^3.16.1",
"eslint-config-airbnb": "^12.0.0",
"eslint-plugin-babel": "^3.0.0",
"eslint-plugin-import": "^1.6.1",
"eslint-plugin-jsx-a11y": "^2.0.1",
"eslint-plugin-markdown": "*",
"eslint-plugin-react": "^6.3.0",
"eslint-tinker": "^0.3.2",
"pre-commit": "^1.2.2"
},
"dependencies": {
"chalk": "^1.1.3",
"child_process": "^1.0.2",
"commander": "^2.9.0",
"prompt": "^1.0.0"
},
"engines": {
"install-node": "7.6.0"
},
"pre-commit": [
"pre-commit"
],
"bin": {
"any": "./bin/any"
},
"license": "ISC"
}複製代碼
pre-commit用來作提交前代碼檢查,運行pre-commit腳本,也就是eslint。
在根目錄下創建/bin 文件夾,建立any文件(無後綴名)。這個 /bin/any是整個腳手架的入口文件,因此咱們首先對它進行編寫。
# !/usr/bin/env node
const add = require('../src/command/add')
const list = require('../src/command/list')
const init = require('../src/command/init')
const del = require('../src/command/del')
const program = require('commander')
const { version } = require('../package')
// 定義當前版本
program
.version(version)
program.parse(process.argv)
if (!program.args.length) {
program.help()
}複製代碼
運行npm link,把當前項目連接到全局。這樣就能夠直接在命令行使用any命令測試/bin/any下面的代碼
若是沒有權限,請自行百度,使用chmod 777爲usr/local目錄添加權限複製代碼
any-cli@npm link
any-cli@any複製代碼
咱們繼續在/bin/any中添加代碼
// 定義使用方法
program
.command('add')
.description('add template')
.alias('a')
.action(add)
program
.command('del')
.description('Delete a template')
.alias('d')
.action(del)
program
.command('list')
.description('List all the templates')
.alias('l')
.action(list)
program
.command('init')
.description('Generate a new project')
.alias('i')
.action(init)複製代碼
command用來配置any命令的參數,alias配置縮寫,action配置運行什麼函數。其餘就不用多說了吧,程序員你懂的。
commander 的具體使用方法在這裏就不展開了,能夠直接到 [官網][2] 去看詳細的文檔。複製代碼
使用any命令,看到輸出以下,證實入口文件已經編寫完成了。
Usage: any [options] [command]
Commands:
add|a add template
del|d Delete a template
list|l List all the templates
init|i Generate a new project
Options:
-h, --help output usage information
-V, --version output the version number複製代碼
接着,咱們建立src/command目錄,下面分別建立剛纔的4個參數所對應的文件。文件內容是具體業務代碼(分別對應增刪查初始化),在此不作介紹。請參考github連接。代碼使用了函數式編程,須要有點函數式基礎。