yx_generate_api_js/README.md

585 lines
18 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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',
// 是否生成 outputDir/index.js。
// 关闭后只保留 shared.js 和各模块文件sync / gen 也会默认不再同步。
generateIndexFile: true,
// 由 `sync` / `gen` 维护的外部 API 入口文件。
externalIndexFile: 'src/api/aixue/index.js',
// 写入到生成模块中的 request 导入路径。
// 这个路径必须相对于每个生成后的模块文件来写。
requestImport: '../request',
// 生成函数的参数风格,可选 'object' 或 'positional'。
paramStyle: 'object',
// 全量生成时,是否清理已经过期的自动生成模块文件。
// 如果是部分模块生成,会自动跳过清理,避免误删其他模块。
cleanOutput: true,
sync: {
// 如果你只想生成文件、不想改 externalIndexFile可以设为 false。
// 当 generateIndexFile=false 时,这个开关默认也会变成 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. 如果 `generateIndexFile=true`,生成 `outputDir/index.js`
4. 如果 `sync.enabled=true`,把导出同步到 `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`。如果不填,默认不会执行同步。
- `generateIndexFile`
是否生成 `outputDir/index.js`。默认 `true`。设为 `false` 后,工具只生成 `shared.js` 和各模块文件;`sync.enabled` 的默认值也会随之变成 `false`
- `requestImport`
生成模块里 `import request from '...'` 的路径。它应该相对于每个生成出来的模块文件。
- `paramStyle`
函数参数风格,可选 `object``positional`,默认 `object`
- `cleanOutput`
是否在“全量生成”时清理当前输出目录里已经过期的自动生成模块文件。默认 `true`
- `sync`
控制 `sync` / `gen` 如何维护外部入口文件。
### sync 配置
- `sync.enabled`
是否启用同步。默认值等于 `Boolean(externalIndexFile && generateIndexFile)`
- `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 * from './class-assignment'
export * from './curriculum'
```
这样你可以同时支持两种写法:
```js
import { curriculumApi } from '@/api/aixue/generated'
import { getCurriculumListApi } from '@/api/aixue/generated'
```
如果你不需要这个聚合入口,也可以这样关闭:
```js
generateIndexFile: false
```
关闭后会有这些变化:
- 不再生成 `generated/index.js`
- 旧的自动生成 `generated/index.js` 会在下次生成时被移除
- 不再对“跨模块扁平导出重名”做 `generated/index.js` 级别的冲突校验
- `sync.enabled` 默认会变成 `false`
如果你又显式把 `sync.enabled` 设成 `true`,命令会直接报错,因为 `sync` 依赖 `generated/index.js`
需要注意:
- 如果不同模块里恰好生成了同名函数,例如都生成了 `getListApi`
- 那么 `generated/index.js` 里的 `export * from './module'` 会产生冲突
- 如果某个接口的默认函数名刚好会和模块命名空间导出名撞上,例如 `/api/Values` 同时会推导出模块命名空间 `valuesApi`
- 工具会在生成阶段自动把函数名调整成不冲突的形式,例如 `getValuesApi`
- 工具会先列出冲突函数、对应模块名,以及能识别到的完整接口 URL
- 在交互终端里会给你两个选择:
- `1` 在函数名后自动追加模块名,例如 `getListEnglishWordApi`
- `2` 退出本次生成
- 如果自动重命名后仍然冲突,工具会直接失败,并提示“依然冲突无法执行”
## 参数风格
`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)
```
适合你明确想要“函数参数看起来更直接”的场景。
## 文件下载接口
如果响应声明为 `type: string, format: binary`、Swagger 2 的 `type: file`,或者使用常见文件下载 MIME 类型,例如 `application/octet-stream`、`application/pdf`、Excel、CSV、图片、音视频等生成代码会自动带上 `responseType: 'blob'`
有些后端 Swagger 不会给导出接口声明响应 `content` / `schema`。这种情况下,如果接口路径、`operationId`、摘要或描述里能识别到 `导出`、`下载`、`export`、`download`,或者 GET 接口名里有独立的 `File` 单词,也会按文件下载处理。若响应已经明确声明为 JSON 或普通 schema则不会走这个兜底推断。
```js
const exportApi = (params = {}) =>
request.get(buildUrl(`/api/v1/report/export`, params), { responseType: 'blob' })
const createExportApi = (data) =>
request.post(`/api/v1/report/export`, data, { responseType: 'blob' })
```
## `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 './curriculum'
// export * 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
```
无论你是直接执行 `npx yx-generate-api gen`,还是走 `.bat`,后续真正执行的都是同一套 CLI。
所以如果遇到 API 函数名冲突,两边都会出现同样的冲突提示和同样的两个处理选项。
## 常见问题
### 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
```
工具会保留其他已有模块,避免误删。
### 6. 为什么生成时报“Duplicate API export names detected”
说明不同模块之间出现了重复的扁平函数导出名。
常见场景有两种:
- 两个或多个模块里生成了同名函数,例如都导出了 `getListApi`
- 你只生成了部分模块,但保留目录里已有模块,导致新旧模块之间出现了同名导出
因为 `generated/index.js` 会使用:
```js
export * from './module-a'
export * from './module-b'
```
这种模式下,重复的扁平导出名不能直接共存。
`/api/Values` 这类“函数默认名刚好和模块命名空间名相同”的情况,工具会在生成阶段自动规避,不会再单独报这个冲突。
如果你是从旧版本升级过来,而 `generated` 目录里还保留着旧规则生成的模块文件,也可能在部分生成时触发这类校验。遇到这种情况,直接做一次全量生成,或者把冲突模块一起重新生成即可。
所以工具会先把冲突明细列出来,内容会尽量包含:
- 冲突函数名
- 对应模块名
- 对应的完整接口 URL
然后给你两个选择:
-`1`
自动把冲突函数改成“原函数名 + 模块名”,例如 `getListApi` -> `getListEnglishWordApi`
-`2`
退出生成,不写入本次结果
如果你选了自动重命名,但重命名后依然冲突,例如模块内已经存在同名函数,那么工具会直接失败并提示当前结果仍然无法安全生成。
## 本地开发
在工具仓库里直接查看帮助:
```bash
node ./bin/yx-generate-api.js --help
node ./bin/yx-generate-api.js generate --help
node ./bin/yx-generate-api.js sync --help
```