命令行创建模板 / 脚手架 / 通用能力

140 阅读3分钟

需求描述

团队有一个后端模板项目,现在要求可以通过命令行拷贝这个项目本地,并且可以配置一些信息,具体要求如下:

  1. 命令行拷贝模板项目
  2. 可以配置git地址
  3. 可以新建git分支

功能设计

经过技术的调研,参考行业的做法,最终得出如下的功能设计:

  1. 通过命令行选择(list的方式)模板地址,并且可以选择手动输入模板地址。
  2. 通过命令行询问(input的方式)模板分支,后面会复制该分支的代码,配置默认值是main(这个根据每个团队的代码习惯修改)。
  3. 通过命令行询问(input的方式)文件夹,用来存放拷贝下来的模板,默认是当前根文件夹。
  4. 通过命令行询问(input的方式)新项目的git地址,目标项目git地址,可以为空,方便后续自定义配置。
  5. 通过命令行询问(input的方式)新项目的git分支,目标项目git分支,可以为空,方便后续自定义配置。
  6. clone模板到当前文件夹
  7. 将clone下来的模板复制到指定文件夹,并且删除多余的文件
  8. 修改git地址,并且新建分支

功能实现

询问方式的具体实现

利用inquirer库去进行选择,具体代码如下

import inquirer from "inquirer";

export default async () => {
  const choices = [
    {
      name: "后端管理系统",
      value:
        "http://gitlab.xxxxxxxx",
    },
    {
      name: "手动输入模版地址",
      value: "",
    },
  ];
  return inquirer.prompt([
    {
      type: "list",
      name: "remoteUrl",
      choices,
      message: "请输入项目新建分支",
    },
  ]);
};

当判断选择的模板为空的时候,可以使用手动输入的方式

import selectTemplate from "../questions/selectTemplate.js";
import gitTemplateRemoteUrl from "../questions/remoteUrl.js";
let template = await selectTemplate();
// console.log(template);
if (!template.remoteUrl) {
  template = await gitTemplateRemoteUrl(templatesDirRootPath);
}
import fs from "fs";
import inquirer from "inquirer";
export default async (templatesDirRootPath) => {
  let remoteUrl =
    "http://gitlab.xxxxxx";
  if (fs.existsSync(`${templatesDirRootPath}/defaultRemoteUrl.txt`)) {
    remoteUrl = fs.readFileSync(
      `${templatesDirRootPath}/defaultRemoteUrl.txt`,
      "utf-8"
    );
  }
  return inquirer.prompt([
    {
      type: "input",
      name: "remoteUrl",
      default: remoteUrl || undefined,
      message: "请设置模板仓库地址",
      validate(val) {
        // git仓库的正则表达式 http://cn.voidcc.com/question/p-qlprjeax-kd.html
        const gitRemoteUrlReg =
          /(\w+:\/\/)([email protected])*([\w\d\.]+)(:[\d]+){0,1}\/*(.*)/;
        if (!val) {
          return true;
        } else if (!gitRemoteUrlReg.test(val)) {
          return "远程仓库地址格式错误,请重新输入";
        } else {
          return true;
        }
      },
    },
  ]);
};


其他的询问方式跟上面类似,只需要改个namemessage

根据获取的信息进行git操作

//当前目录
const templatesDirRootPath = `/${processCwdArr[1]}/${processCwdArr[2]}`;
getGitRemoteResult = execaSync(
    `git`,
    ["clone", "-b", config.branch, config.remoteUrl],
    {
       cwd: templatesDirRootPath,
       stdio: [2, 2, 2], // 使子进程的输入输出流继承父进程,在当前父进程显示子进程的输入与输出
    }
);

进行复制文件到指定文件夹,并且删除模板的.git

     fs.rmSync(
        `${templatesDirRootPath}/${gitRemoteFilename}/.git`,
        { recursive: true, force: true },
        () => {}
      );
      const url = `${templatesDirRootPath}/${gitRemoteFilename}`;
      if (gitRemoteFilename == config.createDir) {
        setGit();
      } else {
        fse.copy(`${url}`, `./${config.createDir}`, (err) => {
          if (err) {
            console.error(err);
          } else {
            console.log("删除多余文件夹", url);
            fs.rm(`${url}`, { recursive: true, force: true }, () => {});
            setGit();

            console.log(chalk.green("创建模块成功!"));
          }
        });
      }

根据配置重新设置git信息

execaSync(`git`, ["init", "-b", config.gitBranch || config.branch], {
    cwd: `${templatesDirRootPath}/${config.createDir}`,
    stdio: [2, 2, 2], // 使子进程的输入输出流继承父进程,在当前父进程显示子进程的输入与输出
});
// execaSync(`git`, ["remote", "set-url", "origin", config.gitUrl]);
execaSync(`git`, ["remote", "add", "origin", config.gitUrl], {
    cwd: `${templatesDirRootPath}/${config.createDir}`,
    stdio: [2, 2, 2], // 使子进程的输入输出流继承父进程,在当前父进程显示子进程的输入与输出
});

execaSync(`git`, ["fetch"], {
    cwd: `${templatesDirRootPath}/${config.createDir}`,
    stdio: [2, 2, 2], // 使子进程的输入输出流继承父进程,在当前父进程显示子进程的输入与输出
});
console.log(chalk.green("同步远程仓库成功!"));

总结

这个需求没多复杂的操作,无非就是询问信息,获取信息,利用git下载模板,复制模板到指定文件夹,配置git的信息。发这篇文章跟大家一起学习。