# 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 ```