cli脚手架工具
# 场景
发现团队成员在开发新项目时,需要发费时间在新项目的搭建上,不熟悉<code>前端工程化</code>的同事花在项目搭建的时间会很长。为了提效,统一维护各种模版,利用<code>zcl-cli脚手架</code>快速选择合适脚手架下载到本地,并自动安装依赖包。
<code>zcl-cli脚手架</code>后续可添加功能来支撑开发。
# 实现
第三方工具库:
- commander (opens new window): node.js命令行工具
- inquirer (opens new window):交互式命令行集合
- chalk (opens new window): 命令行字符样式美化
- ora (opens new window):命令行Loading动画效果
- download-git-repo (opens new window): 下载git仓库
- metalsmith (opens new window): 静态资源生成器。有插件机制
- Handlebars (opens new window): 模版编译。
- fs-extra (opens new window):fs有的fs-extra都有,很多的支持Async/await
设计

提示
package.json的bin字段 (opens new window):bin字段提供命令名到本地<code可执行文件</code>的映射。cmd下输入<code>zcl</code>,即可执行<code>./bin/zcl</code>文件。
./bin/zcl 前面要加上<code>#!/usr/bin/env node</code>,确保用node来执行文件
"bin": {
"zcl": "./bin/zcl"
}
2
3
# 难点
# 1. 本地测试

本地调试代码是必须的,总不能改一下代码,再发包测试吧。要实现本地测试很简单,不过这里和各位深入理解下<code>package.json的bin字段</code>和<code>npm link</code>
前面官网说了,有了bin字段,就可以在命令行输入<code>zcl</code>,即可执行<code>./bin/zcl</code>文件。深入一下,为什么命令行输入zcl就能执行<code>./bin/zcl</code>?
- 实际上, 如果包的package.json存在bin字段,局部安装时,会在
<code>./node_modules/.bin/</code>下,生成zcl可执行文件;全局安装时,会在<code>C:\Users\zhangchengliang\AppData\Roaming\npm</code>下生成可执行文件<code>zcl.cmd</code>,zcl.cmd会去执行<code>\node_modules\zcl-cli\bin\zcl</code>文件。

// C:\Users\zhangchengliang\AppData\Roaming\npm\zcl.cmd
"%_prog%" "%dp0%\node_modules\zcl-cli\bin\zcl" %*
2
- 在
<code>zcl-cli</code>包内,执行npm link,会在全局npm/node_modules/下生成软链接,指向<code>zcl-cli</code>包
本地测试:只需在<code>zcl-cli</code>包执行npm link,然后在代码中打断点,就可以本地调试了
# 2. 模版编译
Metalsmith works in three simple steps:
- Read all the files in a source directory.
- Invoke a series of plugins that manipulate the files.
- Write the results to a destination directory!
模版使用template分支来维护模版,为了编译时替换,用括号括起来
{
"name": "{{projectName}}",
"version": "{{projectVersion}}",
"description": "{{projectDescription}}",
// ...
}
2
3
4
5
6
Metalsmith做资源的生成;Handlebars做编译,替换package.json中的变量。
const Metalsmith = require('metalsmith')
const Handlebars = require('handlebars')
const { logger } = require('./utils')
const rm = require('rimraf').sync
const path = require('path')
class Generator {
constructor() {
this.metalsmith = Metalsmith(process.cwd())
this.config = {}
}
run(config) {
this.config = config
const { metadata, sourceName, destination } = this.config
this.metalsmith = this.metalsmith.metadata(metadata) // metadata 为用户输入的内容
.clean(false)
.source(sourceName)
.destination(destination)
.use(this.generateTemplate)
.build(err => {
rm(path.resolve(process.cwd(), sourceName))
if (err) {
logger.error(`Metalsmith build error: ${err}`)
}
})
}
generateTemplate(files, metalsmith, done) {
console.log('metadata', metalsmith.metadata())
Object.keys(files).forEach(fileName => { //遍历替换模板
try {
// 字体文件、图片等不能用 handlebar 替换: https://www.jianshu.com/p/a8804047d4ed
// if (!/\.(ico|png|jpe?g|gif|svg)$/.test(fileName) || !/\.(woff2?|eot|ttf|otf)$/.test(fileName)) {
// 只替换package.json部分
if (fileName === 'package.json') {
const fileContentsString = files[fileName].contents.toString()
files[fileName].contents = new Buffer.from(Handlebars.compile(fileContentsString)(metalsmith.metadata()))
}
} catch (err) {
logger.error(`fileName------------${fileName}`);
logger.error(`err -------------${err}`);
}
})
done()
}
}
module.exports = Generator
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# 总结
在开发新项目时,需要花费较长的时间在搭建项目上,通过维护公用的模版,开发团队内部使用的脚手架(命令行工具),达到快速下载模版,减少项目搭建时间,提高开发效率。
- 怎么实现一个自定义脚手架?
- 怎么自定义一个Vue-Cli的项目模版?