使用node编写简易版脚手架

现在很多框架例如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仓库的URL
  • project 是要保存到本地的目录名。

函数中创建了一个命令行等待工具 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框架的代码模版下载到项目根目录当中去

相关推荐
也无晴也无风雨37 分钟前
深入剖析输入URL按下回车,浏览器做了什么
前端·后端·计算机网络
Martin -Tang1 小时前
Vue 3 中,ref 和 reactive的区别
前端·javascript·vue.js
FakeOccupational3 小时前
nodejs 020: React语法规则 props和state
前端·javascript·react.js
放逐者-保持本心,方可放逐3 小时前
react 组件应用
开发语言·前端·javascript·react.js·前端框架
曹天骄4 小时前
next中服务端组件共享接口数据
前端·javascript·react.js
阮少年、4 小时前
java后台生成模拟聊天截图并返回给前端
java·开发语言·前端
郝晨妤5 小时前
鸿蒙ArkTS和TS有什么区别?
前端·javascript·typescript·鸿蒙
AvatarGiser6 小时前
《ElementPlus 与 ElementUI 差异集合》Icon 图标 More 差异说明
前端·vue.js·elementui
喝旺仔la6 小时前
vue的样式知识点
前端·javascript·vue.js
别忘了微笑_cuicui6 小时前
elementUI中2个日期组件实现开始时间、结束时间(禁用日期面板、控制开始时间不能超过结束时间的时分秒)实现方案
前端·javascript·elementui