UnoAPI工具篇:从 0 到 1 实现“自动化编码”
上一篇我们已经从“理论上”进行了设计,对 UnoAPI 的功能、原理、使用等方面都有了一个大概的雏形。今天,我将与大家一起从代码层面进行从 0 到 1 的实现。
这是一个系列文章,为了更好地理解文章内容,建议你先去了解一下前面的篇章。
废话不多说,马上开始。
初始化项目
首先回顾一下项目的层级:
- 核心层:@unoapi/core
- cli 层:@unoapi/cli
- vscode 层:@unoapi/vscode-extension
为了方便管理,我们的项目需要使用 Monorepo 结构,将这 3 个模块放在一个项目里进行集中管理:
unoapi/
├── package.json
├── packages/
│ ├── core/
│ │ ├── package.json
│ │ └── src/
│ ├── cli/
│ │ ├── package.json
│ │ └── src/
│ └── vscode-extension/
│ ├── package.json
│ └── src/集中管理有一些好处:
- 不需要在多个项目之间来回切换
- 简化开发时的相互依赖关系
- 环境共享、代码共享,减少重复开发工作
项目中的core和cli两个包,最终是需要发布到 npm 仓库的,vscode-extension 则需要独立打包。
编码思维
从 0 到 1 的开发,最难的是从哪里入手。
虽然我们对需求有了大概的了解,但并没有明确的需求方和产品原型,这跟我们在公司上班写代码可能有点不一样。
我个人习惯的开发方式是:从使用者的角度逐步往前推进。这样写代码的思路会更清晰一些,“使用方”需要什么功能,我们就“往前”去实现什么功能。
因此,我将从cli模块入手进行推进。
这个系列的文章都将与代码同步更新,我现在也不知道最后会做成怎样。希望大家多多支持,我会尽量加快更新进度,项目完成后也会放出完整源码与大家学习和交流。
CLI 命令行实现
1. 安装 commander 包
这里我们需要用到commander这个库,用来定义 CLI 命令行接口。
因为只有cli模块使用到,所以需要进入到packages/cli目录下进行安装:
pnpm add commander此项目将使用 pnpm 作为包管理工具,并且使用 TypeScript 进行编码。
2. 实现命令行代码
上一篇我们已经列出来了两个核心命令行:
- ✅️
uno init - ✅️
uno api url1 url2... -o src/api --func getList
所以,我们根据这两条命令来实现具体的代码。
在packages/cli/src目录下创建cli.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。
{
"name": "@unoapi/cli",
"bin": {
"uno": "dist/cli.js"
},
}这样,别人安装这个依赖包之后,就可以使用uno这个命令了。
我们可以看到,uno指向了dist/cli,js。所以我们需要将src/cli.ts文件编译到dist目录下。
4. 配置 TypeScript
因为我们的项目使用了 Monorepo 结构,所以要尽可能的统一进行管理和复用。
回到unoapi的主目录,创建tsconfig.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依赖包:
pnpm add typescript -D并且在package.json中写上脚本命令:
{
"scripts": {
"build": "pnpm -r build",
}
}这样就可以在主项目下执行所有子项目下的build脚本命令。
所以我们也需要到packages/cli目录下的package.json中配置build脚本:
{
"scripts": {
"build": "tsc --outDir dist"
},
}这里需要注意:必须使用
--outDir dist参数来覆盖主目录下tsconfig.json中的配置,这样是为了让编译输出的dist目录分别在各个子项目下面,而不是在主项目下。
6. 让 pnpm 识别子项目
这是最重要的一步,是 Monorepo 项目结构的关键。现在的目录结构虽然很清晰,但是 pnpm 是不能识别的。
我们需要在主项目下创建pnpm-workspaces.yaml文件,告诉 pnpm 我们的项目结构:
packages:
- "packages/*"到现在,一个“最小”的代码就完成了。
看一下目前的目录结构:

前期的准备工作比较繁琐,我也是踩了不少坑后才整理出来,后续我们就可以愉快地写代码了。
测试 uno 命令
虽然还没有实现具体的功能,但我们还是要先跑一下我们的代码,确认环境配置、命令行代码是正确的。
- 在主项目下执行编译脚本:
pnpm build- 将
@unoapi/cli包链接到全局,方便我们测试。
cd packages/cli
pnpm link --global- 现在就可以输入之前设计的命令进行测试了。
# 初始化配置
uno init
# 生成 API 代码
uno api foo/bar/baz a/b/c -o src/api --func getList总结
本期完成了 UnoAPI 从 0 到 1 的开始阶段,能够运行并跑通一个“最小化”的代码实现。
后续将继续实现我们的目标,并且会在这个合集「UnoAPI 工具篇」持续更新。
“
感谢阅读