使用node搭建一个简单的脚手架工具实践
脚手架实现原理
脚手架实际上就是通过命令行交互,询问用户一些问题,然后将问题的答案结合预设的模板文件,输出映射文件形成项目结构。
脚手架应用,实际就是一个node cli应用。
自定义一个小型脚手架工具
脚手架的需求:
- 创建一个简单的vue项目结构
- 通过命令行交互询问用户项目名称
- 通过命令行询问用户,使用yarn 或npm安装依赖 或 不安装依赖
- 若用户选择安装依赖,通过用户询问,是否使用淘宝镜像安装依赖
搭建过程
创建脚手架项目目录
node-vue-cli
,通过yarn init
初始化一个package.json
创建
cli.js
文件,并在package.json
文件中添加bin
属性,值为cli.js
。cli.js
文件即为脚手架配置文件。node的脚手架配置文件,需添加
#!/uer/bin/env node
的文件头。先简单添加一个log语句,将cli工具link到全局,执行node-vue-cli
,看cli工具是否正常工作。1
2
3
console.log('node-vue-cli')1
2yarn link
node-vue-cli创建模板文件目录
templates
,并添加项目模板文件。命令行交互,需要使用
inquirer
模块,安装inquirer
模块1
yarn add inquirer --dev
通过
inquirer.prompt
发起命令行交互询问。inquirer.prompt
方法接收一个数组,用于配置询问信息。返回值是一个Promise,可以使用then方法获得用户回答的信息,并在then方法结合回答信息执行一些操作,实现项目搭建。1
2
3
4
5
6
7
8
9
const inquirer = require("inquirer")
inquirer.prompt([
//question
]).then(answer=>{
//actions
})首选询问项目名称,结合模板文件,生成项目结构
通过
path
获取templates
的目录地址通过
cwd
获取目标目录地址通过
fs
读取templates
的目录下所有的文件(fs.readdir
),写入文件目标文件(fs.writeFileSync
),创建文件夹(fs.mkdir
)通过
ejs
的ejs.renderFile
读取文件内容
fs.readdir
,接收两个参数:templates目录地址和回调函数,回调函数内接收templates目录下所有文件相对于templates目录的相对路径。
ejs.renderFile
接收三个参数:文件路径、模板替换内容、回调函数,回调函数内接收替换完成的文件内容
首先,我们先写一个文件渲染的函数,因为我们的templates文件夹下有嵌套文件夹,所以需要递归去渲染文件。
1 | /** |
然后编写询问项目名称与是否安装依赖的交互,并渲染开始文件。
1 |
|
因为是否安装依赖与是否使用淘宝镜像是有先后关系的,所有在then函数中返回一个新的Promise,用于询问是否使用淘宝镜像,并将前两个交互的答案合并,传给后续then方法
1 |
|
处理执行安装依赖
在node中,可以使用子进程来实现shell命令执行。
引入子进程依赖
const cProcess = require("chile-process")
使用子进程
exce(<shell cmd>,callback)
方法,实现依赖安装1
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
50
51
52
53
54
55
56
57
58
59
const cProcess = require('child_process');
const inquirer = require("inquirer")
const path = require("path")
const fs = require("fs")
const ejs = require("ejs")
//....renderFiles
inquirer.prompt([
{
type: "input",
name: "name",
message: "项目名称"
},
{
type: "list",
name: "install",
message: "是否安装依赖",
choices:['yarn','npm','none']
}
]).then(answer => {
const tempPath = path.join(__dirname, "templates")
const destPath = process.cwd()
renderFiles(tempPath, destPath, answer)
if(answer.install == "none") return false
return new Promise(resolve=>{
inquirer.prompt([
{
type: "confirm",
name: "ali",
message: "是否使用淘宝镜像",
default:true
}
]).then(answer2=>{
resolve({...answer,...answer2})
})
})
}).then(answer=>{
//不需要安装依赖
if(!answer) return
//拼装命令
shellCwd = `${answer.install == "yarn" ? answer.install : answer.install + ' install'} ${answer.ali ? "--registry=https://registry.npm.taobao.org": ""}`
console.log("执行命令:"+shellCwd)
//执行命令
const install = cProcess.exec(shellCwd,(err, stdout, stderr)=>{
if(err) throw err
});
//显示命令执行详情
install.stdout.on("data",data=>{
console.log(data)
})
})
测试使用
- 使用
yarn link
将cli命令link到全局 - 新建项目目录,在项目目录中使用
node-vue-cli
,回答命令行交互问题,自动执行模板文件下载与开发依赖安装。 - 执行
yarn serve
测试项目是否正常运行