创建自己的项目脚手架.md
之前我们提到Yeoman是一种是通用的工具,可以自定义实现自己的脚手架,我们今天就来自己会实现一个
# generator基本结构
首先我们安装一个基础包,yeoman-generator,里面有生成器必要的依赖;
然后创建以下目录;
├── generators // 生成器目录
│ └── app // 默认app目录
│ └── index.js // 具体实现
└── package.json // 包配置文件
需要注意的是,生成器的名称必须以generator-开头,否则将来使用时就不能正常运行。
# 实现
具体我们看看index.js ,如何去实现。
// 此文件作为 Generator 的核心入口
// 需要导出一个继承自 Yeoman Generator 的类型
// Yeoman Generator 在工作时会自动调用我们在此类型中定义的一些生命周期方法
// 我们在这些方法中可以通过调用父类提供的一些工具方法实现一些功能,例如文件写入
const Gen = required('yeoman-generator')
module.exports = class extends Gen {
writing() {
//yeoman生成文件时调用 我们这里尝试往项目目录中写入文件
this.fs.write(
this.destinationPath('temp.txt'),
Math.random().toString()
)
}
}
上面就是一个简单的生成器,用于生成一个temp的txt文件,内容为随机数;
更多时候我们会根据模版生成。我们还需要接收用户输入。下面这个例子更加全面。
const Generator = require('yeoman-generator')
module.exports = class extends Generator {
prompting () {
return this.prompt([
{
type: 'input',
name: 'name', // 这里的键名接收到的值,会根据cjs语法替换掉模版中的占位。
message: 'Your project name',
default: this.appname
}
])
.then(answers => {
this.answers = answers
})
}
writing () {
// 把每一个文件都通过模板转换到目标路径
// 需要复制的模版文件列表,通过循环处理
const templates = [
// ...
'.browserslistrc',
'package.json',
'postcss.config.js',
'README.md',
'public/favicon.ico',
'public/index.html',
'src/App.vue',
'src/main.js',
'src/router.js',
'src/assets/logo.png',
'src/components/HelloWorld.vue',
'src/views/About.vue',
'src/views/Home.vue',
// ...
]
templates.forEach(item => {
// item => 每个文件路径
this.fs.copyTpl(
this.templatePath(item),
this.destinationPath(item),
this.answers
)
})
}
}
综上,脚手架工具其实就是接收一些预设,通过用户输入,结合模版生成项目结构。
我们用node简单实现下
#!/usr/bin/env node
// Node CLI 应用入口文件必须要有这样的文件头
// 如果是 Linux 或者 macOS 系统下还需要修改此文件的读写权限为 755
// 具体就是通过 chmod 755 cli.js 实现修改
// 脚手架的工作过程:
// 1. 通过命令行交互询问用户问题
// 2. 根据用户回答的结果生成文件
const fs = require('fs')
const path = require('path')
const inquirer = require('inquirer') // 询问库
const ejs = require('ejs') // ejs模版引擎库
inquirer.prompt([
{
type: 'input',
name: 'name',
message: 'Project name?'
}
])
.then(anwsers => {
// console.log(anwsers)
// 根据用户回答的结果生成文件
// 模板目录
const tmplDir = path.join(__dirname, 'templates')
// 目标目录
const destDir = process.cwd()
// 将模板下的文件全部转换到目标目录
fs.readdir(tmplDir, (err, files) => {
if (err) throw err
files.forEach(file => {
// 通过模板引擎渲染文件
ejs.renderFile(path.join(tmplDir, file), anwsers, (err, result) => {
if (err) throw err
// 将结果写入目标文件路径
fs.writeFileSync(path.join(destDir, file), result)
})
})
})
})