swagger2api-v3 1.1.7 → 1.1.9

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.
@@ -35,7 +35,10 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.CodeGenerator = void 0;
37
37
  const path = __importStar(require("path"));
38
+ const child_process_1 = require("child_process");
39
+ const util_1 = require("util");
38
40
  const utils_1 = require("../utils");
41
+ const execPromise = (0, util_1.promisify)(child_process_1.exec);
39
42
  /**
40
43
  * 代码生成器
41
44
  */
@@ -95,13 +98,12 @@ class CodeGenerator {
95
98
  * @returns 类型文件内容
96
99
  */
97
100
  generateTypesContent(types) {
98
- const header = [
101
+ const header = this.generateHeader([
99
102
  '/**',
100
103
  ' * API 类型定义',
101
104
  ' * 此文件由 swagger2api-v3 自动生成,请勿手动修改',
102
- ' */',
103
- ''
104
- ].join('\n');
105
+ ' */'
106
+ ]);
105
107
  const typeDefinitions = types
106
108
  .map((type) => {
107
109
  const comment = type.description
@@ -118,7 +120,7 @@ class CodeGenerator {
118
120
  return `${comment}${definition}`;
119
121
  })
120
122
  .join('\n\n');
121
- return `${header}${typeDefinitions}\n`;
123
+ return `${header}\n\n${typeDefinitions}\n`;
122
124
  }
123
125
  /**
124
126
  * 按标签生成API文件
@@ -158,11 +160,12 @@ class CodeGenerator {
158
160
  generateApiFileContent(apis, types, tag) {
159
161
  const importTemplate = this.config.importTemplate || "import { request } from '@/utils'";
160
162
  const header = [
161
- '/**',
162
- ` * ${tag ? `${tag} ` : ''}API 接口`,
163
- ' * 此文件由 swagger2api-v3 自动生成,请勿手动修改',
164
- ' */',
165
- '',
163
+ this.generateHeader([
164
+ '/**',
165
+ ` * ${tag ? `${tag} ` : ''}API 接口`,
166
+ ' * 此文件由 swagger2api-v3 自动生成,请勿手动修改',
167
+ ' */'
168
+ ]),
166
169
  importTemplate + ';'
167
170
  ];
168
171
  // 收集当前文件实际使用的类型
@@ -233,7 +236,7 @@ class CodeGenerator {
233
236
  }
234
237
  /**
235
238
  * 生成直接参数形式
236
- * @param parameters Swagger参数数组
239
+ * @param parameters OpenAPI 参数数组
237
240
  * @returns 函数参数字符串
238
241
  */
239
242
  generateDirectParameters(parameters, isJavaScript = false) {
@@ -241,7 +244,6 @@ class CodeGenerator {
241
244
  const queryParams = parameters.filter((p) => p.in === 'query');
242
245
  const pathParams = parameters.filter((p) => p.in === 'path');
243
246
  const bodyParams = parameters.filter((p) => p.in === 'body');
244
- const formParams = parameters.filter((p) => p.in === 'formData');
245
247
  // 合并路径参数和查询参数为一个params对象
246
248
  const allParams = [...pathParams, ...queryParams];
247
249
  if (allParams.length > 0) {
@@ -274,21 +276,6 @@ class CodeGenerator {
274
276
  params.push(`data: ${bodyType}`);
275
277
  }
276
278
  }
277
- // 表单参数
278
- if (formParams.length > 0) {
279
- if (isJavaScript) {
280
- params.push('data');
281
- }
282
- else {
283
- const formType = formParams
284
- .map((p) => {
285
- const optional = p.required ? '' : '?';
286
- return `${p.name}${optional}: ${p.type}`;
287
- })
288
- .join(', ');
289
- params.push(`data: { ${formType} }`);
290
- }
291
- }
292
279
  // 添加可选的config参数
293
280
  params.push(isJavaScript ? 'config' : 'config?: any');
294
281
  return params.join(', ');
@@ -299,9 +286,6 @@ class CodeGenerator {
299
286
  * @returns 类型字符串
300
287
  */
301
288
  getTypeFromSchema(schema) {
302
- if (schema.$ref) {
303
- return schema.$ref.split('/').pop() || 'any';
304
- }
305
289
  return (0, utils_1.swaggerTypeToTsType)(schema);
306
290
  }
307
291
  /**
@@ -395,7 +379,7 @@ class CodeGenerator {
395
379
  // 检查是否包含 data 字段且类型为 Record<string, any>
396
380
  const hasDataField = definition.includes('data: Record<string, any>;');
397
381
  // 检查是否包含其他常见的响应容器字段
398
- const hasCommonFields = ['code', 'message', 'success', 'status'].some((field) => definition.includes(`${field}:`));
382
+ const hasCommonFields = ['code', 'message', 'success', 'status'].some((field) => new RegExp(`\\b${field}\\??:`).test(definition));
399
383
  return hasDataField && hasCommonFields;
400
384
  }
401
385
  /**
@@ -430,13 +414,9 @@ class CodeGenerator {
430
414
  }
431
415
  // 请求体数据
432
416
  const bodyParams = api.parameters.filter((p) => p.in === 'body');
433
- const formParams = api.parameters.filter((p) => p.in === 'formData');
434
417
  if (bodyParams.length > 0) {
435
418
  config.push('data');
436
419
  }
437
- else if (formParams.length > 0) {
438
- config.push('data');
439
- }
440
420
  // 在通用请求风格下添加 method 字段
441
421
  if (includeMethod) {
442
422
  config.push(`method: '${api.method}'`);
@@ -456,26 +436,20 @@ class CodeGenerator {
456
436
  this.config.options?.generateModels !== false) {
457
437
  exports.push("export * from './types';");
458
438
  }
459
- if (this.config.groupByTags) {
460
- // 按标签导出
461
- for (const tag of groupedApis.keys()) {
462
- const folderName = this.getTagFileName(tag);
463
- exports.push(`export * from './${folderName}';`);
439
+ if (this.config.options?.generateApis !== false) {
440
+ if (this.config.groupByTags) {
441
+ // 按标签导出
442
+ for (const tag of groupedApis.keys()) {
443
+ const folderName = this.getTagFileName(tag);
444
+ exports.push(`export * from './${folderName}';`);
445
+ }
446
+ }
447
+ else {
448
+ // 导出单个API文件
449
+ exports.push("export * from './api';");
464
450
  }
465
451
  }
466
- else {
467
- // 导出单个API文件
468
- exports.push("export * from './api';");
469
- }
470
- const content = [
471
- '/**',
472
- ' * API 入口文件',
473
- ' * 此文件由 swagger2api-v3 自动生成,请勿手动修改',
474
- ' */',
475
- '',
476
- ...exports,
477
- ''
478
- ].join('\n');
452
+ const content = [this.generateHeader(), '', ...exports, ''].join('\n');
479
453
  const ext = this.config.generator === 'javascript' ? 'js' : 'ts';
480
454
  const filePath = path.join(this.config.output, `index.${ext}`);
481
455
  (0, utils_1.writeFile)(filePath, content);
@@ -498,6 +472,19 @@ class CodeGenerator {
498
472
  return (0, utils_1.toCamelCase)(cleanTag);
499
473
  }
500
474
  }
475
+ /**
476
+ * 生成文件头部注释
477
+ * @param defaultHeader 默认头部注释
478
+ * @returns 文件头部注释内容
479
+ */
480
+ generateHeader(defaultHeader = [
481
+ '/**',
482
+ ' * API 入口文件',
483
+ ' * 此文件由 swagger2api-v3 自动生成,请勿手动修改',
484
+ ' */'
485
+ ]) {
486
+ return this.config.headerComment?.trim() || defaultHeader.join('\n');
487
+ }
501
488
  /**
502
489
  * 在所有文件生成完成后运行lint命令
503
490
  */
@@ -505,9 +492,6 @@ class CodeGenerator {
505
492
  if (!this.config.lint)
506
493
  return;
507
494
  try {
508
- const { exec } = require('child_process');
509
- const util = require('util');
510
- const execPromise = util.promisify(exec);
511
495
  console.log(`🎨 Running lint command: ${this.config.lint} ${this.config.output}`);
512
496
  const result = await execPromise(`${this.config.lint} ${this.config.output}`);
513
497
  if (result.stdout) {
@@ -7,7 +7,7 @@ export declare class SwaggerParser {
7
7
  private config;
8
8
  constructor(document: SwaggerDocument, config: SwaggerConfig);
9
9
  /**
10
- * 获取所有 schemas (包括 definitions 和 components.schemas)
10
+ * 获取 OpenAPI components.schemas
11
11
  * @returns schemas 对象
12
12
  */
13
13
  private getAllSchemas;
@@ -37,6 +37,20 @@ export declare class SwaggerParser {
37
37
  * @returns 类型信息
38
38
  */
39
39
  private parseTypeDefinition;
40
+ /**
41
+ * 获取枚举成员名称
42
+ * @param schema 枚举 schema
43
+ * @param value 枚举值
44
+ * @param index 枚举值索引
45
+ * @returns 枚举成员名称
46
+ */
47
+ private getEnumMemberName;
48
+ /**
49
+ * 解析本地 $ref 引用对象
50
+ * @param value 可能带有 $ref 的对象
51
+ * @returns 引用解析后的对象
52
+ */
53
+ private resolveReference;
40
54
  /**
41
55
  * 按标签分组API
42
56
  * @param apis API接口数组
@@ -53,6 +67,12 @@ export declare class SwaggerParser {
53
67
  * @returns 基础URL字符串
54
68
  */
55
69
  getBaseUrl(): string;
70
+ /**
71
+ * 解析 OpenAPI server 地址
72
+ * @param server OpenAPI server 对象
73
+ * @returns 替换默认变量后的 server URL
74
+ */
75
+ private resolveServerUrl;
56
76
  /**
57
77
  * 获取API文档信息
58
78
  * @returns API文档基本信息
@@ -2,6 +2,15 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SwaggerParser = void 0;
4
4
  const utils_1 = require("../utils");
5
+ const HTTP_METHODS = [
6
+ 'get',
7
+ 'post',
8
+ 'put',
9
+ 'delete',
10
+ 'patch',
11
+ 'head',
12
+ 'options'
13
+ ];
5
14
  /**
6
15
  * Swagger文档解析器
7
16
  */
@@ -11,14 +20,11 @@ class SwaggerParser {
11
20
  this.config = config;
12
21
  }
13
22
  /**
14
- * 获取所有 schemas (包括 definitions 和 components.schemas)
23
+ * 获取 OpenAPI components.schemas
15
24
  * @returns schemas 对象
16
25
  */
17
26
  getAllSchemas() {
18
- return {
19
- ...(this.document.definitions || {}),
20
- ...(this.document.components?.schemas || {})
21
- };
27
+ return this.document.components?.schemas || {};
22
28
  }
23
29
  /**
24
30
  * 解析所有API接口
@@ -28,16 +34,7 @@ class SwaggerParser {
28
34
  const apis = [];
29
35
  const paths = this.document.paths;
30
36
  for (const [path, pathItem] of Object.entries(paths)) {
31
- const methods = [
32
- 'get',
33
- 'post',
34
- 'put',
35
- 'delete',
36
- 'patch',
37
- 'head',
38
- 'options'
39
- ];
40
- for (const method of methods) {
37
+ for (const method of HTTP_METHODS) {
41
38
  const operation = pathItem[method];
42
39
  if (!operation)
43
40
  continue;
@@ -60,22 +57,20 @@ class SwaggerParser {
60
57
  const allParameters = [
61
58
  ...(globalParameters || []),
62
59
  ...(operation.parameters || [])
63
- ];
60
+ ].map((parameter) => this.resolveReference(parameter));
64
61
  // 处理 OpenAPI 3.0 requestBody
65
62
  if (operation.requestBody) {
66
- const requestBody = operation.requestBody;
67
- if (requestBody.content && requestBody.content['application/json']) {
68
- const schema = requestBody.content['application/json'].schema;
69
- if (schema) {
70
- const bodyParam = {
71
- name: 'body',
72
- in: 'body',
73
- required: requestBody.required || false,
74
- schema: schema,
75
- type: 'object'
76
- };
77
- allParameters.push(bodyParam);
78
- }
63
+ const requestBody = this.resolveReference(operation.requestBody);
64
+ const schema = (0, utils_1.getSchemaFromContent)(requestBody.content);
65
+ if (schema) {
66
+ const bodyParam = {
67
+ name: 'body',
68
+ in: 'body',
69
+ required: requestBody.required || false,
70
+ schema,
71
+ type: 'object'
72
+ };
73
+ allParameters.push(bodyParam);
79
74
  }
80
75
  }
81
76
  // 生成函数名
@@ -99,24 +94,22 @@ class SwaggerParser {
99
94
  // 应用前缀忽略规则
100
95
  functionName = (0, utils_1.stripMethodNamePrefixes)(functionName, this.config.methodNameIgnorePrefix);
101
96
  // 获取响应类型
102
- const responseType = (0, utils_1.getResponseType)(operation.responses);
97
+ const responseType = (0, utils_1.getResponseType)(operation.responses, this.getAllSchemas());
103
98
  // 获取请求体类型
104
99
  const bodyParam = allParameters.find((p) => p.in === 'body');
105
- const requestBodyType = bodyParam
100
+ const requestBodyType = bodyParam?.schema
106
101
  ? (0, utils_1.swaggerTypeToTsType)(bodyParam.schema)
107
102
  : undefined;
108
103
  // 解析参数信息
109
104
  const parameters = allParameters.map((param) => {
110
- const type = param.schema
111
- ? (0, utils_1.swaggerTypeToTsType)(param.schema)
112
- : (0, utils_1.swaggerTypeToTsType)({ type: param.type || 'string' });
105
+ const type = (0, utils_1.swaggerTypeToTsType)((0, utils_1.swaggerParameterToSchema)(param), this.getAllSchemas());
113
106
  return {
114
107
  name: param.name,
115
108
  type,
116
109
  in: param.in,
117
110
  required: param.required || false,
118
111
  description: param.description,
119
- schema: param.schema
112
+ schema: (0, utils_1.swaggerParameterToSchema)(param)
120
113
  };
121
114
  });
122
115
  return {
@@ -138,7 +131,6 @@ class SwaggerParser {
138
131
  const types = [];
139
132
  // Debug log
140
133
  console.log('解析类型定义...');
141
- console.log('Has definitions:', !!this.document.definitions);
142
134
  console.log('Has components:', !!this.document.components);
143
135
  if (this.document.components) {
144
136
  console.log('Has schemas:', !!this.document.components.schemas);
@@ -146,15 +138,7 @@ class SwaggerParser {
146
138
  console.log('Schema keys:', Object.keys(this.document.components.schemas));
147
139
  }
148
140
  }
149
- // 解析 Swagger 2.0 definitions
150
- if (this.document.definitions) {
151
- for (const [name, schema] of Object.entries(this.document.definitions)) {
152
- const sanitizedName = (0, utils_1.sanitizeTypeName)(name); // Use it
153
- const typeInfo = this.parseTypeDefinition(sanitizedName, schema);
154
- types.push(typeInfo);
155
- }
156
- }
157
- // 解析 OpenAPI 3.0 components.schemas
141
+ // 解析 OpenAPI 3.x components.schemas
158
142
  if (this.document.components?.schemas) {
159
143
  for (const [name, schema] of Object.entries(this.document.components.schemas)) {
160
144
  const sanitizedName = (0, utils_1.sanitizeTypeName)(name); // Use it
@@ -194,28 +178,14 @@ class SwaggerParser {
194
178
  else if (schema.type === 'array') {
195
179
  // 数组类型 - 生成指向 items 类型的别名(不带 [])
196
180
  // 在引用时会自动添加 []
197
- const itemType = (0, utils_1.swaggerTypeToTsType)(schema.items, allSchemas);
181
+ const itemType = (0, utils_1.swaggerTypeToTsType)(schema.items || {}, allSchemas);
198
182
  definition = `export type ${typeName} = ${itemType};`;
199
183
  }
200
184
  else if (schema.enum) {
201
185
  // 枚举类型
202
186
  const enumValues = schema.enum
203
187
  .map((value, index) => {
204
- let key = value;
205
- // 优先使用 x-enum-varnames 或 x-enumNames 扩展字段
206
- if ((schema['x-enum-varnames'] && schema['x-enum-varnames'][index]) ||
207
- (schema['x-enumNames'] && schema['x-enumNames'][index])) {
208
- key =
209
- schema['x-enum-varnames']?.[index] ||
210
- schema['x-enumNames']?.[index];
211
- }
212
- else if (/^\d+$/.test(value)) {
213
- // 对于数字枚举,使用 VALUE_ 前缀
214
- key = `VALUE_${value}`;
215
- }
216
- else {
217
- key = value.toUpperCase();
218
- }
188
+ const key = this.getEnumMemberName(schema, value, index);
219
189
  return ` ${key} = '${value}'`;
220
190
  })
221
191
  .join(',\n');
@@ -232,6 +202,43 @@ class SwaggerParser {
232
202
  description: schema.description
233
203
  };
234
204
  }
205
+ /**
206
+ * 获取枚举成员名称
207
+ * @param schema 枚举 schema
208
+ * @param value 枚举值
209
+ * @param index 枚举值索引
210
+ * @returns 枚举成员名称
211
+ */
212
+ getEnumMemberName(schema, value, index) {
213
+ const extensionName = schema['x-enum-varnames']?.[index] || schema['x-enumNames']?.[index];
214
+ if (extensionName) {
215
+ return extensionName;
216
+ }
217
+ const strValue = String(value);
218
+ if (/^\d+$/.test(strValue)) {
219
+ return `VALUE_${strValue}`;
220
+ }
221
+ return strValue.toUpperCase();
222
+ }
223
+ /**
224
+ * 解析本地 $ref 引用对象
225
+ * @param value 可能带有 $ref 的对象
226
+ * @returns 引用解析后的对象
227
+ */
228
+ resolveReference(value) {
229
+ if (!value || !value.$ref) {
230
+ return value;
231
+ }
232
+ const refPath = value.$ref.replace(/^#\//, '').split('/');
233
+ let current = this.document;
234
+ for (const segment of refPath) {
235
+ current = current?.[segment];
236
+ if (!current) {
237
+ return value;
238
+ }
239
+ }
240
+ return current;
241
+ }
235
242
  /**
236
243
  * 按标签分组API
237
244
  * @param apis API接口数组
@@ -262,16 +269,7 @@ class SwaggerParser {
262
269
  }
263
270
  // 从路径操作中获取
264
271
  for (const pathItem of Object.values(this.document.paths)) {
265
- const methods = [
266
- 'get',
267
- 'post',
268
- 'put',
269
- 'delete',
270
- 'patch',
271
- 'head',
272
- 'options'
273
- ];
274
- for (const method of methods) {
272
+ for (const method of HTTP_METHODS) {
275
273
  const operation = pathItem[method];
276
274
  if (operation?.tags) {
277
275
  operation.tags.forEach((tag) => tags.add(tag));
@@ -285,12 +283,23 @@ class SwaggerParser {
285
283
  * @returns 基础URL字符串
286
284
  */
287
285
  getBaseUrl() {
288
- if (this.document.host) {
289
- const scheme = this.document.schemes?.[0] || 'https';
290
- const basePath = this.document.basePath || '';
291
- return `${scheme}://${this.document.host}${basePath}`;
292
- }
293
- return '';
286
+ const server = this.document.servers?.[0];
287
+ if (!server)
288
+ return '';
289
+ return this.resolveServerUrl(server);
290
+ }
291
+ /**
292
+ * 解析 OpenAPI server 地址
293
+ * @param server OpenAPI server 对象
294
+ * @returns 替换默认变量后的 server URL
295
+ */
296
+ resolveServerUrl(server) {
297
+ if (!server.variables)
298
+ return server.url;
299
+ return server.url.replace(/\{([^}]+)\}/g, (match, name) => {
300
+ const variable = server.variables?.[name];
301
+ return variable?.default ?? match;
302
+ });
294
303
  }
295
304
  /**
296
305
  * 获取API文档信息
package/dist/index.d.ts CHANGED
@@ -13,6 +13,12 @@ export declare class Swagger2API {
13
13
  * 验证配置
14
14
  */
15
15
  validateConfig(): boolean;
16
+ /**
17
+ * 根据配置过滤 API
18
+ * @param apis API接口数组
19
+ * @returns 过滤后的 API接口数组
20
+ */
21
+ private filterApis;
16
22
  }
17
23
  /**
18
24
  * 从配置文件生成API
package/dist/index.js CHANGED
@@ -44,6 +44,7 @@ const fs = __importStar(require("fs"));
44
44
  const parser_1 = require("./core/parser");
45
45
  const generator_1 = require("./core/generator");
46
46
  const utils_1 = require("./utils");
47
+ const validator_1 = require("./config/validator");
47
48
  /**
48
49
  * Swagger2API 主类
49
50
  */
@@ -69,7 +70,7 @@ class Swagger2API {
69
70
  // 2. 解析文档
70
71
  console.log('🔍 解析API接口...');
71
72
  const parser = new parser_1.SwaggerParser(document, this.config);
72
- const apis = parser.parseApis();
73
+ const apis = this.filterApis(parser.parseApis());
73
74
  const types = parser.parseTypes();
74
75
  const groupedApis = parser.groupApisByTags(apis);
75
76
  console.log(`✅ 解析完成: ${apis.length} 个接口, ${types.length} 个类型`);
@@ -94,18 +95,7 @@ class Swagger2API {
94
95
  * 验证配置
95
96
  */
96
97
  validateConfig() {
97
- const errors = [];
98
- if (!this.config.input) {
99
- errors.push('input 配置项不能为空');
100
- }
101
- if (!this.config.output) {
102
- errors.push('output 配置项不能为空');
103
- }
104
- // 支持 typescript 与 javascript 两种生成器
105
- if (this.config.generator !== 'typescript' &&
106
- this.config.generator !== 'javascript') {
107
- errors.push('目前只支持 typescript 或 javascript 生成器');
108
- }
98
+ const errors = (0, validator_1.validateSwaggerConfig)(this.config);
109
99
  if (errors.length > 0) {
110
100
  console.error('❌ 配置验证失败:');
111
101
  errors.forEach((error) => console.error(` - ${error}`));
@@ -113,6 +103,24 @@ class Swagger2API {
113
103
  }
114
104
  return true;
115
105
  }
106
+ /**
107
+ * 根据配置过滤 API
108
+ * @param apis API接口数组
109
+ * @returns 过滤后的 API接口数组
110
+ */
111
+ filterApis(apis) {
112
+ const includeTags = this.config.filter?.include?.tags;
113
+ const excludeTags = this.config.filter?.exclude?.tags;
114
+ if (!includeTags?.length && !excludeTags?.length) {
115
+ return apis;
116
+ }
117
+ return apis.filter((api) => {
118
+ const tags = api.tags || [];
119
+ const included = !includeTags?.length || tags.some((tag) => includeTags.includes(tag));
120
+ const excluded = !!excludeTags?.length && tags.some((tag) => excludeTags.includes(tag));
121
+ return included && !excluded;
122
+ });
123
+ }
116
124
  }
117
125
  exports.Swagger2API = Swagger2API;
118
126
  /**