CodeGenius 灵活组合的 CLI 工具

🎄Hi~ 大家好,我是小鑫同学,资深 IT 从业者,InfoQ 的签约作者,擅长前端开发并在这一领域有多年的经验,致力于分享我在技术方面的见解和心得

CodeGenius 是我最近开发的 CLI 工具, 它与你在社区看到的其它 CLI 工具的本质区别是可以 灵活组合 , 这样你既可以为不同的项目组合不同的 CLI 功能, 也可以通过插件开发来替换掉已经过时或不兼容的功能, 灵活组合 对于 CLI 功能较多或项目间应用规则不一致的情况是一个良好的解决方案.

认识 CodeGenius

通过框架图可以了解到, 在 CodeGenius 的核心部分提供了 CLI 命令行注册注册的功能, 还内置了常用的 commit, scrript, fix 命令, 并提供了配置文件的支持, 对于其他额外的功能全部放到插件里面去做, 通过配置文件来进行插件的组合, 形成符合某一个或某一系列项目的 CLI 工具.

CLI 基础功能搭建

这里使用 cac 来作为 CLI 基础功能的搭建, 所有的 CLI 功能全部通过 setup 函数进行注册.

typescript 复制代码
import cac from "cac";

import { handleError, setup } from "@/helper";
import { plugins } from "@/index";

import pkg from "../package.json";

const setupCli = async () => {
  const cli = cac("codeg");

  await setup(cli, plugins);

  cli.help();
  cli.version(pkg.version);

  cli.parse(process.argv, { run: false });

  await cli.runMatchedCommand();
};

setupCli().catch(handleError);

由于内置命令和外置命令在自定义配置时略有差别, 所以在 setup 函数中内置的命令需要在此执行并传入自定义配置, rootInstaller 作为特殊的内置命令, 承接着所所有命令通过选项式执行的兜底功能.

typescript 复制代码
export async function setup(cli: CAC, plugins: BuiltInPlugins) {
  const uconfig = await loadConfigModule();
  const commands: Array<CommandOptions> = [];
  
  const builtInPluginsIns = plugins.map((fn) => fn(uconfig?.commands));

  for (const pluginIns of builtInPluginsIns.concat(uconfig?.plugins || [])) {
    if (pluginIns.command && pluginIns.describe) {
      commands.push({
        display: pluginIns.name || pluginIns.command,
        command: pluginIns.command,
        description: pluginIns.describe,
      });
    }
    pluginIns.setup(cli);
  }
  // 特殊处理
  rootInstaller(commands.concat(defaultCommands)).setup(cli);
}

自定义配置

自定义配置需要用到的一个关键技术就是 模块动态加载 , 目前仅支持加载 codeg.config.js, codeg.config.mjs 文件且必须是 ESM 模块, 如果需要加载更多的模块类型, 还需要额外的兼容.

typescript 复制代码
export async function loadConfigModule(): Promise<
  CodeGeniusOptions | undefined
> {
  let resolvedPath: string | undefined;
  for (const filename of DEFAULT_CONFIG_FILES) {
    const filePath = path.resolve(process.cwd(), filename);
    if (!fs.existsSync(filePath)) continue;
    resolvedPath = filePath;
    break;
  }

  if (!resolvedPath) return;
  const moduleURL = pathToFileURL(resolvedPath).href;
  return (await import(moduleURL)).default;
}

同样的我也为自定义配置添加了 defineConfig 函数, 在配置时可以减少一些不必要的麻烦.

typescript 复制代码
export interface CodeGeniusOptions {
  commands?: CommandsOptions;
  plugins?: Plugins;
}

export const defineConfig = (config: CodeGeniusOptions): CodeGeniusOptions =>
  config;

script 命令

script 命令的主要功能是, 代理运行 package.scripts 脚本, 用于项目有大量 scripts 的情况, 可以生成单独的配置文件用来描述每个 script 的作用, 并通过询问模式来执行对应的 script.

typescript 复制代码
export default function scriptRunInstaller() {
  return {
    name: "script",
    describe: "代理运行 package.scripts 脚本",
    command: "script",
    setup: (cli: CAC) => {
      cli
        .command("script", "代理运行 package.scripts 脚本")
        .action(async () => {
          await generateScripts();
          await scriptRun();
        });
    },
  };
}

CodeGenius 插件开发

verify 命令是用来检查 GitCommit 时所编写的描述是否符合规范的功能, 现在我通过 verify 示例来演示 CodeGenius 的插件开发.

注册函数

gitCommitVerifyInstaller 函数返回的对象中, name, describe, command, 都是为 root 命令在提供数据, 在这 root 兜底功能中很有必要.

setup 函数中通过调用 cacAPI 来实现 verify 命令的注册, 并在 action 中调用功能函数.

typescript 复制代码
const gitCommitVerifyInstaller = () => {
  return {
    name: "verify",
    describe: "校验 COMMIT_EDITMSG 中的信息是否符合 Angualr 规范",
    command: "verify",
    setup: (cli: CAC) => {
      cli
        .command("verify", "校验 COMMIT_EDITMSG 中的信息是否符合 Angualr 规范")
        .action(async () => {
          await gitCommitVerify();
        });
    },
  };
};

功能函数

COMMIT_EDITMSG 是使用 git commit 命令时生成用于存储 git 提交信息的临时文件, 所以读取和校验就变得很简单了.

typescript 复制代码
const gitCommitVerify = async () => {
  const dogit = await execCommand("git", ["rev-parse", "--show-toplevel"]);
  const root = path.join(dogit || "", ".git", "COMMIT_EDITMSG");

  const content = readFileSync(root, { encoding: "utf-8" }).trim();

  const REG_EXP =
    /(?<type>[a-z]+)(\((?<scope>.+)\))?(?<breaking>!)?: (?<description>.+)/i;
  if (!REG_EXP.test(content)) {
    printError(
      "Git 提交信息不符合 Angualr 规范, 建议运行 `npx code-genius commit` 生成规范信息",
    );
    process.exit(1);
  } else {
    printInfo("Git 提交信息校验通过");
  }
};

接着导出功能函数和注册函数, 以便插件注册和 API 方式调用.

typescript 复制代码
export { gitCommitVerify, gitCommitVerifyInstaller };

CodeGenius 插件使用

在你所需要的用到 verify 命令的项目中安装 code-geniu@codegenius/verify-plugin, 并在项目根目录增夹 codeg.config.mjs 文件, 下面是配置的具体内容.

typescript 复制代码
import { defineConfig } from "code-genius";
import { gitCommitVerifyInstaller } from "@codegenius/verify-plugin";

export default defineConfig({
  plugins: [
    gitCommitVerifyInstaller(),
  ],
});

配置完成后你就可以通过 codeg 来列出所有已注册命令的选项, 当然你也可以通过 codeg -h 来查看所有已注册的命令.

CodeGenius 已有插件

因目前仍集中在调教插件化的核心功能, 上面的插件仅作为参考使用, 更多细节可以在 github.com/FE-CodeGeni... 查看.

相关推荐
编程零零七2 小时前
Python数据分析工具(三):pymssql的用法
开发语言·前端·数据库·python·oracle·数据分析·pymssql
(⊙o⊙)~哦4 小时前
JavaScript substring() 方法
前端
无心使然云中漫步5 小时前
GIS OGC之WMTS地图服务,通过Capabilities XML描述文档,获取matrixIds,origin,计算resolutions
前端·javascript
Bug缔造者5 小时前
Element-ui el-table 全局表格排序
前端·javascript·vue.js
xnian_5 小时前
解决ruoyi-vue-pro-master框架引入报错,启动报错问题
前端·javascript·vue.js
麒麟而非淇淋6 小时前
AJAX 入门 day1
前端·javascript·ajax
2401_858120536 小时前
深入理解MATLAB中的事件处理机制
前端·javascript·matlab
阿树梢6 小时前
【Vue】VueRouter路由
前端·javascript·vue.js
随笔写8 小时前
vue使用关于speak-tss插件的详细介绍
前端·javascript·vue.js
史努比.8 小时前
redis群集三种模式:主从复制、哨兵、集群
前端·bootstrap·html