swagger2api-v3 1.1.10 → 1.1.11
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 +4 -4
- package/dist/.swagger2api.schema.json +2 -2
- package/dist/cli/index.js +2 -2
- package/dist/config/validator.js +92 -9
- package/dist/core/generator.d.ts +71 -17
- package/dist/core/generator.js +261 -106
- package/dist/core/parser.d.ts +14 -16
- package/dist/core/parser.js +32 -90
- package/dist/core/query-parameter-generator.d.ts +26 -0
- package/dist/core/query-parameter-generator.js +114 -0
- package/dist/core/type-generator.d.ts +73 -0
- package/dist/core/type-generator.js +215 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +5 -3
- package/dist/types/index.d.ts +27 -6
- package/dist/utils/comment.d.ts +4 -1
- package/dist/utils/comment.js +6 -2
- package/dist/utils/file.d.ts +10 -3
- package/dist/utils/file.js +48 -19
- package/dist/utils/naming.js +3 -2
- package/dist/utils/schema.d.ts +79 -0
- package/dist/utils/schema.js +536 -0
- package/dist/utils/type.d.ts +7 -37
- package/dist/utils/type.js +29 -260
- package/package.json +4 -4
package/dist/core/parser.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SwaggerParser = void 0;
|
|
4
4
|
const utils_1 = require("../utils");
|
|
5
|
+
const type_generator_1 = require("./type-generator");
|
|
5
6
|
const HTTP_METHODS = [
|
|
6
7
|
'get',
|
|
7
8
|
'post',
|
|
@@ -9,13 +10,19 @@ const HTTP_METHODS = [
|
|
|
9
10
|
'delete',
|
|
10
11
|
'patch',
|
|
11
12
|
'head',
|
|
12
|
-
'options'
|
|
13
|
+
'options',
|
|
14
|
+
'trace'
|
|
13
15
|
];
|
|
14
16
|
const MAX_REF_RESOLVE_DEPTH = 20;
|
|
15
17
|
/**
|
|
16
|
-
*
|
|
18
|
+
* OpenAPI 3.0 文档解析器
|
|
17
19
|
*/
|
|
18
20
|
class SwaggerParser {
|
|
21
|
+
/**
|
|
22
|
+
* 创建 OpenAPI 文档解析器
|
|
23
|
+
* @param document OpenAPI 文档
|
|
24
|
+
* @param config 生成配置
|
|
25
|
+
*/
|
|
19
26
|
constructor(document, config) {
|
|
20
27
|
this.document = document;
|
|
21
28
|
this.config = config;
|
|
@@ -34,7 +41,8 @@ class SwaggerParser {
|
|
|
34
41
|
parseApis() {
|
|
35
42
|
const apis = [];
|
|
36
43
|
const paths = this.document.paths;
|
|
37
|
-
for (const [path,
|
|
44
|
+
for (const [path, rawPathItem] of Object.entries(paths)) {
|
|
45
|
+
const pathItem = this.resolveReference(rawPathItem);
|
|
38
46
|
for (const method of HTTP_METHODS) {
|
|
39
47
|
const operation = pathItem[method];
|
|
40
48
|
if (!operation)
|
|
@@ -92,15 +100,16 @@ class SwaggerParser {
|
|
|
92
100
|
// 应用前缀忽略规则
|
|
93
101
|
functionName = (0, utils_1.stripMethodNamePrefixes)(functionName, this.config.methodNameIgnorePrefix);
|
|
94
102
|
// 获取响应类型
|
|
95
|
-
const
|
|
103
|
+
const responses = this.resolveResponses(operation.responses);
|
|
104
|
+
const responseType = (0, utils_1.getResponseType)(responses, this.getAllSchemas());
|
|
96
105
|
// 获取请求体类型
|
|
97
106
|
const bodyParam = allParameters.find((p) => p.in === 'body');
|
|
98
107
|
const requestBodyType = bodyParam?.schema
|
|
99
|
-
? (0, utils_1.swaggerTypeToTsType)(bodyParam.schema, this.getAllSchemas())
|
|
108
|
+
? (0, utils_1.swaggerTypeToTsType)(bodyParam.schema, this.getAllSchemas(), 'request')
|
|
100
109
|
: undefined;
|
|
101
110
|
// 解析参数信息
|
|
102
111
|
const parameters = allParameters.map((param) => {
|
|
103
|
-
const type = (0, utils_1.swaggerTypeToTsType)((0, utils_1.swaggerParameterToSchema)(param), this.getAllSchemas());
|
|
112
|
+
const type = (0, utils_1.swaggerTypeToTsType)((0, utils_1.swaggerParameterToSchema)(param), this.getAllSchemas(), 'request');
|
|
104
113
|
return {
|
|
105
114
|
name: param.name,
|
|
106
115
|
type,
|
|
@@ -127,7 +136,6 @@ class SwaggerParser {
|
|
|
127
136
|
* @returns 类型信息数组
|
|
128
137
|
*/
|
|
129
138
|
parseTypes() {
|
|
130
|
-
const types = [];
|
|
131
139
|
// Debug log
|
|
132
140
|
utils_1.logger.debug('解析类型定义...');
|
|
133
141
|
utils_1.logger.debugKV('components', !!this.document.components);
|
|
@@ -137,91 +145,13 @@ class SwaggerParser {
|
|
|
137
145
|
utils_1.logger.debugKV('schema 列表', Object.keys(this.document.components.schemas).join(', '));
|
|
138
146
|
}
|
|
139
147
|
}
|
|
140
|
-
|
|
141
|
-
if (this.document.components?.schemas) {
|
|
142
|
-
for (const [name, schema] of Object.entries(this.document.components.schemas)) {
|
|
143
|
-
const sanitizedName = (0, utils_1.sanitizeTypeName)(name); // Use it
|
|
144
|
-
const typeInfo = this.parseTypeDefinition(sanitizedName, schema);
|
|
145
|
-
types.push(typeInfo);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
return types;
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* 解析单个类型定义
|
|
152
|
-
* @param name 类型名称
|
|
153
|
-
* @param schema 模式对象
|
|
154
|
-
* @returns 类型信息
|
|
155
|
-
*/
|
|
156
|
-
parseTypeDefinition(name, schema) {
|
|
157
|
-
const typeName = (0, utils_1.toPascalCase)(name);
|
|
158
|
-
let definition;
|
|
159
|
-
// 获取所有 schemas 用于类型解析
|
|
160
|
-
const allSchemas = this.getAllSchemas();
|
|
161
|
-
if (schema.type === 'object' && schema.properties) {
|
|
162
|
-
// 对象类型
|
|
163
|
-
const properties = Object.entries(schema.properties)
|
|
164
|
-
.map(([key, value]) => {
|
|
165
|
-
const optional = schema.required?.includes(key) ? '' : '?';
|
|
166
|
-
let type = (0, utils_1.swaggerTypeToTsType)(value, allSchemas);
|
|
167
|
-
if (optional === '?')
|
|
168
|
-
type = (0, utils_1.stripNullFromUnion)(type);
|
|
169
|
-
const comment = value.description
|
|
170
|
-
? ` /** ${value.description} */`
|
|
171
|
-
: '';
|
|
172
|
-
return `${comment}\n ${(0, utils_1.formatTsPropertyName)(key)}${optional}: ${type};`;
|
|
173
|
-
})
|
|
174
|
-
.join('\n');
|
|
175
|
-
definition = `export interface ${typeName} {\n${properties}\n}`;
|
|
176
|
-
}
|
|
177
|
-
else if (schema.type === 'array') {
|
|
178
|
-
// 数组类型 - 生成指向 items 类型的别名(不带 [])
|
|
179
|
-
// 在引用时会自动添加 []
|
|
180
|
-
const itemType = (0, utils_1.swaggerTypeToTsType)(schema.items || {}, allSchemas);
|
|
181
|
-
definition = `export type ${typeName} = ${itemType};`;
|
|
182
|
-
}
|
|
183
|
-
else if (schema.enum) {
|
|
184
|
-
// 枚举类型
|
|
185
|
-
const enumValues = schema.enum
|
|
186
|
-
.map((value, index) => {
|
|
187
|
-
const key = this.getEnumMemberName(schema, value, index);
|
|
188
|
-
return ` ${key} = ${JSON.stringify(String(value))}`;
|
|
189
|
-
})
|
|
190
|
-
.join(',\n');
|
|
191
|
-
definition = `export enum ${typeName} {\n${enumValues}\n}`;
|
|
192
|
-
}
|
|
193
|
-
else {
|
|
194
|
-
// 其他类型
|
|
195
|
-
const type = (0, utils_1.swaggerTypeToTsType)(schema);
|
|
196
|
-
definition = `export type ${typeName} = ${type};`;
|
|
197
|
-
}
|
|
198
|
-
return {
|
|
199
|
-
name: typeName,
|
|
200
|
-
definition,
|
|
201
|
-
description: schema.description
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
/**
|
|
205
|
-
* 获取枚举成员名称
|
|
206
|
-
* @param schema 枚举 schema
|
|
207
|
-
* @param value 枚举值
|
|
208
|
-
* @param index 枚举值索引
|
|
209
|
-
* @returns 枚举成员名称
|
|
210
|
-
*/
|
|
211
|
-
getEnumMemberName(schema, value, index) {
|
|
212
|
-
const extensionName = schema['x-enum-varnames']?.[index] || schema['x-enumNames']?.[index];
|
|
213
|
-
if (extensionName) {
|
|
214
|
-
return extensionName;
|
|
215
|
-
}
|
|
216
|
-
const strValue = String(value);
|
|
217
|
-
if (/^\d+$/.test(strValue)) {
|
|
218
|
-
return `VALUE_${strValue}`;
|
|
219
|
-
}
|
|
220
|
-
return strValue.toUpperCase();
|
|
148
|
+
return new type_generator_1.OpenAPITypeGenerator(this.getAllSchemas()).generate();
|
|
221
149
|
}
|
|
222
150
|
/**
|
|
223
151
|
* 解析本地 $ref 引用对象
|
|
224
152
|
* @param value 可能带有 $ref 的对象
|
|
153
|
+
* @param seenRefs 已访问的引用
|
|
154
|
+
* @param depth 当前解析深度
|
|
225
155
|
* @returns 引用解析后的对象
|
|
226
156
|
*/
|
|
227
157
|
resolveReference(value, seenRefs = new Set(), depth = 0) {
|
|
@@ -230,7 +160,7 @@ class SwaggerParser {
|
|
|
230
160
|
}
|
|
231
161
|
const ref = value.$ref;
|
|
232
162
|
if (!ref.startsWith('#/')) {
|
|
233
|
-
throw new Error(
|
|
163
|
+
throw new Error(`外部 $ref 需要先通过 loadSwaggerDocument 打包: ${ref}`);
|
|
234
164
|
}
|
|
235
165
|
if (depth >= MAX_REF_RESOLVE_DEPTH) {
|
|
236
166
|
throw new Error(`$ref 解析超过最大深度: ${ref}`);
|
|
@@ -252,6 +182,17 @@ class SwaggerParser {
|
|
|
252
182
|
}
|
|
253
183
|
return this.resolveReference(current, seenRefs, depth + 1);
|
|
254
184
|
}
|
|
185
|
+
/**
|
|
186
|
+
* 解析响应对象中的本地引用
|
|
187
|
+
* @param responses OpenAPI 响应集合
|
|
188
|
+
* @returns 已解析的响应集合
|
|
189
|
+
*/
|
|
190
|
+
resolveResponses(responses) {
|
|
191
|
+
return Object.fromEntries(Object.entries(responses).map(([status, response]) => [
|
|
192
|
+
status,
|
|
193
|
+
this.resolveReference(response)
|
|
194
|
+
]));
|
|
195
|
+
}
|
|
255
196
|
/**
|
|
256
197
|
* 合并同名同位置参数,后出现的 operation 级参数覆盖 path 级参数
|
|
257
198
|
* @param parameters 参数数组
|
|
@@ -295,7 +236,8 @@ class SwaggerParser {
|
|
|
295
236
|
this.document.tags.forEach((tag) => tags.add(tag.name));
|
|
296
237
|
}
|
|
297
238
|
// 从路径操作中获取
|
|
298
|
-
for (const
|
|
239
|
+
for (const rawPathItem of Object.values(this.document.paths)) {
|
|
240
|
+
const pathItem = this.resolveReference(rawPathItem);
|
|
299
241
|
for (const method of HTTP_METHODS) {
|
|
300
242
|
const operation = pathItem[method];
|
|
301
243
|
if (operation?.tags) {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ApiInfo, TypeInfo } from '../types';
|
|
2
|
+
/** API 查询参数类型及其所属接口 */
|
|
3
|
+
export interface QueryParameterTypeInfo extends TypeInfo {
|
|
4
|
+
/** 所属 API */
|
|
5
|
+
api: ApiInfo;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* 获取 API 查询参数类型名称
|
|
9
|
+
* @param api API 接口信息
|
|
10
|
+
* @returns 查询参数类型名称
|
|
11
|
+
*/
|
|
12
|
+
export declare function getQueryParameterTypeName(api: ApiInfo): string;
|
|
13
|
+
/**
|
|
14
|
+
* 创建 API 查询参数类型
|
|
15
|
+
* @param apis API 接口数组
|
|
16
|
+
* @param reservedTypeNames 已占用的类型名称
|
|
17
|
+
* @returns 查询参数类型定义
|
|
18
|
+
*/
|
|
19
|
+
export declare function generateQueryParameterTypes(apis: ApiInfo[], reservedTypeNames?: Iterable<string>): QueryParameterTypeInfo[];
|
|
20
|
+
/**
|
|
21
|
+
* 收集查询参数类型依赖的组件模型
|
|
22
|
+
* @param apis API 接口数组
|
|
23
|
+
* @param modelTypes 组件模型类型
|
|
24
|
+
* @returns 组件模型名称
|
|
25
|
+
*/
|
|
26
|
+
export declare function collectQueryParameterModelTypes(apis: ApiInfo[], modelTypes: TypeInfo[]): string[];
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getQueryParameterTypeName = getQueryParameterTypeName;
|
|
4
|
+
exports.generateQueryParameterTypes = generateQueryParameterTypes;
|
|
5
|
+
exports.collectQueryParameterModelTypes = collectQueryParameterModelTypes;
|
|
6
|
+
const utils_1 = require("../utils");
|
|
7
|
+
const PRIMITIVE_TYPES = new Set([
|
|
8
|
+
'string',
|
|
9
|
+
'number',
|
|
10
|
+
'boolean',
|
|
11
|
+
'object',
|
|
12
|
+
'array',
|
|
13
|
+
'any',
|
|
14
|
+
'blob',
|
|
15
|
+
'void',
|
|
16
|
+
'null',
|
|
17
|
+
'undefined'
|
|
18
|
+
]);
|
|
19
|
+
/**
|
|
20
|
+
* 获取 API 查询参数类型名称
|
|
21
|
+
* @param api API 接口信息
|
|
22
|
+
* @returns 查询参数类型名称
|
|
23
|
+
*/
|
|
24
|
+
function getQueryParameterTypeName(api) {
|
|
25
|
+
return `${(0, utils_1.sanitizeTypeName)(api.name)}Params`;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* 创建 API 查询参数类型
|
|
29
|
+
* @param apis API 接口数组
|
|
30
|
+
* @param reservedTypeNames 已占用的类型名称
|
|
31
|
+
* @returns 查询参数类型定义
|
|
32
|
+
*/
|
|
33
|
+
function generateQueryParameterTypes(apis, reservedTypeNames = []) {
|
|
34
|
+
const parameterTypes = [];
|
|
35
|
+
const usedTypeNames = new Set(reservedTypeNames);
|
|
36
|
+
for (const api of apis) {
|
|
37
|
+
const queryParameters = api.parameters.filter((parameter) => parameter.in === 'query');
|
|
38
|
+
if (queryParameters.length === 0)
|
|
39
|
+
continue;
|
|
40
|
+
const typeName = createAvailableTypeName(getQueryParameterTypeName(api), usedTypeNames);
|
|
41
|
+
usedTypeNames.add(typeName);
|
|
42
|
+
const members = renderQueryParameterMembers(queryParameters);
|
|
43
|
+
parameterTypes.push({
|
|
44
|
+
api,
|
|
45
|
+
name: typeName,
|
|
46
|
+
definition: `export interface ${typeName} {\n${members}\n}`
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return parameterTypes;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* 创建未被占用的类型名称
|
|
53
|
+
* @param baseName 基础类型名称
|
|
54
|
+
* @param usedTypeNames 已占用的类型名称
|
|
55
|
+
* @returns 可用类型名称
|
|
56
|
+
*/
|
|
57
|
+
function createAvailableTypeName(baseName, usedTypeNames) {
|
|
58
|
+
if (!usedTypeNames.has(baseName))
|
|
59
|
+
return baseName;
|
|
60
|
+
let suffix = 1;
|
|
61
|
+
while (usedTypeNames.has(`${baseName}${suffix}`))
|
|
62
|
+
suffix++;
|
|
63
|
+
return `${baseName}${suffix}`;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* 收集查询参数类型依赖的组件模型
|
|
67
|
+
* @param apis API 接口数组
|
|
68
|
+
* @param modelTypes 组件模型类型
|
|
69
|
+
* @returns 组件模型名称
|
|
70
|
+
*/
|
|
71
|
+
function collectQueryParameterModelTypes(apis, modelTypes) {
|
|
72
|
+
const modelTypeNames = new Set(modelTypes.map((type) => type.name));
|
|
73
|
+
const usedTypeNames = new Set();
|
|
74
|
+
for (const api of apis) {
|
|
75
|
+
for (const parameter of api.parameters) {
|
|
76
|
+
if (parameter.in !== 'query')
|
|
77
|
+
continue;
|
|
78
|
+
extractTypeNames(parameter.type).forEach((name) => {
|
|
79
|
+
if (modelTypeNames.has(name))
|
|
80
|
+
usedTypeNames.add(name);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return Array.from(usedTypeNames).sort();
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* 渲染查询参数类型成员
|
|
88
|
+
* @param parameters 查询参数数组
|
|
89
|
+
* @returns 查询参数类型成员
|
|
90
|
+
*/
|
|
91
|
+
function renderQueryParameterMembers(parameters) {
|
|
92
|
+
return parameters
|
|
93
|
+
.map((parameter) => {
|
|
94
|
+
const optional = parameter.required ? '' : '?';
|
|
95
|
+
const type = optional
|
|
96
|
+
? (0, utils_1.stripNullFromUnion)(parameter.type)
|
|
97
|
+
: parameter.type;
|
|
98
|
+
const comment = parameter.description
|
|
99
|
+
? ` /** ${(0, utils_1.escapeJSDocText)(parameter.description)} */\n`
|
|
100
|
+
: '';
|
|
101
|
+
const member = `${(0, utils_1.formatTsPropertyName)(parameter.name)}${optional}: ${type}`;
|
|
102
|
+
return `${comment} ${member};`;
|
|
103
|
+
})
|
|
104
|
+
.join('\n');
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* 从类型字符串中提取非基础类型名称
|
|
108
|
+
* @param type 类型字符串
|
|
109
|
+
* @returns 类型名称
|
|
110
|
+
*/
|
|
111
|
+
function extractTypeNames(type) {
|
|
112
|
+
const matches = type.match(/[A-Za-z_][A-Za-z0-9_]*/g) || [];
|
|
113
|
+
return Array.from(new Set(matches.filter((name) => !PRIMITIVE_TYPES.has(name.toLowerCase()))));
|
|
114
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { SwaggerSchema, TypeInfo } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* OpenAPI 组件类型生成器
|
|
4
|
+
*/
|
|
5
|
+
export declare class OpenAPITypeGenerator {
|
|
6
|
+
private readonly schemas;
|
|
7
|
+
/**
|
|
8
|
+
* 创建类型生成器
|
|
9
|
+
* @param schemas OpenAPI components.schemas
|
|
10
|
+
*/
|
|
11
|
+
constructor(schemas: Record<string, SwaggerSchema>);
|
|
12
|
+
/**
|
|
13
|
+
* 生成全部组件类型
|
|
14
|
+
* @returns 类型信息数组
|
|
15
|
+
*/
|
|
16
|
+
generate(): TypeInfo[];
|
|
17
|
+
/**
|
|
18
|
+
* 创建单个类型信息
|
|
19
|
+
* @param typeName 输出类型名称
|
|
20
|
+
* @param schema OpenAPI Schema
|
|
21
|
+
* @param usage Schema 使用场景
|
|
22
|
+
* @returns 类型信息
|
|
23
|
+
*/
|
|
24
|
+
private createTypeInfo;
|
|
25
|
+
/**
|
|
26
|
+
* 创建类型定义
|
|
27
|
+
* @param typeName 输出类型名称
|
|
28
|
+
* @param schema OpenAPI Schema
|
|
29
|
+
* @param usage Schema 使用场景
|
|
30
|
+
* @returns TypeScript 类型定义
|
|
31
|
+
*/
|
|
32
|
+
private createDefinition;
|
|
33
|
+
/**
|
|
34
|
+
* 判断 Schema 是否可以生成 TypeScript enum
|
|
35
|
+
* @param schema OpenAPI Schema
|
|
36
|
+
* @returns 是否可以生成 enum
|
|
37
|
+
*/
|
|
38
|
+
private canGenerateEnum;
|
|
39
|
+
/**
|
|
40
|
+
* 创建枚举定义
|
|
41
|
+
* @param typeName 枚举名称
|
|
42
|
+
* @param schema 枚举 Schema
|
|
43
|
+
* @returns TypeScript 枚举定义
|
|
44
|
+
*/
|
|
45
|
+
private createEnumDefinition;
|
|
46
|
+
/**
|
|
47
|
+
* 创建合法且唯一的枚举成员名称
|
|
48
|
+
* @param schema 枚举 Schema
|
|
49
|
+
* @param value 枚举值
|
|
50
|
+
* @param index 枚举索引
|
|
51
|
+
* @param usedNames 已使用名称
|
|
52
|
+
* @returns 枚举成员名称
|
|
53
|
+
*/
|
|
54
|
+
private createEnumMemberName;
|
|
55
|
+
/**
|
|
56
|
+
* 从枚举值创建默认成员名称
|
|
57
|
+
* @param value 枚举值
|
|
58
|
+
* @param index 枚举索引
|
|
59
|
+
* @returns 默认成员名称
|
|
60
|
+
*/
|
|
61
|
+
private createEnumValueName;
|
|
62
|
+
/**
|
|
63
|
+
* 校验基础类型和请求/响应变体不会重名
|
|
64
|
+
*/
|
|
65
|
+
private validateGeneratedTypeNames;
|
|
66
|
+
/**
|
|
67
|
+
* 注册生成类型名称并检测冲突
|
|
68
|
+
* @param owners 类型名称归属映射
|
|
69
|
+
* @param typeName 生成类型名称
|
|
70
|
+
* @param owner 原始 Schema 描述
|
|
71
|
+
*/
|
|
72
|
+
private registerTypeName;
|
|
73
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OpenAPITypeGenerator = void 0;
|
|
4
|
+
const schema_1 = require("../utils/schema");
|
|
5
|
+
const naming_1 = require("../utils/naming");
|
|
6
|
+
const RESERVED_IDENTIFIERS = new Set([
|
|
7
|
+
'break',
|
|
8
|
+
'case',
|
|
9
|
+
'catch',
|
|
10
|
+
'class',
|
|
11
|
+
'const',
|
|
12
|
+
'continue',
|
|
13
|
+
'debugger',
|
|
14
|
+
'default',
|
|
15
|
+
'delete',
|
|
16
|
+
'do',
|
|
17
|
+
'else',
|
|
18
|
+
'enum',
|
|
19
|
+
'export',
|
|
20
|
+
'extends',
|
|
21
|
+
'false',
|
|
22
|
+
'finally',
|
|
23
|
+
'for',
|
|
24
|
+
'function',
|
|
25
|
+
'if',
|
|
26
|
+
'import',
|
|
27
|
+
'in',
|
|
28
|
+
'instanceof',
|
|
29
|
+
'new',
|
|
30
|
+
'null',
|
|
31
|
+
'return',
|
|
32
|
+
'super',
|
|
33
|
+
'switch',
|
|
34
|
+
'this',
|
|
35
|
+
'throw',
|
|
36
|
+
'true',
|
|
37
|
+
'try',
|
|
38
|
+
'typeof',
|
|
39
|
+
'var',
|
|
40
|
+
'void',
|
|
41
|
+
'while',
|
|
42
|
+
'with',
|
|
43
|
+
'yield'
|
|
44
|
+
]);
|
|
45
|
+
/**
|
|
46
|
+
* OpenAPI 组件类型生成器
|
|
47
|
+
*/
|
|
48
|
+
class OpenAPITypeGenerator {
|
|
49
|
+
/**
|
|
50
|
+
* 创建类型生成器
|
|
51
|
+
* @param schemas OpenAPI components.schemas
|
|
52
|
+
*/
|
|
53
|
+
constructor(schemas) {
|
|
54
|
+
this.schemas = schemas;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* 生成全部组件类型
|
|
58
|
+
* @returns 类型信息数组
|
|
59
|
+
*/
|
|
60
|
+
generate() {
|
|
61
|
+
this.validateGeneratedTypeNames();
|
|
62
|
+
const types = [];
|
|
63
|
+
for (const [schemaName, schema] of Object.entries(this.schemas)) {
|
|
64
|
+
const typeName = (0, naming_1.sanitizeTypeName)(schemaName);
|
|
65
|
+
types.push(this.createTypeInfo(typeName, schema, 'neutral'));
|
|
66
|
+
if ((0, schema_1.schemaNeedsUsageVariant)(schema, this.schemas, 'request')) {
|
|
67
|
+
types.push(this.createTypeInfo(`${typeName}Input`, schema, 'request'));
|
|
68
|
+
}
|
|
69
|
+
if ((0, schema_1.schemaNeedsUsageVariant)(schema, this.schemas, 'response')) {
|
|
70
|
+
types.push(this.createTypeInfo(`${typeName}Output`, schema, 'response'));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return types;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* 创建单个类型信息
|
|
77
|
+
* @param typeName 输出类型名称
|
|
78
|
+
* @param schema OpenAPI Schema
|
|
79
|
+
* @param usage Schema 使用场景
|
|
80
|
+
* @returns 类型信息
|
|
81
|
+
*/
|
|
82
|
+
createTypeInfo(typeName, schema, usage) {
|
|
83
|
+
return {
|
|
84
|
+
name: typeName,
|
|
85
|
+
definition: this.createDefinition(typeName, schema, usage),
|
|
86
|
+
description: schema.description
|
|
87
|
+
? (0, schema_1.escapeJSDocText)(schema.description)
|
|
88
|
+
: undefined
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* 创建类型定义
|
|
93
|
+
* @param typeName 输出类型名称
|
|
94
|
+
* @param schema OpenAPI Schema
|
|
95
|
+
* @param usage Schema 使用场景
|
|
96
|
+
* @returns TypeScript 类型定义
|
|
97
|
+
*/
|
|
98
|
+
createDefinition(typeName, schema, usage) {
|
|
99
|
+
if (schema.enum && this.canGenerateEnum(schema)) {
|
|
100
|
+
return this.createEnumDefinition(typeName, schema);
|
|
101
|
+
}
|
|
102
|
+
if ((0, schema_1.isObjectSchema)(schema) &&
|
|
103
|
+
!schema.allOf &&
|
|
104
|
+
!schema.oneOf &&
|
|
105
|
+
!schema.anyOf &&
|
|
106
|
+
!schema.$ref &&
|
|
107
|
+
schema.nullable !== true &&
|
|
108
|
+
(0, schema_1.getVisibleSchemaProperties)(schema, usage).length > 0) {
|
|
109
|
+
const isGeneric = (0, schema_1.isGenericResponseSchema)(schema);
|
|
110
|
+
const genericDeclaration = isGeneric ? '<T = Record<string, any>>' : '';
|
|
111
|
+
const members = (0, schema_1.renderObjectMembers)(schema, this.schemas, usage, isGeneric ? { data: 'T' } : {});
|
|
112
|
+
return `export interface ${typeName}${genericDeclaration} {\n${members}\n}`;
|
|
113
|
+
}
|
|
114
|
+
const type = (0, schema_1.swaggerTypeToTsType)(schema, this.schemas, usage);
|
|
115
|
+
return `export type ${typeName} = ${type};`;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* 判断 Schema 是否可以生成 TypeScript enum
|
|
119
|
+
* @param schema OpenAPI Schema
|
|
120
|
+
* @returns 是否可以生成 enum
|
|
121
|
+
*/
|
|
122
|
+
canGenerateEnum(schema) {
|
|
123
|
+
return (schema.nullable !== true &&
|
|
124
|
+
!!schema.enum?.length &&
|
|
125
|
+
schema.enum.every((value) => typeof value === 'string' || typeof value === 'number'));
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* 创建枚举定义
|
|
129
|
+
* @param typeName 枚举名称
|
|
130
|
+
* @param schema 枚举 Schema
|
|
131
|
+
* @returns TypeScript 枚举定义
|
|
132
|
+
*/
|
|
133
|
+
createEnumDefinition(typeName, schema) {
|
|
134
|
+
const usedNames = new Set();
|
|
135
|
+
const members = (schema.enum || []).map((value, index) => {
|
|
136
|
+
const memberName = this.createEnumMemberName(schema, value, index, usedNames);
|
|
137
|
+
return ` ${memberName} = ${JSON.stringify(value)}`;
|
|
138
|
+
});
|
|
139
|
+
return `export enum ${typeName} {\n${members.join(',\n')}\n}`;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* 创建合法且唯一的枚举成员名称
|
|
143
|
+
* @param schema 枚举 Schema
|
|
144
|
+
* @param value 枚举值
|
|
145
|
+
* @param index 枚举索引
|
|
146
|
+
* @param usedNames 已使用名称
|
|
147
|
+
* @returns 枚举成员名称
|
|
148
|
+
*/
|
|
149
|
+
createEnumMemberName(schema, value, index, usedNames) {
|
|
150
|
+
const extensionName = schema['x-enum-varnames']?.[index] || schema['x-enumNames']?.[index];
|
|
151
|
+
const sourceName = extensionName || this.createEnumValueName(value, index);
|
|
152
|
+
let memberName = sourceName.replace(/[^A-Za-z0-9_$]+/g, '_');
|
|
153
|
+
if (!memberName)
|
|
154
|
+
memberName = `VALUE_${index}`;
|
|
155
|
+
if (/^\d/.test(memberName))
|
|
156
|
+
memberName = `VALUE_${memberName}`;
|
|
157
|
+
if (RESERVED_IDENTIFIERS.has(memberName))
|
|
158
|
+
memberName = `_${memberName}`;
|
|
159
|
+
if (!(0, schema_1.isValidIdentifier)(memberName))
|
|
160
|
+
memberName = `VALUE_${index}`;
|
|
161
|
+
const baseName = memberName;
|
|
162
|
+
let suffix = 2;
|
|
163
|
+
while (usedNames.has(memberName)) {
|
|
164
|
+
memberName = `${baseName}_${suffix}`;
|
|
165
|
+
suffix++;
|
|
166
|
+
}
|
|
167
|
+
usedNames.add(memberName);
|
|
168
|
+
return memberName;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* 从枚举值创建默认成员名称
|
|
172
|
+
* @param value 枚举值
|
|
173
|
+
* @param index 枚举索引
|
|
174
|
+
* @returns 默认成员名称
|
|
175
|
+
*/
|
|
176
|
+
createEnumValueName(value, index) {
|
|
177
|
+
if (typeof value === 'number') {
|
|
178
|
+
return `VALUE_${String(value)
|
|
179
|
+
.replace(/^-/, 'NEGATIVE_')
|
|
180
|
+
.replace(/\./g, '_')}`;
|
|
181
|
+
}
|
|
182
|
+
const text = String(value).trim();
|
|
183
|
+
return text ? text.toUpperCase() : `VALUE_${index}`;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* 校验基础类型和请求/响应变体不会重名
|
|
187
|
+
*/
|
|
188
|
+
validateGeneratedTypeNames() {
|
|
189
|
+
const owners = new Map();
|
|
190
|
+
for (const [schemaName, schema] of Object.entries(this.schemas)) {
|
|
191
|
+
const typeName = (0, naming_1.sanitizeTypeName)(schemaName);
|
|
192
|
+
this.registerTypeName(owners, typeName, schemaName);
|
|
193
|
+
if ((0, schema_1.schemaNeedsUsageVariant)(schema, this.schemas, 'request')) {
|
|
194
|
+
this.registerTypeName(owners, `${typeName}Input`, `${schemaName} 的请求类型`);
|
|
195
|
+
}
|
|
196
|
+
if ((0, schema_1.schemaNeedsUsageVariant)(schema, this.schemas, 'response')) {
|
|
197
|
+
this.registerTypeName(owners, `${typeName}Output`, `${schemaName} 的响应类型`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* 注册生成类型名称并检测冲突
|
|
203
|
+
* @param owners 类型名称归属映射
|
|
204
|
+
* @param typeName 生成类型名称
|
|
205
|
+
* @param owner 原始 Schema 描述
|
|
206
|
+
*/
|
|
207
|
+
registerTypeName(owners, typeName, owner) {
|
|
208
|
+
const existingOwner = owners.get(typeName);
|
|
209
|
+
if (existingOwner && existingOwner !== owner) {
|
|
210
|
+
throw new Error(`Schema 类型名称冲突: ${existingOwner} 和 ${owner} 都会生成 ${typeName}`);
|
|
211
|
+
}
|
|
212
|
+
owners.set(typeName, owner);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
exports.OpenAPITypeGenerator = OpenAPITypeGenerator;
|
package/dist/index.d.ts
CHANGED
|
@@ -33,4 +33,5 @@ export declare function generate(config: SwaggerConfig): Promise<void>;
|
|
|
33
33
|
export * from './types';
|
|
34
34
|
export { SwaggerParser } from './core/parser';
|
|
35
35
|
export { CodeGenerator } from './core/generator';
|
|
36
|
+
export { OpenAPITypeGenerator } from './core/type-generator';
|
|
36
37
|
export * from './utils';
|
package/dist/index.js
CHANGED
|
@@ -36,7 +36,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
36
36
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.CodeGenerator = exports.SwaggerParser = exports.Swagger2API = void 0;
|
|
39
|
+
exports.OpenAPITypeGenerator = exports.CodeGenerator = exports.SwaggerParser = exports.Swagger2API = void 0;
|
|
40
40
|
exports.generateFromConfig = generateFromConfig;
|
|
41
41
|
exports.generate = generate;
|
|
42
42
|
const path = __importStar(require("path"));
|
|
@@ -64,8 +64,8 @@ class Swagger2API {
|
|
|
64
64
|
try {
|
|
65
65
|
utils_1.logger.banner('🚀 swagger2api-v3', '开始生成 API 接口文件');
|
|
66
66
|
utils_1.logger.setTotalSteps(4);
|
|
67
|
-
// 1. 加载
|
|
68
|
-
utils_1.logger.stepTitle('📖', '加载
|
|
67
|
+
// 1. 加载 OpenAPI 文档
|
|
68
|
+
utils_1.logger.stepTitle('📖', '加载 OpenAPI 文档');
|
|
69
69
|
const document = await (0, utils_1.loadSwaggerDocument)(this.config.input);
|
|
70
70
|
utils_1.logger.success(`文档加载成功: ${document.info.title} v${document.info.version}`);
|
|
71
71
|
// 2. 解析文档
|
|
@@ -179,4 +179,6 @@ var parser_2 = require("./core/parser");
|
|
|
179
179
|
Object.defineProperty(exports, "SwaggerParser", { enumerable: true, get: function () { return parser_2.SwaggerParser; } });
|
|
180
180
|
var generator_2 = require("./core/generator");
|
|
181
181
|
Object.defineProperty(exports, "CodeGenerator", { enumerable: true, get: function () { return generator_2.CodeGenerator; } });
|
|
182
|
+
var type_generator_1 = require("./core/type-generator");
|
|
183
|
+
Object.defineProperty(exports, "OpenAPITypeGenerator", { enumerable: true, get: function () { return type_generator_1.OpenAPITypeGenerator; } });
|
|
182
184
|
__exportStar(require("./utils"), exports);
|