489 lines
13 KiB
Markdown
489 lines
13 KiB
Markdown
# yx-generate-api
|
||
|
||
`yx-generate-api` 是一个面向前端项目的 Node CLI,用来从 Swagger/OpenAPI JSON 生成 API 文件,并把生成目录的导出同步到你项目里的统一入口文件。
|
||
|
||
它主要解决两件事:
|
||
|
||
- 根据接口文档自动生成 `generated/*.js`
|
||
- 自动维护业务侧 `index.js` 里的导出区块,避免手写和漏改
|
||
|
||
## 先理解这 4 个命令
|
||
|
||
如果你先记住这四个命令,基本就会用了:
|
||
|
||
- `init`
|
||
在当前目录生成模板配置文件和 Windows 启动脚本。
|
||
- `generate`
|
||
只生成 API 文件,不改外部入口文件。
|
||
- `sync`
|
||
只同步外部 `index.js` 的受管导出区块。
|
||
- `gen`
|
||
先执行 `generate`,再执行 `sync`。日常最常用。
|
||
|
||
## 环境要求
|
||
|
||
- Node `^20.19.0 || >=22.12.0`
|
||
- 业务项目里要有一个默认导出的 `request` 模块,生成代码会按 `requestImport` 指向它
|
||
- Swagger 源可以是 `http(s)` 地址、`file://` URL,或者本地 JSON 文件路径
|
||
|
||
## 安装
|
||
|
||
在业务项目中直接安装:
|
||
|
||
```bash
|
||
npm install -D git+https://gitea.23544.com/wangyang/yx_generate_api_js.git
|
||
```
|
||
|
||
安装完成后,通常通过 `npx yx-generate-api ...` 调用。
|
||
|
||
## 快速开始
|
||
|
||
### 1. 生成模板文件
|
||
|
||
在业务项目根目录执行:
|
||
|
||
```bash
|
||
npx yx-generate-api init
|
||
```
|
||
|
||
会创建两个文件:
|
||
|
||
- `yx-generate-api.config.mjs`
|
||
- `run-yx-generate-api.bat`
|
||
|
||
其中 `yx-generate-api.config.mjs` 现在默认自带字段注释,方便首次接入时直接按提示修改。
|
||
|
||
如果文件已存在,使用 `--force` 覆盖:
|
||
|
||
```bash
|
||
npx yx-generate-api init --force
|
||
```
|
||
|
||
### 2. 修改配置
|
||
|
||
下面是一个和 `init` 模板一致的带注释示例:
|
||
|
||
```js
|
||
export default {
|
||
// Swagger/OpenAPI 来源。
|
||
// 支持 http(s)、file://,也支持本地 JSON 文件路径。
|
||
swaggerUrl: 'http://127.0.0.1:8080/swagger/v1/swagger.json',
|
||
|
||
// 远程 Swagger 加载超时时间,单位毫秒。
|
||
swaggerTimeoutMs: 20000,
|
||
|
||
// 生成文件输出目录。
|
||
// 相对路径会基于当前配置文件所在目录解析。
|
||
outputDir: 'src/api/aixue/generated',
|
||
|
||
// 由 `sync` / `gen` 维护的外部 API 入口文件。
|
||
externalIndexFile: 'src/api/aixue/index.js',
|
||
|
||
// 写入到生成模块中的 request 导入路径。
|
||
// 这个路径必须相对于每个生成后的模块文件来写。
|
||
requestImport: '../request',
|
||
|
||
// 生成函数的参数风格,可选 'object' 或 'positional'。
|
||
paramStyle: 'object',
|
||
|
||
// 全量生成时,是否清理已经过期的自动生成模块文件。
|
||
// 如果是部分模块生成,会自动跳过清理,避免误删其他模块。
|
||
cleanOutput: true,
|
||
|
||
sync: {
|
||
// 如果你只想生成文件、不想改 externalIndexFile,可以设为 false。
|
||
enabled: true,
|
||
|
||
// 是否在受管区块中附带 generated/index.js 的注释快照。
|
||
includeGeneratedIndexSnapshot: true,
|
||
|
||
// externalIndexFile 里受管区块的开始和结束标记。
|
||
blockStart: '// AUTO-GENERATED API EXPORTS START',
|
||
blockEnd: '// AUTO-GENERATED API EXPORTS END',
|
||
|
||
// 可选:
|
||
// snapshotTitle: '// generated/index.js content:',
|
||
// exportFrom: './generated',
|
||
},
|
||
}
|
||
```
|
||
|
||
### 3. 执行生成
|
||
|
||
```bash
|
||
npx yx-generate-api gen
|
||
```
|
||
|
||
这条命令会:
|
||
|
||
1. 拉取 `swaggerUrl`
|
||
2. 在 `outputDir` 下生成 API 文件
|
||
3. 生成 `outputDir/index.js`
|
||
4. 把导出同步到 `externalIndexFile`
|
||
|
||
## 一个完整的日常流程
|
||
|
||
第一次接入时:
|
||
|
||
```bash
|
||
npx yx-generate-api init
|
||
npx yx-generate-api gen
|
||
```
|
||
|
||
后端接口更新后,通常只需要再执行一次:
|
||
|
||
```bash
|
||
npx yx-generate-api gen
|
||
```
|
||
|
||
如果你只是想重新整理外部入口文件,而不重新拉 Swagger:
|
||
|
||
```bash
|
||
npx yx-generate-api sync
|
||
```
|
||
|
||
## 配置说明
|
||
|
||
所有写在配置文件里的路径,默认都相对于配置文件所在目录,而不是命令执行目录。
|
||
|
||
### 顶层配置
|
||
|
||
- `swaggerUrl`
|
||
Swagger/OpenAPI JSON 来源。支持 `http(s)`、`file://`、本地 JSON 文件路径。生成时必填。
|
||
- `swaggerTimeoutMs`
|
||
拉取远程 Swagger 的超时时间,单位毫秒,默认 `20000`。
|
||
- `outputDir`
|
||
生成目录。默认是 `src/api/generated`。
|
||
- `externalIndexFile`
|
||
业务侧统一导出文件,例如 `src/api/index.js`。如果不填,默认不会执行同步。
|
||
- `requestImport`
|
||
生成模块里 `import request from '...'` 的路径。它应该相对于每个生成出来的模块文件。
|
||
- `paramStyle`
|
||
函数参数风格,可选 `object` 或 `positional`,默认 `object`。
|
||
- `cleanOutput`
|
||
是否在“全量生成”时清理当前输出目录里已经过期的自动生成模块文件。默认 `true`。
|
||
- `sync`
|
||
控制 `sync` / `gen` 如何维护外部入口文件。
|
||
|
||
### sync 配置
|
||
|
||
- `sync.enabled`
|
||
是否启用同步。默认值等于 `Boolean(externalIndexFile)`。
|
||
- `sync.blockStart`
|
||
受管区块开始标记。
|
||
- `sync.blockEnd`
|
||
受管区块结束标记。
|
||
- `sync.includeGeneratedIndexSnapshot`
|
||
是否把 `generated/index.js` 的内容以注释形式写进受管区块。默认 `true`。
|
||
- `sync.snapshotTitle`
|
||
快照标题。默认是 `// generated/index.js content:`。
|
||
- `sync.exportFrom`
|
||
自定义 `export * from '...'` 的路径。不填时会自动根据 `externalIndexFile` 和 `outputDir` 计算。
|
||
|
||
## 命令详解
|
||
|
||
### `init`
|
||
|
||
用途:在当前目录创建模板配置文件和 Windows 启动脚本。
|
||
|
||
```bash
|
||
npx yx-generate-api init
|
||
npx yx-generate-api init --force
|
||
```
|
||
|
||
### `generate`
|
||
|
||
用途:只生成 API 文件,不同步外部入口。
|
||
|
||
```bash
|
||
npx yx-generate-api generate
|
||
```
|
||
|
||
常用写法:
|
||
|
||
```bash
|
||
npx yx-generate-api generate Curriculum
|
||
npx yx-generate-api generate class-assignment Ranking
|
||
npx yx-generate-api generate --modules=Curriculum,class-assignment
|
||
npx yx-generate-api generate --config ./yx-generate-api.config.mjs
|
||
npx yx-generate-api generate --url=http://127.0.0.1:8080/swagger/v1/swagger.json
|
||
npx yx-generate-api generate --url ./swagger/swagger.json
|
||
npx yx-generate-api generate --outDir=src/api/tmp-generated
|
||
npx yx-generate-api generate --requestImport=../request
|
||
npx yx-generate-api generate --timeout 10000
|
||
npx yx-generate-api generate --no-clean
|
||
npx yx-generate-api generate --paramStyle=positional
|
||
```
|
||
|
||
支持的参数:
|
||
|
||
- `--config=...`
|
||
指定配置文件路径。
|
||
- `--url=...`
|
||
临时覆盖 `swaggerUrl`。
|
||
- `--timeout=...`
|
||
临时覆盖 Swagger 拉取超时,单位毫秒。
|
||
- `--outDir=...`
|
||
临时覆盖输出目录。
|
||
- `--requestImport=...`
|
||
临时覆盖生成文件里的 request 导入路径。
|
||
- `--modules=...`
|
||
逗号分隔的模块列表。
|
||
- `--clean`
|
||
全量生成时清理过期的自动生成模块文件。
|
||
- `--no-clean`
|
||
保留已有生成模块文件。
|
||
- `--paramStyle=object|positional`
|
||
临时覆盖参数风格。
|
||
|
||
命令行参数既支持 `--key=value`,也支持 `--key value`。
|
||
|
||
### `sync`
|
||
|
||
用途:只同步外部入口文件。
|
||
|
||
```bash
|
||
npx yx-generate-api sync
|
||
npx yx-generate-api sync --config=./yx-generate-api.config.mjs
|
||
```
|
||
|
||
如果 `sync.enabled=false`,命令会直接跳过。
|
||
|
||
### `gen`
|
||
|
||
用途:先 `generate`,再 `sync`。这是推荐的日常命令。
|
||
|
||
```bash
|
||
npx yx-generate-api gen
|
||
npx yx-generate-api gen Curriculum
|
||
npx yx-generate-api gen --modules=Curriculum,class-assignment
|
||
```
|
||
|
||
`gen` 接受和 `generate` 相同的运行时参数,例如 `--config`、`--url`、`--timeout`、`--outDir`、`--requestImport`、`--paramStyle`、`--modules`、`--clean`。
|
||
|
||
## 模块筛选规则
|
||
|
||
如果不传模块名,会生成 Swagger 里的全部模块。
|
||
|
||
如果传了模块名,只会生成匹配到的模块,例如:
|
||
|
||
```bash
|
||
npx yx-generate-api generate Curriculum
|
||
npx yx-generate-api generate class-assignment Ranking
|
||
npx yx-generate-api generate --modules=Curriculum,class-assignment
|
||
```
|
||
|
||
模块匹配时会同时参考这些值:
|
||
|
||
- 推导出的模块名
|
||
- 模块文件名的 kebab-case 形式
|
||
- Swagger operation 的 `tags`
|
||
|
||
匹配时会忽略大小写和大部分分隔符,所以下面这些通常都能匹配到同一个模块:
|
||
|
||
- `Curriculum`
|
||
- `curriculum`
|
||
- `class-assignment`
|
||
- `classAssignment`
|
||
|
||
### 模块名是怎么推导的
|
||
|
||
生成器会优先从接口路径推导模块名:
|
||
|
||
- `/api/v1/Curriculum/list` -> `Curriculum`
|
||
- `/api/Curriculum/list` -> `Curriculum`
|
||
- 其他路径会优先使用第一个路径段
|
||
- 如果路径不合适,会退回到 Swagger `tags[0]`
|
||
|
||
## 生成结果长什么样
|
||
|
||
假设配置如下:
|
||
|
||
```js
|
||
export default {
|
||
outputDir: 'src/api/aixue/generated',
|
||
externalIndexFile: 'src/api/aixue/index.js',
|
||
requestImport: '../request',
|
||
}
|
||
```
|
||
|
||
执行 `npx yx-generate-api gen` 后,通常会得到这样的结构:
|
||
|
||
```text
|
||
src/api/aixue/
|
||
index.js
|
||
request.js
|
||
generated/
|
||
shared.js
|
||
curriculum.js
|
||
class-assignment.js
|
||
index.js
|
||
```
|
||
|
||
其中:
|
||
|
||
- `shared.js`
|
||
提供 `buildUrl`、`stringifyParams` 等公共方法,不再依赖额外的 `qs` 包。
|
||
- `curriculum.js`
|
||
某个模块的 API 方法集合。
|
||
- `generated/index.js`
|
||
汇总导出每个模块,同时提供“命名空间导出”和“直接函数导出”。
|
||
- `src/api/aixue/index.js`
|
||
业务侧入口文件,`sync` 会在里面维护一个受管区块。
|
||
|
||
`generated/index.js` 的内容类似这样:
|
||
|
||
```js
|
||
export * as classAssignmentApi from './class-assignment'
|
||
export * as curriculumApi from './curriculum'
|
||
|
||
export { getClassAssignmentListApi } from './class-assignment'
|
||
export { getCurriculumListApi } from './curriculum'
|
||
```
|
||
|
||
如果不同模块里恰好生成了同名函数,`generated/index.js` 会自动为冲突项补上模块前缀别名,避免导出冲突。
|
||
|
||
## 参数风格
|
||
|
||
`paramStyle` 决定生成函数的签名长什么样。
|
||
|
||
### `object`
|
||
|
||
默认值。路径参数和查询参数统一放到 `params`,请求体放到 `data`。
|
||
|
||
```js
|
||
const detailApi = (params = {}) => request.get(buildUrl(`/api/v1/course/{id}`, params))
|
||
|
||
const createApi = (params = {}, data) => request.post(buildUrl(`/api/v1/course/{id}`, params), data)
|
||
```
|
||
|
||
适合大多数前端项目,调用时更稳定,也更适合参数经常变动的接口。
|
||
|
||
### `positional`
|
||
|
||
路径参数和查询参数会展开成位置参数,请求体仍然放最后一个 `data`。
|
||
|
||
```js
|
||
const detailApi = (id, tab) => request.get(buildUrl(`/api/v1/course/{id}`, { id, tab }))
|
||
|
||
const createApi = (id, data) => request.post(buildUrl(`/api/v1/course/{id}`, { id }), data)
|
||
```
|
||
|
||
适合你明确想要“函数参数看起来更直接”的场景。
|
||
|
||
## `sync` 会怎么改外部入口文件
|
||
|
||
`sync` 不会粗暴覆盖整个 `externalIndexFile`,它只维护一段带开始和结束标记的受管区块。
|
||
|
||
默认写进去的内容类似这样:
|
||
|
||
```js
|
||
// AUTO-GENERATED API EXPORTS START
|
||
// Synced from 'src/api/aixue/generated/index.js'. Do not edit manually.
|
||
// generated/index.js content:
|
||
// export * as curriculumApi from './curriculum'
|
||
// export * as rankingApi from './ranking'
|
||
export * from './generated'
|
||
// AUTO-GENERATED API EXPORTS END
|
||
```
|
||
|
||
规则是:
|
||
|
||
- 如果外部文件里已经有这段标记,`sync` 会替换这段区块
|
||
- 如果还没有,`sync` 会把区块追加到文件末尾
|
||
- 标记外的内容会保留
|
||
|
||
如果你不想把 `generated/index.js` 的快照写进注释,可以把:
|
||
|
||
```js
|
||
sync: {
|
||
includeGeneratedIndexSnapshot: false,
|
||
}
|
||
```
|
||
|
||
## Windows 双击运行
|
||
|
||
`init` 会同时创建 `run-yx-generate-api.bat`。
|
||
|
||
它支持两种使用方式:
|
||
|
||
- 直接双击运行
|
||
会先提示你输入模块名或额外参数;留空则执行全量生成。
|
||
- 在命令行里带参数运行
|
||
会直接把参数透传给 `yx-generate-api gen`。
|
||
|
||
它内部的核心行为相当于:
|
||
|
||
```bat
|
||
npx yx-generate-api gen %*
|
||
```
|
||
|
||
双击时可输入的内容示例:
|
||
|
||
```bat
|
||
Curriculum
|
||
Ranking EnglishWord
|
||
--modules=Ranking,EnglishWord
|
||
```
|
||
|
||
适合给不常开命令行的同事直接双击执行,也保留了命令行传参的灵活性。
|
||
|
||
也可以在命令行里继续传参:
|
||
|
||
```bat
|
||
run-yx-generate-api.bat Curriculum
|
||
run-yx-generate-api.bat --modules=Curriculum,class-assignment
|
||
```
|
||
|
||
## 常见问题
|
||
|
||
### 1. 为什么 `gen` 没有同步外部 `index.js`
|
||
|
||
通常是下面几种情况:
|
||
|
||
- 没有配置 `externalIndexFile`
|
||
- `sync.enabled=false`
|
||
- 你执行的是 `generate`,不是 `gen`
|
||
|
||
### 2. 为什么生成文件里的 `request` 路径不对
|
||
|
||
`requestImport` 会原样写进生成文件,所以它必须相对于生成后的模块文件来写,而不是相对于 `externalIndexFile`。
|
||
|
||
例如生成目录是 `src/api/aixue/generated`,请求封装在 `src/api/aixue/request.js`,那么应该写:
|
||
|
||
```js
|
||
requestImport: '../request'
|
||
```
|
||
|
||
### 3. 为什么命令行传的 `--outDir` 看起来和配置文件规则不一样
|
||
|
||
配置文件里的路径相对于“配置文件所在目录”。
|
||
|
||
命令行传入的 `--config`、`--outDir`,相对于“当前执行命令的目录”。
|
||
|
||
### 4. 为什么提示模块找不到
|
||
|
||
说明你传入的模块名没有匹配到任何已解析模块。可以先不带模块参数跑一次全量生成,观察生成出来的模块文件名,再按那个名字筛选。
|
||
|
||
### 5. 为什么有些旧模块文件没有被删掉
|
||
|
||
只有“全量生成”时,`cleanOutput=true` 才会清理过期的自动生成模块文件。
|
||
|
||
如果你这次是只生成部分模块,例如:
|
||
|
||
```bash
|
||
npx yx-generate-api generate Curriculum
|
||
```
|
||
|
||
工具会保留其他已有模块,避免误删。
|
||
|
||
## 本地开发
|
||
|
||
在工具仓库里直接查看帮助:
|
||
|
||
```bash
|
||
node ./bin/yx-generate-api.js --help
|
||
node ./bin/yx-generate-api.js generate --help
|
||
node ./bin/yx-generate-api.js sync --help
|
||
```
|