swagger2api-v3 1.1.7 → 1.1.8
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 +15 -1
- package/dist/.swagger2api.schema.json +120 -0
- package/dist/cli/index.js +119 -47
- package/dist/config/validator.d.ts +7 -0
- package/dist/config/validator.js +218 -0
- package/dist/core/generator.d.ts +7 -1
- package/dist/core/generator.js +26 -44
- package/dist/core/parser.d.ts +13 -1
- package/dist/core/parser.js +54 -40
- package/dist/index.d.ts +6 -0
- package/dist/index.js +21 -13
- package/dist/types/index.d.ts +75 -38
- package/dist/utils/comment.d.ts +8 -0
- package/dist/utils/comment.js +47 -0
- package/dist/utils/file.d.ts +23 -0
- package/dist/utils/file.js +98 -0
- package/dist/utils/index.d.ts +4 -99
- package/dist/utils/index.js +6 -566
- package/dist/utils/naming.d.ts +51 -0
- package/dist/utils/naming.js +124 -0
- package/dist/utils/type.d.ts +39 -0
- package/dist/utils/type.js +331 -0
- package/package.json +3 -2
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.pathToFunctionName = pathToFunctionName;
|
|
4
|
+
exports.toKebabCase = toKebabCase;
|
|
5
|
+
exports.toPascalCase = toPascalCase;
|
|
6
|
+
exports.toCamelCase = toCamelCase;
|
|
7
|
+
exports.stripMethodNamePrefixes = stripMethodNamePrefixes;
|
|
8
|
+
exports.removeMethodSuffix = removeMethodSuffix;
|
|
9
|
+
exports.sanitizeFilename = sanitizeFilename;
|
|
10
|
+
exports.sanitizeTypeName = sanitizeTypeName;
|
|
11
|
+
/**
|
|
12
|
+
* 将路径转换为小驼峰命名的函数名
|
|
13
|
+
* @param method HTTP方法
|
|
14
|
+
* @param path 接口路径
|
|
15
|
+
* @returns 小驼峰命名的函数名
|
|
16
|
+
*/
|
|
17
|
+
function pathToFunctionName(method, path) {
|
|
18
|
+
const cleanPath = path.replace(/\{([^}]+)\}/g, '$1');
|
|
19
|
+
const segments = cleanPath.split('/').filter((segment) => segment.length > 0);
|
|
20
|
+
const pathParts = segments.map((part, index) => {
|
|
21
|
+
const cleanPart = part.replace(/[^a-zA-Z0-9]/g, '');
|
|
22
|
+
if (index === 0) {
|
|
23
|
+
return cleanPart.toLowerCase();
|
|
24
|
+
}
|
|
25
|
+
return cleanPart.charAt(0).toUpperCase() + cleanPart.slice(1).toLowerCase();
|
|
26
|
+
});
|
|
27
|
+
const methodSuffix = method.charAt(0).toUpperCase() + method.slice(1).toLowerCase();
|
|
28
|
+
const baseName = pathParts.join('');
|
|
29
|
+
return baseName + methodSuffix;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* 将字符串转换为kebab-case
|
|
33
|
+
* @param str 输入字符串
|
|
34
|
+
* @returns kebab-case字符串
|
|
35
|
+
*/
|
|
36
|
+
function toKebabCase(str) {
|
|
37
|
+
return str
|
|
38
|
+
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
39
|
+
.replace(/[\s_]+/g, '-')
|
|
40
|
+
.toLowerCase();
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 将字符串转换为PascalCase
|
|
44
|
+
* @param str 输入字符串
|
|
45
|
+
* @returns PascalCase字符串
|
|
46
|
+
*/
|
|
47
|
+
function toPascalCase(str) {
|
|
48
|
+
return str
|
|
49
|
+
.replace(/[\s-_]+(.)?/g, (_, char) => (char ? char.toUpperCase() : ''))
|
|
50
|
+
.replace(/^(.)/, (char) => char.toUpperCase());
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* 将字符串转换为camelCase
|
|
54
|
+
* @param str 输入字符串
|
|
55
|
+
* @returns camelCase字符串
|
|
56
|
+
*/
|
|
57
|
+
function toCamelCase(str) {
|
|
58
|
+
const pascalCase = toPascalCase(str);
|
|
59
|
+
return pascalCase.charAt(0).toLowerCase() + pascalCase.slice(1);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* 从方法名中移除指定的前缀
|
|
63
|
+
* @param methodName 方法名
|
|
64
|
+
* @param prefixes 需要移除的前缀数组
|
|
65
|
+
* @returns 移除前缀后的方法名
|
|
66
|
+
*/
|
|
67
|
+
function stripMethodNamePrefixes(methodName, prefixes) {
|
|
68
|
+
if (!prefixes || prefixes.length === 0) {
|
|
69
|
+
return methodName;
|
|
70
|
+
}
|
|
71
|
+
let result = methodName;
|
|
72
|
+
let changed = true;
|
|
73
|
+
while (changed) {
|
|
74
|
+
changed = false;
|
|
75
|
+
for (const prefix of prefixes) {
|
|
76
|
+
if (!prefix)
|
|
77
|
+
continue;
|
|
78
|
+
const camelPrefix = toCamelCase(prefix);
|
|
79
|
+
const lowerMethodName = result.toLowerCase();
|
|
80
|
+
const lowerPrefix = camelPrefix.toLowerCase();
|
|
81
|
+
if (lowerMethodName.startsWith(lowerPrefix)) {
|
|
82
|
+
const remaining = result.substring(camelPrefix.length);
|
|
83
|
+
if (remaining.length > 0) {
|
|
84
|
+
result = remaining.charAt(0).toLowerCase() + remaining.slice(1);
|
|
85
|
+
changed = true;
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* 从函数名中移除 HTTP method 后缀
|
|
95
|
+
* @param functionName 函数名
|
|
96
|
+
* @param method HTTP 方法
|
|
97
|
+
* @returns 移除后缀后的函数名
|
|
98
|
+
*/
|
|
99
|
+
function removeMethodSuffix(functionName, method) {
|
|
100
|
+
const methodSuffix = method.charAt(0).toUpperCase() + method.slice(1).toLowerCase();
|
|
101
|
+
if (functionName.endsWith(methodSuffix)) {
|
|
102
|
+
return functionName.slice(0, -methodSuffix.length);
|
|
103
|
+
}
|
|
104
|
+
return functionName;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* 清理文件名,移除非法字符
|
|
108
|
+
* @param filename 文件名
|
|
109
|
+
* @returns 清理后的文件名
|
|
110
|
+
*/
|
|
111
|
+
function sanitizeFilename(filename) {
|
|
112
|
+
return filename.replace(/[<>:"/\\|?*]/g, '-').replace(/\s+/g, '-');
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* 清理类型名称并转换为 PascalCase
|
|
116
|
+
* @param name 类型名称
|
|
117
|
+
* @returns 清理后的类型名称
|
|
118
|
+
*/
|
|
119
|
+
function sanitizeTypeName(name) {
|
|
120
|
+
if (!name)
|
|
121
|
+
return name;
|
|
122
|
+
const replaced = name.replace(/[^a-zA-Z0-9_]/g, '_');
|
|
123
|
+
return toPascalCase(replaced);
|
|
124
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { SwaggerParameter } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* 移除联合类型中的顶层 null 类型
|
|
4
|
+
* @param typeStr 类型字符串
|
|
5
|
+
* @returns 移除 null 后的类型字符串
|
|
6
|
+
*/
|
|
7
|
+
export declare function stripNullFromUnion(typeStr: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* 将 OpenAPI schema 转换为 TypeScript 类型
|
|
10
|
+
* @param schema OpenAPI schema
|
|
11
|
+
* @param schemas 可选的 schemas 上下文,用于查找被引用的类型定义
|
|
12
|
+
* @returns TypeScript类型字符串
|
|
13
|
+
*/
|
|
14
|
+
export declare function swaggerTypeToTsType(schema: any, schemas?: any): string;
|
|
15
|
+
/**
|
|
16
|
+
* 将 OpenAPI 参数转换为 schema 对象
|
|
17
|
+
* @param parameter OpenAPI 参数
|
|
18
|
+
* @returns 参数对应的 schema
|
|
19
|
+
*/
|
|
20
|
+
export declare function swaggerParameterToSchema(parameter: SwaggerParameter): any;
|
|
21
|
+
/**
|
|
22
|
+
* 从 OpenAPI 参数生成 TypeScript 参数类型
|
|
23
|
+
* @param parameters OpenAPI 参数数组
|
|
24
|
+
* @returns TypeScript参数类型定义
|
|
25
|
+
*/
|
|
26
|
+
export declare function generateParameterTypes(parameters: SwaggerParameter[]): string;
|
|
27
|
+
/**
|
|
28
|
+
* 获取响应类型
|
|
29
|
+
* @param responses OpenAPI 响应对象
|
|
30
|
+
* @param schemas OpenAPI components.schemas
|
|
31
|
+
* @returns TypeScript类型字符串
|
|
32
|
+
*/
|
|
33
|
+
export declare function getResponseType(responses: any, schemas?: any): string;
|
|
34
|
+
/**
|
|
35
|
+
* 从 OpenAPI content 对象中获取最合适的 schema
|
|
36
|
+
* @param content OpenAPI content 对象
|
|
37
|
+
* @returns 匹配到的 schema
|
|
38
|
+
*/
|
|
39
|
+
export declare function getSchemaFromContent(content: any): any;
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.stripNullFromUnion = stripNullFromUnion;
|
|
4
|
+
exports.swaggerTypeToTsType = swaggerTypeToTsType;
|
|
5
|
+
exports.swaggerParameterToSchema = swaggerParameterToSchema;
|
|
6
|
+
exports.generateParameterTypes = generateParameterTypes;
|
|
7
|
+
exports.getResponseType = getResponseType;
|
|
8
|
+
exports.getSchemaFromContent = getSchemaFromContent;
|
|
9
|
+
const naming_1 = require("./naming");
|
|
10
|
+
/**
|
|
11
|
+
* 移除联合类型中的顶层 null 类型
|
|
12
|
+
* @param typeStr 类型字符串
|
|
13
|
+
* @returns 移除 null 后的类型字符串
|
|
14
|
+
*/
|
|
15
|
+
function stripNullFromUnion(typeStr) {
|
|
16
|
+
if (!typeStr)
|
|
17
|
+
return 'any';
|
|
18
|
+
const parts = [];
|
|
19
|
+
let current = '';
|
|
20
|
+
let parenDepth = 0;
|
|
21
|
+
let angleDepth = 0;
|
|
22
|
+
let braceDepth = 0;
|
|
23
|
+
let bracketDepth = 0;
|
|
24
|
+
for (let i = 0; i < typeStr.length; i++) {
|
|
25
|
+
const ch = typeStr[i];
|
|
26
|
+
if (ch === '(')
|
|
27
|
+
parenDepth++;
|
|
28
|
+
else if (ch === ')')
|
|
29
|
+
parenDepth = Math.max(0, parenDepth - 1);
|
|
30
|
+
else if (ch === '<')
|
|
31
|
+
angleDepth++;
|
|
32
|
+
else if (ch === '>')
|
|
33
|
+
angleDepth = Math.max(0, angleDepth - 1);
|
|
34
|
+
else if (ch === '{')
|
|
35
|
+
braceDepth++;
|
|
36
|
+
else if (ch === '}')
|
|
37
|
+
braceDepth = Math.max(0, braceDepth - 1);
|
|
38
|
+
else if (ch === '[')
|
|
39
|
+
bracketDepth++;
|
|
40
|
+
else if (ch === ']')
|
|
41
|
+
bracketDepth = Math.max(0, bracketDepth - 1);
|
|
42
|
+
const isTopLevel = parenDepth === 0 &&
|
|
43
|
+
angleDepth === 0 &&
|
|
44
|
+
braceDepth === 0 &&
|
|
45
|
+
bracketDepth === 0;
|
|
46
|
+
if (ch === '|' && isTopLevel) {
|
|
47
|
+
parts.push(current.trim());
|
|
48
|
+
current = '';
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
current += ch;
|
|
52
|
+
}
|
|
53
|
+
if (current.trim())
|
|
54
|
+
parts.push(current.trim());
|
|
55
|
+
const normalized = Array.from(new Set(parts.filter((part) => part && part !== 'null')));
|
|
56
|
+
return normalized.length > 0 ? normalized.join(' | ') : 'any';
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 将 OpenAPI schema 转换为 TypeScript 类型
|
|
60
|
+
* @param schema OpenAPI schema
|
|
61
|
+
* @param schemas 可选的 schemas 上下文,用于查找被引用的类型定义
|
|
62
|
+
* @returns TypeScript类型字符串
|
|
63
|
+
*/
|
|
64
|
+
function swaggerTypeToTsType(schema, schemas) {
|
|
65
|
+
if (!schema)
|
|
66
|
+
return 'any';
|
|
67
|
+
let baseType = 'any';
|
|
68
|
+
if (Array.isArray(schema.type)) {
|
|
69
|
+
const types = schema.type.map((type) => swaggerTypeToTsType({ ...schema, type, nullable: false }, schemas));
|
|
70
|
+
const uniqueTypes = Array.from(new Set(types));
|
|
71
|
+
baseType = uniqueTypes.includes('any') ? 'any' : uniqueTypes.join(' | ');
|
|
72
|
+
}
|
|
73
|
+
else if (schema.enum) {
|
|
74
|
+
baseType = schema.enum
|
|
75
|
+
.map((value) => toLiteralType(value))
|
|
76
|
+
.join(' | ');
|
|
77
|
+
}
|
|
78
|
+
else if (schema.allOf) {
|
|
79
|
+
const refSchema = schema.allOf.find((item) => item.$ref);
|
|
80
|
+
const secondSchema = schema.allOf.find((item) => !item.$ref);
|
|
81
|
+
if (refSchema && secondSchema) {
|
|
82
|
+
const refName = refSchema.$ref.split('/').pop();
|
|
83
|
+
const sanitizedRefName = (0, naming_1.sanitizeTypeName)(refName || '');
|
|
84
|
+
if (secondSchema.properties) {
|
|
85
|
+
const propertyTypes = [];
|
|
86
|
+
for (const propSchema of Object.values(secondSchema.properties)) {
|
|
87
|
+
const propType = swaggerTypeToTsType(propSchema, schemas);
|
|
88
|
+
propertyTypes.push(propType);
|
|
89
|
+
}
|
|
90
|
+
if (propertyTypes.length === 1) {
|
|
91
|
+
baseType = `${sanitizedRefName}<${propertyTypes[0]}>`;
|
|
92
|
+
}
|
|
93
|
+
else if (propertyTypes.length > 1) {
|
|
94
|
+
const combinedType = `{ ${Object.entries(secondSchema.properties)
|
|
95
|
+
.map(([key, value]) => {
|
|
96
|
+
const optional = secondSchema.required?.includes(key) ? '' : '?';
|
|
97
|
+
let type = swaggerTypeToTsType(value, schemas);
|
|
98
|
+
if (optional === '?')
|
|
99
|
+
type = stripNullFromUnion(type);
|
|
100
|
+
return `${key}${optional}: ${type}`;
|
|
101
|
+
})
|
|
102
|
+
.join('; ')} }`;
|
|
103
|
+
baseType = `${sanitizedRefName}<${combinedType}>`;
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
baseType = sanitizedRefName || 'any';
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
baseType = sanitizedRefName || 'any';
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
const types = schema.allOf
|
|
115
|
+
.map((item) => swaggerTypeToTsType(item))
|
|
116
|
+
.filter((type) => type !== 'any');
|
|
117
|
+
baseType = types.length > 0 ? types[0] : 'any';
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
else if (schema.anyOf || schema.oneOf) {
|
|
121
|
+
const types = (schema.anyOf || schema.oneOf).map((item) => {
|
|
122
|
+
if (item.type === 'null')
|
|
123
|
+
return 'null';
|
|
124
|
+
return swaggerTypeToTsType(item, schemas);
|
|
125
|
+
});
|
|
126
|
+
if (types.includes('any')) {
|
|
127
|
+
baseType = 'any';
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
const uniqueTypes = Array.from(new Set(types));
|
|
131
|
+
baseType = uniqueTypes.length > 0 ? uniqueTypes.join(' | ') : 'any';
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
else if (schema.$ref) {
|
|
135
|
+
const refName = schema.$ref.split('/').pop();
|
|
136
|
+
baseType = (0, naming_1.sanitizeTypeName)(refName || 'any');
|
|
137
|
+
if (schemas && refName) {
|
|
138
|
+
const referencedSchema = schemas[refName];
|
|
139
|
+
if (referencedSchema && referencedSchema.type === 'array') {
|
|
140
|
+
baseType = `${baseType}[]`;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
else if (schema.type === 'array') {
|
|
145
|
+
const itemSchema = schema.items;
|
|
146
|
+
const itemType = swaggerTypeToTsType(itemSchema, schemas);
|
|
147
|
+
if (itemSchema?.$ref && schemas) {
|
|
148
|
+
const refName = itemSchema.$ref.split('/').pop();
|
|
149
|
+
const referencedSchema = refName ? schemas[refName] : undefined;
|
|
150
|
+
baseType =
|
|
151
|
+
referencedSchema?.type === 'array' ? itemType : `${itemType}[]`;
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
baseType = `${itemType}[]`;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
else if (schema.type === 'object') {
|
|
158
|
+
if (schema.properties) {
|
|
159
|
+
const properties = Object.entries(schema.properties)
|
|
160
|
+
.map(([key, value]) => {
|
|
161
|
+
const optional = schema.required?.includes(key) ? '' : '?';
|
|
162
|
+
let type = swaggerTypeToTsType(value, schemas);
|
|
163
|
+
if (optional === '?')
|
|
164
|
+
type = stripNullFromUnion(type);
|
|
165
|
+
return ` ${key}${optional}: ${type};`;
|
|
166
|
+
})
|
|
167
|
+
.join('\n');
|
|
168
|
+
baseType = `{\n${properties}\n}`;
|
|
169
|
+
}
|
|
170
|
+
else if (schema.additionalProperties &&
|
|
171
|
+
typeof schema.additionalProperties === 'object') {
|
|
172
|
+
const valueType = swaggerTypeToTsType(schema.additionalProperties, schemas);
|
|
173
|
+
baseType = `Record<string, ${valueType}>`;
|
|
174
|
+
}
|
|
175
|
+
else if (schema.additionalProperties === false) {
|
|
176
|
+
baseType = 'Record<string, never>';
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
baseType = 'Record<string, any>';
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
switch (schema.type) {
|
|
184
|
+
case 'integer':
|
|
185
|
+
case 'number':
|
|
186
|
+
baseType = 'number';
|
|
187
|
+
break;
|
|
188
|
+
case 'string':
|
|
189
|
+
baseType = 'string';
|
|
190
|
+
break;
|
|
191
|
+
case 'boolean':
|
|
192
|
+
baseType = 'boolean';
|
|
193
|
+
break;
|
|
194
|
+
case 'file':
|
|
195
|
+
baseType = 'File';
|
|
196
|
+
break;
|
|
197
|
+
case 'null':
|
|
198
|
+
baseType = 'null';
|
|
199
|
+
break;
|
|
200
|
+
default:
|
|
201
|
+
baseType = 'any';
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (schema.nullable === true) {
|
|
206
|
+
if (baseType === 'any')
|
|
207
|
+
return 'any';
|
|
208
|
+
if (baseType.includes('null'))
|
|
209
|
+
return baseType;
|
|
210
|
+
return `${baseType} | null`;
|
|
211
|
+
}
|
|
212
|
+
return baseType;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* 将枚举值转换为 TypeScript 字面量类型
|
|
216
|
+
* @param value 枚举值
|
|
217
|
+
* @returns 字面量类型字符串
|
|
218
|
+
*/
|
|
219
|
+
function toLiteralType(value) {
|
|
220
|
+
if (value === null)
|
|
221
|
+
return 'null';
|
|
222
|
+
if (typeof value === 'string')
|
|
223
|
+
return `'${value}'`;
|
|
224
|
+
return String(value);
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* 将 OpenAPI 参数转换为 schema 对象
|
|
228
|
+
* @param parameter OpenAPI 参数
|
|
229
|
+
* @returns 参数对应的 schema
|
|
230
|
+
*/
|
|
231
|
+
function swaggerParameterToSchema(parameter) {
|
|
232
|
+
if (parameter.schema) {
|
|
233
|
+
return parameter.schema;
|
|
234
|
+
}
|
|
235
|
+
return {
|
|
236
|
+
type: parameter.type || (parameter.items ? 'array' : 'string'),
|
|
237
|
+
format: parameter.format,
|
|
238
|
+
items: parameter.items,
|
|
239
|
+
enum: parameter.enum
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* 从 OpenAPI 参数生成 TypeScript 参数类型
|
|
244
|
+
* @param parameters OpenAPI 参数数组
|
|
245
|
+
* @returns TypeScript参数类型定义
|
|
246
|
+
*/
|
|
247
|
+
function generateParameterTypes(parameters) {
|
|
248
|
+
if (!parameters || parameters.length === 0) {
|
|
249
|
+
return '';
|
|
250
|
+
}
|
|
251
|
+
const queryParams = parameters.filter((param) => param.in === 'query');
|
|
252
|
+
const pathParams = parameters.filter((param) => param.in === 'path');
|
|
253
|
+
const bodyParams = parameters.filter((param) => param.in === 'body');
|
|
254
|
+
const types = [];
|
|
255
|
+
if (pathParams.length > 0) {
|
|
256
|
+
const pathType = pathParams
|
|
257
|
+
.map((param) => `${param.name}: ${swaggerTypeToTsType(swaggerParameterToSchema(param))}`)
|
|
258
|
+
.join(', ');
|
|
259
|
+
types.push(`pathParams: { ${pathType} }`);
|
|
260
|
+
}
|
|
261
|
+
if (queryParams.length > 0) {
|
|
262
|
+
const queryType = queryParams
|
|
263
|
+
.map((param) => {
|
|
264
|
+
const optional = param.required ? '' : '?';
|
|
265
|
+
return `${param.name}${optional}: ${swaggerTypeToTsType(swaggerParameterToSchema(param))}`;
|
|
266
|
+
})
|
|
267
|
+
.join(', ');
|
|
268
|
+
types.push(`queryParams${queryParams.every((param) => !param.required) ? '?' : ''}: { ${queryType} }`);
|
|
269
|
+
}
|
|
270
|
+
if (bodyParams.length > 0) {
|
|
271
|
+
const bodyParam = bodyParams[0];
|
|
272
|
+
const bodyType = swaggerTypeToTsType(bodyParam.schema);
|
|
273
|
+
types.push(`data: ${bodyType}`);
|
|
274
|
+
}
|
|
275
|
+
return types.join(', ');
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* 获取响应类型
|
|
279
|
+
* @param responses OpenAPI 响应对象
|
|
280
|
+
* @param schemas OpenAPI components.schemas
|
|
281
|
+
* @returns TypeScript类型字符串
|
|
282
|
+
*/
|
|
283
|
+
function getResponseType(responses, schemas) {
|
|
284
|
+
if (!responses) {
|
|
285
|
+
return 'any';
|
|
286
|
+
}
|
|
287
|
+
const successResponse = getSuccessResponse(responses);
|
|
288
|
+
if (!successResponse) {
|
|
289
|
+
return 'any';
|
|
290
|
+
}
|
|
291
|
+
const contentSchema = getSchemaFromContent(successResponse.content);
|
|
292
|
+
if (contentSchema) {
|
|
293
|
+
return swaggerTypeToTsType(contentSchema, schemas);
|
|
294
|
+
}
|
|
295
|
+
return successResponse.description === 'No Content' ? 'void' : 'any';
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* 从响应集合中获取优先成功响应
|
|
299
|
+
* @param responses OpenAPI响应对象
|
|
300
|
+
* @returns 成功响应对象
|
|
301
|
+
*/
|
|
302
|
+
function getSuccessResponse(responses) {
|
|
303
|
+
if (responses['200'])
|
|
304
|
+
return responses['200'];
|
|
305
|
+
if (responses['201'])
|
|
306
|
+
return responses['201'];
|
|
307
|
+
const successStatus = Object.keys(responses)
|
|
308
|
+
.filter((status) => /^2\d\d$/.test(status))
|
|
309
|
+
.sort()[0];
|
|
310
|
+
return successStatus ? responses[successStatus] : responses.default;
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* 从 OpenAPI content 对象中获取最合适的 schema
|
|
314
|
+
* @param content OpenAPI content 对象
|
|
315
|
+
* @returns 匹配到的 schema
|
|
316
|
+
*/
|
|
317
|
+
function getSchemaFromContent(content) {
|
|
318
|
+
if (!content)
|
|
319
|
+
return undefined;
|
|
320
|
+
const exactJson = content['application/json']?.schema;
|
|
321
|
+
if (exactJson)
|
|
322
|
+
return exactJson;
|
|
323
|
+
const jsonLikeMediaType = Object.keys(content).find((mediaType) => mediaType.endsWith('+json') || mediaType.includes('/json'));
|
|
324
|
+
if (jsonLikeMediaType && content[jsonLikeMediaType]?.schema) {
|
|
325
|
+
return content[jsonLikeMediaType].schema;
|
|
326
|
+
}
|
|
327
|
+
const firstMediaTypeWithSchema = Object.keys(content).find((mediaType) => content[mediaType]?.schema);
|
|
328
|
+
return firstMediaTypeWithSchema
|
|
329
|
+
? content[firstMediaTypeWithSchema].schema
|
|
330
|
+
: undefined;
|
|
331
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "swagger2api-v3",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.8",
|
|
4
4
|
"description": "A command-line tool for generating TypeScript API interfaces from Swagger (OAS 3.0) documentation",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
"test:watch": "jest --watch",
|
|
19
19
|
"test:coverage": "jest --coverage",
|
|
20
20
|
"test:ci": "jest --ci --coverage --watchAll=false",
|
|
21
|
-
"build": "tsc",
|
|
21
|
+
"build": "tsc && npm run copy:schema",
|
|
22
|
+
"copy:schema": "node build/copy-schema.js",
|
|
22
23
|
"clean": "rimraf dist",
|
|
23
24
|
"prebuild": "npm run clean",
|
|
24
25
|
"start": "node dist/cli/index.js",
|