1.0.1 • Published 9 months ago

cc-cli-app v1.0.1

Weekly downloads
-
License
ISC
Repository
-
Last release
9 months ago

日常开发中,你们是怎么样来统一自己的前端基础架构的呢?目前我们公司还是在 gitlab 上建一个基础的模板仓库,然后再 clone 到本地进行开发,这样的话仓库可能要关联多个 git 远程,万一操作失误可就难办了,而且也没有办法做一些定制化的设定。

接下来,几分钟让你掌握打造一个类似于 vue-cli、create-react-app 的脚手架工具,每次开发新项目直接 init 一下就好啦!

1. 项目初始化

首先,创建文件夹,包名自定义,这里我新建文件夹 cc-cli-app

cd cc-cli-app
pnpm init

init 后根目录创建包 bin,新建文件 index.js

修改 package.json 文件,添加

{
    ...,
    "type": "module",
    "bin":{
        //这里的cc-cli可以换成任何你自己想设定的npm命令 注意别和其他冲突即可
        "cc-cli": "./bin/index.js"
    }
}

2. 安装相关插件

这里我们直接一次性安装所有需要的包

pnpm add command-line-args command-line-usage chalk prompts ora download-git-repo

3. 编写脚本

首先,我们先定义执行环境,以及 npm link 下方便调试

在 bin 下的 index.js 输入如下

#! /usr/bin/env node

console.log("hello cli");

终端执行

npm link

之后我们在终端输入 cc-cli,会发现 hello cli 输出出来了

(这里的 cc-cli 是你在 package.json 中定义的指令,后续不再重复)

image.png

在 bin/index.js 引入之前安装的包

import commandLineArgs from "command-line-args";
import commandLineUsage from "command-line-usage";
import chalk from "chalk";
import prompts from "prompts";
import ora from "ora";
import download from "download-git-repo";
import fs from "fs";

接下来,一个个告诉你这些作用都是什么

commandLineArgs

通过预设一些参数,让我们能正常的接收到期望得到的数据进行后续处理

const argOptions = [
  { name: "version", alias: "v", type: Boolean },
  { name: "name", type: String },
  { name: "age", type: Number },
];

console.log(commandLineArgs(argOptions));

这时,执行的命令后面加上 --name 参数

若在预设的 options 内,则会正确打印,否则抛出错误 image.png

commandLineUsage

commandLineUsage 可以添加帮助说明,让其他不熟悉我们脚手架的小伙伴尽快上手。 这里我们继续修改 bin/index.js

const argOptions = [
  { name: "help", alias: "h", type: Boolean },
  { name: "version", alias: "v", type: Boolean },
  { name: "name", type: String },
  { name: "age", type: Number },
];

const helpSections = [
  {
    header: "cc-cli",
    content: "一个快速生成开发环境的脚手架",
  },
  {
    header: "Options",
    optionList: [
      {
        name: "version",
        typeLabel: "{underline boolean}",
        description: "版本号",
      },
      {
        name: "name",
        typeLabel: "{underline string}",
        description: "姓名",
      },
      {
        name: "age",
        typeLabel: "{underline number}",
        description: "年龄",
      },
    ],
  },
];

const options = commandLineArgs(argOptions);

if (options.help) {
  console.log(commandLineUsage(helpSections));
}

image.png

关键内容

剩下的几个插件这里简单介绍下,接下来在代码中会更好地体现 chalk:自定义终端输出文本的颜色,比如 log 的内容包上 chalk.green(xxx),输出的则是绿色的文本啦 prompts:可以对我们脚手架做一些自定义的设置,类似 vue-cli 初始化时设置是否需要 ts、router 这样 download-git-repo: 下载远程 git 仓库到本地 ora:在下载时提供 loading 展示

这里,我们首先配置下在初始化项目时需要进行哪些设定

// 更多type设置可去prompts文档查看
const promptsOptions = [
  {
    type: "text", // 输入
    name: "name",
    message: "项目名称",
    validate(val) {
      if (!val) return "模板名称不能为空!";
      if (fs.existsSync(val)) return "项目名已存在";
      if (val.match(/[^A-Za-z0-9\u4e00-\u9fa5_-]/g))
        return "模板名称包含非法字符,请重新输入";
      return true;
    },
  },

  {
    type: "select", // 单选
    name: "template",
    message: "选择来源",
    choices: [
      { title: "gitee", value: 1 },
      { title: "github", value: 2 },
    ],
  },
];

const getInputInfo = async () => {
  const res = await prompts(promptsOptions);
  console.log(res);
};

getInputInfo();

打印输入的结果返回

image.png

那么接下来,我们可以根据返回的结果做一些自定义的处理 比如说下载远程仓库

首先编写 gitClone 函数

const gitClone = (remote, name, option) => {
  // 这里通过ora开启loading状态
  const downSpinner = ora("正在下载模板...").start();
  return new Promise((resolve, reject) => {
    download(remote, name, option, (err) => {
      if (err) {
        downSpinner.fail();
        console.log("err", chalk.red(err));
        reject(err);
        return;
      }
      // 下载完成后关闭loading,chalk改变提示文本颜色
      downSpinner.succeed(chalk.green("模板下载成功!"));
      console.log(`Done. Now run:\r\n`);
      console.log(chalk.green(`cd ${name}`));
      console.log(chalk.blue("npm install"));
      console.log("npm run dev\r\n");
      resolve();
    });
  });
};

设置远程仓库地址以及分支

const remoteList = {
  1: "https://gitee.com/theGreatWallCCG/vue3-template-cli.git",
  2: "https://github.com/BenjaminCCG/vue3-template-cli.git",
};
const branch = "main";

修改 getInputInfo 函数

const getInputInfo = async () => {
  const res = await prompts(promptsOptions);
  if (!res.name || !res.template) return;
  gitClone(`direct:${remoteList[res.template]}#${branch}`, res.name, {
    clone: true,
  });
};

此时新建个目录,终端执行 cc-cli

image.png

大功告成!!!

发布 npm

接下来终端执行 npm login 输入自己的 npm 账号密码,没有的自行注册

然后执行 npm publish 进行发布

image.png

image.png

这样你的小伙伴就可以执行 npm i cc-cli-app -g 去使用你开发的脚手架啦

完整代码

#! /usr/bin/env node
import commandLineArgs from "command-line-args";
import commandLineUsage from "command-line-usage";
import chalk from "chalk";
import prompts from "prompts";
import ora from "ora";
import download from "download-git-repo";
import fs from "fs";

const argOptions = [
  { name: "help", alias: "h", type: Boolean },
  { name: "version", alias: "v", type: Boolean },
  { name: "init", type: Boolean },
];

const helpSections = [
  {
    header: "cc-cli",
    content: "一个快速生成开发环境的脚手架",
  },
  {
    header: "Options",
    optionList: [
      {
        name: "version",
        typeLabel: "{underline boolean}",
        description: "版本号",
      },
      {
        name: "init",
        typeLabel: "{underline boolean}",
        description: "初始化",
      },
    ],
  },
];

const options = commandLineArgs(argOptions);

if (options.help) {
  console.log(commandLineUsage(helpSections));
}

const promptsOptions = [
  {
    type: "text", // 输入
    name: "name",
    message: "项目名称",
    validate(val) {
      if (!val) return "模板名称不能为空!";
      if (fs.existsSync(val)) return "项目名已存在";
      if (val.match(/[^A-Za-z0-9\u4e00-\u9fa5_-]/g))
        return "模板名称包含非法字符,请重新输入";
      return true;
    },
  },

  {
    type: "select", // 单选
    name: "template",
    message: "选择来源",
    choices: [
      { title: "gitee", value: 1 },
      { title: "github", value: 2 },
    ],
  },
];

const gitClone = (remote, name, option) => {
  const downSpinner = ora("正在下载模板...").start();
  return new Promise((resolve, reject) => {
    download(remote, name, option, (err) => {
      if (err) {
        downSpinner.fail();
        console.log("err", chalk.red(err));
        reject(err);
        return;
      }
      downSpinner.succeed(chalk.green("模板下载成功!"));
      console.log(`Done. Now run:\r\n`);
      console.log(chalk.green(`cd ${name}`));
      console.log(chalk.blue("npm install"));
      console.log("npm run dev\r\n");
      resolve();
    });
  });
};

const remoteList = {
  1: "https://gitee.com/theGreatWallCCG/vue3-template-cli.git",
  2: "https://github.com/BenjaminCCG/vue3-template-cli.git",
};
const branch = "main";

const getInputInfo = async () => {
  const res = await prompts(promptsOptions);
  if (!res.name || !res.template) return;
  gitClone(`direct:${remoteList[res.template]}#${branch}`, res.name, {
    clone: true,
  });
};

getInputInfo();