swagger2api-v3 1.1.2 → 1.1.4
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 +25 -17
- package/dist/cli/index.js +5 -0
- package/dist/core/parser.d.ts +5 -0
- package/dist/core/parser.js +35 -10
- package/dist/types/index.d.ts +2 -0
- package/dist/utils/index.d.ts +11 -2
- package/dist/utils/index.js +75 -18
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -36,6 +36,7 @@ npx swagger2api-v3 init
|
|
|
36
36
|
The tool automatically generates configuration files in the corresponding format based on the project environment:
|
|
37
37
|
|
|
38
38
|
**CommonJS Environment** (`"type": "commonjs"` or not set):
|
|
39
|
+
|
|
39
40
|
```javascript
|
|
40
41
|
const config = {
|
|
41
42
|
input: 'https://petstore.swagger.io/v2/swagger.json',
|
|
@@ -55,6 +56,7 @@ module.exports = config;
|
|
|
55
56
|
```
|
|
56
57
|
|
|
57
58
|
**ES Module Environment** (`"type": "module"`):
|
|
59
|
+
|
|
58
60
|
```javascript
|
|
59
61
|
const config = {
|
|
60
62
|
// ... same configuration
|
|
@@ -71,19 +73,20 @@ npx swagger2api-v3 generate
|
|
|
71
73
|
|
|
72
74
|
## ⚙️ Configuration Options
|
|
73
75
|
|
|
74
|
-
| Option
|
|
75
|
-
|
|
76
|
-
| `input`
|
|
77
|
-
| `output`
|
|
78
|
-
| `generator`
|
|
79
|
-
| `groupByTags`
|
|
80
|
-
| `overwrite`
|
|
81
|
-
| `prefix`
|
|
82
|
-
| `importTemplate`
|
|
83
|
-
| `requestStyle`
|
|
84
|
-
| `lint`
|
|
85
|
-
| `methodNameIgnorePrefix` | string[]
|
|
86
|
-
| `
|
|
76
|
+
| Option | Type | Default | Description |
|
|
77
|
+
| ------------------------ | --------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
78
|
+
| `input` | string | - | Swagger JSON file path or URL |
|
|
79
|
+
| `output` | string | `'./src/api'` | Output directory for generated code |
|
|
80
|
+
| `generator` | string | `'typescript'` | Code generator type. Supports `'typescript'` and `'javascript'`. `'javascript'` outputs `.js` files and skips type file generation |
|
|
81
|
+
| `groupByTags` | boolean | `true` | Whether to group files by tags |
|
|
82
|
+
| `overwrite` | boolean | `true` | Whether to overwrite existing files |
|
|
83
|
+
| `prefix` | string | `''` | Common prefix for API paths |
|
|
84
|
+
| `importTemplate` | string | - | Import statement template for request function |
|
|
85
|
+
| `requestStyle` | 'method' \| 'generic' | `'generic'` | Request call style: `method` uses `request.get/post`, `generic` uses `request({ method })` |
|
|
86
|
+
| `lint` | string | - | Code formatting command (optional) |
|
|
87
|
+
| `methodNameIgnorePrefix` | string[] | `[]` | Array of prefixes to ignore when generating method names. For example, `['api', 'auth']` will transform `apiGetName` to `getName` and `authUserInfo` to `userInfo` |
|
|
88
|
+
| `addMethodSuffix` | boolean | `true` | Whether to add HTTP method suffix to generated function names. `true` generates `userListPost`, `false` generates `userList` |
|
|
89
|
+
| `options.addComments` | boolean | `true` | Whether to add detailed comments |
|
|
87
90
|
|
|
88
91
|
## 📁 Generated File Structure
|
|
89
92
|
|
|
@@ -119,7 +122,12 @@ Example generated API function (generic style):
|
|
|
119
122
|
|
|
120
123
|
```javascript
|
|
121
124
|
export const codeAuth = (data, config) => {
|
|
122
|
-
return request({
|
|
125
|
+
return request({
|
|
126
|
+
url: '/api/auth/codeAuth',
|
|
127
|
+
method: 'POST',
|
|
128
|
+
data,
|
|
129
|
+
...config
|
|
130
|
+
});
|
|
123
131
|
};
|
|
124
132
|
```
|
|
125
133
|
|
|
@@ -222,14 +230,14 @@ Support automatic execution of formatting commands after generation:
|
|
|
222
230
|
// In configuration file
|
|
223
231
|
const config = {
|
|
224
232
|
// ... other configurations
|
|
225
|
-
lint: 'prettier --write'
|
|
233
|
+
lint: 'prettier --write' // or 'eslint --fix', etc.
|
|
226
234
|
};
|
|
227
235
|
```
|
|
228
236
|
|
|
229
237
|
## 🤝 Contributing
|
|
230
238
|
|
|
231
|
-
|
|
239
|
+
If you encounter any problems or have suggestions, please feel free to [submit an issue](https://github.com/xiaoyang33/swagger2api-v3/issues) on GitHub. Pull Requests are also welcome!
|
|
232
240
|
|
|
233
241
|
## 📄 License
|
|
234
242
|
|
|
235
|
-
MIT License
|
|
243
|
+
MIT License
|
package/dist/cli/index.js
CHANGED
|
@@ -147,6 +147,11 @@ const config = {
|
|
|
147
147
|
// 如果方法名是 apiAuthGetName,会依次移除所有匹配的前缀,最终变成 getName
|
|
148
148
|
methodNameIgnorePrefix: [],
|
|
149
149
|
|
|
150
|
+
// 是否在生成的方法名中添加 HTTP method 后缀,默认为 true
|
|
151
|
+
// true: userListPost, userDetailGet
|
|
152
|
+
// false: userList, userDetail
|
|
153
|
+
addMethodSuffix: true,
|
|
154
|
+
|
|
150
155
|
// 生成选项
|
|
151
156
|
options: {
|
|
152
157
|
// 是否添加注释
|
package/dist/core/parser.d.ts
CHANGED
|
@@ -6,6 +6,11 @@ export declare class SwaggerParser {
|
|
|
6
6
|
private document;
|
|
7
7
|
private config;
|
|
8
8
|
constructor(document: SwaggerDocument, config: SwaggerConfig);
|
|
9
|
+
/**
|
|
10
|
+
* 获取所有 schemas (包括 definitions 和 components.schemas)
|
|
11
|
+
* @returns schemas 对象
|
|
12
|
+
*/
|
|
13
|
+
private getAllSchemas;
|
|
9
14
|
/**
|
|
10
15
|
* 解析所有API接口
|
|
11
16
|
* @returns API接口信息数组
|
package/dist/core/parser.js
CHANGED
|
@@ -10,6 +10,16 @@ class SwaggerParser {
|
|
|
10
10
|
this.document = document;
|
|
11
11
|
this.config = config;
|
|
12
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* 获取所有 schemas (包括 definitions 和 components.schemas)
|
|
15
|
+
* @returns schemas 对象
|
|
16
|
+
*/
|
|
17
|
+
getAllSchemas() {
|
|
18
|
+
return {
|
|
19
|
+
...(this.document.definitions || {}),
|
|
20
|
+
...(this.document.components?.schemas || {})
|
|
21
|
+
};
|
|
22
|
+
}
|
|
13
23
|
/**
|
|
14
24
|
* 解析所有API接口
|
|
15
25
|
* @returns API接口信息数组
|
|
@@ -70,11 +80,21 @@ class SwaggerParser {
|
|
|
70
80
|
}
|
|
71
81
|
// 生成函数名
|
|
72
82
|
let functionName = operation.operationId || (0, utils_1.pathToFunctionName)(method, path);
|
|
73
|
-
//
|
|
83
|
+
// 根据配置决定是否添加/保留 HTTP method 后缀
|
|
84
|
+
const shouldAddMethodSuffix = this.config.addMethodSuffix !== false; // 默认为 true
|
|
74
85
|
if (operation.operationId) {
|
|
75
|
-
//
|
|
76
|
-
|
|
77
|
-
|
|
86
|
+
// 如果使用了 operationId,根据配置决定是否添加 HTTP 方法后缀
|
|
87
|
+
if (shouldAddMethodSuffix) {
|
|
88
|
+
const methodSuffix = method.charAt(0).toUpperCase() + method.slice(1).toLowerCase();
|
|
89
|
+
functionName = functionName + methodSuffix;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
// 如果没有 operationId,pathToFunctionName 已经包含了 method 后缀
|
|
94
|
+
// 如果配置为 false,需要移除后缀
|
|
95
|
+
if (!shouldAddMethodSuffix) {
|
|
96
|
+
functionName = (0, utils_1.removeMethodSuffix)(functionName, method);
|
|
97
|
+
}
|
|
78
98
|
}
|
|
79
99
|
// 应用前缀忽略规则
|
|
80
100
|
functionName = (0, utils_1.stripMethodNamePrefixes)(functionName, this.config.methodNameIgnorePrefix);
|
|
@@ -129,14 +149,16 @@ class SwaggerParser {
|
|
|
129
149
|
// 解析 Swagger 2.0 definitions
|
|
130
150
|
if (this.document.definitions) {
|
|
131
151
|
for (const [name, schema] of Object.entries(this.document.definitions)) {
|
|
132
|
-
const
|
|
152
|
+
const sanitizedName = (0, utils_1.sanitizeTypeName)(name); // Use it
|
|
153
|
+
const typeInfo = this.parseTypeDefinition(sanitizedName, schema);
|
|
133
154
|
types.push(typeInfo);
|
|
134
155
|
}
|
|
135
156
|
}
|
|
136
157
|
// 解析 OpenAPI 3.0 components.schemas
|
|
137
158
|
if (this.document.components?.schemas) {
|
|
138
159
|
for (const [name, schema] of Object.entries(this.document.components.schemas)) {
|
|
139
|
-
const
|
|
160
|
+
const sanitizedName = (0, utils_1.sanitizeTypeName)(name); // Use it
|
|
161
|
+
const typeInfo = this.parseTypeDefinition(sanitizedName, schema);
|
|
140
162
|
types.push(typeInfo);
|
|
141
163
|
}
|
|
142
164
|
}
|
|
@@ -151,12 +173,14 @@ class SwaggerParser {
|
|
|
151
173
|
parseTypeDefinition(name, schema) {
|
|
152
174
|
const typeName = (0, utils_1.toPascalCase)(name);
|
|
153
175
|
let definition;
|
|
176
|
+
// 获取所有 schemas 用于类型解析
|
|
177
|
+
const allSchemas = this.getAllSchemas();
|
|
154
178
|
if (schema.type === 'object' && schema.properties) {
|
|
155
179
|
// 对象类型
|
|
156
180
|
const properties = Object.entries(schema.properties)
|
|
157
181
|
.map(([key, value]) => {
|
|
158
182
|
const optional = schema.required?.includes(key) ? '' : '?';
|
|
159
|
-
const type = (0, utils_1.swaggerTypeToTsType)(value);
|
|
183
|
+
const type = (0, utils_1.swaggerTypeToTsType)(value, allSchemas);
|
|
160
184
|
const comment = value.description
|
|
161
185
|
? ` /** ${value.description} */`
|
|
162
186
|
: '';
|
|
@@ -166,9 +190,10 @@ class SwaggerParser {
|
|
|
166
190
|
definition = `export interface ${typeName} {\n${properties}\n}`;
|
|
167
191
|
}
|
|
168
192
|
else if (schema.type === 'array') {
|
|
169
|
-
// 数组类型
|
|
170
|
-
|
|
171
|
-
|
|
193
|
+
// 数组类型 - 生成指向 items 类型的别名(不带 [])
|
|
194
|
+
// 在引用时会自动添加 []
|
|
195
|
+
const itemType = (0, utils_1.swaggerTypeToTsType)(schema.items, allSchemas);
|
|
196
|
+
definition = `export type ${typeName} = ${itemType};`;
|
|
172
197
|
}
|
|
173
198
|
else if (schema.enum) {
|
|
174
199
|
// 枚举类型
|
package/dist/types/index.d.ts
CHANGED
|
@@ -31,6 +31,8 @@ export interface SwaggerConfig {
|
|
|
31
31
|
lint?: string;
|
|
32
32
|
/** 生成方法名时需要忽略的前缀数组,如 ['api', 'auth'] */
|
|
33
33
|
methodNameIgnorePrefix?: string[];
|
|
34
|
+
/** 是否在生成的方法名中添加 HTTP method 后缀,默认为 true。true: userListPost, false: userList */
|
|
35
|
+
addMethodSuffix?: boolean;
|
|
34
36
|
}
|
|
35
37
|
/**
|
|
36
38
|
* 标签分组配置
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SwaggerDocument,
|
|
1
|
+
import { SwaggerDocument, SwaggerParameter } from '../types';
|
|
2
2
|
/**
|
|
3
3
|
* 工具函数集合
|
|
4
4
|
*/
|
|
@@ -34,12 +34,20 @@ export declare function toCamelCase(str: string): string;
|
|
|
34
34
|
* @returns 移除前缀后的方法名
|
|
35
35
|
*/
|
|
36
36
|
export declare function stripMethodNamePrefixes(methodName: string, prefixes?: string[]): string;
|
|
37
|
+
/**
|
|
38
|
+
* 从函数名中移除 HTTP method 后缀
|
|
39
|
+
* @param functionName 函数名
|
|
40
|
+
* @param method HTTP 方法
|
|
41
|
+
* @returns 移除后缀后的函数名
|
|
42
|
+
*/
|
|
43
|
+
export declare function removeMethodSuffix(functionName: string, method: string): string;
|
|
37
44
|
/**
|
|
38
45
|
* 将Swagger类型转换为TypeScript类型
|
|
39
46
|
* @param schema Swagger模式
|
|
47
|
+
* @param schemas 可选的 schemas 上下文,用于查找被引用的类型定义
|
|
40
48
|
* @returns TypeScript类型字符串
|
|
41
49
|
*/
|
|
42
|
-
export declare function swaggerTypeToTsType(schema?:
|
|
50
|
+
export declare function swaggerTypeToTsType(schema: any, schemas?: any): string;
|
|
43
51
|
/**
|
|
44
52
|
* 从Swagger参数生成TypeScript参数类型
|
|
45
53
|
* @param parameters Swagger参数数组
|
|
@@ -87,3 +95,4 @@ export declare function sanitizeFilename(filename: string): string;
|
|
|
87
95
|
* @returns TypeScript类型字符串
|
|
88
96
|
*/
|
|
89
97
|
export declare function getResponseType(responses: any): string;
|
|
98
|
+
export declare function sanitizeTypeName(name: string): string;
|
package/dist/utils/index.js
CHANGED
|
@@ -41,6 +41,7 @@ exports.toKebabCase = toKebabCase;
|
|
|
41
41
|
exports.toPascalCase = toPascalCase;
|
|
42
42
|
exports.toCamelCase = toCamelCase;
|
|
43
43
|
exports.stripMethodNamePrefixes = stripMethodNamePrefixes;
|
|
44
|
+
exports.removeMethodSuffix = removeMethodSuffix;
|
|
44
45
|
exports.swaggerTypeToTsType = swaggerTypeToTsType;
|
|
45
46
|
exports.generateParameterTypes = generateParameterTypes;
|
|
46
47
|
exports.ensureDirectoryExists = ensureDirectoryExists;
|
|
@@ -50,6 +51,7 @@ exports.writeFile = writeFile;
|
|
|
50
51
|
exports.generateApiComment = generateApiComment;
|
|
51
52
|
exports.sanitizeFilename = sanitizeFilename;
|
|
52
53
|
exports.getResponseType = getResponseType;
|
|
54
|
+
exports.sanitizeTypeName = sanitizeTypeName;
|
|
53
55
|
const fs = __importStar(require("fs"));
|
|
54
56
|
const path = __importStar(require("path"));
|
|
55
57
|
const axios_1 = __importDefault(require("axios"));
|
|
@@ -150,26 +152,42 @@ function stripMethodNamePrefixes(methodName, prefixes) {
|
|
|
150
152
|
}
|
|
151
153
|
return result;
|
|
152
154
|
}
|
|
155
|
+
/**
|
|
156
|
+
* 从函数名中移除 HTTP method 后缀
|
|
157
|
+
* @param functionName 函数名
|
|
158
|
+
* @param method HTTP 方法
|
|
159
|
+
* @returns 移除后缀后的函数名
|
|
160
|
+
*/
|
|
161
|
+
function removeMethodSuffix(functionName, method) {
|
|
162
|
+
const methodSuffix = method.charAt(0).toUpperCase() + method.slice(1).toLowerCase();
|
|
163
|
+
if (functionName.endsWith(methodSuffix)) {
|
|
164
|
+
return functionName.slice(0, -methodSuffix.length);
|
|
165
|
+
}
|
|
166
|
+
return functionName;
|
|
167
|
+
}
|
|
153
168
|
/**
|
|
154
169
|
* 将Swagger类型转换为TypeScript类型
|
|
155
170
|
* @param schema Swagger模式
|
|
171
|
+
* @param schemas 可选的 schemas 上下文,用于查找被引用的类型定义
|
|
156
172
|
* @returns TypeScript类型字符串
|
|
157
173
|
*/
|
|
158
|
-
function swaggerTypeToTsType(schema) {
|
|
159
|
-
if (!schema)
|
|
174
|
+
function swaggerTypeToTsType(schema, schemas) {
|
|
175
|
+
if (!schema)
|
|
160
176
|
return 'any';
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
const
|
|
167
|
-
if (
|
|
168
|
-
const refName =
|
|
169
|
-
const
|
|
170
|
-
//
|
|
177
|
+
let baseType = 'any';
|
|
178
|
+
// 处理 allOf (通常用于继承或泛型)
|
|
179
|
+
if (schema.allOf) {
|
|
180
|
+
// 简单处理:如果是引用 + 对象定义,可能是泛型包装
|
|
181
|
+
const refSchema = schema.allOf.find((s) => s.$ref);
|
|
182
|
+
const secondSchema = schema.allOf.find((s) => !s.$ref);
|
|
183
|
+
if (refSchema && secondSchema) {
|
|
184
|
+
const refName = refSchema.$ref.split('/').pop();
|
|
185
|
+
const sanitizedRefName = sanitizeTypeName(refName || '');
|
|
186
|
+
// 检查是否是泛型容器 (如 ResOp)
|
|
187
|
+
// 注意:secondSchema 可能没有显式声明 type: 'object',但如果有 properties,则视为对象
|
|
171
188
|
if (secondSchema.properties) {
|
|
172
|
-
//
|
|
189
|
+
// 尝试提取泛型参数类型
|
|
190
|
+
// 这里假设泛型参数是 properties 中的第一个属性
|
|
173
191
|
const propertyTypes = [];
|
|
174
192
|
for (const [propName, propSchema] of Object.entries(secondSchema.properties)) {
|
|
175
193
|
const propType = swaggerTypeToTsType(propSchema);
|
|
@@ -177,7 +195,7 @@ function swaggerTypeToTsType(schema) {
|
|
|
177
195
|
}
|
|
178
196
|
// 如果只有一个属性,直接作为泛型参数
|
|
179
197
|
if (propertyTypes.length === 1) {
|
|
180
|
-
baseType = `${
|
|
198
|
+
baseType = `${sanitizedRefName}<${propertyTypes[0]}>`;
|
|
181
199
|
}
|
|
182
200
|
// 如果有多个属性,组合成联合类型或对象类型
|
|
183
201
|
else if (propertyTypes.length > 1) {
|
|
@@ -188,14 +206,14 @@ function swaggerTypeToTsType(schema) {
|
|
|
188
206
|
return `${key}${optional}: ${type}`;
|
|
189
207
|
})
|
|
190
208
|
.join('; ')} }`;
|
|
191
|
-
baseType = `${
|
|
209
|
+
baseType = `${sanitizedRefName}<${combinedType}>`;
|
|
192
210
|
}
|
|
193
211
|
else {
|
|
194
|
-
baseType =
|
|
212
|
+
baseType = sanitizedRefName || 'any';
|
|
195
213
|
}
|
|
196
214
|
}
|
|
197
215
|
else {
|
|
198
|
-
baseType =
|
|
216
|
+
baseType = sanitizedRefName || 'any';
|
|
199
217
|
}
|
|
200
218
|
}
|
|
201
219
|
else {
|
|
@@ -206,10 +224,37 @@ function swaggerTypeToTsType(schema) {
|
|
|
206
224
|
baseType = types.length > 0 ? types[0] : 'any';
|
|
207
225
|
}
|
|
208
226
|
}
|
|
227
|
+
// 处理 anyOf 或 oneOf
|
|
228
|
+
else if (schema.anyOf || schema.oneOf) {
|
|
229
|
+
const types = (schema.anyOf || schema.oneOf)
|
|
230
|
+
.map((s) => {
|
|
231
|
+
// 特殊处理 type: 'null'
|
|
232
|
+
if (s.type === 'null')
|
|
233
|
+
return 'null';
|
|
234
|
+
return swaggerTypeToTsType(s);
|
|
235
|
+
})
|
|
236
|
+
.filter((t) => t !== 'any');
|
|
237
|
+
// 去重
|
|
238
|
+
const uniqueTypes = Array.from(new Set(types));
|
|
239
|
+
if (uniqueTypes.length > 0) {
|
|
240
|
+
baseType = uniqueTypes.join(' | ');
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
baseType = 'any';
|
|
244
|
+
}
|
|
245
|
+
}
|
|
209
246
|
// 处理引用类型
|
|
210
247
|
else if (schema.$ref) {
|
|
211
248
|
const refName = schema.$ref.split('/').pop();
|
|
212
|
-
baseType = refName || 'any';
|
|
249
|
+
baseType = sanitizeTypeName(refName || 'any');
|
|
250
|
+
// 如果提供了 schemas 上下文,检查被引用的 schema 是否是数组类型
|
|
251
|
+
if (schemas && refName) {
|
|
252
|
+
const referencedSchema = schemas[refName];
|
|
253
|
+
if (referencedSchema && referencedSchema.type === 'array') {
|
|
254
|
+
// 被引用的 schema 是数组类型,添加 []
|
|
255
|
+
baseType = `${baseType}[]`;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
213
258
|
}
|
|
214
259
|
// 处理数组类型
|
|
215
260
|
else if (schema.type === 'array') {
|
|
@@ -253,6 +298,9 @@ function swaggerTypeToTsType(schema) {
|
|
|
253
298
|
case 'file':
|
|
254
299
|
baseType = 'File';
|
|
255
300
|
break;
|
|
301
|
+
case 'null': // Add this
|
|
302
|
+
baseType = 'null';
|
|
303
|
+
break;
|
|
256
304
|
default:
|
|
257
305
|
baseType = 'any';
|
|
258
306
|
break;
|
|
@@ -452,3 +500,12 @@ function getResponseType(responses) {
|
|
|
452
500
|
}
|
|
453
501
|
return 'any';
|
|
454
502
|
}
|
|
503
|
+
function sanitizeTypeName(name) {
|
|
504
|
+
if (!name)
|
|
505
|
+
return name;
|
|
506
|
+
// 1. 替换非法字符(包括点号)为下划线
|
|
507
|
+
const replaced = name.replace(/[^a-zA-Z0-9_]/g, '_');
|
|
508
|
+
// 2. 转换为 PascalCase
|
|
509
|
+
// 使用现有的 toPascalCase,它能处理下划线
|
|
510
|
+
return toPascalCase(replaced);
|
|
511
|
+
}
|
package/package.json
CHANGED