有时候,项目模板中并不是所有文件都是需要的。为了保证新生成的项目中尽可能的不存在脏代码,我们可能需要根据脚手架的输入项来确认最终生成的项目结构,将没用的文件或者目录移除。比如vue-cli,创建项目时会询问我们是否需要加入测试模块,如果不需要,最终生成的项目代码中是不包含测试相关的代码的。这个功能如何实现呢?
实现的思路
我参考了git的思路,定义个 ignore 文件,将需要被忽略的文件名列在这个 ignore 文件里,配上模板语法。脚手架在生成项目的时候,根据输入项先渲染这个 ignore 文件,然后根据 ignore 文件的内容移除不需要的模板文件,然后再渲染真正会用到的项目模板,最终生成项目。
实现方案
根据以上思路,我先定义了属于我们项目自己的 ignore 文件,取名为 templates.ignore 。
然后在这个 ignore 文件中添加需要被忽略的文件名。
{{#unless supportMacawAdmin}} # 如果不开启admin后台,登录页面和密码修改页面是不需要的 src/entry/login.js src/entry/password.js {{/unless}} # 最终生成的项目中不需要ignore文字自身 templates.ignore
然后在 lib/generator.js 中添加对 templates.ignore 的处理逻辑
// ... const minimatch = require('minimatch') // https://github.com/isaacs/minimatch module.exports = function (metadata = {}, src, dest = '.') { if (!src) { return Promise.reject(new Error(`无效的source:${src}`)) } return new Promise((resolve, reject) => { const metalsmith = Metalsmith(process.cwd()) .metadata(metadata) .clean(false) .source(src) .destination(dest) // 判断下载的项目模板中是否有templates.ignore const ignoreFile = path.join(src, 'templates.ignore') if (fs.existsSync(ignoreFile)) { // 定义一个用于移除模板中被忽略文件的metalsmith插件 metalsmith.use((files, metalsmith, done) => { const meta = metalsmith.metadata() // 先对ignore文件进行渲染,然后按行切割ignore文件的内容,拿到被忽略清单 const ignores = Handlebars.compile(fs.readFileSync(ignoreFile).toString())(meta) .split('\n').filter(item => !!item.length) Object.keys(files).forEach(fileName => { // 移除被忽略的文件 ignores.forEach(ignorePattern => { if (minimatch(fileName, ignorePattern)) { delete files[fileName] } }) }) done() }) } metalsmith.use((files, metalsmith, done) => { const meta = metalsmith.metadata() Object.keys(files).forEach(fileName => { const t = files[fileName].contents.toString() files[fileName].contents = new Buffer(Handlebars.compile(t)(meta)) }) done() }).build(err => { rm(src) err ? reject(err) : resolve() }) }) }
基于插件思想的metalsmith很好扩展,实现也不复杂,具体过程可参见代码中的注释。
总结
经过对vue-cli的整理,借助了很多node模块,整个脚手架的实现并不复杂。
将项目模板与脚手架工具分离,可以更好的维护模板和脚手架工具。
通过commander.js处理命令行
通过download-git-repo处理下载
通过inquirer.js处理终端交互
通过metalsmith和模板引擎将交互输入项插入到项目模板中
参考了git的ignore的思路,利用自定义的templates.ignore动态化的移除不必要的文件和目录
以上就是我开发脚手架的主要经历,中间还有很多不足的地方,今后再慢慢完善吧。
最后说一下,其实vue-cli能做的事情还有很多,具体的可以看看项目的README和源码。关于脚手架的开发,不一定要完全造个轮子,可以看看另外一个很强大的模块YEOMAN,借助这个模块也可以很快的实现自己的脚手架工具。