# Futu5 Node.js 接入层脚手架方案



## 背景


目前开发基于 futu5 node.js 接入层为基础的项目时，直接从之前的项目copy过来然后进行修改。表面上看问题不大，但其实存在很多问题：

* 重复性工作，繁琐而且浪费时间
* copy过来的模板容易存在与个人需求无关的代码
* 人工操作永远都有可能犯错，建新项目时，总要花时间去排错
* 项目中有很多需要配置的地方，容易忽略一些配置点，进而埋坑
* 缺乏版本控制：内部框架也在不停的迭代，人工建项目往往不知道框架最新的版本号是多少，使用旧版本的框架可能会重新引入一些bug
* 新的 feature 和 bug fix 难以同步

针对以上问题，开发人员可以根据交互动态生成项目结构、自动添加依赖和配置、移除不需要的文件的脚手架工具是有必要的。

像我们熟悉的 `vue-cli，react-native-cli` 等脚手架，只需要输入简单的命令 `vue init webpack project`，即可快速帮我们生成一个初始项目。因此，在实际工作中，我们可以定制一个属于自己的脚手架，来提高自己的工作效率。


## 需求分析

![需求分析](./imgs/final/13.jpg)

命令格式：
```bash
# 命令
futu5 init '<template-name>#[branch-name]' [project-name]
# options
# 使用 git clone 方式下载模版，默认 http
-c, --clone
# 输出帮助信息
-h, --help
# 使用本地缓存模版模式
-o, --offline
```

命令示例：

```bash
# 根据模版创建项目
futu5 init futu5_ipo my-project
# 根据指定模版版本创建项目 - 拉取指定分支
futu5 init futu5_ipo#1.0 my-project
# 使用本地缓存模版创建项目
futu5 init futu5_ipo my-project -o
# 使用 git clone 方式下载模版创建项目
futu2 init futu5_ipo my-project -c
```

## 方案介绍

![方案介绍](./imgs/final/12.png)


## 方案比较

![方案比较](./imgs/final/10.png)


## 方案优劣

![方案优劣](./imgs/final/11.png)


因此，本次将采用 vue-cli 脚手架方案进行开发，以下是脚手架分析：


## 系统结构

* 用户输入指定命令及参数，脚手架解析出目标项目名称、目标项目构建路径、模版仓库
* 脚手架从模板仓库拉取模板文件，然后根据模板仓库根目录下定义描述文件进行收集问题
* 脚手架结合所收集问题答案过滤文件并生成接入层项目基本框架

![系统架构](./imgs/final/1.png)



## 主流程图

![流程图](./imgs/final/9.jpg)

## 核心功能

### 1. 命令及参数解析



脚手架根据用户输入的命令及参数(progress.argv 包含当启动 Node.js 进程时传入的命令行参数)进行解析获得模版仓库地址/模版名（模版仓库地址为第三方扩展模版）、模版版本、下载模版的方式（http/clone）、目标项目构建路径、目标项目名称等。

![命令及参数解析](./imgs/final/2.png)


### 2. 目标项目构建路径判断

脚手架根据解析用户输入所得 **目标项目构建路径** 进行判断（是否已存在、是否是当前目录）。

![目标项目构建路径判断](./imgs/final/3.png)


### 3. 模板下载

脚手架根据解析用户输入所得 **模版仓库地址/名** 拉取模版文件。

![模板下载](./imgs/final/4.png)

### 4. 模版配置文件解析

解析所拉取模版文件下包含的模版定义数据文件（即 `[meta.{js, json}](https://github.com/vuejs-templates/webpack/blob/develop/meta.js)`）。该文件描述初始化项目时命令行的交互动作及文件过滤依据（即关于目标项目名称、目标项目描述、目标项目依赖配置选择等问题的定义）。返回模版配置对象。

![模版定义数据解析](./imgs/final/5.png)


### 5. 问题收集
将 `meta.js` 或者 `meta.json` 模版配置对象中的 `prompts`字段解析成对应的问题询问，并收集答案存于 `metalsmith.metadata()`。


![问题收集](./imgs/final/6.png)

### 6. 文件过滤

根据问题答案收集所得 `metalsmith.metadata()` 删除一些不需要的模版文件。

![文件过滤](./imgs/final/7.png)

### 7. 模板渲染

将已过滤模版文件中内容含模版引擎语法的文件内容替换成 `render` 函数（对模版文件的变量占位符实现动态插值）的返回，其他文件保持不变。

![模板渲染](./imgs/final/8.png)

### 8. 输出提示信息

输出项目启动帮助信息。

## 技术选型

### 1. 使用语言：Node.js

### 2. 命令及参数解析：commander.js

* 将用户输入参数项解析成一个对象的属性
* 如，可直接通过 `program.clone` 知道模版的下载方式
* 简化了命令行开发

### 3. 路径存在判断：fs.existsSync(path)

### 4. 模板下载：download-git-repo
  * 下载并提取 git 仓库，用于下载项目模板
  * 支持 clone 以及 http 模式

### 5. 问题收集：inquirer.js
  * 用于与用户进行交互，接受一个问题对象的数组
  * 在用户与终端交互过程中，将用户的输入存放在一个答案对象
  * 支持多种类型的问题形式：confirm、数组、字符串、checkbox、password、editor等
  * 支持根据前面问题回答情况决定后面问题是否需要进行提问

### 6. 文件读取：metalsmith
  * 一个简单的静态网站生成器，可插拔。所有的逻辑都是由插件来处理的。以文件流的形式链接起来。
  * 工作原理：
    * 读取源目录中的所有文件。
    * 调用一系列操作文件的插件。可在处理文件时使用中间件来对模版进行处理
    * 将结果写入目标目录
  * 在本项目中：
    * 读取模版目录中所有文件
    * 调用自定义文件处理中间件：询问问题、过滤文件、渲染模版
    * 将结果写入目标目录

### 7. 过滤文件：minimatch
  * 文件匹配

### 8. 模板引擎：handlerbars
  * 模板文件中通过 handlerbars 引擎去处理与项目依赖等相关的模版文件。

### 9. 模板渲染：consolidate
  * 模板引擎结合体。
  * 在使⽤时，输⼊统⼀的模版数据对象及配置即使后期更换模版，也不必在主逻辑中修改，只需把引⼊的 render 函数改成其他模版引擎的 render 函数

### 10. 模版配置对象解析
  * 读取 json 文件：read-metadata
  * 检验项目名是否合法：validate-npm-package-name

### 11. 其他
  * 加载等待动画：ora
  * 高亮终端打印输出信息： chalk
