anl 26.106.1 → 26.107.0
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.ar.md +300 -37
- package/README.es.md +299 -36
- package/README.fr.md +354 -36
- package/README.jp.md +246 -15
- package/README.md +262 -29
- package/README.ru.md +246 -15
- package/README.zh.md +289 -50
- package/lib/package.json.js +1 -1
- package/lib/src/build-type/core/path.js +1 -1
- package/lib/src/build-type/index.js +1 -1
- package/package.json +1 -1
package/README.zh.md
CHANGED
|
@@ -106,32 +106,30 @@ $ anl type
|
|
|
106
106
|
"saveApiListFolderPath": "apps/api/",
|
|
107
107
|
"saveEnumFolderPath": "apps/enums",
|
|
108
108
|
"importEnumPath": "../../enums",
|
|
109
|
-
"swaggerServers": {
|
|
110
|
-
"url": "https://generator3.swagger.io/openapi2.json",
|
|
111
|
-
"apiListFileName": "index.ts",
|
|
112
|
-
"publicPrefix": "api",
|
|
113
|
-
"headers": {}
|
|
114
|
-
},
|
|
115
109
|
"requestMethodsImportPath": "./fetch",
|
|
116
110
|
"dataLevel": "serve",
|
|
111
|
+
"parameterSeparator": "_",
|
|
117
112
|
"formatting": {
|
|
118
113
|
"indentation": "\t",
|
|
119
114
|
"lineEnding": "\n"
|
|
120
115
|
},
|
|
121
|
-
"
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
116
|
+
"swaggerServers": {
|
|
117
|
+
"url": "https://generator3.swagger.io/openapi2.json",
|
|
118
|
+
"apiListFileName": "index.ts",
|
|
119
|
+
"publicPrefix": "/api",
|
|
120
|
+
"pathPrefix": "/gateway",
|
|
121
|
+
"dataLevel": "serve",
|
|
122
|
+
"parameterSeparator": "_",
|
|
123
|
+
"headers": {
|
|
124
|
+
"Authorization": "Bearer token"
|
|
125
|
+
},
|
|
126
|
+
"includeInterface": [
|
|
127
|
+
{
|
|
128
|
+
"path": "/api/user",
|
|
129
|
+
"method": "get"
|
|
130
|
+
}
|
|
131
|
+
]
|
|
132
|
+
},
|
|
135
133
|
"enmuConfig": {
|
|
136
134
|
"erasableSyntaxOnly": false,
|
|
137
135
|
"varnames": "enum-varnames",
|
|
@@ -164,12 +162,22 @@ $ anl type
|
|
|
164
162
|
{
|
|
165
163
|
"url": "https://generator3.swagger.io/openapi1.json",
|
|
166
164
|
"apiListFileName": "op.ts",
|
|
167
|
-
"
|
|
165
|
+
"pathPrefix": "/forward",
|
|
166
|
+
"dataLevel": "serve",
|
|
167
|
+
"parameterSeparator": "_",
|
|
168
|
+
"headers": {},
|
|
169
|
+
"includeInterface": [
|
|
170
|
+
{
|
|
171
|
+
"path": "/op/trade/order/queryPage",
|
|
172
|
+
"method": "post"
|
|
173
|
+
}
|
|
174
|
+
]
|
|
168
175
|
},
|
|
169
176
|
{
|
|
170
177
|
"url": "https://generator3.swagger.io/openapi2.json",
|
|
171
178
|
"apiListFileName": "index.ts",
|
|
172
179
|
"publicPrefix": "/api",
|
|
180
|
+
"dataLevel": "data",
|
|
173
181
|
"headers": {}
|
|
174
182
|
}
|
|
175
183
|
]
|
|
@@ -178,30 +186,39 @@ $ anl type
|
|
|
178
186
|
|
|
179
187
|
#### 配置项说明
|
|
180
188
|
|
|
181
|
-
| 配置项
|
|
182
|
-
|
|
|
183
|
-
| saveTypeFolderPath
|
|
184
|
-
| saveApiListFolderPath
|
|
185
|
-
| saveEnumFolderPath
|
|
186
|
-
| importEnumPath
|
|
187
|
-
| swaggerJsonUrl
|
|
188
|
-
| swaggerServers
|
|
189
|
-
| swaggerServers[].url
|
|
190
|
-
| swaggerServers[].publicPrefix
|
|
191
|
-
| swaggerServers[].
|
|
192
|
-
| swaggerServers[].
|
|
193
|
-
|
|
|
194
|
-
| dataLevel
|
|
195
|
-
|
|
|
196
|
-
|
|
|
197
|
-
|
|
|
198
|
-
|
|
|
199
|
-
|
|
|
200
|
-
|
|
|
201
|
-
|
|
|
202
|
-
|
|
|
203
|
-
|
|
|
204
|
-
|
|
|
189
|
+
| 配置项 | 类型 | 必填 | 说明 |
|
|
190
|
+
| ----------------------------------- | ------------------------------------- | ---- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
191
|
+
| saveTypeFolderPath | string | 是 | 类型定义文件保存路径 |
|
|
192
|
+
| saveApiListFolderPath | string | 是 | API 请求函数文件保存路径 |
|
|
193
|
+
| saveEnumFolderPath | string | 是 | 枚举数据文件保存路径 |
|
|
194
|
+
| importEnumPath | string | 是 | 枚举导入路径(apps/types/models/\*.ts 中 enum 文件的引用的路径) |
|
|
195
|
+
| swaggerJsonUrl | string | 否 | Swagger JSON 文档地址(已迁移到 `swaggerServers`,保留用于兼容旧版配置)**后面迭代版本会删除该字段** |
|
|
196
|
+
| swaggerServers | object \| Array<object> | 否 | Swagger 服务器配置。单个服务器可直接填写对象,多个服务器使用数组。每个服务器可配置 `url`、`publicPrefix`、`pathPrefix`、`apiListFileName`、`headers`、`dataLevel`、`parameterSeparator`、`includeInterface`、`excludeInterface`<br />这个字段 对应 单 Swagger 服务器配置 与 多 Swagger 服务器配置 示例,请向上滚动查看 |
|
|
197
|
+
| swaggerServers[].url | string | 是 | Swagger JSON 文档地址 |
|
|
198
|
+
| swaggerServers[].publicPrefix | string | 否 | url path 上的公共前缀,例如:api/users、api/users/{id} ,api 就是公共前缀 |
|
|
199
|
+
| swaggerServers[].pathPrefix | string | 否 | 请求路径前缀(可以理解为模块名),会自动添加到每个 API 请求路径前面。<br />例如:`pathPrefix: "/forward"` 时,<br />`/publicPrefix/pathPrefix/user` , 会变成 `/api/forward/user` |
|
|
200
|
+
| swaggerServers[].apiListFileName | string | 否 | API 列表文件名,默认为 `index.ts`。多个服务器时,每个服务器的API 列表文件名必须唯一 |
|
|
201
|
+
| swaggerServers[].headers | object | 否 | 该服务器的请求头配置 |
|
|
202
|
+
| swaggerServers[].dataLevel | 'data' \| 'serve' \| 'axios' | 否 | 该服务器的接口返回数据层级。若未设置,使用全局 `dataLevel` 配置 |
|
|
203
|
+
| swaggerServers[].parameterSeparator | '$' \| '\_' | 否 | 该服务器生成 API 名称和类型名称时使用的分隔符。若未设置,使用全局 `parameterSeparator` 配置 |
|
|
204
|
+
| swaggerServers[].includeInterface | Array<{path: string, method: string}> | 否 | 该服务器包含的接口列表。若未设置,使用全局 `includeInterface` 配置 |
|
|
205
|
+
| swaggerServers[].excludeInterface | Array<{path: string, method: string}> | 否 | 该服务器排除的接口列表。若未设置,使用全局 `excludeInterface` 配置 |
|
|
206
|
+
| requestMethodsImportPath | string | 是 | 请求方法导入路径 |
|
|
207
|
+
| dataLevel | 'data' \| 'serve' \| 'axios' | 否 | 全局接口返回数据层级配置,默认值:`'serve'`。各服务器可单独配置覆盖 |
|
|
208
|
+
| formatting | object | 否 | 代码格式化配置 |
|
|
209
|
+
| formatting.indentation | string | 否 | 代码缩进字符,例如:`"\t"` 或 `" "`(两个空格) |
|
|
210
|
+
| formatting.lineEnding | string | 否 | 换行符,例如:`"\n"` (LF) 或 `"\r\n"` (CRLF) |
|
|
211
|
+
| headers | object | 否 | 全局请求头配置(已迁移到 `swaggerServers`,保留用于兼容旧版配置) |
|
|
212
|
+
| includeInterface | Array<{path: string, method: string}> | 否 | 全局包含的接口:`saveApiListFolderPath`指定的接口列表文件,只会包含列表中的接口,与 `excludeInterface` 字段互斥。各服务器可单独配置覆盖 |
|
|
213
|
+
| excludeInterface | Array<{path: string, method: string}> | 否 | 全局排除的接口: `saveApiListFolderPath` 指定的接口列表文本,不存在该列表中的接口,与 `includeInterface` 互斥。各服务器可单独配置覆盖 |
|
|
214
|
+
| publicPrefix | string | 否 | 全局 url path 上的公共前缀(已迁移到 `swaggerServers`,保留用于兼容旧版配置) |
|
|
215
|
+
| pathPrefix | string | 否 | 全局请求路径前缀(各服务器可单独配置覆盖) |
|
|
216
|
+
| apiListFileName | string | 否 | 全局 API 列表文件名,默认为 `index.ts`(已迁移到 `swaggerServers`,保留用于兼容旧版配置) |
|
|
217
|
+
| enmuConfig | object | 是 | 枚举配置对象 |
|
|
218
|
+
| enmuConfig.erasableSyntaxOnly | boolean | 是 | 与 tsconfig.json 的 `compilerOptions.erasableSyntaxOnly` 选项保持一致。为 `true` 时,生成 const 对象而非 enum(仅类型语法)。默认值:`false` |
|
|
219
|
+
| enmuConfig.varnames | string | 否 | Swagger schema 中自定义枚举成员名所在的字段名。默认值:`enum-varnames`。 |
|
|
220
|
+
| enmuConfig.comment | string | 否 | Swagger schema 中自定义枚举描述所在的字段名(用于生成注释)。默认值:`enum-descriptions`。 |
|
|
221
|
+
| parameterSeparator | '$' \| '\_' | 否 | 全局生成 API 名称和类型名称时,路径段和参数之间使用的分隔符。例如,`/users/{userId}/posts` 使用分隔符 `'_'` 会生成 `users_userId_posts_GET`。默认值:`'_'`。各服务器可单独配置覆盖 |
|
|
205
222
|
|
|
206
223
|
#### 配置项与生成的文件对应关系
|
|
207
224
|
|
|
@@ -255,6 +272,48 @@ export const userDetailGet = (params: UserDetail_GET.Query) => GET<UserDetail_GE
|
|
|
255
272
|
|
|
256
273
|
### 特性说明
|
|
257
274
|
|
|
275
|
+
#### 配置优先级
|
|
276
|
+
|
|
277
|
+
工具支持全局配置和服务器级别配置,遵循以下优先级规则:
|
|
278
|
+
|
|
279
|
+
**优先级:服务器级别配置 > 全局配置 > 默认值**
|
|
280
|
+
|
|
281
|
+
以下配置项支持服务器级别覆盖全局配置:
|
|
282
|
+
|
|
283
|
+
- `dataLevel`:接口返回数据层级
|
|
284
|
+
- `parameterSeparator`:API 名称和类型名称的分隔符
|
|
285
|
+
- `includeInterface`:包含的接口列表
|
|
286
|
+
- `excludeInterface`:排除的接口列表
|
|
287
|
+
- `pathPrefix`:请求路径前缀
|
|
288
|
+
- `publicPrefix`:URL 公共前缀
|
|
289
|
+
- `headers`:请求头配置
|
|
290
|
+
|
|
291
|
+
**示例:**
|
|
292
|
+
|
|
293
|
+
```json
|
|
294
|
+
{
|
|
295
|
+
"dataLevel": "serve",
|
|
296
|
+
"parameterSeparator": "_",
|
|
297
|
+
"swaggerServers": [
|
|
298
|
+
{
|
|
299
|
+
"url": "http://api1.example.com/swagger.json",
|
|
300
|
+
"dataLevel": "data",
|
|
301
|
+
"apiListFileName": "api1.ts"
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
"url": "http://api2.example.com/swagger.json",
|
|
305
|
+
"apiListFileName": "api2.ts"
|
|
306
|
+
}
|
|
307
|
+
]
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
在上面的配置中:
|
|
312
|
+
|
|
313
|
+
- `api1.ts` 使用 `dataLevel: "data"`(服务器级别配置)
|
|
314
|
+
- `api2.ts` 使用 `dataLevel: "serve"`(全局配置)
|
|
315
|
+
- 两个服务器都使用 `parameterSeparator: "_"`(全局配置)
|
|
316
|
+
|
|
258
317
|
#### 类型解析
|
|
259
318
|
|
|
260
319
|
- 支持所有 OpenAPI 3.0 规范的数据类型
|
|
@@ -305,6 +364,46 @@ interface User {
|
|
|
305
364
|
}
|
|
306
365
|
```
|
|
307
366
|
|
|
367
|
+
#### 数据层级配置(dataLevel)
|
|
368
|
+
|
|
369
|
+
`dataLevel` 用于配置接口返回数据的提取层级,支持三个选项:
|
|
370
|
+
|
|
371
|
+
1. **`'serve'`(默认值)**:提取服务器返回的 `data` 字段
|
|
372
|
+
|
|
373
|
+
```typescript
|
|
374
|
+
// 服务器返回: { code: 200, message: 'success', data: { id: 1, name: 'user' } }
|
|
375
|
+
// 函数返回: { id: 1, name: 'user' }
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
2. **`'data'`**:提取 `data.data` 字段(适用于嵌套 data 的场景)
|
|
379
|
+
|
|
380
|
+
```typescript
|
|
381
|
+
// 服务器返回: { data: { code: 200, data: { id: 1, name: 'user' } } }
|
|
382
|
+
// 函数返回: { id: 1, name: 'user' }
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
3. **`'axios'`**:返回完整的 axios 响应对象
|
|
386
|
+
```typescript
|
|
387
|
+
// 服务器返回: { code: 200, message: 'success', data: { id: 1, name: 'user' } }
|
|
388
|
+
// 函数返回: { code: 200, message: 'success', data: { id: 1, name: 'user' } }
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
**配置示例:**
|
|
392
|
+
|
|
393
|
+
```json
|
|
394
|
+
{
|
|
395
|
+
"dataLevel": "serve",
|
|
396
|
+
"swaggerServers": [
|
|
397
|
+
{
|
|
398
|
+
"url": "http://api1.example.com/swagger.json",
|
|
399
|
+
"dataLevel": "data"
|
|
400
|
+
}
|
|
401
|
+
]
|
|
402
|
+
}
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
> **注意**:服务器级别的 `dataLevel` 配置会覆盖全局配置。
|
|
406
|
+
|
|
308
407
|
#### 文件上传
|
|
309
408
|
|
|
310
409
|
当检测到文件上传类型时,会自动添加对应的请求头:
|
|
@@ -316,6 +415,33 @@ export const uploadFile = (params: UploadFile.Body) =>
|
|
|
316
415
|
});
|
|
317
416
|
```
|
|
318
417
|
|
|
418
|
+
#### 代码格式化
|
|
419
|
+
|
|
420
|
+
工具支持自定义代码格式化选项,通过 `formatting` 配置控制:
|
|
421
|
+
|
|
422
|
+
**配置示例:**
|
|
423
|
+
|
|
424
|
+
```json
|
|
425
|
+
{
|
|
426
|
+
"formatting": {
|
|
427
|
+
"indentation": "\t",
|
|
428
|
+
"lineEnding": "\n"
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
**配置说明:**
|
|
434
|
+
|
|
435
|
+
- `indentation`:代码缩进字符
|
|
436
|
+
- `"\t"`:使用 Tab 缩进(默认)
|
|
437
|
+
- `" "`:使用 2 个空格缩进
|
|
438
|
+
- `" "`:使用 4 个空格缩进
|
|
439
|
+
- `lineEnding`:换行符类型
|
|
440
|
+
- `"\n"`:LF(Linux/macOS 风格,推荐)
|
|
441
|
+
- `"\r\n"`:CRLF(Windows 风格)
|
|
442
|
+
|
|
443
|
+
**注意:** 如果项目中配置了 Prettier,生成的代码会自动使用 Prettier 进行格式化,`formatting` 配置可能会被 Prettier 覆盖。
|
|
444
|
+
|
|
319
445
|
#### 错误处理
|
|
320
446
|
|
|
321
447
|
工具内置了完善的错误处理机制:
|
|
@@ -372,6 +498,51 @@ export const uploadFile = (params: UploadFile.Body) =>
|
|
|
372
498
|
- 后续服务器的 API 会追加到各自的 `apiListFileName` 文件中
|
|
373
499
|
- 类型定义和枚举会合并到统一的文件夹中,避免重复
|
|
374
500
|
|
|
501
|
+
**服务器级别配置:**
|
|
502
|
+
|
|
503
|
+
每个服务器支持独立配置以下选项,若未设置则使用全局配置:
|
|
504
|
+
|
|
505
|
+
- `dataLevel` - 接口返回数据层级
|
|
506
|
+
- `parameterSeparator` - API 名称和类型名称的分隔符
|
|
507
|
+
- `includeInterface` - 包含的接口列表
|
|
508
|
+
- `excludeInterface` - 排除的接口列表
|
|
509
|
+
- `pathPrefix` - 请求路径前缀
|
|
510
|
+
|
|
511
|
+
#### 路径前缀(pathPrefix)
|
|
512
|
+
|
|
513
|
+
`pathPrefix` 用于在所有 API 请求路径前自动添加前缀,这在以下场景特别有用:
|
|
514
|
+
|
|
515
|
+
1. **反向代理场景**:当后端服务通过反向代理转发时
|
|
516
|
+
2. **API 网关**:统一在路径前添加网关前缀
|
|
517
|
+
3. **多环境配置**:不同环境使用不同的路径前缀
|
|
518
|
+
|
|
519
|
+
**使用示例:**
|
|
520
|
+
|
|
521
|
+
```json
|
|
522
|
+
{
|
|
523
|
+
"swaggerServers": [
|
|
524
|
+
{
|
|
525
|
+
"url": "http://api.example.com/swagger.json",
|
|
526
|
+
"pathPrefix": "/forward",
|
|
527
|
+
"apiListFileName": "api.ts"
|
|
528
|
+
}
|
|
529
|
+
]
|
|
530
|
+
}
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
**效果:**
|
|
534
|
+
|
|
535
|
+
Swagger 中定义的路径 `/api/user/list` 会生成为:
|
|
536
|
+
|
|
537
|
+
```typescript
|
|
538
|
+
export const apiUserListGet = (params: ApiUserList_GET.Query) => GET<ApiUserList_GET.Response>('/forward/api/user/list', params);
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
**与 publicPrefix 的区别:**
|
|
542
|
+
|
|
543
|
+
- `publicPrefix`:用于从接口路径中移除公共前缀(仅影响生成的函数名)
|
|
544
|
+
- `pathPrefix`:用于在实际请求路径前添加前缀(影响运行时的请求 URL)
|
|
545
|
+
|
|
375
546
|
**配置示例:**
|
|
376
547
|
|
|
377
548
|
```json
|
|
@@ -381,14 +552,24 @@ export const uploadFile = (params: UploadFile.Body) =>
|
|
|
381
552
|
"url": "http://api1.example.com/swagger.json",
|
|
382
553
|
"apiListFileName": "api1.ts",
|
|
383
554
|
"publicPrefix": "/api/v1",
|
|
555
|
+
"pathPrefix": "/forward",
|
|
556
|
+
"dataLevel": "serve",
|
|
557
|
+
"parameterSeparator": "_",
|
|
384
558
|
"headers": {
|
|
385
559
|
"Authorization": "Bearer token1"
|
|
386
|
-
}
|
|
560
|
+
},
|
|
561
|
+
"includeInterface": [
|
|
562
|
+
{
|
|
563
|
+
"path": "/api/v1/users",
|
|
564
|
+
"method": "get"
|
|
565
|
+
}
|
|
566
|
+
]
|
|
387
567
|
},
|
|
388
568
|
{
|
|
389
569
|
"url": "http://api2.example.com/swagger.json",
|
|
390
570
|
"apiListFileName": "api2.ts",
|
|
391
571
|
"publicPrefix": "/api/v2",
|
|
572
|
+
"dataLevel": "data",
|
|
392
573
|
"headers": {
|
|
393
574
|
"Authorization": "Bearer token2"
|
|
394
575
|
}
|
|
@@ -422,21 +603,79 @@ export const uploadFile = (params: UploadFile.Body) =>
|
|
|
422
603
|
|
|
423
604
|
1. 确保 Swagger JSON 文档地址可访问
|
|
424
605
|
2. 配置文件中的路径需要是相对于项目根目录的路径
|
|
425
|
-
3.
|
|
606
|
+
3. 生成的文件会覆盖已存在的同名文件(但 `config.ts`、`error-message.ts`、`fetch.ts`、`api-type.d.ts` 这些文件如果已存在则不会覆盖)
|
|
426
607
|
4. 建议将生成的文件加入版本控制
|
|
427
608
|
5. 使用多 Swagger 服务器时,确保每个服务器的 `apiListFileName` 唯一,避免文件覆盖
|
|
428
609
|
6. 多个服务器配置时,类型定义和枚举会合并,如果不同服务器有同名类型,可能会产生冲突
|
|
610
|
+
7. 服务器级别的配置(`dataLevel`、`parameterSeparator`、`includeInterface`、`excludeInterface`、`pathPrefix`)会覆盖全局配置
|
|
611
|
+
8. `includeInterface` 和 `excludeInterface` 不能同时配置,如果同时配置,会优先使用 `includeInterface`
|
|
429
612
|
|
|
430
613
|
### 常见问题
|
|
431
614
|
|
|
432
|
-
1.
|
|
615
|
+
1. **生成的类型文件格式化失败**
|
|
433
616
|
- 检查是否安装了 prettier
|
|
434
617
|
- 确认项目根目录下是否有 prettier 配置文件
|
|
618
|
+
- 检查 `formatting` 配置是否正确
|
|
435
619
|
|
|
436
|
-
2.
|
|
437
|
-
- 检查 requestMethodsImportPath 配置是否正确
|
|
620
|
+
2. **请求函数导入路径错误**
|
|
621
|
+
- 检查 `requestMethodsImportPath` 配置是否正确
|
|
438
622
|
- 确认请求方法文件是否存在
|
|
439
623
|
|
|
624
|
+
3. **什么时候使用 `pathPrefix`?**
|
|
625
|
+
- 当你的 API 需要通过反向代理或网关访问时
|
|
626
|
+
- 例如:Swagger 中定义的是 `/api/user`,但实际请求需要是 `/gateway/api/user`
|
|
627
|
+
- 设置 `pathPrefix: "/gateway"` 即可
|
|
628
|
+
|
|
629
|
+
4. **`publicPrefix` 和 `pathPrefix` 有什么区别?**
|
|
630
|
+
- `publicPrefix`:从接口路径中移除前缀,只影响生成的函数名
|
|
631
|
+
- 例如:`/api/user/list` 移除 `/api` 后,函数名为 `userListGet`
|
|
632
|
+
- `pathPrefix`:在请求路径前添加前缀,影响实际请求的 URL
|
|
633
|
+
- 例如:`/api/user/list` 添加 `/forward` 后,请求 URL 为 `/forward/api/user/list`
|
|
634
|
+
|
|
635
|
+
5. **多个服务器如何配置不同的 `dataLevel`?**
|
|
636
|
+
|
|
637
|
+
```json
|
|
638
|
+
{
|
|
639
|
+
"dataLevel": "serve",
|
|
640
|
+
"swaggerServers": [
|
|
641
|
+
{
|
|
642
|
+
"url": "http://old-api.com/swagger.json",
|
|
643
|
+
"dataLevel": "axios",
|
|
644
|
+
"apiListFileName": "old-api.ts"
|
|
645
|
+
},
|
|
646
|
+
{
|
|
647
|
+
"url": "http://new-api.com/swagger.json",
|
|
648
|
+
"apiListFileName": "new-api.ts"
|
|
649
|
+
}
|
|
650
|
+
]
|
|
651
|
+
}
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
- `old-api.ts` 使用 `dataLevel: "axios"`
|
|
655
|
+
- `new-api.ts` 使用全局的 `dataLevel: "serve"`
|
|
656
|
+
|
|
657
|
+
6. **如何只生成部分接口?**
|
|
658
|
+
- 使用 `includeInterface` 配置:
|
|
659
|
+
```json
|
|
660
|
+
{
|
|
661
|
+
"swaggerServers": [
|
|
662
|
+
{
|
|
663
|
+
"url": "http://api.com/swagger.json",
|
|
664
|
+
"includeInterface": [
|
|
665
|
+
{ "path": "/api/user", "method": "get" },
|
|
666
|
+
{ "path": "/api/user/{id}", "method": "post" }
|
|
667
|
+
]
|
|
668
|
+
}
|
|
669
|
+
]
|
|
670
|
+
}
|
|
671
|
+
```
|
|
672
|
+
- 或使用 `excludeInterface` 排除不需要的接口
|
|
673
|
+
|
|
674
|
+
7. **生成的文件被覆盖了怎么办?**
|
|
675
|
+
- `config.ts`、`error-message.ts`、`fetch.ts`、`api-type.d.ts` 这些文件只会在首次不存在时生成
|
|
676
|
+
- API 列表文件和类型文件每次都会重新生成
|
|
677
|
+
- 建议将生成的文件纳入版本控制,便于查看变更
|
|
678
|
+
|
|
440
679
|
# `anl lint` 命令使用说明
|
|
441
680
|
|
|
442
681
|
> 提供**交互式多选**配置前端项目各种 lint 工具的功能,包括:
|
package/lib/package.json.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e="26.
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e="26.107.0",i="FE command line tool",t="bin/an-cli.js",s={registry:"https://registry.npmjs.org"},o={dev:"rollup --config rollup.config.ts --configPlugin @rollup/plugin-typescript -w",build:"rimraf lib && rollup --config rollup.config.ts --configPlugin @rollup/plugin-typescript",pub:"bash publish.sh",ts:"tsc ./src/int.ts --noEmit --watch",blink:"npm run build && npm link","sync-docs":"node scripts/sync-docs.js"},r={anl:"bin/an-cli.js"},n="Gleason <bianliuzhu@gmail.com>",l={"@commitlint/cli":"^17.4.3","@commitlint/config-conventional":"^17.4.3","@rollup/plugin-commonjs":"^21.0.1","@rollup/plugin-json":"^4.1.0","@rollup/plugin-node-resolve":"^13.1.3","@rollup/plugin-typescript":"^8.3.0","@types/inquirer":"^9.0.7","@types/shelljs":"^0.8.11","@typescript-eslint/eslint-plugin":"^5.52.0","@typescript-eslint/parser":"^5.52.0","cross-env":"^7.0.3",eslint:"^8.7.0",husky:"^8.0.3","openapi-types":"^12.1.3",prettier:"^3.3.2",rimraf:"^5.0.7",rollup:"^2.64.0","rollup-plugin-cleandir":"^2.0.0","rollup-plugin-copy":"^3.5.0","rollup-plugin-terser":"^7.0.2",typescript:"^4.5.4"},p={"app-root-path":"^3.1.0",cac:"^6.7.12",chalk:"4.*","clear-console":"^1.1.0",commander:"14.0.1",figures:"^6.1.0",inquirer:"^10.1.8","log-symbols":"^5.1.0",ora:"5.*","progress-estimator":"^0.3.0",shelljs:"^0.8.5"},c=["cli","command-line","frontend","typescript","type-generation","code-generator","swagger","openapi","api","rest-api","eslint","stylelint","prettier","commitlint","linter","formatter","react","vue","git","gitflow","developer-tools","automation","scaffolding"],a=["package.json","README.md","lib","template"],u={type:"git",url:"git+https://github.com/bianliuzhu/an-cli.git"},g="commonjs",m={name:"anl",version:e,description:i,main:t,publishConfig:s,scripts:o,bin:r,author:n,license:"ISC",devDependencies:l,dependencies:p,keywords:c,files:a,repository:u,type:g};exports.author=n,exports.bin=r,exports.default=m,exports.dependencies=p,exports.description=i,exports.devDependencies=l,exports.files=a,exports.keywords=c,exports.license="ISC",exports.main=t,exports.name="anl",exports.publishConfig=s,exports.repository=u,exports.scripts=o,exports.type=g,exports.version=e;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("../../utils/index.js");const t="\t";var n;!function(e){e.GET="get",e.PUT="put",e.POST="post",e.DELETE="delete",e.OPTIONS="options",e.HEAD="head",e.PATCH="patch",e.TRACE="trace"}(n||(n={}));const r={typeMapping:new Map([["integer","number"],["string","string"],["boolean","boolean"],["binary","File"],["number","number"],["null","null"],["undefined","undefined"],["date","Date"],["time","Date"],["datetime","Date"],["timestamp","Date"]]),errorHandling:{throwOnError:!1,logErrors:!0}},s="#/components/schemas/",a="#/components/parameters/",i="#/definitions/",o=["application/json","text/json","text/plain","application/x-www-form-urlencoded","application/xml","text/xml","*/*","application/octet-stream","multipart/form-data"],c=["application/octet-stream","multipart/form-data"];class p{pathsObject={};nonArrayType=["boolean","object","number","string","integer"];pathKey="";contentBody={payload:{path:[],query:[],body:[]},response:"",_response:"",fileName:"",method:"",requestPath:"",summary:"",apiName:"",typeName:"",deprecated:!1,contentType:"application/json"};Map=new Map;config;errors=[];referenceCache=new Map;parameters={};schemas={};templates={exportConst:e=>`export const ${e}`,typeDefinition:(e,t)=>`type ${e} = ${t}`,interfaceDefinition:e=>`interface ${e}`};constructor(e,t,n,s){this.pathsObject=e,this.parameters=t??{},this.schemas=n??{},this.config={...r,...s,typeMapping:new Map([...r.typeMapping||[],...s.typeMapping||[]])}}handleError(t){if(this.errors.push(t),this.config.errorHandling?.logErrors&&e.log.error(`${t.type}: ${t.message}${t.path?` at ${t.path}`:""}`),this.config.errorHandling?.throwOnError)throw new Error(`${t.type}: ${t.message}`)}getIndentation(){return this.config.formatting?.indentation||t}getEnumTypeName(e){return this.config.enmuConfig.erasableSyntaxOnly?`${e}Type`:e}typeNameToFileName(e){return e=(e=(e=e.replace(/_/g,"-")).replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase()).replace(/-+/g,"-")}stringifySchemaResult(e){if(Array.isArray(e)){const t=this.config.formatting?.lineEnding??"\n",n=this.getIndentation();return`{${t}${e.join(t)}${t}${n}}`}return e}handleComplexType(e){try{return e.oneOf?e.oneOf.map(e=>this.stringifySchemaResult(this.schemaParse(e))).join(" | "):e.allOf?e.allOf.map(e=>this.stringifySchemaResult(this.schemaParse(e))).join(" & "):e.anyOf?e.anyOf.map(e=>this.stringifySchemaResult(this.schemaParse(e))).join(" | "):e.enum?"number"===e.type||"integer"===e.type?e.enum.join(" | "):e.enum.map(e=>`'${e}'`).join(" | "):"unknown"}catch(e){return this.handleError({type:"SCHEMA",message:"Failed to handle complex type",details:e}),"unknown"}}propertiesParse(e){if(!e)return[];const n=[];for(const r in e){const s=e[r],a=this.schemaParse(s);let i="";i=Array.isArray(a)?`${t}${t}${r}: {${a.join("\n")}};`:`${t}${t}${r}: ${a};`,n.push(i)}return n}nonArraySchemaObjectParse(e){if(!e)return"unknown";if("binary"===e.format||"string"===e.type&&"binary"===e.format)return"File";switch(e.type){case"boolean":return"boolean";case"integer":case"number":return"number";case"object":return this.propertiesParse(e.properties);case"string":return"binary"===e.format?"File":"string";default:return"unknown"}}arraySchemaObjectParse(e){if("array"!==e.type)return"";const{items:t}=e,n="$ref"in t?t:null,r=t;if(n){return`Array<${this.referenceObjectParse(n)}>`}if(r){const e=this.schemaParse(t);return Array.isArray(e)?`Array<{${e.join("\n")}}>`:`Array<${e}>`}return""}referenceObjectParse(e){try{const t=e.$ref,n=this.referenceCache.get(t);if(n)return n;let r=t;t.startsWith(s)&&(r=t.replace(s,"")),t.startsWith(a)&&(r=t.replace(a,"")),t.startsWith(i)&&(r=t.replace(i,""));const o=this.typeNameToFileName(r),c=/enum/gi.test(r),p=this.schemas?.[r],l=p&&"enum"in p&&Array.isArray(p.enum),h=c||l,y=h?this.getEnumTypeName(r):r,m=h?`import('${this.config.importEnumPath}/${o}').${y}`:`import('../models/${o}').${r}`;return this.referenceCache.set(t,m),m}catch(e){return this.handleError({type:"REFERENCE",message:"Failed to parse reference object",details:e}),"unknown"}}schemaParse(e){try{if(!e)return"unknown";if("oneOf"in e||"allOf"in e||"anyOf"in e||"enum"in e)return this.handleComplexType(e);if("$ref"in e)return this.referenceObjectParse(e);const t=e,n=t.type,r=!0===t.nullable?" | null":"";if(e.format&&this.config.typeMapping?.has(e.format))return this.config.typeMapping.get(e.format)+r;if(n&&this.config.typeMapping?.has(n))return this.config.typeMapping.get(n)+r;if("array"===n&&t.items){const e=this.schemaParse(t.items);if(Array.isArray(e)){const t=this.config.formatting?.lineEnding,n=this.config.formatting?.indentation;return`Array<{${t}${e.join("\n")}${t}${n}${n}}>`}return`Array<${e}>`}if("object"===n||"object"==typeof t){if(t.properties){const e=this.propertiesParse(t.properties);return e.length?e:["unknown"]}if(!0===t.additionalProperties)return"Record<string, unknown>"+r;if("object"==typeof t.additionalProperties){return`Record<string, ${this.schemaParse(t.additionalProperties)}>`+r}}return"unknown"}catch(e){return this.handleError({type:"SCHEMA",message:"Failed to parse schema",details:e}),"unknown"}}responseObjectParse(e){try{const t=e.content;if(!t)return"";let n;for(const e of o)if(t[e]?.schema){n=t[e].schema;break}return n?this.schemaParse(n):""}catch(e){return this.handleError({type:"RESPONSE",message:"Failed to parse response object",details:e}),""}}responseHandle(e){const t=e[200];if(!t)return;const n="content"in t?t:null,r="$ref"in t?t:null;if(null===n&&null===r&&(this.contentBody.response="type Response = unknown",this.contentBody._response="unknown"),r){const e=this.referenceObjectParse(r);this.contentBody.response=`type Response = ${e}`,this.contentBody._response=e}if(n){const e=this.responseObjectParse(n);if(Array.isArray(e)){if(1===e.length&&"unknown"===e[0])this.contentBody.response=`type Response = ${e.join("\n")};`;else{const t=this.config.formatting?.lineEnding,n=this.config.formatting?.indentation;this.contentBody.response=`interface Response {${t}${e.join("\n")}${t}${n}};`}this.contentBody._response=`${e.join("\n")}`}else this.contentBody.response=`type Response = ${e}`,this.contentBody._response=`${e}`}}requestBodyObjectParse(e){const n=Object.values(e.content),{schema:r}=n[0]||{schema:null};if(r){const e=r?.type,n="$ref"in r?r:null,s="array"===e?r:null,a=e&&this.nonArrayType.includes(e)?r:null;if(n){const e=this.referenceObjectParse(n);return`${t}type Body = ${e}`}if(s){const e=this.arraySchemaObjectParse(s);return`${t}type Body = ${e}`}if(a){const e=this.nonArraySchemaObjectParse(a);return Array.isArray(e)?0===e.length?[`${t}type Body = ${a.type};`]:[`${t}interface Body {`,...e.map(e=>e.replace(/: string;/,": File;")),"}"]:[`${t}type Body = ${e}`]}}}requestBodyParse(e){if(!e)return"{}";const n="$ref"in e?e:null,r="content"in e?e:null;if(n){const e=this.referenceObjectParse(n);return`${t}type Body = ${e}`}return r&&"[object Object]"===String(e)&&0!==Reflect.ownKeys(e).length?this.requestBodyObjectParse(r):"{}"}parametersItemHandle(e,n,r){const s="$ref"in e?e:null,i="name"in e?e:null;if(s&&s.$ref&&s.$ref.startsWith(a)&&this.parameters){const e=s.$ref.replace(a,""),t=this.parameters[e];this.parametersItemHandle(t,n,r)}if(i){if("path"===i.in){const e=this.schemaParse(i.schema);n.push(`${t}${t}type ${i.name} = ${e};`),this.contentBody.payload._path?"string"==typeof e?this.contentBody.payload._path[i.name]=e:console.log("Unexpected v2value type:",e):"string"==typeof e?this.contentBody.payload._path={[i.name]:e}:console.log("Unexpected v2value type:",e)}if("query"===i.in){const e=this.schemaParse(i.schema);r.push(`${t}${t}${i.name}: ${e};`),this.contentBody.payload._query?"string"==typeof e?this.contentBody.payload._query[i.name]=e:console.log("Unexpected v2value type:",e):"string"==typeof e?this.contentBody.payload._query={[i.name]:e}:console.log("Unexpected v2value type:",e)}}}requestParametersParse(e){const n=[],r=[];e?.map(e=>this.parametersItemHandle(e,n,r)),0!==n.length&&(n.unshift(`${t}namespace Path {`),n.push(`${t}}`)),0!==r.length&&(r.unshift(`${t}interface Query {`),r.push(`${t}}`)),this.contentBody.payload.path=n,this.contentBody.payload.query=r}requestHandle(e){if(e.parameters&&this.requestParametersParse(e.parameters),e.requestBody){const t=this.requestBodyParse(e.requestBody);if(Array.isArray(t))this.contentBody.payload.body=t;else if(t){const e=t?.split("\n")||[];this.contentBody.payload.body=e}}}apiRequestItemHandle(e){const{payload:t,requestPath:n,_response:r,method:s,typeName:a,apiName:i,contentType:o}=e,{_path:p,_query:l,body:h}=t,y=this.config.dataLevel||"serve",m=(()=>{const e=[];for(const t in p)e.push(`${t}: ${a}.Path.${t}`);const t=e.join(e.length>1?",":"");return""===t?t:t+","})(),u=(()=>{const e=l?`query: ${a}.Query,`:"";return""===e?"":`${e}`})(),d=(()=>{const e=h.length>0?`body: ${a}.Body,`:"";return""===e?"":`${e}`})(),f=(m+u+d).replace(/,$/,"");return[`export const ${i} = `,"(",f,""===f?"params?: IRequestFnParams":", params?: IRequestFnParams",")"," => ",s,""+(r?`<${a}.Response>`:""),"(","`"+n+"`,",(()=>{const e=c.includes(o)?`headers: { 'Content-Type': '${o}' }`:void 0;return["{",e?`${e},`:"","...params, ",""===u?"":"query,",""===d?"":"body,","},"].join("")})(),`'${y}'`,");"].join("")}parsePathItemObject(e,t){if(e)for(const n in e){const r=e[n];if(r){if(this.config.includeInterface&&this.config.includeInterface.length>0){const e=this.config.includeInterface?.find(e=>t.includes(e.path)&&e.method===n);if(!e)return}else if(this.config.excludeInterface&&this.config.excludeInterface.length>0){const e=this.config.excludeInterface?.find(e=>t.includes(e.path)&&e.method===n);if(e)return}const e=n.toUpperCase(),s=t+"|"+e,{apiName:a,typeName:i,fileName:c,path:p}=this.convertEndpointString(s),l=r.requestBody&&"content"in r.requestBody&&r.requestBody.content,h="object"==typeof l?Object.keys(l)[0]:"application/json";this.contentBody={payload:{path:[],query:[],body:[]},response:"",_response:"",fileName:c,method:e,typeName:i,requestPath:p,apiName:a,summary:r.summary,deprecated:r.deprecated??!1,contentType:o.includes(h)?h:"application/json"},this.requestHandle(r),this.responseHandle(r.responses),this.Map.has(s)||this.Map.set(s,JSON.parse(JSON.stringify(this.contentBody)))}}}cleanSegment(e){let t=e.replace(/^[0-9]+/,"");return t?(t=t.replace(/[^a-zA-Z0-9_.-]/g,""),t):"num"+e}toCamelCase(e,t=!1){const n=this.cleanSegment(e).split(/[-_.]+/).filter(e=>e.length>0);return 0===n.length?"":n.map((e,n)=>0===n&&t?e.toLowerCase():e.charAt(0).toUpperCase()+e.slice(1).toLowerCase()).join("")}toPascalCase(e){const t=this.cleanSegment(e).split(/[-_.]+/).filter(e=>e.length>0);return 0===t.length?"":t.map(e=>e.charAt(0).toUpperCase()+e.slice(1).toLowerCase()).join("")}convertEndpointString(e){let t=e;e.startsWith("/")||(t="/"+e);const[n,r]=t.split("|");let s="";this.config.publicPrefix&&(s=this.config.publicPrefix.replace(/^\/+|\/+$/g,""));let a=n.replace(/^\/+/,"");if(s&&a.startsWith(s+"/"))a=a.slice(s.length+1);else if(s&&a===s)a="";else if(s&&a.startsWith(s)){const e=a.slice(s.length);(e.startsWith("/")||""===e)&&(a=e.replace(/^\/+/,""))}const i=a?a.split("/").filter(e=>e):[],o=i.map(e=>{if(e.startsWith("{")&&e.endsWith("}")){const t=e.slice(1,-1);return{type:"param",original:e,normalized:t}}return{type:"normal",original:e,normalized:e}}),c=[];for(let e=0;e<o.length;e++){const t=o[e],n=o[e+1];if("normal"===t.type&&n&&"param"===n.type){if(this.cleanSegment(t.normalized).toLowerCase().replace(/[-_.]/g,"")===this.cleanSegment(n.normalized).toLowerCase().replace(/[-_.]/g,""))continue}c.push(t)}const p=this.config.parameterSeparator||"_";let l="",h=[];const y=e=>{if(h.length>0){const t=h.map((t,n)=>this.toCamelCase(t,e&&0===n)).join("");l+=(l&&h.length>0?p:"")+t,h=[]}};for(let e=0;e<c.length;e++){const t=c[e];if("normal"===t.type)h.push(t.normalized);else{y(""===l);const e=this.cleanSegment(t.normalized);l+=(l?p:"")+e}}y(""===l);let m="";h=[];const u=()=>{if(h.length>0){const e=h.map(e=>this.toPascalCase(e)).join("");m+=(m&&h.length>0?p:"")+e,h=[]}};for(let e=0;e<c.length;e++){const t=c[e];if("normal"===t.type)h.push(t.normalized);else{u();const e=this.toPascalCase(t.normalized);m+=(m?p:"")+e}}u();let d="";i.length>0?(d=i.map(e=>e.replace(/[{}]/g,"")).join("-"),d=`${d}-${r}`.toLowerCase()):d=`${r}`.toLowerCase();let f="/"+a;a||(f="/"),f=f.replace(/\{(\w+)\}/g,(e,t)=>`\${${t}}`);return{apiName:l?`${l}_${r}`:r,fileName:d,typeName:m?`${m}_${r}`:r,path:f}}parseData(){return new Promise((e,t)=>{try{for(const e in this.pathsObject){const t=this.pathsObject[e];t&&this.parsePathItemObject(t,e)}e(this.Map)}catch(e){this.handleError({type:"SCHEMA",message:"Failed to parse schema",details:e}),t(e)}})}async writeFile(){const n=[],r=[],s=this.config.saveTypeFolderPath,a=[],i=(n,i)=>new Promise((o,c)=>{try{const{payload:p,response:l,fileName:h,summary:y,typeName:m,deprecated:u}=i,[,d]=n.split("|");!a.includes(d)&&a.push(d);const f=[`declare namespace ${m} {`,...p.path,...p.query,...p.body,`${t}${l}`,"}"];y&&r.push(["/**","\n",u?` * @deprecated ${y}`:` * ${y}`,"\n"," */"].join(""));const g=this.apiRequestItemHandle(i);r.push(g,"");const $=`${s}/connectors/${h}.d.ts`;e.writeFileRecursive($,f.join("\n")).then(()=>{e.log.info(`${$.padEnd(80)} - Write done!`),o(1)}).catch(e=>{this.handleError({type:"FILE_WRITE",message:"Failed to write type definition file",path:h,details:e}),c(e)})}catch(e){this.handleError({type:"PATH",message:"Failed to process path item",path:n,details:e}),c(e)}});for(const[e,t]of this.Map)n.push(i(e,t));try{await Promise.all(n),e.log.success("Path parse & write done!"),r.unshift(`import { ${a.join(", ")} } from '${this.config.requestMethodsImportPath||"./api"}';`,"\n");const t=this.config.apiListFileName||"index.ts",s=`${this.config.saveApiListFolderPath}/${t}`;await e.clearDir(s),await e.writeFileRecursive(s,r.join("\n")),this.Map=new Map,this.errors.length>0&&e.log.warning(`Completed with ${this.errors.length} errors`)}catch(e){throw this.handleError({type:"FILE_WRITE",message:"Failed to write API list file",details:e}),e}}async handle(){try{await this.parseData(),await this.writeFile()}catch(e){if(this.handleError({type:"SCHEMA",message:"Failed to handle schema",details:e}),this.config.errorHandling?.throwOnError)throw e}}}exports.PathParse=p,exports.default=p;
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("../../utils/index.js");const t="\t";var n;!function(e){e.GET="get",e.PUT="put",e.POST="post",e.DELETE="delete",e.OPTIONS="options",e.HEAD="head",e.PATCH="patch",e.TRACE="trace"}(n||(n={}));const r={typeMapping:new Map([["integer","number"],["string","string"],["boolean","boolean"],["binary","File"],["number","number"],["null","null"],["undefined","undefined"],["date","Date"],["time","Date"],["datetime","Date"],["timestamp","Date"]]),errorHandling:{throwOnError:!1,logErrors:!0}},s="#/components/schemas/",a="#/components/parameters/",i="#/definitions/",o=["application/json","text/json","text/plain","application/x-www-form-urlencoded","application/xml","text/xml","*/*","application/octet-stream","multipart/form-data"],c=["application/octet-stream","multipart/form-data"];class p{pathsObject={};nonArrayType=["boolean","object","number","string","integer"];pathKey="";contentBody={payload:{path:[],query:[],body:[]},response:"",_response:"",fileName:"",method:"",requestPath:"",summary:"",apiName:"",typeName:"",deprecated:!1,contentType:"application/json"};Map=new Map;config;errors=[];referenceCache=new Map;parameters={};schemas={};templates={exportConst:e=>`export const ${e}`,typeDefinition:(e,t)=>`type ${e} = ${t}`,interfaceDefinition:e=>`interface ${e}`};constructor(e,t,n,s){this.pathsObject=e,this.parameters=t??{},this.schemas=n??{},this.config={...r,...s,typeMapping:new Map([...r.typeMapping||[],...s.typeMapping||[]])}}handleError(t){if(this.errors.push(t),this.config.errorHandling?.logErrors&&e.log.error(`${t.type}: ${t.message}${t.path?` at ${t.path}`:""}`),this.config.errorHandling?.throwOnError)throw new Error(`${t.type}: ${t.message}`)}getIndentation(){return this.config.formatting?.indentation||t}getEnumTypeName(e){return this.config.enmuConfig.erasableSyntaxOnly?`${e}Type`:e}typeNameToFileName(e){return e=(e=(e=e.replace(/_/g,"-")).replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase()).replace(/-+/g,"-")}stringifySchemaResult(e){if(Array.isArray(e)){const t=this.config.formatting?.lineEnding??"\n",n=this.getIndentation();return`{${t}${e.join(t)}${t}${n}}`}return e}handleComplexType(e){try{return e.oneOf?e.oneOf.map(e=>this.stringifySchemaResult(this.schemaParse(e))).join(" | "):e.allOf?e.allOf.map(e=>this.stringifySchemaResult(this.schemaParse(e))).join(" & "):e.anyOf?e.anyOf.map(e=>this.stringifySchemaResult(this.schemaParse(e))).join(" | "):e.enum?"number"===e.type||"integer"===e.type?e.enum.join(" | "):e.enum.map(e=>`'${e}'`).join(" | "):"unknown"}catch(e){return this.handleError({type:"SCHEMA",message:"Failed to handle complex type",details:e}),"unknown"}}propertiesParse(e){if(!e)return[];const n=[];for(const r in e){const s=e[r],a=this.schemaParse(s);let i="";i=Array.isArray(a)?`${t}${t}${r}: {${a.join("\n")}};`:`${t}${t}${r}: ${a};`,n.push(i)}return n}nonArraySchemaObjectParse(e){if(!e)return"unknown";if("binary"===e.format||"string"===e.type&&"binary"===e.format)return"File";switch(e.type){case"boolean":return"boolean";case"integer":case"number":return"number";case"object":return this.propertiesParse(e.properties);case"string":return"binary"===e.format?"File":"string";default:return"unknown"}}arraySchemaObjectParse(e){if("array"!==e.type)return"";const{items:t}=e,n="$ref"in t?t:null,r=t;if(n){return`Array<${this.referenceObjectParse(n)}>`}if(r){const e=this.schemaParse(t);return Array.isArray(e)?`Array<{${e.join("\n")}}>`:`Array<${e}>`}return""}referenceObjectParse(e){try{const t=e.$ref,n=this.referenceCache.get(t);if(n)return n;let r=t;t.startsWith(s)&&(r=t.replace(s,"")),t.startsWith(a)&&(r=t.replace(a,"")),t.startsWith(i)&&(r=t.replace(i,""));const o=this.typeNameToFileName(r),c=/enum/gi.test(r),p=this.schemas?.[r],l=p&&"enum"in p&&Array.isArray(p.enum),h=c||l,m=h?this.getEnumTypeName(r):r,y=h?`import('${this.config.importEnumPath}/${o}').${m}`:`import('../models/${o}').${r}`;return this.referenceCache.set(t,y),y}catch(e){return this.handleError({type:"REFERENCE",message:"Failed to parse reference object",details:e}),"unknown"}}schemaParse(e){try{if(!e)return"unknown";if("oneOf"in e||"allOf"in e||"anyOf"in e||"enum"in e)return this.handleComplexType(e);if("$ref"in e)return this.referenceObjectParse(e);const t=e,n=t.type,r=!0===t.nullable?" | null":"";if(e.format&&this.config.typeMapping?.has(e.format))return this.config.typeMapping.get(e.format)+r;if(n&&this.config.typeMapping?.has(n))return this.config.typeMapping.get(n)+r;if("array"===n&&t.items){const e=this.schemaParse(t.items);if(Array.isArray(e)){const t=this.config.formatting?.lineEnding,n=this.config.formatting?.indentation;return`Array<{${t}${e.join("\n")}${t}${n}${n}}>`}return`Array<${e}>`}if("object"===n||"object"==typeof t){if(t.properties){const e=this.propertiesParse(t.properties);return e.length?e:["unknown"]}if(!0===t.additionalProperties)return"Record<string, unknown>"+r;if("object"==typeof t.additionalProperties){return`Record<string, ${this.schemaParse(t.additionalProperties)}>`+r}}return"unknown"}catch(e){return this.handleError({type:"SCHEMA",message:"Failed to parse schema",details:e}),"unknown"}}responseObjectParse(e){try{const t=e.content;if(!t)return"";let n;for(const e of o)if(t[e]?.schema){n=t[e].schema;break}return n?this.schemaParse(n):""}catch(e){return this.handleError({type:"RESPONSE",message:"Failed to parse response object",details:e}),""}}responseHandle(e){const t=e[200];if(!t)return;const n="content"in t?t:null,r="$ref"in t?t:null;if(null===n&&null===r&&(this.contentBody.response="type Response = unknown",this.contentBody._response="unknown"),r){const e=this.referenceObjectParse(r);this.contentBody.response=`type Response = ${e}`,this.contentBody._response=e}if(n){const e=this.responseObjectParse(n);if(Array.isArray(e)){if(1===e.length&&"unknown"===e[0])this.contentBody.response=`type Response = ${e.join("\n")};`;else{const t=this.config.formatting?.lineEnding,n=this.config.formatting?.indentation;this.contentBody.response=`interface Response {${t}${e.join("\n")}${t}${n}};`}this.contentBody._response=`${e.join("\n")}`}else this.contentBody.response=`type Response = ${e}`,this.contentBody._response=`${e}`}}requestBodyObjectParse(e){const n=Object.values(e.content),{schema:r}=n[0]||{schema:null};if(r){const e=r?.type,n="$ref"in r?r:null,s="array"===e?r:null,a=e&&this.nonArrayType.includes(e)?r:null;if(n){const e=this.referenceObjectParse(n);return`${t}type Body = ${e}`}if(s){const e=this.arraySchemaObjectParse(s);return`${t}type Body = ${e}`}if(a){const e=this.nonArraySchemaObjectParse(a);return Array.isArray(e)?0===e.length?[`${t}type Body = ${a.type};`]:[`${t}interface Body {`,...e.map(e=>e.replace(/: string;/,": File;")),"}"]:[`${t}type Body = ${e}`]}}}requestBodyParse(e){if(!e)return"{}";const n="$ref"in e?e:null,r="content"in e?e:null;if(n){const e=this.referenceObjectParse(n);return`${t}type Body = ${e}`}return r&&"[object Object]"===String(e)&&0!==Reflect.ownKeys(e).length?this.requestBodyObjectParse(r):"{}"}parametersItemHandle(e,n,r){const s="$ref"in e?e:null,i="name"in e?e:null;if(s&&s.$ref&&s.$ref.startsWith(a)&&this.parameters){const e=s.$ref.replace(a,""),t=this.parameters[e];this.parametersItemHandle(t,n,r)}if(i){if("path"===i.in){const e=this.schemaParse(i.schema);n.push(`${t}${t}type ${i.name} = ${e};`),this.contentBody.payload._path?"string"==typeof e?this.contentBody.payload._path[i.name]=e:console.log("Unexpected v2value type:",e):"string"==typeof e?this.contentBody.payload._path={[i.name]:e}:console.log("Unexpected v2value type:",e)}if("query"===i.in){const e=this.schemaParse(i.schema);r.push(`${t}${t}${i.name}: ${e};`),this.contentBody.payload._query?"string"==typeof e?this.contentBody.payload._query[i.name]=e:console.log("Unexpected v2value type:",e):"string"==typeof e?this.contentBody.payload._query={[i.name]:e}:console.log("Unexpected v2value type:",e)}}}requestParametersParse(e){const n=[],r=[];e?.map(e=>this.parametersItemHandle(e,n,r)),0!==n.length&&(n.unshift(`${t}namespace Path {`),n.push(`${t}}`)),0!==r.length&&(r.unshift(`${t}interface Query {`),r.push(`${t}}`)),this.contentBody.payload.path=n,this.contentBody.payload.query=r}requestHandle(e){if(e.parameters&&this.requestParametersParse(e.parameters),e.requestBody){const t=this.requestBodyParse(e.requestBody);if(Array.isArray(t))this.contentBody.payload.body=t;else if(t){const e=t?.split("\n")||[];this.contentBody.payload.body=e}}}normalizemodulePrefix(e){if(!e||""===e.trim())return"";let t=e.trim();return t=t.replace(/\/+$/g,""),t.startsWith("/")||(t="/"+t),t}apiRequestItemHandle(e){const{payload:t,requestPath:n,_response:r,method:s,typeName:a,apiName:i,contentType:o}=e,{_path:p,_query:l,body:h}=t,m=this.config.dataLevel||"serve",y=this.normalizemodulePrefix(this.config.modulePrefix),u=(()=>{const e=[];for(const t in p)e.push(`${t}: ${a}.Path.${t}`);const t=e.join(e.length>1?",":"");return""===t?t:t+","})(),d=(()=>{const e=l?`query: ${a}.Query,`:"";return""===e?"":`${e}`})(),f=(()=>{const e=h.length>0?`body: ${a}.Body,`:"";return""===e?"":`${e}`})(),g=(u+d+f).replace(/,$/,"");return[`export const ${i} = `,"(",g,""===g?"params?: IRequestFnParams":", params?: IRequestFnParams",")"," => ",s,""+(r?`<${a}.Response>`:""),"(",`\`${y}${n}\`,`,(()=>{const e=c.includes(o)?`headers: { 'Content-Type': '${o}' }`:void 0;return["{",e?`${e},`:"","...params, ",""===d?"":"query,",""===f?"":"body,","},"].join("")})(),`'${m}'`,");"].join("")}parsePathItemObject(e,t){if(e)for(const n in e){const r=e[n];if(r){if(this.config.includeInterface&&this.config.includeInterface.length>0){const e=this.config.includeInterface?.find(e=>t.includes(e.path)&&e.method===n);if(!e)return}else if(this.config.excludeInterface&&this.config.excludeInterface.length>0){const e=this.config.excludeInterface?.find(e=>t.includes(e.path)&&e.method===n);if(e)return}const e=n.toUpperCase(),s=t+"|"+e,{apiName:a,typeName:i,fileName:c,path:p}=this.convertEndpointString(s),l=r.requestBody&&"content"in r.requestBody&&r.requestBody.content,h="object"==typeof l?Object.keys(l)[0]:"application/json";this.contentBody={payload:{path:[],query:[],body:[]},response:"",_response:"",fileName:c,method:e,typeName:i,requestPath:p,apiName:a,summary:r.summary,deprecated:r.deprecated??!1,contentType:o.includes(h)?h:"application/json"},this.requestHandle(r),this.responseHandle(r.responses),this.Map.has(s)||this.Map.set(s,JSON.parse(JSON.stringify(this.contentBody)))}}}cleanSegment(e){let t=e.replace(/^[0-9]+/,"");return t?(t=t.replace(/[^a-zA-Z0-9_.-]/g,""),t):"num"+e}toCamelCase(e,t=!1){const n=this.cleanSegment(e).split(/[-_.]+/).filter(e=>e.length>0);return 0===n.length?"":n.map((e,n)=>0===n&&t?e.toLowerCase():e.charAt(0).toUpperCase()+e.slice(1).toLowerCase()).join("")}toPascalCase(e){const t=this.cleanSegment(e).split(/[-_.]+/).filter(e=>e.length>0);return 0===t.length?"":t.map(e=>e.charAt(0).toUpperCase()+e.slice(1).toLowerCase()).join("")}convertEndpointString(e){let t=e;e.startsWith("/")||(t="/"+e);const[n,r]=t.split("|");let s="";this.config.publicPrefix&&(s=this.config.publicPrefix.replace(/^\/+|\/+$/g,""));let a=n.replace(/^\/+/,"");if(s&&a.startsWith(s+"/"))a=a.slice(s.length+1);else if(s&&a===s)a="";else if(s&&a.startsWith(s)){const e=a.slice(s.length);(e.startsWith("/")||""===e)&&(a=e.replace(/^\/+/,""))}const i=a?a.split("/").filter(e=>e):[],o=i.map(e=>{if(e.startsWith("{")&&e.endsWith("}")){const t=e.slice(1,-1);return{type:"param",original:e,normalized:t}}return{type:"normal",original:e,normalized:e}}),c=[];for(let e=0;e<o.length;e++){const t=o[e],n=o[e+1];if("normal"===t.type&&n&&"param"===n.type){if(this.cleanSegment(t.normalized).toLowerCase().replace(/[-_.]/g,"")===this.cleanSegment(n.normalized).toLowerCase().replace(/[-_.]/g,""))continue}c.push(t)}const p=this.config.parameterSeparator||"_";let l="",h=[];const m=e=>{if(h.length>0){const t=h.map((t,n)=>this.toCamelCase(t,e&&0===n)).join("");l+=(l&&h.length>0?p:"")+t,h=[]}};for(let e=0;e<c.length;e++){const t=c[e];if("normal"===t.type)h.push(t.normalized);else{m(""===l);const e=this.cleanSegment(t.normalized);l+=(l?p:"")+e}}m(""===l);let y="";h=[];const u=()=>{if(h.length>0){const e=h.map(e=>this.toPascalCase(e)).join("");y+=(y&&h.length>0?p:"")+e,h=[]}};for(let e=0;e<c.length;e++){const t=c[e];if("normal"===t.type)h.push(t.normalized);else{u();const e=this.toPascalCase(t.normalized);y+=(y?p:"")+e}}u();let d="";i.length>0?(d=i.map(e=>e.replace(/[{}]/g,"")).join("-"),d=`${d}-${r}`.toLowerCase()):d=`${r}`.toLowerCase();let f="/"+a;a||(f="/"),f=f.replace(/\{(\w+)\}/g,(e,t)=>`\${${t}}`);return{apiName:l?`${l}_${r}`:r,fileName:d,typeName:y?`${y}_${r}`:r,path:f}}parseData(){return new Promise((e,t)=>{try{for(const e in this.pathsObject){const t=this.pathsObject[e];t&&this.parsePathItemObject(t,e)}e(this.Map)}catch(e){this.handleError({type:"SCHEMA",message:"Failed to parse schema",details:e}),t(e)}})}async writeFile(){const n=[],r=[],s=this.config.saveTypeFolderPath,a=[],i=(n,i)=>new Promise((o,c)=>{try{const{payload:p,response:l,fileName:h,summary:m,typeName:y,deprecated:u}=i,[,d]=n.split("|");!a.includes(d)&&a.push(d);const f=[`declare namespace ${y} {`,...p.path,...p.query,...p.body,`${t}${l}`,"}"];m&&r.push(["/**","\n",u?` * @deprecated ${m}`:` * ${m}`,"\n"," */"].join(""));const g=this.apiRequestItemHandle(i);r.push(g,"");const $=`${s}/connectors/${h}.d.ts`;e.writeFileRecursive($,f.join("\n")).then(()=>{e.log.info(`${$.padEnd(80)} - Write done!`),o(1)}).catch(e=>{this.handleError({type:"FILE_WRITE",message:"Failed to write type definition file",path:h,details:e}),c(e)})}catch(e){this.handleError({type:"PATH",message:"Failed to process path item",path:n,details:e}),c(e)}});for(const[e,t]of this.Map)n.push(i(e,t));try{await Promise.all(n),e.log.success("Path parse & write done!"),r.unshift(`import { ${a.join(", ")} } from '${this.config.requestMethodsImportPath||"./api"}';`,"\n");const t=this.config.apiListFileName||"index.ts",s=`${this.config.saveApiListFolderPath}/${t}`;await e.clearDir(s),await e.writeFileRecursive(s,r.join("\n")),this.Map=new Map,this.errors.length>0&&e.log.warning(`Completed with ${this.errors.length} errors`)}catch(e){throw this.handleError({type:"FILE_WRITE",message:"Failed to write API list file",details:e}),e}}async handle(){try{await this.parseData(),await this.writeFile()}catch(e){if(this.handleError({type:"SCHEMA",message:"Failed to handle schema",details:e}),this.config.errorHandling?.throwOnError)throw e}}}exports.PathParse=p,exports.default=p;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("fs"),r=require("../utils/index.js"),a=require("./core/components.js"),s=require("./core/get-data.js"),t=require("./core/path.js"),i=require("shelljs"),o=require("chalk"),n=require("path");function l(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var c=l(e),g=l(o),u=l(n);let d;const p="debug"===process.env.NODE_ENV,f={saveTypeFolderPath:p?"apps/types":"src/api/types",saveApiListFolderPath:p?"apps/types":"src/api",saveEnumFolderPath:p?"apps/types/enums":"src/enums",importEnumPath:"../../../enums",requestMethodsImportPath:"./fetch",formatting:{indentation:"\t",lineEnding:"\n"},swaggerServers:{url:"https://generator3.swagger.io/openapi.json",apiListFileName:"index.ts",headers:{},dataLevel:"serve",parameterSeparator:"_",includeInterface:[],excludeInterface:[]},enmuConfig:{erasableSyntaxOnly:!1,varnames:"enum-varnames",comment:"enum-descriptions"}};class h{schemas={};paths={};async handle(e,r){try{const i=await s.getSwaggerJson(e);if(!i)throw new Error("无法获取 Swagger 数据");this.schemas=i.components?.schemas||{},this.paths=i.paths||{};const o=new a.default(this.schemas,e,{appendMode:r}),n=new t.PathParse(this.paths,i.components?.parameters,this.schemas,e);return await o.handle(),await n.handle(),!0}catch(e){if(e instanceof Error)throw new Error(`Handle Swagger data failed: ${e.message}`);throw new Error("Handle Swagger data failed: unknown error")}}async formatGeneratedFiles(e){const a=`npx prettier --write "${e.saveTypeFolderPath}/**/*.{ts,d.ts}"`;try{await c.default.promises.access(e.saveTypeFolderPath);const{stderr:s}=await new Promise((e,r)=>{i.exec(a,(a,s,t)=>{a?r(a):e({stdout:s,stderr:t})})});s&&(console.log("\n"),console.log("$",g.default.yellow(a)),console.log("\n")),r.log.success("File formatting successful"),console.log("\n")}catch(e){console.log(""),console.log(e),r.log.error("Format failed, please manually execute the following command:"),console.log("$",g.default.yellow(a)),console.log("")}}async copyAjaxConfigFiles(e){try{const a=["config.ts","error-message.ts","fetch.ts","api-type.d.ts"],s=p?u.default.join(__dirname,"..","..","postbuild-assets","ajax-config"):u.default.join(__dirname,"..","..","ajax-config"),t=e;for(const e of a){const a=u.default.join(s,e),i=u.default.join(t,e);try{await c.default.promises.access(a);try{await c.default.promises.access(i),r.log.info(`${e} already exists, skipping generation.`)}catch{await c.default.promises.copyFile(a,i),r.log.success(`${e} create done.`)}}catch(e){r.log.error(`Source file ${a} does not exist`);continue}}}catch(e){return e}}getSystemLocale(){try{return Intl.DateTimeFormat().resolvedOptions().locale.toLowerCase()}catch{return(process.env.LANG||process.env.LC_ALL||process.env.LC_MESSAGES||"").toLowerCase()}}showLegacyConfigHint(e){const r={url:e.swaggerJsonUrl||"https://your.swagger.json",publicPrefix:e.publicPrefix||"",apiListFileName:e.apiListFileName||"index.ts",headers:e.headers||{}},a=this.getSystemLocale();a.startsWith("zh")||a.includes("chinese")?(console.log("\n检测到旧版配置,请更新 an.config.json:"),console.log("1) 将 swaggerJsonUrl / publicPrefix / headers 移到 swaggerServers 字段。"),console.log("2) 单个服务可直接填写对象,多个服务请使用数组,并确保 apiListFileName 唯一。"),console.log("示例:"),console.log(JSON.stringify({swaggerServers:r},null,2)),console.log("")):(console.log("\nLegacy configuration detected, please update an.config.json:"),console.log("1) Move swaggerJsonUrl / publicPrefix / headers to swaggerServers field."),console.log("2) Single service can be an object directly, multiple services should use an array, and ensure apiListFileName is unique."),console.log("Example:"),console.log(JSON.stringify({swaggerServers:r},null,2)),console.log(""))}normalizeSwaggerServers(e,r){let a=!1,s=r?e.swaggerServers:void 0;s||(a=!0,s={url:e.swaggerJsonUrl||"",publicPrefix:e.publicPrefix||"",apiListFileName:e.apiListFileName||"index.ts",headers:e.headers||{}});const t=(r,s)=>{const t=r.url||e.swaggerJsonUrl;if(!t)throw new Error(`swaggerServers[${s}] 缺少 url,请补充后重试。`);const i=r.publicPrefix??e.publicPrefix??"";!r.url&&e.swaggerJsonUrl&&(a=!0);return{url:t,publicPrefix:i,apiListFileName:(r.apiListFileName||e.apiListFileName||"index.ts").trim()||"index.ts",headers:r.headers||e.headers||{},dataLevel:r.dataLevel||e.dataLevel||"serve",parameterSeparator:r.parameterSeparator||e.parameterSeparator||"_",includeInterface:r.includeInterface||e.includeInterface||[],excludeInterface:r.excludeInterface||e.excludeInterface||[]}},i=Array.isArray(s)?s.map((e,r)=>t(e,r)):[t(s,0)];if(0===i.length)throw new Error("swaggerServers 不能为空,请至少配置一个 swagger 服务。");if(i.length>1){const e=new Set;i.forEach(r=>{if(e.has(r.apiListFileName))throw new Error(`swaggerServers 中 apiListFileName 重复:${r.apiListFileName},请为每个服务设置唯一文件名。`);e.add(r.apiListFileName)})}return a&&this.showLegacyConfigHint(e),i}buildServerConfig(e,r){return{...e,swaggerJsonUrl:r.url,publicPrefix:r.publicPrefix??e.publicPrefix,headers:r.headers,apiListFileName:r.apiListFileName,dataLevel:r.dataLevel,parameterSeparator:r.parameterSeparator,includeInterface:r.includeInterface,excludeInterface:r.excludeInterface,swaggerServers:r}}async getConfig(e){try{const r=await c.default.promises.readFile(e,"utf8");return d=!0,JSON.parse(r)}catch(a){return d=!1,r.log.warning("Config file does not exist, will automatically create config file."),await r.writeFileRecursive(e,JSON.stringify(f,null,2)),r.log.success("Please check the an.config.json file in the project root directory"),f}}async initialize(){const e=process.cwd()+"/an.config.json";try{const a=await this.getConfig(e),s={...f,...a},t=Object.prototype.hasOwnProperty.call(a,"swaggerServers"),i=this.normalizeSwaggerServers(s,t);if(!d)return;await c.default.promises.mkdir(s.saveApiListFolderPath,{recursive:!0}),await this.copyAjaxConfigFiles(s.saveApiListFolderPath),await r.clearDir(s.saveTypeFolderPath),await r.clearDir(s.saveEnumFolderPath);for(let e=0;e<i.length;e++){const r=this.buildServerConfig(s,i[e]),a=e>0;await this.handle(r,a)}await this.formatGeneratedFiles(s),r.log.success("Successfully, all done, see you next time!"),console.log("\n")}catch(e){const a=e instanceof Error?e.message:"Unknown error";r.log.error(`Initialization failed: ${a}`)}}}if(p){(new h).initialize()}exports.Main=h;
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("fs"),r=require("../utils/index.js"),a=require("./core/components.js"),s=require("./core/get-data.js"),t=require("./core/path.js"),i=require("shelljs"),o=require("chalk"),n=require("path");function l(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var c=l(e),g=l(o),u=l(n);let d;const p="debug"===process.env.NODE_ENV,f={saveTypeFolderPath:p?"apps/types":"src/api/types",saveApiListFolderPath:p?"apps/types":"src/api",saveEnumFolderPath:p?"apps/types/enums":"src/enums",importEnumPath:"../../../enums",requestMethodsImportPath:"./fetch",formatting:{indentation:"\t",lineEnding:"\n"},swaggerServers:{url:"https://generator3.swagger.io/openapi.json",apiListFileName:"index.ts",headers:{},dataLevel:"serve",parameterSeparator:"_",includeInterface:[],excludeInterface:[]},enmuConfig:{erasableSyntaxOnly:!1,varnames:"enum-varnames",comment:"enum-descriptions"}};class h{schemas={};paths={};async handle(e,r){try{const i=await s.getSwaggerJson(e);if(!i)throw new Error("无法获取 Swagger 数据");this.schemas=i.components?.schemas||{},this.paths=i.paths||{};const o=new a.default(this.schemas,e,{appendMode:r}),n=new t.PathParse(this.paths,i.components?.parameters,this.schemas,e);return await o.handle(),await n.handle(),!0}catch(e){if(e instanceof Error)throw new Error(`Handle Swagger data failed: ${e.message}`);throw new Error("Handle Swagger data failed: unknown error")}}async formatGeneratedFiles(e){const a=`npx prettier --write "${e.saveTypeFolderPath}/**/*.{ts,d.ts}"`;try{await c.default.promises.access(e.saveTypeFolderPath);const{stderr:s}=await new Promise((e,r)=>{i.exec(a,(a,s,t)=>{a?r(a):e({stdout:s,stderr:t})})});s&&(console.log("\n"),console.log("$",g.default.yellow(a)),console.log("\n")),r.log.success("File formatting successful"),console.log("\n")}catch(e){console.log(""),console.log(e),r.log.error("Format failed, please manually execute the following command:"),console.log("$",g.default.yellow(a)),console.log("")}}async copyAjaxConfigFiles(e){try{const a=["config.ts","error-message.ts","fetch.ts","api-type.d.ts"],s=p?u.default.join(__dirname,"..","..","postbuild-assets","ajax-config"):u.default.join(__dirname,"..","..","ajax-config"),t=e;for(const e of a){const a=u.default.join(s,e),i=u.default.join(t,e);try{await c.default.promises.access(a);try{await c.default.promises.access(i),r.log.info(`${e} already exists, skipping generation.`)}catch{await c.default.promises.copyFile(a,i),r.log.success(`${e} create done.`)}}catch(e){r.log.error(`Source file ${a} does not exist`);continue}}}catch(e){return e}}getSystemLocale(){try{return Intl.DateTimeFormat().resolvedOptions().locale.toLowerCase()}catch{return(process.env.LANG||process.env.LC_ALL||process.env.LC_MESSAGES||"").toLowerCase()}}showLegacyConfigHint(e){const r={url:e.swaggerJsonUrl||"https://your.swagger.json",publicPrefix:e.publicPrefix||"",apiListFileName:e.apiListFileName||"index.ts",headers:e.headers||{}},a=this.getSystemLocale();a.startsWith("zh")||a.includes("chinese")?(console.log("\n检测到旧版配置,请更新 an.config.json:"),console.log("1) 将 swaggerJsonUrl / publicPrefix / headers 移到 swaggerServers 字段。"),console.log("2) 单个服务可直接填写对象,多个服务请使用数组,并确保 apiListFileName 唯一。"),console.log("示例:"),console.log(JSON.stringify({swaggerServers:r},null,2)),console.log("")):(console.log("\nLegacy configuration detected, please update an.config.json:"),console.log("1) Move swaggerJsonUrl / publicPrefix / headers to swaggerServers field."),console.log("2) Single service can be an object directly, multiple services should use an array, and ensure apiListFileName is unique."),console.log("Example:"),console.log(JSON.stringify({swaggerServers:r},null,2)),console.log(""))}normalizeSwaggerServers(e,r){let a=!1,s=r?e.swaggerServers:void 0;s||(a=!0,s={url:e.swaggerJsonUrl||"",publicPrefix:e.publicPrefix||"",apiListFileName:e.apiListFileName||"index.ts",headers:e.headers||{},modulePrefix:e.modulePrefix});const t=(r,s)=>{const t=r.url||e.swaggerJsonUrl;if(!t)throw new Error(`swaggerServers[${s}] 缺少 url,请补充后重试。`);const i=r.publicPrefix??e.publicPrefix??"";!r.url&&e.swaggerJsonUrl&&(a=!0);return{url:t,publicPrefix:i,apiListFileName:(r.apiListFileName||e.apiListFileName||"index.ts").trim()||"index.ts",headers:r.headers||e.headers||{},dataLevel:r.dataLevel||e.dataLevel||"serve",parameterSeparator:r.parameterSeparator||e.parameterSeparator||"_",includeInterface:r.includeInterface||e.includeInterface||[],excludeInterface:r.excludeInterface||e.excludeInterface||[],modulePrefix:r.modulePrefix??e.modulePrefix??""}},i=Array.isArray(s)?s.map((e,r)=>t(e,r)):[t(s,0)];if(0===i.length)throw new Error("swaggerServers 不能为空,请至少配置一个 swagger 服务。");if(i.length>1){const e=new Set;i.forEach(r=>{if(e.has(r.apiListFileName))throw new Error(`swaggerServers 中 apiListFileName 重复:${r.apiListFileName},请为每个服务设置唯一文件名。`);e.add(r.apiListFileName)})}return a&&this.showLegacyConfigHint(e),i}buildServerConfig(e,r){return{...e,swaggerJsonUrl:r.url,publicPrefix:r.publicPrefix??e.publicPrefix,headers:r.headers,apiListFileName:r.apiListFileName,dataLevel:r.dataLevel,parameterSeparator:r.parameterSeparator,includeInterface:r.includeInterface,excludeInterface:r.excludeInterface,modulePrefix:r.modulePrefix,swaggerServers:r}}async getConfig(e){try{const r=await c.default.promises.readFile(e,"utf8");return d=!0,JSON.parse(r)}catch(a){return d=!1,r.log.warning("Config file does not exist, will automatically create config file."),await r.writeFileRecursive(e,JSON.stringify(f,null,2)),r.log.success("Please check the an.config.json file in the project root directory"),f}}async initialize(){const e=process.cwd()+"/an.config.json";try{const a=await this.getConfig(e),s={...f,...a},t=Object.prototype.hasOwnProperty.call(a,"swaggerServers"),i=this.normalizeSwaggerServers(s,t);if(!d)return;await c.default.promises.mkdir(s.saveApiListFolderPath,{recursive:!0}),await this.copyAjaxConfigFiles(s.saveApiListFolderPath),await r.clearDir(s.saveTypeFolderPath),await r.clearDir(s.saveEnumFolderPath);for(let e=0;e<i.length;e++){const r=this.buildServerConfig(s,i[e]),a=e>0;await this.handle(r,a)}await this.formatGeneratedFiles(s),r.log.success("Successfully, all done, see you next time!"),console.log("\n")}catch(e){const a=e instanceof Error?e.message:"Unknown error";r.log.error(`Initialization failed: ${a}`)}}}if(p){(new h).initialize()}exports.Main=h;
|