现在很多框架例如Vue,React等都提供了脚手架,作者在学习node的过程中尝试编写一个简单版的脚手架,来实践自己所学习到的一些知识。
一个完整的脚手架大致需要以下几个部分:
- 创建自定义命令
- 命令行交互功能
- 下载代码模版
- 命令行执行任务时的等待效果
- 命令行输出内容样式优化
如果能把上述几个点的内容都串起来,那基本就是一个比较简单的脚手架功能了。
1.初始化项目
创建新文件夹,并初始化npm项目
bash
mkdir 04_scaffold
cd 04_scaffold
npm init -y
2.创建脚手架入口文件
在项目文件夹中创建一个入口文件,例如 cli.js。这个文件将会作为脚手架的入口点,处理用户的命令行输入。
入口文件可以如下: #!/usr/bin/env node
是一个Shebang行,用于告诉操作系统使用Node.js来执行脚本。
javascript
#!/usr/bin/env node
console.log('My CLI is working!');
除了创建入口文件外,需要在当前自定义的npm项目中的package.json 文件中添加一个 "bin" 字段。这样可以将该文件设置成一个可执行命令。
换句话来说,bin" 字段允许将项目中的某个脚本文件链接到一个命令名上,使得在终端中可以直接运行这个命令名。
json
"bin": {
"mycli": "bin/cli.js"
},
设置完之后,可以在命令行中直接运行 mycli,实际上执行的就是 bin/cli.js
中的逻辑。
3.实现自定义命令
这里需要用到commander模块来构建自定义命令,以达到在终端输入类似mycli create xxx
就可以创建脚手架的功能。
这里我们采用commander模块的command() 方法来定义命令,具体用法如下:
javascript
program
.command("create <project> [other...]")
.description("创建项目")
.action((project, args) => {
console.log(`创建${project}`);
console.log(args);
});
可以看到,.action((project, args) => { /* 命令行的执行逻辑代码 */ })
定义了命令 create 被触发时的执行逻辑。所以创建脚手架的后续过程作者将会在action内部进行实现,不过此处我们还是专注于如何创建自定义命令。
此处可以看到我们创建了一个create自定义命令,并添加了命令的描述,action中包含运行create命令后执行的逻辑,此处先不展开。
javascript
#! /usr/bin/env node
const { program } = require("commander");
const myaction = require("../lib/core/action");
// option用于定义命令行选项的方法
program
.option("-f --framework <framework>", "设置框架")
.option("-d --debug", "开启调试");
program
.command("create <project> [other...]")
.description("创建项目")
.action(myaction);
// parse用于解析命令行参数
program.parse(process.argv);
在此处,如果在终端中执行命令mycli
,可以看到如下提示:按照提示,我们就可以通过create命令来创建脚手架了
4.实现命令行问答交互功能
在使用vue的脚手架创建项目时,会询问多个问题让用户回答,以便根据用户需求创建不同的脚手架。作者在文中使用inquirer模块
来实现类似的功能。
通过使用inquirer.prompt可以向其中插入多个type的问题,每个问题按照顺序依次展示。如下:
具体的脚手架构建逻辑我们都编写在myAction.js文件当中,在下面位置,我们设置了一个问题,用于让用户选择创建egg框架 or express框架
。具体的选项通过choices
提供。
在这里作者为了程序的可扩展性,将可供选择的框架,以及对应的仓库地址,编写在了config配置文件中
java
module.exports = {
// 可供选择的框架
framework: ["egg", "express"],
frameToUrl: {
egg: "direct:https://gitee.com/wei_hao_yang/egg.git",
express: "direct:https://gitee.com/wei_hao_yang/express.git",
},
};
下列为myAction.js
中的具体逻辑。可以看到会执行命令行交互过程,并将用户的选择返还给answer
ini
const inquirer = require("inquirer");
const config = require("../../config");
const myAction = async (project, args) => {
const answer = await inquirer.prompt([
{
type: "list",
name: "framework",
choices: config.framework,
message: "请选择所需框架",
},
]);
};
在完成问答交互后,作者就可以得知用户想要创建哪种框架,并对应进行代码模版的下载。
5.下载代码模版
上一部分提到,在完成命令行交互后,我们会得到用户想要创建的具体框架的信息。得到具体信息后就可以为用户在项目路径中下载具体的代码模版。
这一部分功能采用了download-git-repo
这一模块来实现代码模版的下载。为了保证下载速度,作者将代码模版上载到gitee上进行托管,方便国内用户进行使用。
因此需要根据文档来采用非github仓库到方式进行下载,具体用法如下
javascript
const download = require("download-git-repo");
download(
"direct:https://gitee.com/wei_hao_yang/egg.git",
"./cms",
{ clone: true },
function (err) {
console.log(err);
}
);
根据上述案例,就可以根据不同的框架名称来下载代码模版
6.设置命令行执行任务时的等待效果以及命令行样式
可以采用命令行任务等待工具Ora来设置下载代码模版时的一些运行信息,以下代码是download.js
的具体逻辑实现,我们将下载代码模版封装为一个函数,并之后在action.js
中进行执行。
download-git-repo 用于从Git仓库下载项目。ora 用于在命令行中显示加载动画。chalk 用于在命令行中添加颜色样式。
ini
const download = require("download-git-repo");
const ora = require("ora");
const chalk = require("chalk");
downloadFn 是一个函数,接收两个参数:
url
是要下载的Git仓库的URLproject
是要保存到本地的目录名。
函数中创建了一个命令行等待工具 spinner 并显示 "downloading..." 文本,表示正在下载中。
使用 download-git-repo 库下载指定的Git仓库到本地。如果下载成功,显示成功信息和启动项目的命令。如果下载失败,显示失败信息。
ini
const downloadFn = (url, project) => {
// 设置命令行等待工具的对象
const spinner = ora().start();
spinner.text = "downloading...";
download(url, project, { clone: true }, (err) => {
if (!err) {
spinner.succeed(`download successful!`);
console.log(chalk.blue.bold("run follow commands to start the project"));
console.log(chalk.green("cd " + project));
console.log(chalk.green("npm install"));
console.log(chalk.green("npm run dev"));
} else {
spinner.fail("download fail");
}
});
};
module.exports = downloadFn;
在编写完具体的下载代码模版逻辑后,在action.js
中进行引用。行文至此,已经基本实现了一个简易版的脚手架工具。
ini
const inquirer = require("inquirer");
const config = require("../../config");
const downloadFn = require("../core/download");
const myAction = async (project, args) => {
const answer = await inquirer.prompt([
{
type: "list",
name: "framework",
choices: config.framework,
message: "请选择所需框架",
},
]);
// 下载代码模块
downloadFn(config.frameToUrl[answer.framework], project);
};
module.exports = myAction;
此时运行命令 mycli create expressProject
,就可以成功将express框架的代码模版下载到项目根目录当中去