随着开发团队不断发展壮大,在人员增长的同时也带来了协做成本的增长;业务项目愈来愈多,类型也各不相同。常见的类型有基础组件、业务组件、基于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