跳转到内容

UnoAPI工具篇:从 0 到 1 实现“自动化编码”

上一篇我们已经从“理论上”进行了设计,对 UnoAPI 的功能、原理、使用等方面都有了一个大概的雏形。今天,我将与大家一起从代码层面进行从 0 到 1 的实现。

这是一个系列文章,为了更好地理解文章内容,建议你先去了解一下前面的篇章。

废话不多说,马上开始。

初始化项目

首先回顾一下项目的层级:

  • 核心层:@unoapi/core
  • cli 层:@unoapi/cli
  • vscode 层:@unoapi/vscode-extension

为了方便管理,我们的项目需要使用 Monorepo 结构,将这 3 个模块放在一个项目里进行集中管理:

css
unoapi/
├── package.json
├── packages/
│   ├── core/
│   │   ├── package.json
│   │   └── src/
│   ├── cli/
│   │   ├── package.json
│   │   └── src/
│   └── vscode-extension/
│       ├── package.json
│       └── src/

集中管理有一些好处:

  • 不需要在多个项目之间来回切换
  • 简化开发时的相互依赖关系
  • 环境共享、代码共享,减少重复开发工作

项目中的corecli两个包,最终是需要发布到 npm 仓库的,vscode-extension 则需要独立打包。

编码思维

从 0 到 1 的开发,最难的是从哪里入手。

虽然我们对需求有了大概的了解,但并没有明确的需求方和产品原型,这跟我们在公司上班写代码可能有点不一样。

我个人习惯的开发方式是:从使用者的角度逐步往前推进。这样写代码的思路会更清晰一些,“使用方”需要什么功能,我们就“往前”去实现什么功能。

因此,我将从cli模块入手进行推进。

这个系列的文章都将与代码同步更新,我现在也不知道最后会做成怎样。希望大家多多支持,我会尽量加快更新进度,项目完成后也会放出完整源码与大家学习和交流

CLI 命令行实现

1. 安装 commander 包

这里我们需要用到commander这个库,用来定义 CLI 命令行接口。

因为只有cli模块使用到,所以需要进入到packages/cli目录下进行安装:

bash
pnpm add commander

此项目将使用 pnpm 作为包管理工具,并且使用 TypeScript 进行编码。

2. 实现命令行代码

上一篇我们已经列出来了两个核心命令行:

  • ✅️ uno init
  • ✅️ uno api url1 url2... -o src/api --func getList

所以,我们根据这两条命令来实现具体的代码。

packages/cli/src目录下创建cli.ts:

ts
#!/usr/bin/env node
import { Command } from 'commander';

const program = new Command();

// init 初始化配置文件
program
  .command('init')
  .option('--openapi-url [openapiUrl]', 'OpenAPI URL 地址')
  .description('初始化UnoAPI配置文件')
  .action((options) => {
    console.log('生成配置文件 unoapi.config.js');
    console.log('options:', options);
  });

// api 生成API代码
program
  .command('api', { isDefault: true })
  .argument('[urls...]', '接口 URL,可以是多个,用空格分隔')
  .description('生成 API 代码')
  .option('-o, --output [output]', '输出目录', 'src/api')
  .option('--func [functionName]', '自定义 API 函数名称')
  .action((urls, options) => {
    console.log('生成API代码');
    console.log('urls:', urls);
    console.log('options:', options);
  });

program.parse(process.argv);

代码中,我们将api指令作为默认命令,参数urls作为不固定参数,并且可选。

  • 考虑到uno api是我们最核心命令,作为默认命令可以省略api指令使用;
  • 支持一次性传一个或多个 URL,如果不传,则生成所有的 API 接口。

代码第一行的#!/usr/bin/env node很重要,它可以将我们的文件变成可执行文件,不需要使用node cli.js这样执行。

3. 配置 uno 命令

packages/cli目录下的package.json文件中,我们可以指定“uno”作为主命令,并且指定包名@unoapi/cli

json
{
  "name": "@unoapi/cli",
  "bin": {
    "uno": "dist/cli.js"
  },
}

这样,别人安装这个依赖包之后,就可以使用uno这个命令了。

我们可以看到,uno指向了dist/cli,js。所以我们需要将src/cli.ts文件编译到dist目录下。

4. 配置 TypeScript

因为我们的项目使用了 Monorepo 结构,所以要尽可能的统一进行管理和复用。

回到unoapi的主目录,创建tsconfig.json文件:

json
{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    "moduleResolution": "node",
    "strict": true,
    "outDir": "dist"
  },
  "include": ["src", "packages/**/src"],
  "exclude": ["node_modules"]
}

先写一些最基本的配置,后续有需要我们再进行增加和修改。

include配置的"packages/**/src"可以对 3 个子项目生效,这样就不需要每个子包中独立写一遍。

5. 配置编译脚本

同样在主项目下安装typescript依赖包:

bash
pnpm add typescript -D

并且在package.json中写上脚本命令:

json
{
  "scripts": {
    "build": "pnpm -r build",
  }
}

这样就可以在主项目下执行所有子项目下的build脚本命令。

所以我们也需要到packages/cli目录下的package.json中配置build脚本:

json
{
  "scripts": {
    "build": "tsc --outDir dist"
  },
}

这里需要注意:必须使用--outDir dist参数来覆盖主目录下tsconfig.json中的配置,这样是为了让编译输出的dist目录分别在各个子项目下面,而不是在主项目下。

6. 让 pnpm 识别子项目

这是最重要的一步,是 Monorepo 项目结构的关键。现在的目录结构虽然很清晰,但是 pnpm 是不能识别的。

我们需要在主项目下创建pnpm-workspaces.yaml文件,告诉 pnpm 我们的项目结构:

yaml
packages:
  - "packages/*"

到现在,一个“最小”的代码就完成了。

看一下目前的目录结构:

前期的准备工作比较繁琐,我也是踩了不少坑后才整理出来,后续我们就可以愉快地写代码了。

测试 uno 命令

虽然还没有实现具体的功能,但我们还是要先跑一下我们的代码,确认环境配置、命令行代码是正确的

  1. 在主项目下执行编译脚本:
bash
pnpm build
  1. @unoapi/cli包链接到全局,方便我们测试。
bash
cd packages/cli

pnpm link --global
  1. 现在就可以输入之前设计的命令进行测试了。
bash
# 初始化配置
uno init

# 生成 API 代码
uno api foo/bar/baz a/b/c -o src/api --func getList

总结

本期完成了 UnoAPI 从 0 到 1 的开始阶段,能够运行并跑通一个“最小化”的代码实现。

后续将继续实现我们的目标,并且会在这个合集「UnoAPI 工具篇」持续更新。

感谢阅读