2023年,你该这样搭建脚手架!

脚手架构建一般流程:

  • 构建命令
  • QA 过程
  • 下载模版
  • 安装依赖

下面介绍一种更加轻量简洁的方式搭建脚手架,看下最终效果(完整代码见文末):

如果大家只想体验的话,可以直接输入以下命令、查看详细文档了解更多用法:

npm install -g @szmxx/ucli

接下来我将详细介绍内部实现

构建命令

相信大家都是使用 commander 来构建命令的,这里我使用一个更简洁的工具 citty,先给大家来个栗子:

ts 复制代码
import { defineCommand } from "citty";
export default defineCommand({
  meta: {
    name: "create",
    description: "create a new project",
  },
  args: {
    name: {
      type: "positional",
      description: "<project-name>",
      required: true,
    },
  },
  async run({ args }) {
    console.log(args.name)
  }
});

非常简洁的实现了 create 命令,当然你可以把它作为一个子命令,接下来我将详细介绍。

定义主命令

相信大家都用过脚手架工具,比如在 vue 中创建模版工程,只需要输入 vue create <project-name> 就可以启动创建项目的流程,其实这里有三个概念:

  • 主命令
  • 子命令
  • 位置参数

vue 就是主命令,create 是子命令,位置参数就是我们输入的 project-name

接下来我们使用 citty 来创建脚手架的主命令

ts 复制代码
import { defineCommand, runMain } from "citty";
import { name, version, description } from "../package.json";
function start() {
  try {
    const main = defineCommand({
      meta: {
        name: name,
        version: version,
        description: description,
      },
      run({ args }) {
          consola.log(
            figlet.textSync(name, {
              font: "Star Wars",
            })
          );
      },
    });
    runMain(main);
  } catch (error) {
    consola.error(error);
  }
}
start();

这样就定义好主命令了,如果我们指定 package.jsonbin 链接到我们这个代码,再通过 npm link 托管到全局,那就可以直接在 命令行输入 ucli,则会打印出对应的像素化信息。这里使用 figlet 来打印像素化信息

定义子命令

如果我们想要实现 ucli create 的效果,那么需要在主命令中定义子命令

定义子命令很简单,只需要在主命令中声明一个 subCommands 字段

ts 复制代码
  const main = defineCommand({
      meta: {
        name: name,
        version: version,
        description: description,
      },
      subCommands: {
        create: () => import("./commands/create").then((r) => r.default),
      },
      run({ args }) {
          consola.log(
            figlet.textSync(name, {
              font: "Star Wars",
            })
          );
      }
    });

子命令本身定义和主命令是一致的

ts 复制代码
export default defineCommand({
  meta: {
    name: "create",
    description: "create a new project",
  },
  args: {
    name: {
      type: "positional",
      description: "<project-name>",
      required: true,
    },
  },
  run({ args }) {
    consola.log(args.name)
  },
});

这里定义了一个位置参数 name,这样我们就实现了 ucli create <project-name>,在 run 中我们就可以实现 QA 过程了

QA 过程

QA 过程直接采用 inquirer 来实现,我们是动态 QA,所以使用额外使用 rxjs

ts 复制代码
import inquirer from "inquirer";
import { Subject } from "rxjs";
import { TemplateList } from "../config";
export function create(){
  return new Promise<Record<string, unknown>>((resolve) => {
    const prompts = new Subject();
    const result: Record<string, unknown> = {};
    inquirer
      .prompt(prompts as any)
      .ui.process.subscribe((res: Record<string, string>) => {
        result[res.name] = res.answer;
      });

    prompts.next({
      type: "list",
      name: "template",
      choices: TemplateList,
      message: "请选择一个模版",
    });
  });

下载模版

在国内,模版主要来源 githubgitlab以及企业私有部署的代码仓库,我们之前一直使用的是download-git-repo,这里我介绍一种新的方式:giget,它非常方便的实现了模版的下载,并且支持自定义来源。举个简单的栗子:

ts 复制代码
import { downloadTemplate } from "giget";
const {
  username = "szmxx",
  provider = "github",
  name,
  dirName = "",
  auth = "",
  refName = "main"
} = conf
const { dir } = await downloadTemplate(
  `${provider}:${username}/${name}#${refName}`,
  {
    auth: auth,
    dir: dirName,
  }
);

这里我稍微解释下(以这个链接): github.com/szmxx/ucli

  • provider
    仓库的来源,比如 github
  • username
    仓库所属:szmxx
  • name
    仓库名称:ucli
  • refName
    代码分支,默认 main
  • auth
    如果是 github,那么则是你的 Personal access tokens
  • dir
    输出的目录

那么我们根据前面的 QA 结果,生成对应的下载参数,直接调用下载即可

创建远程仓库

使用 github openapi 我们可以得到动态操作 github 的能力,其他平台也是如此,那么在下载完代码之后,我们需要创建对应的远程仓库

ts 复制代码
export async function createRepo(data: Record<string, unknown>, auth: string) {
  return fetch(`${baseURL}/user/repos`, {
    method: "POST",
    body: JSON.stringify(data),
    headers: {
      authorization: `Bearer ${auth}`,
    },
  });
}

const res = await createRepo(
  {
    name: pkg.name,
    description: pkg.description,
    homepage: pkg.homepage,
    private: pkg.private,
    license_template: pkg.license,
  },
  auth
);

其他

当然还有一些额外操作,比如 gitpnpm install等,我们可以使用 execa 来执行相应的命令:

ts 复制代码
import { $ } from "execa";

const sshURL = '';
await $`git init`;
// 切换 main 分支
await $`git checkout -b main`;
// 初始化远程仓库
await $`git remote add origin ${sshURL}`;

完整体验

ucli 基础版目前已经开源了,大家可以使用 npm install -g @szmxx/ucli 来安装体验。

完整代码见 github

详细使用见 npm

相关推荐
哟哟耶耶几秒前
component-Echarts圆环数据展示-延长线,label,鼠标移动提示,圆环数据中心总数,信息面板
前端·javascript·echarts
全栈软件开发1 分钟前
Fidelity充电桩投资理财系统源码-前端uniapp纯源码+后端PHP
前端·uni-app·php
程序员刘禹锡2 分钟前
文档流与盒子模型 (12.25日)
前端·css·css3
plmm烟酒僧5 分钟前
使用 OpenVINO 本地部署 DeepSeek-R1 量化大模型(第二章:前端交互与后端服务)
前端·人工智能·大模型·intel·openvino·端侧部署·deepseek
Rhys..6 分钟前
js-三元运算符
前端·javascript·数据库
倔强的石头1067 分钟前
金仓数据库 MongoDB 兼容:多模融合下的架构之道与实战体验
数据库·mongodb·架构·kingbase
不是,你来真的啊?7 分钟前
Vue3响应式原理(源码)【reactive,ref,computed】
前端·vue·源码
snow@li10 分钟前
前端:拖动悬浮小窗
开发语言·前端·javascript
2301_7965125210 分钟前
ModelEngine平台创建知识库体系 ,帮助“前端职业导航师”定制化私域知识累积
前端·modelengine
鹏程十八少12 分钟前
Android ANR项目实战:Reason: Broadcast { act=android.intent.action.TIME_TICK}
android·前端·人工智能