@karry.sun/yapi-gen 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,72 +1,123 @@
1
1
  # yapi-gen
2
2
 
3
- 基于 [yapi-to-typescript](https://github.com/fjc0k/yapi-to-typescript) 的 YApi 前端代码生成 CLI,支持可配置的请求适配器与多项目复用。适用于 Vite、Nuxt、Vue 等不同请求封装(axios、defHttp、Nuxt request 等)。
3
+ 基于 [yapi-to-typescript](https://github.com/fjc0k/yapi-to-typescript) 的 YApi 接口代码生成 CLI,支持可配置的请求适配器、目录映射与按需生成。适用于 Vite、Nuxt、Vue 等不同请求封装。
4
+
5
+ ---
4
6
 
5
7
  ## 安装
6
8
 
7
9
  ```bash
8
- pnpm add -D yapi-gen yapi-to-typescript
10
+ pnpm add -D @karry.sun/yapi-gen
9
11
  ```
10
12
 
11
- ## 快速开始
13
+ 本包已内置 yapi-to-typescript,无需单独安装。
12
14
 
13
- ### 1. 初始化配置
15
+ ---
16
+
17
+ ## 快速开始
14
18
 
15
- 在项目根目录执行:
19
+ ### 1. 初始化
16
20
 
17
21
  ```bash
18
22
  pnpm yapi-gen init
19
23
  ```
20
24
 
21
- 会生成 `yapi-gen.config.ts`,按需修改 `serverUrl`、`token`、`requestAdapter`、`outputFilePath` 等。
25
+ 会生成:
22
26
 
23
- ### 2. 选择请求适配方式
27
+ - `yapi-gen.config.ts`:主配置(serverUrl、token、requestAdapter 等)
28
+ - `src/yapi/gen-api-mapping.json`:分类→目录名映射(可手动维护)
24
29
 
25
- - **内置预设(推荐)**:在配置中设置 `requestAdapter: 'axios'` 或 `requestAdapter: 'nuxt-bbm'`,无需在各仓库维护 `request.ts`,运行 `yapi-gen` 时会自动生成并写入 `src/yapi/request.ts`(路径可配)。
26
- - **axios**:适用于封装了 axios 的项目(如 defHttp)。可选 `requestAdapter: { type: 'axios', axiosInstancePath: '/@/utils/http/axios' }` 指定实例路径,默认 `'/@/utils/http/axios'`(defHttp 常见路径)。
27
- - **nuxt-bbm**:适用于 Nuxt 项目,使用 Layer 的 `#imports` 或 `@bitunix/bbm` 的 request。可选 `requestAdapter: { type: 'nuxt-bbm', requestImportPath: '#imports' }`,默认 `'#imports'`。
28
- - **自定义**:设置 `requestAdapter: 'custom'` 并配置 `requestFunctionFilePath`,在对应文件中自行实现适配逻辑;或参考包内 `templates/request-*.ts` 复制后修改。
30
+ ### 2. 修改配置
29
31
 
30
- ### 3. 生成接口代码
32
+ 编辑 `yapi-gen.config.ts`,至少修改:
33
+
34
+ - `serverUrl`:YApi 服务器地址
35
+ - `projects[0].token`:项目 token(建议用 `process.env.YAPI_TOKEN`)
36
+
37
+ ### 3. 生成接口
31
38
 
32
39
  ```bash
33
40
  pnpm yapi-gen
34
41
  ```
35
42
 
36
- 会在当前目录查找 `yapi-gen.config.ts`(或 `.yapi-genrc.ts` 等),生成临时 ytt 配置并执行 `ytt`,输出到你在 `outputFilePath` 中配置的目录。
43
+ 或在 `package.json` 中配置 `"gen:api": "yapi-gen"`,然后执行 `pnpm gen:api`。
37
44
 
38
- ## 配置说明
45
+ ---
46
+
47
+ ## 请求适配方式
48
+
49
+ | 方式 | 说明 |
50
+ |------|------|
51
+ | `'axios'` | 封装 axios/defHttp 的项目,自动生成 `src/yapi/request.ts` |
52
+ | `'nuxt-bbm'` | Nuxt 项目,使用 `#imports` 或 `@bitunix/bbm` |
53
+ | `'custom'` | 自定义,需配置 `requestFunctionFilePath` 指向项目内 request 文件 |
54
+
55
+ **axios 示例**:`requestAdapter: { type: 'axios', axiosInstancePath: '/@/utils/http/axios' }`
56
+
57
+ **nuxt-bbm 示例**:`requestAdapter: { type: 'nuxt-bbm', requestImportPath: '#imports' }`
58
+
59
+ ---
60
+
61
+ ## 目录映射与按需生成
62
+
63
+ ### 命令用法
64
+
65
+ ```bash
66
+ # 全量生成
67
+ pnpm yapi-gen
68
+
69
+ # 按分类生成
70
+ pnpm yapi-gen cat_401
71
+ pnpm yapi-gen cat_401=financialReconciliation # 指定目录名并写入映射
72
+
73
+ # 批量映射
74
+ pnpm yapi-gen --map cat_401=financialReconciliation,cat_413=specialAccount
75
+
76
+ # 按接口生成(合并模式)
77
+ pnpm yapi-gen 2116
78
+ pnpm yapi-gen 2116 --map cat_413=specialAccount
79
+
80
+ # 自定义路径
81
+ pnpm yapi-gen --output-dir src/yapi --mapping-file src/yapi/gen-api-mapping.json
82
+ ```
83
+
84
+ ### 映射文件
39
85
 
40
- `yapi-gen.config.ts` 使用 `defineYapiGenConfig` 导出单条配置(或数组,多条会合并为 `defineConfig([...])`)。主要字段与 [yapi-to-typescript 配置](https://fjc0k.github.io/yapi-to-typescript/handbook/config.html) 对齐:
86
+ - **路径**:`src/yapi/gen-api-mapping.json`(默认)
87
+ - **格式**:`{ "cat_401": "financialReconciliation", "cat_413": "specialAccount" }`
88
+ - **优先级**:命令行 > 配置文件 > 默认 `cat_xxx`
89
+ - **映射变更时**:旧目录会自动重命名为新目录名
90
+
91
+ ```json
92
+ // src/yapi/gen-api-mapping.json(由业务维护)
93
+ {
94
+ "cat_401": "publicCategory",
95
+ "cat_413": "specialAccountAsset"
96
+ }
97
+ ```
98
+
99
+ ---
100
+
101
+ ## 配置说明
41
102
 
42
103
  | 字段 | 说明 | 必填 |
43
104
  |------|------|------|
44
105
  | `serverUrl` | YApi 服务器地址 | 是 |
45
- | `serverType` | `'yapi'` \| `'swagger'` | 否,默认 `'yapi'` |
46
- | `requestAdapter` | 请求适配方式:`'axios'` / `'nuxt-bbm'` / `'custom'`,或对象形式带可选参数 | 否,与 `requestFunctionFilePath` 二选一 |
47
- | `requestFunctionFilePath` | 请求适配器文件路径;使用预设时可选(默认 `src/yapi/request.ts`),custom 时必填 | custom 时必填 |
48
- | `outputFilePath` | 生成文件路径或函数 `(interfaceInfo, changeCase) => string` | 是 |
49
- | `projects` | 项目列表,每项含 `token`、`categories` | 是 |
50
- | `dataKey` | 业务数据字段,如 `'result'`,适配器需据此解包 | 否 |
51
- | `prodEnvName` / `devEnvName` | 生产/测试环境名称(用于域名) | 否 |
52
- | `typesOnly` | 是否只生成类型 | 否,默认 `false` |
53
- | `target` | `'typescript'` \| `'javascript'` | 否,默认 `'typescript'` |
54
- | `reactHooks` | React Hooks 生成配置 | 否 |
106
+ | `serverType` | `'yapi'` \| `'swagger'` | |
107
+ | `requestAdapter` | `'axios'` / `'nuxt-bbm'` / `'custom'` | |
108
+ | `requestFunctionFilePath` | 自定义 request 文件路径 | custom 时必填 |
109
+ | `outputFilePath` | 生成文件路径或函数 | 是 |
110
+ | `projects` | 项目列表(tokencategories | 是 |
111
+ | `dataKey` | 业务数据字段,如 `'result'` | 否 |
55
112
  | `preproccessInterface` | 预处理/过滤接口 | 否 |
56
- | `getRequestFunctionName` | 请求函数命名 | 否 |
57
-
58
- YApi 的 **token** 建议通过环境变量注入,例如在 `projects[].token` 中写 `process.env.YAPI_TOKEN || 'fallback'`。
59
-
60
- ## 在现有项目中使用
61
113
 
62
- - **web-finance-admin(defHttp)**:在 `yapi-gen.config.ts` 中设置 `requestAdapter: 'axios'`(或 `{ type: 'axios', axiosInstancePath: '/@/utils/http/axios' }`),无需再维护 `request.ts`;或继续使用 `requestAdapter: 'custom'` 并保留现有 `requestFunctionFilePath`。
63
- - **pc(Nuxt / @bitunix/bbm)**:设置 `requestAdapter: 'nuxt-bbm'`(或 `{ type: 'nuxt-bbm', requestImportPath: '#imports' }`),无需再维护 `request.ts`;或使用 `custom` 并指向现有文件。
114
+ ---
64
115
 
65
116
  ## 配置文件查找顺序
66
117
 
67
- CLI 会按以下顺序查找配置(找到即用):
118
+ `yapi-gen.config.ts`、`yapi-gen.config.mts`、`yapi-gen.config.cts`、`yapi-gen.config.js`、`yapi-gen.config.mjs`、`yapi-gen.config.cjs`、`.yapi-genrc.ts`、`.yapi-genrc.mts`、`.yapi-genrc.js`、`.yapi-genrc.mjs`
68
119
 
69
- `yapi-gen.config.ts`、`yapi-gen.config.mts`、`yapi-gen.config.cts`、`yapi-gen.config.js`、`yapi-gen.config.mjs`、`yapi-gen.config.cjs`、`.yapi-genrc.ts`、`.yapi-genrc.mts`、`.yapi-genrc.js`、`.yapi-genrc.mjs`。
120
+ ---
70
121
 
71
122
  ## License
72
123
 
package/bin/yapi-gen.js CHANGED
@@ -9,6 +9,7 @@ const runPath = join(__dirname, '..', 'dist', 'run.js');
9
9
  const runUrl = pathToFileURL(runPath).href;
10
10
 
11
11
  const { run } = await import(runUrl);
12
- const init = process.argv.includes('init');
13
- const code = run({ cwd: process.cwd(), init });
12
+ const argv = process.argv.slice(2);
13
+ const init = argv.includes('init');
14
+ const code = run({ cwd: process.cwd(), init, argv });
14
15
  process.exit(code);
@@ -50,6 +50,10 @@ export interface YapiGenUserConfig {
50
50
  devEnvName?: string;
51
51
  /** 业务数据字段,如 'result' / 'data',请求函数需据此解包 */
52
52
  dataKey?: string | string[];
53
+ /** 生成文件输出根目录,用于目录映射与合并模式,默认 'src/yapi' */
54
+ outputDir?: string;
55
+ /** 分类->目录名映射文件路径,默认 '{outputDir}/gen-api-mapping.json',可被业务维护 */
56
+ categoryMappingPath?: string;
53
57
  /**
54
58
  * 请求适配方式(与 requestFunctionFilePath 二选一):
55
59
  * - 'axios':封装 axios/defHttp,可选 axiosInstancePath
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * gen-api CLI 参数解析与目录映射逻辑
3
+ * 对应 web-finance-admin scripts/gen-api.mjs 的能力
4
+ */
5
+ export interface GenApiArgs {
6
+ interfaceIds: string[];
7
+ categoryIds: string[];
8
+ manualMapping: Record<string, string>;
9
+ outputDir: string;
10
+ mappingFilePath: string;
11
+ }
12
+ /**
13
+ * 解析命令行参数
14
+ * 用法:
15
+ * yapi-gen # 全量
16
+ * yapi-gen 2116 # 仅接口 2116(合并模式)
17
+ * yapi-gen cat_401 # 仅分类 401
18
+ * yapi-gen cat_401=financialReconciliation
19
+ * yapi-gen --map cat_401=financialReconciliation,cat_413=specialAccount
20
+ * yapi-gen --output-dir src/yapi --mapping-file src/yapi/gen-api-mapping.json
21
+ */
22
+ export declare function parseArgs(argv: string[] | undefined, cwd: string): GenApiArgs;
23
+ export declare function loadMapping(mappingPath: string): Record<string, string>;
24
+ export declare function saveMapping(mappingPath: string, mapping: Record<string, string>): void;
25
+ export declare function getDirName(catId: string, fileMapping: Record<string, string>, manualMapping: Record<string, string>): string;
26
+ /** 映射变更时重命名目录 */
27
+ export declare function renameDirsOnMappingChange(yapiDir: string, fileMapping: Record<string, string>, mergedMapping: Record<string, string>): void;
28
+ /** 转为 ytt 需要的格式:数字 key */
29
+ export declare function mappingForYtt(m: Record<string, string>): Record<string, string>;
30
+ /** 接口模式:将 .temp 下的单接口文件合并到分类文件中 */
31
+ export declare function mergeTempToTarget(yapiDir: string, tempDir: string, interfaceIds: string[], fileMapping: Record<string, string>, manualMapping: Record<string, string>, tempFilePattern?: RegExp): void;
32
+ //# sourceMappingURL=gen-api-cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gen-api-cli.d.ts","sourceRoot":"","sources":["../src/gen-api-cli.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAaH,MAAM,WAAW,UAAU;IACzB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;CACzB;AAKD;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,YAAwB,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,CA2CzF;AAED,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAevE;AAED,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAItF;AAMD,wBAAgB,UAAU,CACxB,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACnC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACpC,MAAM,CAKR;AAED,iBAAiB;AACjB,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACnC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACpC,IAAI,CAoBN;AAED,0BAA0B;AAC1B,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAO/E;AA4ED,mCAAmC;AACnC,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,EAAE,EACtB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACnC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACrC,eAAe,GAAE,MAA0B,GAC1C,IAAI,CAuDN"}
@@ -0,0 +1,245 @@
1
+ /**
2
+ * gen-api CLI 参数解析与目录映射逻辑
3
+ * 对应 web-finance-admin scripts/gen-api.mjs 的能力
4
+ */
5
+ import { readdirSync, readFileSync, writeFileSync, rmSync, existsSync, mkdirSync, renameSync, } from 'fs';
6
+ import { join } from 'path';
7
+ const DEFAULT_OUTPUT_DIR = 'src/yapi';
8
+ const DEFAULT_MAPPING_FILENAME = 'gen-api-mapping.json';
9
+ /**
10
+ * 解析命令行参数
11
+ * 用法:
12
+ * yapi-gen # 全量
13
+ * yapi-gen 2116 # 仅接口 2116(合并模式)
14
+ * yapi-gen cat_401 # 仅分类 401
15
+ * yapi-gen cat_401=financialReconciliation
16
+ * yapi-gen --map cat_401=financialReconciliation,cat_413=specialAccount
17
+ * yapi-gen --output-dir src/yapi --mapping-file src/yapi/gen-api-mapping.json
18
+ */
19
+ export function parseArgs(argv = process.argv.slice(2), cwd) {
20
+ const interfaceIds = [];
21
+ const categoryIds = [];
22
+ const manualMapping = {};
23
+ let outputDir = DEFAULT_OUTPUT_DIR;
24
+ let mappingFilePath = '';
25
+ for (let i = 0; i < argv.length; i++) {
26
+ const arg = argv[i];
27
+ if (arg === '--ids' && argv[i + 1]) {
28
+ interfaceIds.push(...argv[i + 1].split(',').map((s) => s.trim()));
29
+ i++;
30
+ }
31
+ else if (arg === '--map' && argv[i + 1]) {
32
+ for (const pair of argv[i + 1].split(',')) {
33
+ const [key, val] = pair.split('=').map((s) => s.trim());
34
+ if (key && val) {
35
+ const catKey = /^\d+$/.test(key) ? `cat_${key}` : key;
36
+ manualMapping[catKey] = val;
37
+ }
38
+ }
39
+ i++;
40
+ }
41
+ else if (arg === '--output-dir' && argv[i + 1]) {
42
+ outputDir = argv[i + 1].trim();
43
+ i++;
44
+ }
45
+ else if (arg === '--mapping-file' && argv[i + 1]) {
46
+ mappingFilePath = argv[i + 1].trim();
47
+ i++;
48
+ }
49
+ else if (arg?.startsWith('cat_')) {
50
+ const parts = arg.split('=');
51
+ const catPart = parts[0].replace(/^cat_/, '');
52
+ categoryIds.push(catPart);
53
+ if (parts[1])
54
+ manualMapping[parts[0].trim()] = parts[1].trim();
55
+ }
56
+ else if (/^\d+$/.test(arg)) {
57
+ interfaceIds.push(arg);
58
+ }
59
+ }
60
+ if (!mappingFilePath) {
61
+ mappingFilePath = join(outputDir, DEFAULT_MAPPING_FILENAME);
62
+ }
63
+ mappingFilePath = join(cwd, mappingFilePath);
64
+ return { interfaceIds, categoryIds, manualMapping, outputDir, mappingFilePath };
65
+ }
66
+ export function loadMapping(mappingPath) {
67
+ try {
68
+ const content = readFileSync(mappingPath, 'utf-8');
69
+ const raw = JSON.parse(content);
70
+ const normalized = {};
71
+ for (const [k, v] of Object.entries(raw)) {
72
+ if (k && v) {
73
+ const key = /^\d+$/.test(k) ? `cat_${k}` : k;
74
+ normalized[key] = String(v);
75
+ }
76
+ }
77
+ return normalized;
78
+ }
79
+ catch {
80
+ return {};
81
+ }
82
+ }
83
+ export function saveMapping(mappingPath, mapping) {
84
+ const dir = join(mappingPath, '..');
85
+ mkdirSync(dir, { recursive: true });
86
+ writeFileSync(mappingPath, JSON.stringify(mapping, null, 2) + '\n', 'utf-8');
87
+ }
88
+ function toCatKey(catId) {
89
+ return catId.startsWith('cat_') ? catId : `cat_${catId}`;
90
+ }
91
+ export function getDirName(catId, fileMapping, manualMapping) {
92
+ const key = toCatKey(catId);
93
+ if (manualMapping[key])
94
+ return manualMapping[key];
95
+ if (fileMapping[key])
96
+ return fileMapping[key];
97
+ return `cat_${catId}`;
98
+ }
99
+ /** 映射变更时重命名目录 */
100
+ export function renameDirsOnMappingChange(yapiDir, fileMapping, mergedMapping) {
101
+ for (const catKey of Object.keys(mergedMapping)) {
102
+ const oldDirName = fileMapping[catKey];
103
+ const newDirName = mergedMapping[catKey];
104
+ if (!oldDirName || !newDirName || oldDirName === newDirName)
105
+ continue;
106
+ const oldPath = join(yapiDir, oldDirName);
107
+ const newPath = join(yapiDir, newDirName);
108
+ if (!existsSync(oldPath))
109
+ continue;
110
+ try {
111
+ if (existsSync(newPath)) {
112
+ rmSync(newPath, { recursive: true });
113
+ }
114
+ renameSync(oldPath, newPath);
115
+ console.log(`[yapi-gen] 已重命名目录: ${oldDirName} -> ${newDirName}`);
116
+ }
117
+ catch (err) {
118
+ console.warn(`[yapi-gen] 重命名 ${oldDirName} -> ${newDirName} 失败:`, err?.message);
119
+ }
120
+ }
121
+ }
122
+ /** 转为 ytt 需要的格式:数字 key */
123
+ export function mappingForYtt(m) {
124
+ const out = {};
125
+ for (const [k, v] of Object.entries(m)) {
126
+ const num = k.replace(/^cat_/, '');
127
+ if (/^\d+$/.test(num))
128
+ out[num] = v;
129
+ }
130
+ return out;
131
+ }
132
+ function extractCatId(content) {
133
+ const m = content.match(/interface\/api\/cat_(\d+)/);
134
+ return m ? m[1] : null;
135
+ }
136
+ function extractInterfaceBlock(content, interfaceId) {
137
+ const idStr = String(interfaceId);
138
+ const idRegex = new RegExp(`interface/api/${idStr}\\b`);
139
+ const nextInterfaceRegex = new RegExp(`interface/api/(?!${idStr}\\b)\\d+`);
140
+ const lines = content.split('\n');
141
+ let startIdx = -1;
142
+ let endIdx = -1;
143
+ for (let i = 0; i < lines.length; i++) {
144
+ if (idRegex.test(lines[i]) && startIdx === -1)
145
+ startIdx = i;
146
+ if (startIdx >= 0 && endIdx === -1 && i > startIdx && nextInterfaceRegex.test(lines[i])) {
147
+ endIdx = i;
148
+ break;
149
+ }
150
+ }
151
+ if (startIdx === -1)
152
+ return null;
153
+ if (endIdx === -1)
154
+ endIdx = lines.length;
155
+ return lines.slice(startIdx, endIdx).join('\n').trim();
156
+ }
157
+ function replaceInterfaceBlock(targetContent, interfaceId, newBlock) {
158
+ const idStr = String(interfaceId);
159
+ const idRegex = new RegExp(`interface/api/${idStr}\\b`);
160
+ const nextInterfaceRegex = new RegExp(`interface/api/(?!${idStr}\\b)\\d+`);
161
+ const lines = targetContent.split('\n');
162
+ let startIdx = -1;
163
+ let endIdx = -1;
164
+ for (let i = 0; i < lines.length; i++) {
165
+ if (idRegex.test(lines[i]) && startIdx === -1)
166
+ startIdx = i;
167
+ if (startIdx >= 0 && endIdx === -1 && i > startIdx && nextInterfaceRegex.test(lines[i])) {
168
+ endIdx = i;
169
+ break;
170
+ }
171
+ }
172
+ if (startIdx === -1)
173
+ return null;
174
+ if (endIdx === -1)
175
+ endIdx = lines.length;
176
+ const before = lines.slice(0, startIdx).join('\n');
177
+ const after = lines.slice(endIdx).join('\n');
178
+ return before + '\n\n' + newBlock + '\n\n' + after;
179
+ }
180
+ function replaceConstantRefs(block, targetContent) {
181
+ const targetMatch = targetContent && targetContent.match(/(mockUrl|devUrl|prodUrl|dataKey)_([a-zA-Z0-9_]+)/);
182
+ if (!targetMatch)
183
+ return block;
184
+ const targetSuffix = targetMatch[2];
185
+ return block.replace(/(mockUrl|devUrl|prodUrl|dataKey)_[a-zA-Z0-9_]+/g, (m) => m.replace(/_[a-zA-Z0-9_]+$/, '_' + targetSuffix));
186
+ }
187
+ function appendInterfaceBlock(targetContent, newBlock) {
188
+ const insertMarker = '/* prettier-ignore-end */';
189
+ const idx = targetContent.lastIndexOf(insertMarker);
190
+ if (idx >= 0) {
191
+ return (targetContent.slice(0, idx).trimEnd() + '\n\n' + newBlock + '\n\n' + targetContent.slice(idx));
192
+ }
193
+ return targetContent.trimEnd() + '\n\n' + newBlock + '\n';
194
+ }
195
+ /** 接口模式:将 .temp 下的单接口文件合并到分类文件中 */
196
+ export function mergeTempToTarget(yapiDir, tempDir, interfaceIds, fileMapping, manualMapping, tempFilePattern = /^api_(\d+)\.ts$/) {
197
+ if (!existsSync(tempDir))
198
+ return;
199
+ const tempFiles = readdirSync(tempDir).filter((f) => tempFilePattern.test(f));
200
+ for (const file of tempFiles) {
201
+ const match = file.match(tempFilePattern);
202
+ if (!match || !interfaceIds.includes(match[1]))
203
+ continue;
204
+ const interfaceId = match[1];
205
+ const tempPath = join(tempDir, file);
206
+ const tempContent = readFileSync(tempPath, 'utf-8');
207
+ const catId = extractCatId(tempContent);
208
+ if (!catId) {
209
+ console.warn(`[yapi-gen] 无法从 ${file} 提取分类 ID,跳过`);
210
+ continue;
211
+ }
212
+ const dirName = getDirName(catId, fileMapping, manualMapping);
213
+ const targetDir = join(yapiDir, dirName);
214
+ const targetPath = join(targetDir, 'index.ts');
215
+ let targetContent = existsSync(targetPath) ? readFileSync(targetPath, 'utf-8') : '';
216
+ let newContent;
217
+ if (!targetContent || targetContent.trim() === '') {
218
+ mkdirSync(targetDir, { recursive: true });
219
+ newContent = tempContent;
220
+ console.log(`[yapi-gen] 已新增接口 ${interfaceId} -> ${dirName}/index.ts(新建分类)`);
221
+ }
222
+ else {
223
+ let newBlock = extractInterfaceBlock(tempContent, interfaceId);
224
+ if (!newBlock) {
225
+ console.warn(`[yapi-gen] 无法从 ${file} 提取接口块,跳过`);
226
+ continue;
227
+ }
228
+ newBlock = replaceConstantRefs(newBlock, targetContent);
229
+ const replaced = replaceInterfaceBlock(targetContent, interfaceId, newBlock);
230
+ if (!replaced) {
231
+ newContent = appendInterfaceBlock(targetContent, newBlock);
232
+ console.log(`[yapi-gen] 已新增接口 ${interfaceId} -> ${dirName}/index.ts`);
233
+ }
234
+ else {
235
+ newContent = replaced;
236
+ console.log(`[yapi-gen] 已更新接口 ${interfaceId} -> ${dirName}/index.ts`);
237
+ }
238
+ }
239
+ writeFileSync(targetPath, newContent, 'utf-8');
240
+ }
241
+ try {
242
+ rmSync(tempDir, { recursive: true });
243
+ }
244
+ catch (_) { }
245
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-config.d.ts","sourceRoot":"","sources":["../src/resolve-config.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,iBAAiB,EAAyB,MAAM,iBAAiB,CAAC;AAiFhF;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,iBAAiB,EAAE,GAAG,EAAE,MAAM,GAAG,iBAAiB,CA0CvF"}
1
+ {"version":3,"file":"resolve-config.d.ts","sourceRoot":"","sources":["../src/resolve-config.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,iBAAiB,EAAyB,MAAM,iBAAiB,CAAC;AA4FhF;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,iBAAiB,EAAE,GAAG,EAAE,MAAM,GAAG,iBAAiB,CA2CvF"}
@@ -1,8 +1,20 @@
1
1
  /**
2
2
  * 在运行 ytt 前解析配置:处理 requestAdapter 预设,生成并写入 request 文件,得到 ytt 可用的配置
3
3
  */
4
- import { existsSync, mkdirSync, writeFileSync } from 'fs';
5
- import { resolve, dirname } from 'path';
4
+ import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
5
+ import { resolve, dirname, join } from 'path';
6
+ import { fileURLToPath } from 'url';
7
+ function getPackageName() {
8
+ try {
9
+ const __dirname = dirname(fileURLToPath(import.meta.url));
10
+ const pkgPath = join(__dirname, '..', 'package.json');
11
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
12
+ return (pkg && pkg.name) || 'yapi-gen';
13
+ }
14
+ catch {
15
+ return 'yapi-gen';
16
+ }
17
+ }
6
18
  const DEFAULT_REQUEST_FILE_PATH = 'src/yapi/request.ts';
7
19
  const DEFAULT_AXIOS_INSTANCE_PATH = "/@/utils/http/axios";
8
20
  const DEFAULT_NUXT_REQUEST_IMPORT_PATH = '#imports';
@@ -46,7 +58,7 @@ function normalizeRequestAdapter(adapter) {
46
58
  }
47
59
  return { type: 'custom' };
48
60
  }
49
- function getAxiosAdapterFileContent(axiosInstancePath, exportName) {
61
+ function getAxiosAdapterFileContent(packageName, axiosInstancePath, exportName) {
50
62
  const importLine = exportName === 'default'
51
63
  ? `import defHttp from '${axiosInstancePath}';`
52
64
  : `import { defHttp } from '${axiosInstancePath}';`;
@@ -54,18 +66,18 @@ function getAxiosAdapterFileContent(axiosInstancePath, exportName) {
54
66
  * 由 yapi-gen 预设 "axios" 自动生成,请勿直接修改。
55
67
  * 如需自定义请求逻辑,请将配置改为 requestAdapter: 'custom' 并维护 requestFunctionFilePath 指向的文件。
56
68
  */
57
- import { createAxiosAdapter } from 'yapi-gen/adapters/axios';
69
+ import { createAxiosAdapter } from '${packageName}/adapters/axios';
58
70
  ${importLine}
59
71
 
60
72
  export default createAxiosAdapter(defHttp);
61
73
  `;
62
74
  }
63
- function getNuxtBbmAdapterFileContent(requestImportPath) {
75
+ function getNuxtBbmAdapterFileContent(packageName, requestImportPath) {
64
76
  return `/**
65
77
  * 由 yapi-gen 预设 "nuxt-bbm" 自动生成,请勿直接修改。
66
78
  * 如需自定义请求逻辑,请将配置改为 requestAdapter: 'custom' 并维护 requestFunctionFilePath 指向的文件。
67
79
  */
68
- import { createNuxtAdapter } from 'yapi-gen/adapters/nuxt-bbm';
80
+ import { createNuxtAdapter } from '${packageName}/adapters/nuxt-bbm';
69
81
  import { request } from '${requestImportPath}';
70
82
 
71
83
  export default createNuxtAdapter(request);
@@ -79,8 +91,9 @@ export function resolveConfig(config, cwd) {
79
91
  const resolved = { ...config };
80
92
  const adapter = normalizeRequestAdapter(resolved.requestAdapter);
81
93
  const requestFilePath = resolved.requestFunctionFilePath ?? adapter.requestFunctionFilePath ?? DEFAULT_REQUEST_FILE_PATH;
94
+ const packageName = getPackageName();
82
95
  if (adapter.type === 'axios') {
83
- const content = getAxiosAdapterFileContent(adapter.axiosInstancePath, adapter.axiosExportName ?? 'defHttp');
96
+ const content = getAxiosAdapterFileContent(packageName, adapter.axiosInstancePath, adapter.axiosExportName ?? 'defHttp');
84
97
  const absolutePath = resolve(cwd, requestFilePath);
85
98
  const dir = dirname(absolutePath);
86
99
  if (!existsSync(dir)) {
@@ -92,7 +105,7 @@ export function resolveConfig(config, cwd) {
92
105
  return resolved;
93
106
  }
94
107
  if (adapter.type === 'nuxt-bbm') {
95
- const content = getNuxtBbmAdapterFileContent(adapter.requestImportPath);
108
+ const content = getNuxtBbmAdapterFileContent(packageName, adapter.requestImportPath);
96
109
  const absolutePath = resolve(cwd, requestFilePath);
97
110
  const dir = dirname(absolutePath);
98
111
  if (!existsSync(dir)) {
package/dist/run.d.ts CHANGED
@@ -2,5 +2,6 @@ export declare function run(options?: {
2
2
  cwd?: string;
3
3
  configPath?: string;
4
4
  init?: boolean;
5
+ argv?: string[];
5
6
  }): number;
6
7
  //# sourceMappingURL=run.d.ts.map
package/dist/run.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAiGA,wBAAgB,GAAG,CAAC,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,MAAM,CAkC/F"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAqKA,wBAAgB,GAAG,CAAC,OAAO,GAAE;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACZ,GAAG,MAAM,CAqFd"}
package/dist/run.js CHANGED
@@ -1,11 +1,27 @@
1
1
  /**
2
2
  * CLI 执行逻辑:解析用户配置、生成 ytt 包装配置并执行 ytt
3
+ * 从本包依赖的 yapi-to-typescript 调用 ytt,无需使用方单独安装
4
+ * 支持目录映射、按接口/分类生成、合并模式(对应 web-finance-admin gen-api.mjs)
3
5
  */
4
6
  import { spawnSync } from 'child_process';
5
7
  import { existsSync, mkdirSync, writeFileSync } from 'fs';
6
- import { resolve, dirname, basename, relative } from 'path';
8
+ import { resolve, dirname, basename, relative, join } from 'path';
7
9
  import { fileURLToPath } from 'url';
10
+ import { createRequire } from 'module';
11
+ import { readFileSync } from 'fs';
12
+ import { parseArgs, loadMapping, saveMapping, renameDirsOnMappingChange, mappingForYtt, mergeTempToTarget, } from './gen-api-cli';
8
13
  const __dirname = dirname(fileURLToPath(import.meta.url));
14
+ const require = createRequire(import.meta.url);
15
+ function getPackageName() {
16
+ try {
17
+ const pkgPath = join(__dirname, '..', 'package.json');
18
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
19
+ return (pkg && pkg.name) || 'yapi-gen';
20
+ }
21
+ catch {
22
+ return 'yapi-gen';
23
+ }
24
+ }
9
25
  const CONFIG_NAMES = [
10
26
  'yapi-gen.config.ts',
11
27
  'yapi-gen.config.mts',
@@ -33,6 +49,7 @@ function findConfig(cwd) {
33
49
  * 该文件会 import 用户配置并传入 defineConfig
34
50
  */
35
51
  function getWrapperContent(cwd, configPath) {
52
+ const packageName = getPackageName();
36
53
  const absoluteConfig = resolve(cwd, configPath);
37
54
  const configDir = dirname(absoluteConfig);
38
55
  const configBase = basename(absoluteConfig).replace(/\.[^.]+$/, '') || 'yapi-gen.config';
@@ -41,7 +58,7 @@ function getWrapperContent(cwd, configPath) {
41
58
  const relativeImport = (rel ? rel + '/' : '') + configBase;
42
59
  return `/* 由 yapi-gen 自动生成,请勿修改 */
43
60
  import { defineConfig } from 'yapi-to-typescript';
44
- import { resolveConfig } from 'yapi-gen';
61
+ import { resolveConfig } from '${packageName}';
45
62
 
46
63
  const userConfig = await import('${relativeImport}');
47
64
  const raw = userConfig.default ?? userConfig;
@@ -53,23 +70,55 @@ export default defineConfig(resolvedList);
53
70
  }
54
71
  function runInit(cwd) {
55
72
  const configPath = resolve(cwd, 'yapi-gen.config.ts');
73
+ const outputDir = 'src/yapi';
74
+ const mappingPath = resolve(cwd, outputDir, 'gen-api-mapping.json');
75
+ let createdConfig = false;
56
76
  if (existsSync(configPath)) {
57
77
  console.log('[yapi-gen] 已存在 yapi-gen.config.ts,跳过创建');
58
78
  }
59
79
  else {
60
- const tpl = `import { defineYapiGenConfig } from 'yapi-gen';
80
+ const packageName = getPackageName();
81
+ const tpl = `/**
82
+ * YApi 接口代码生成配置
83
+ * 生成后执行: pnpm yapi-gen 或 pnpm gen:api
84
+ */
85
+ import { defineYapiGenConfig } from '${packageName}';
61
86
 
62
87
  export default defineYapiGenConfig({
88
+ // 替换为 Yapi 服务器地址
63
89
  serverUrl: 'https://yapi.example.com',
64
90
  serverType: 'yapi',
65
- // 请求适配:'axios' | 'nuxt-bbm' | 'custom',使用预设时可不填 requestFunctionFilePath
91
+ // 请求适配:'axios' | 'nuxt-bbm' | 'custom'
92
+ // axios 时可选指定实例路径,默认 '/@/utils/http/axios'
93
+ // requestAdapter: { type: 'axios', axiosInstancePath: '/@/utils/http/axios' },
66
94
  requestAdapter: 'axios',
67
- // requestFunctionFilePath: 'src/yapi/request.ts', // custom 时必填;预设时可选,默认 src/yapi/request.ts
68
95
  outputFilePath: (interfaceInfo, changeCase) => {
96
+ const ids = process.env.YTT_INTERFACE_IDS;
97
+ if (ids) return \`src/yapi/.temp/api_\${interfaceInfo._id}.ts\`;
69
98
  const catId = interfaceInfo.catid ?? 0;
70
- return \`src/yapi/cat_\${catId}/index.ts\`;
99
+ let dirName = \`cat_\${catId}\`;
100
+ try {
101
+ const mapping = process.env.YTT_CAT_MAPPING
102
+ ? (JSON.parse(process.env.YTT_CAT_MAPPING) as Record<string, string>)
103
+ : {};
104
+ if (mapping[String(catId)]) dirName = mapping[String(catId)];
105
+ } catch (_) {}
106
+ return \`src/yapi/\${dirName}/index.ts\`;
71
107
  },
72
108
  dataKey: 'result',
109
+ preproccessInterface(interfaceInfo) {
110
+ // 可选:按环境变量过滤接口。YTT_INTERFACE_IDS=1,2,3 或 YTT_CATEGORY_IDS=401,413
111
+ const ids = process.env.YTT_INTERFACE_IDS;
112
+ const catIds = process.env.YTT_CATEGORY_IDS;
113
+ if (ids) {
114
+ const allowList = ids.split(',').map((s) => parseInt(s.trim(), 10));
115
+ if (!allowList.includes(interfaceInfo._id)) return false;
116
+ } else if (catIds) {
117
+ const allowList = catIds.split(',').map((s) => s.trim());
118
+ if (!allowList.includes(String(interfaceInfo.catid))) return false;
119
+ }
120
+ return interfaceInfo;
121
+ },
73
122
  projects: [
74
123
  {
75
124
  token: process.env.YAPI_TOKEN || 'your-project-token',
@@ -86,13 +135,23 @@ export default defineYapiGenConfig({
86
135
  });
87
136
  `;
88
137
  writeFileSync(configPath, tpl, 'utf-8');
89
- console.log('[yapi-gen] 已创建 yapi-gen.config.ts,请按需修改 serverUrl、token 等');
138
+ console.log('[yapi-gen] 已创建 yapi-gen.config.ts');
139
+ createdConfig = true;
140
+ }
141
+ if (!existsSync(mappingPath)) {
142
+ mkdirSync(resolve(cwd, outputDir), { recursive: true });
143
+ writeFileSync(mappingPath, JSON.stringify({}, null, 2) + '\n', 'utf-8');
144
+ console.log('[yapi-gen] 已创建 src/yapi/gen-api-mapping.json(分类→目录名映射,可手动维护)');
145
+ }
146
+ if (createdConfig) {
147
+ console.log('[yapi-gen] 请修改 serverUrl、token 后执行 pnpm yapi-gen 生成接口');
90
148
  }
91
149
  return 0;
92
150
  }
93
151
  export function run(options = {}) {
94
152
  const cwd = options.cwd ?? process.cwd();
95
- if (options.init) {
153
+ const argv = options.argv ?? process.argv.slice(2);
154
+ if (options.init || argv.includes('init')) {
96
155
  return runInit(cwd);
97
156
  }
98
157
  const configPath = options.configPath ?? findConfig(cwd);
@@ -101,6 +160,36 @@ export function run(options = {}) {
101
160
  console.error('[yapi-gen] 或使用 init 子命令:pnpm yapi-gen init');
102
161
  return 1;
103
162
  }
163
+ // 解析 gen-api CLI 参数(目录映射、按接口/分类生成)
164
+ const args = parseArgs(argv, cwd);
165
+ const { interfaceIds, categoryIds, manualMapping, outputDir, mappingFilePath } = args;
166
+ const isInterfaceMode = interfaceIds.length > 0;
167
+ const isCategoryMode = categoryIds.length > 0;
168
+ if (isInterfaceMode && isCategoryMode) {
169
+ console.error('[yapi-gen] 接口模式与分类模式不能同时使用');
170
+ return 1;
171
+ }
172
+ const yapiDir = resolve(cwd, outputDir);
173
+ const tempDir = join(yapiDir, '.temp');
174
+ const fileMapping = loadMapping(mappingFilePath);
175
+ const mergedMapping = { ...fileMapping, ...manualMapping };
176
+ if (Object.keys(manualMapping).length > 0) {
177
+ renameDirsOnMappingChange(yapiDir, fileMapping, mergedMapping);
178
+ saveMapping(mappingFilePath, mergedMapping);
179
+ console.log('[yapi-gen] 已更新映射配置:', Object.keys(manualMapping).join(', '));
180
+ }
181
+ process.env.YTT_CAT_MAPPING = JSON.stringify(mappingForYtt(mergedMapping));
182
+ if (isInterfaceMode) {
183
+ process.env.YTT_INTERFACE_IDS = interfaceIds.join(',');
184
+ console.log(`[yapi-gen] 仅更新接口: ${interfaceIds.join(', ')}(合并模式,同分类其他接口保持不变)`);
185
+ }
186
+ else if (isCategoryMode) {
187
+ process.env.YTT_CATEGORY_IDS = categoryIds.join(',');
188
+ console.log(`[yapi-gen] 仅生成分类: cat_${categoryIds.join(', cat_')}`);
189
+ }
190
+ else {
191
+ console.log('[yapi-gen] 全量生成');
192
+ }
104
193
  const wrapperDir = resolve(cwd, WRAPPER_DIR);
105
194
  if (!existsSync(wrapperDir)) {
106
195
  mkdirSync(wrapperDir, { recursive: true });
@@ -108,10 +197,22 @@ export function run(options = {}) {
108
197
  const wrapperPath = resolve(wrapperDir, WRAPPER_FILENAME);
109
198
  const content = getWrapperContent(cwd, configPath);
110
199
  writeFileSync(wrapperPath, content, 'utf-8');
111
- const result = spawnSync('npx', ['ytt', '-c', wrapperPath], {
200
+ const yttCliPath = require.resolve('yapi-to-typescript/lib/cjs/cli.js');
201
+ const result = spawnSync(process.execPath, [yttCliPath, '-c', wrapperPath], {
112
202
  cwd,
113
203
  stdio: 'inherit',
114
- shell: true,
204
+ env: {
205
+ ...process.env,
206
+ YTT_INTERFACE_IDS: process.env.YTT_INTERFACE_IDS,
207
+ YTT_CATEGORY_IDS: process.env.YTT_CATEGORY_IDS,
208
+ YTT_CAT_MAPPING: process.env.YTT_CAT_MAPPING,
209
+ },
115
210
  });
116
- return result.status ?? 1;
211
+ if (result.status !== 0) {
212
+ return result.status ?? 1;
213
+ }
214
+ if (isInterfaceMode) {
215
+ mergeTempToTarget(yapiDir, tempDir, interfaceIds, fileMapping, manualMapping);
216
+ }
217
+ return 0;
117
218
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@karry.sun/yapi-gen",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "基于 yapi-to-typescript 的 YApi 前端代码生成 CLI,支持可配置的请求适配器与多项目复用",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -34,14 +34,6 @@
34
34
  "dependencies": {
35
35
  "yapi-to-typescript": "^3.38.0"
36
36
  },
37
- "peerDependencies": {
38
- "yapi-to-typescript": ">=3.0.0"
39
- },
40
- "peerDependenciesMeta": {
41
- "yapi-to-typescript": {
42
- "optional": true
43
- }
44
- },
45
37
  "devDependencies": {
46
38
  "typescript": "^5.0.0"
47
39
  },