模板引擎我选择handlebars。当然,还可以有其他选择,例如ejs、 jade 、 swig 。
用handlebars的语法对模板做一些调整,例如修改模板中的 package.json
{ "name": "{{projectName}}", "version": "{{projectVersion}}", "description": "{{projectDescription}}", "author": "Forcs Zhang", "private": true, "scripts": { "dev": "node build/dev-server.js", "start": "node build/dev-server.js", "build": "node build/build.js", "unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run", "test": "npm run unit", "lint": "eslint --ext .js,.vue src test/unit/specs" }, "dependencies": { "element-ui": "^2.0.7", "macaw-ui": "{{supportUiVersion}}", "vue": "^2.5.2", "vue-router": "^2.3.1" }, ... }
package.json 的 name 、 version 、 description 字段的内容被替换成了handlebar语法的占位符,模板中其他地方也做类似的替换,完成后重新提交模板的更新。
实现脚手架给模板插值的功能
在 lib 目录下创建 generator.js ,封装metalsmith。
touch ./lib/generator.js
// npm i handlebars metalsmith -D const Metalsmith = require('metalsmith') const Handlebars = require('handlebars') const rm = require('rimraf').sync module.exports = function (metadata = {}, src, dest = '.') { if (!src) { return Promise.reject(new Error(`无效的source:${src}`)) } return new Promise((resolve, reject) => { Metalsmith(process.cwd()) .metadata(metadata) .clean(false) .source(src) .destination(dest) .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() }) }) }
给 macaw-init.js 的 go() 添加生成逻辑。
// ... const generator = require('../lib/generator') function go () { next.then(projectRoot => { // ... }).then(context => { // 添加生成的逻辑 return generator(context) }).then(context => { console.log('创建成功:)') }).catch(err => { console.error(`创建失败:${err.message}`) }) }
至此,一个带交互,可动态给模板插值的脚手架算是基本完成了。
tips:墙裂推荐一下tj的另一个工具包: consolidate.js ,在vue-cli中发现的,感兴趣的话可以去了解一下。
美化我们的脚手架
通过一些工具包,让脚手架更加人性化。这里介绍两个在vue-cli中发现的工具包:
ora - 显示spinner
chalk - 给枯燥的终端界面添加一些色彩
这两个工具包用起来不复杂,用好了会让脚手架看起来更加高大上
用ora优化加载等待的交互
ora可以用在加载等待的场景中,比如脚手架中下载项目模板的时候可以使用,如果给模板插值生成项目的过程也有明显等待的话,也可以使用。
以下载为例,对 download.js 做一些改良:
npm i ora -D
const download = require('download-git-repo') const ora = require('ora') module.exports = function (target) { target = path.join(target || '.', '.download-temp') return new Promise(resolve, reject) { const url = 'https://github.com:username/templates-repo.git#master' const spinner = ora(`正在下载项目模板,源地址:${url}`) spinner.start() download(url, target, { clone: true }, (err) => { if (err) { spinner.fail() // wrong :( reject(err) } else { spinner.succeed() // ok :) resolve(target) } }) } }
用chalk优化终端信息的显示效果
chalk可以给终端文字设置颜色。
// ... const chalk = require('chalk') const logSymbols = require('log-symbols') // ... function go () { // ... next.then(/* ... */) /* ... */ .then(context => { // 成功用绿色显示,给出积极的反馈 console.log(logSymbols.success, chalk.green('创建成功:)')) console.log() console.log(chalk.green('cd ' + context.root + '\nnpm install\nnpm run dev')) }).catch(err => { // 失败了用红色,增强提示 console.error(logSymbols.error, chalk.red(`创建失败:${error.message}`)) }) }
根据输入项移除模板中不需要的文件