隨着開發團隊不斷髮展壯大,在人員增長的同時也帶來了協做成本的增長;業務項目愈來愈多,類型也各不相同。常見的類型有基礎組件、業務組件、基於React的業務項目、基於Vue的業務項目等等。若是想要對每一個項目進行一些規範上的約束好比Git提交規範、Javascript規範簡直難於登天。全部的這些,只是由於還欠缺一個好用的工程化工具,在項目建立的初期自動的將這些目錄結構和文件生成、而且集成工程常見的規範來進行約束。javascript
本文分爲兩部分,首先會談談目前團隊的痛點以及基於yeoman generator的設計思路;而後會詳細介紹如何實現定製的generator,過程當中遇到的問題和解決辦法。html
咱們須要給每一個工程類型的項目建立一個generator。按照目前前端技術棧的發展狀況來看,一個團隊通常會有3~5個generator。把這些generator當作一個個的插件,經過工具上層的CLI命令來暴露給開發者使用。前端
在generator之下,須要開發一系列服務和集成規範。包括和Git倉庫打通,也就是經過腳手架初始化目錄時,先對開發者鑑權。以後根據開發者輸入的項目名稱在遠程Git倉庫裏面建立倉庫而且授予開發者權限。後期功能完善以後,能夠作一些錦上添花的工做,好比進行數據統計,分析各個業務倉庫使用的generator版本信息,是否集成了最新的feature等等。java
總體系統架構以下:git
下面我準備開發一個適用於Now直播活動類搭建的腳手架了,名字是generator-now-activitygithub
├───package.json └───generators/ ├───app/ | ├───templates/ | | ├─── src/ | | |─── _cilintrc.js | | |─── _eslintrc.js | | |─── _fis-conf.js | | |─── _package.json | | |─── _project.js | | |─── _README.md | | |─── editorconfig | | |─── gitignore | | └─── vcmrc │ └───index.js └───utils.js
在generator的外層index.js文件裏,經過繼承yeoman-generator來擴展咱們本身的generator,而後模塊暴露給外部。算法
const Generator = require('yeoman-generator'); module.exports = class extends Generator { }
一個 Yeoman Generator 被建立後(構造函數必然是最早被調用的),會依次調用它原型上的方法,且每個方法中的 this 都被綁定爲 Generator 實例自己,調用的順序以下:shell
Yeoman提供了API來讓generator和用戶進行交互,直接經過this.prompts函數,它的內部實現是使用了Inquire.js。npm
/** * 提示用戶輸入配置項 * @returns {Promise.<TResult>} */ prompting() { return this.prompt([{ type: 'input', name: 'projectName', message: '請輸入活動的名稱 (now-activity):', default: 'now-activity-default' }]).then((answers) => { this.log('活動名稱', answers.projectName); this.props = answers; }); }
對於工程src目錄部分直接經過深度優先算法拷貝寫入。對於工程的規範類、配置的文件須要單獨寫入,這一類可能須要接受用戶的輸入,同時須要集中進行維護,所以須要和src的拷貝方式進行區分。json
src深度優先拷貝代碼以下:
const fs = require('fs'); const path = require('path'); function read(root, filter, files, prefix) { prefix = prefix || ''; files = files || []; filter = filter || noDotFiles; const dir = path.join(root, prefix); if (!fs.existsSync(dir)) return files; if (fs.statSync(dir).isDirectory()) fs.readdirSync(dir) .filter(filter) .forEach(function (name) { read(root, filter, files, path.join(prefix, name)); }); else files.push(prefix); return files } function noDotFiles(x) { return x[0] !== '.'; } module.exports = { read };
在外層經過Yeoman提供的API this.fs.copy()方法來進行文件拷貝
/** * 源代碼模板 */ const sourceCode = () => { const sourceDir = path.join(this.templatePath(), './src/'); const filePaths = utils.read(sourceDir); _.each(filePaths, (filePath) => { this.fs.copy( this.templatePath('./src/' + filePath), this.destinationPath('./src/' + filePath) ); }); };
開發完generator以後,就能夠經過yo now-activity來進行使用了。
前面提到的yo now-activity的方式使用可能存在一些問題,由於這種方式要求代碼必須上傳到github上。對於公司內部的工具,不走正常的開源流程顯然是不被容許的。那麼,有沒有什麼方法,不添加generator到Yeoman的generator列表裏就可以使用呢?
幸運的是,Yeoman提供了yeoman-environment來幫助咱們在其它工具中集成編寫好的generator,yo其實也只是yeoman-environment暴露到上層的一個命令而已。
const yeoman = require('yeoman-environment'); const yeomanEnv = yeoman.createEnv(); /** * Lookup方法會在本地查找已經安裝過的generator */ yeomanEnv.lookup(() => { yeomanEnv.run('@tencent/now-activity', {'skip-install': true}, err => { console.log('done'); }); });
安裝示例(限內部)
$ tnpm install -g yo generator-generator @tencent/feflow-cli $ feflow init