swagger2api-v3 1.1.9 → 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/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"));
@@ -62,32 +62,36 @@ class Swagger2API {
62
62
  */
63
63
  async generate() {
64
64
  try {
65
- console.log('🚀 开始生成API接口文件...');
66
- // 1. 加载Swagger文档
67
- console.log('📖 加载Swagger文档...');
65
+ utils_1.logger.banner('🚀 swagger2api-v3', '开始生成 API 接口文件');
66
+ utils_1.logger.setTotalSteps(4);
67
+ // 1. 加载 OpenAPI 文档
68
+ utils_1.logger.stepTitle('📖', '加载 OpenAPI 文档');
68
69
  const document = await (0, utils_1.loadSwaggerDocument)(this.config.input);
69
- console.log(`✅ 成功加载文档: ${document.info.title} v${document.info.version}`);
70
+ utils_1.logger.success(`文档加载成功: ${document.info.title} v${document.info.version}`);
70
71
  // 2. 解析文档
71
- console.log('🔍 解析API接口...');
72
+ utils_1.logger.stepTitle('🔍', '解析 API 接口');
72
73
  const parser = new parser_1.SwaggerParser(document, this.config);
73
74
  const apis = this.filterApis(parser.parseApis());
74
75
  const types = parser.parseTypes();
75
76
  const groupedApis = parser.groupApisByTags(apis);
76
- console.log(`✅ 解析完成: ${apis.length} 个接口, ${types.length} 个类型`);
77
+ utils_1.logger.success(`解析完成: ${apis.length} 个接口, ${types.length} 个类型`);
77
78
  if (this.config.groupByTags) {
78
- console.log(`📁 按标签分组: ${groupedApis.size} 个分组`);
79
+ utils_1.logger.info(`按标签分组: ${groupedApis.size} 个分组`);
79
80
  for (const [tag, tagApis] of groupedApis) {
80
- console.log(` - ${tag}: ${tagApis.length} 个接口`);
81
+ utils_1.logger.listItem(tag, `${tagApis.length} 个接口`);
81
82
  }
82
83
  }
83
84
  // 3. 生成代码
84
- console.log('⚡ 生成代码文件...');
85
+ utils_1.logger.stepTitle('⚡', '生成代码文件');
85
86
  const generator = new generator_1.CodeGenerator(this.config);
86
87
  await generator.generateAll(apis, types, groupedApis);
87
- console.log(`✅ 代码生成完成,输出目录: ${this.config.output}`);
88
+ // 4. 完成
89
+ utils_1.logger.stepTitle('📦', '生成完成');
90
+ utils_1.logger.path('输出目录', this.config.output);
91
+ utils_1.logger.done('API 接口文件生成完成');
88
92
  }
89
93
  catch (error) {
90
- console.error('❌ 生成失败:', error);
94
+ utils_1.logger.error('生成失败', error);
91
95
  throw error;
92
96
  }
93
97
  }
@@ -97,8 +101,7 @@ class Swagger2API {
97
101
  validateConfig() {
98
102
  const errors = (0, validator_1.validateSwaggerConfig)(this.config);
99
103
  if (errors.length > 0) {
100
- console.error('配置验证失败:');
101
- errors.forEach((error) => console.error(` - ${error}`));
104
+ utils_1.logger.errorList('配置验证失败:', errors);
102
105
  return false;
103
106
  }
104
107
  return true;
@@ -134,9 +137,9 @@ async function generateFromConfig(configPath) {
134
137
  let config;
135
138
  // 读取并解析 JSON 配置文件
136
139
  if (!fs.existsSync(fullPath)) {
137
- console.error(`❌ 找不到配置文件: ${fullPath}`);
138
- console.error('请确保配置文件存在并且路径正确');
139
- console.error('提示: 运行 swagger2api-v3 init 来创建配置文件');
140
+ utils_1.logger.error(`找不到配置文件: ${fullPath}`);
141
+ utils_1.logger.error('请确保配置文件存在并且路径正确');
142
+ utils_1.logger.error('提示: 运行 swagger2api-v3 init 来创建配置文件');
140
143
  process.exit(1);
141
144
  }
142
145
  const configContent = fs.readFileSync(fullPath, 'utf-8');
@@ -149,12 +152,12 @@ async function generateFromConfig(configPath) {
149
152
  }
150
153
  catch (error) {
151
154
  if (error instanceof SyntaxError) {
152
- console.error(`❌ 配置文件 JSON 格式错误: ${fullPath}`);
153
- console.error('请检查 JSON 语法是否正确');
154
- console.error('错误详情:', error.message);
155
+ utils_1.logger.error(`配置文件 JSON 格式错误: ${fullPath}`);
156
+ utils_1.logger.error('请检查 JSON 语法是否正确');
157
+ utils_1.logger.error(`错误详情: ${error.message}`);
155
158
  }
156
159
  else {
157
- console.error('❌ 加载配置文件失败:', error);
160
+ utils_1.logger.error('加载配置文件失败', error);
158
161
  }
159
162
  process.exit(1);
160
163
  }
@@ -176,4 +179,6 @@ var parser_2 = require("./core/parser");
176
179
  Object.defineProperty(exports, "SwaggerParser", { enumerable: true, get: function () { return parser_2.SwaggerParser; } });
177
180
  var generator_2 = require("./core/generator");
178
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; } });
179
184
  __exportStar(require("./utils"), exports);
@@ -7,7 +7,7 @@
7
7
  export interface SwaggerConfig {
8
8
  /** 本地 JSON Schema 路径,用于编辑器提示 */
9
9
  $schema?: string;
10
- /** Swagger JSON 文件路径或 URL */
10
+ /** OpenAPI 3.0 JSON/YAML 文件路径或 URL */
11
11
  input: string;
12
12
  /** 输出目录 */
13
13
  output: string;
@@ -15,6 +15,8 @@ export interface SwaggerConfig {
15
15
  generator: 'typescript' | 'javascript';
16
16
  /** 按 tags 分组生成文件 */
17
17
  groupByTags: boolean;
18
+ /** 多 tag 接口分组策略,first 只使用第一个 tag,all 将所有 tags 合成一个分组 */
19
+ multiTagStrategy?: 'first' | 'all';
18
20
  /** 是否覆盖更新,默认为true。为true时会先删除输出目录下的所有文件 */
19
21
  overwrite?: boolean;
20
22
  /** 接口路径公共前缀,默认为空字符串 */
@@ -108,6 +110,13 @@ export interface SwaggerDocument {
108
110
  components?: SwaggerComponents;
109
111
  tags?: SwaggerTag[];
110
112
  }
113
+ /**
114
+ * OpenAPI 引用对象
115
+ */
116
+ export interface SwaggerReference {
117
+ /** 引用地址 */
118
+ $ref: string;
119
+ }
111
120
  /**
112
121
  * OpenAPI Server 对象
113
122
  */
@@ -161,6 +170,8 @@ export interface SwaggerPaths {
161
170
  * Swagger路径项
162
171
  */
163
172
  export interface SwaggerPathItem {
173
+ /** Path Item 引用 */
174
+ $ref?: string;
164
175
  get?: SwaggerOperation;
165
176
  post?: SwaggerOperation;
166
177
  put?: SwaggerOperation;
@@ -168,6 +179,7 @@ export interface SwaggerPathItem {
168
179
  patch?: SwaggerOperation;
169
180
  head?: SwaggerOperation;
170
181
  options?: SwaggerOperation;
182
+ trace?: SwaggerOperation;
171
183
  parameters?: SwaggerParameter[];
172
184
  }
173
185
  /**
@@ -190,7 +202,7 @@ export interface SwaggerRequestBody {
190
202
  /** 本地引用路径 */
191
203
  $ref?: string;
192
204
  description?: string;
193
- content: {
205
+ content?: {
194
206
  [mediaType: string]: {
195
207
  schema?: SwaggerSchema;
196
208
  example?: any;
@@ -222,13 +234,15 @@ export interface SwaggerParameter {
222
234
  * OpenAPI 响应
223
235
  */
224
236
  export interface SwaggerResponses {
225
- [statusCode: string]: SwaggerResponse;
237
+ [statusCode: string]: SwaggerResponse | SwaggerReference;
226
238
  }
227
239
  /**
228
240
  * OpenAPI 响应项
229
241
  */
230
242
  export interface SwaggerResponse {
231
- description: string;
243
+ /** 本地或已打包的引用路径 */
244
+ $ref?: string;
245
+ description?: string;
232
246
  content?: {
233
247
  [mediaType: string]: {
234
248
  schema?: SwaggerSchema;
@@ -279,7 +293,7 @@ export interface SwaggerSchema {
279
293
  $ref?: string;
280
294
  example?: any;
281
295
  examples?: any[];
282
- discriminator?: string;
296
+ discriminator?: OpenAPIDiscriminator;
283
297
  readOnly?: boolean;
284
298
  writeOnly?: boolean;
285
299
  xml?: SwaggerXml;
@@ -291,6 +305,15 @@ export interface SwaggerSchema {
291
305
  /** OpenAPI 扩展字段:枚举名称 */
292
306
  'x-enumNames'?: string[];
293
307
  }
308
+ /**
309
+ * OpenAPI discriminator 对象
310
+ */
311
+ export interface OpenAPIDiscriminator {
312
+ /** 用作类型判别的属性名 */
313
+ propertyName: string;
314
+ /** 判别值到 Schema 引用的映射 */
315
+ mapping?: Record<string, string>;
316
+ }
294
317
  /**
295
318
  * Swagger项目
296
319
  */
@@ -310,7 +333,7 @@ export interface SwaggerComponents {
310
333
  [name: string]: SwaggerSchema;
311
334
  };
312
335
  responses?: {
313
- [name: string]: SwaggerResponse;
336
+ [name: string]: SwaggerResponse | SwaggerReference;
314
337
  };
315
338
  parameters?: {
316
339
  [name: string]: SwaggerParameter;
@@ -373,6 +396,8 @@ export interface ApiInfo {
373
396
  description?: string;
374
397
  /** 标签 */
375
398
  tags: string[];
399
+ /** 是否废弃 */
400
+ deprecated?: boolean;
376
401
  /** 参数 */
377
402
  parameters: ParameterInfo[];
378
403
  /** 响应类型 */
@@ -13,7 +13,10 @@ export interface ApiCommentOperation {
13
13
  /**
14
14
  * 注释生成需要的参数信息
15
15
  */
16
- export type ApiCommentParameter = Pick<ParameterInfo, 'in' | 'description'>;
16
+ export type ApiCommentParameter = Pick<ParameterInfo, 'in' | 'description'> & {
17
+ /** 生成后的函数参数名 */
18
+ name?: string;
19
+ };
17
20
  /**
18
21
  * 生成接口注释
19
22
  * @param operation 接口操作信息
@@ -23,8 +23,12 @@ function generateApiComment(operation, parameters) {
23
23
  const hasData = bodyParams.length > 0;
24
24
  if (hasParams || hasData) {
25
25
  comments.push(' *');
26
- if (hasParams) {
27
- const paramDescriptions = [...pathParams, ...queryParams]
26
+ pathParams.forEach((param, index) => {
27
+ const name = param.name || `pathParam${index + 1}`;
28
+ comments.push(` * @param ${name} ${param.description || '路径参数'}`);
29
+ });
30
+ if (queryParams.length > 0) {
31
+ const paramDescriptions = queryParams
28
32
  .map((param) => param.description || '')
29
33
  .filter((description) => description)
30
34
  .join(', ');
@@ -10,9 +10,16 @@ export declare function ensureDirectoryExists(dirPath: string): void;
10
10
  */
11
11
  export declare function removeDirectory(dirPath: string): void;
12
12
  /**
13
- * 读取Swagger文档
14
- * @param input 文件路径或URL
15
- * @returns Swagger文档对象
13
+ * 判断目标路径是否严格位于父路径内部
14
+ * @param parentPath 父路径
15
+ * @param targetPath 目标路径
16
+ * @returns 目标路径是否位于父路径内部
17
+ */
18
+ export declare function isPathInside(parentPath: string, targetPath: string): boolean;
19
+ /**
20
+ * 读取并打包 OpenAPI 3.0 文档
21
+ * @param input 文件路径或 URL
22
+ * @returns OpenAPI 文档对象
16
23
  */
17
24
  export declare function loadSwaggerDocument(input: string): Promise<SwaggerDocument>;
18
25
  /**
@@ -32,17 +32,16 @@ var __importStar = (this && this.__importStar) || (function () {
32
32
  return result;
33
33
  };
34
34
  })();
35
- var __importDefault = (this && this.__importDefault) || function (mod) {
36
- return (mod && mod.__esModule) ? mod : { "default": mod };
37
- };
38
35
  Object.defineProperty(exports, "__esModule", { value: true });
39
36
  exports.ensureDirectoryExists = ensureDirectoryExists;
40
37
  exports.removeDirectory = removeDirectory;
38
+ exports.isPathInside = isPathInside;
41
39
  exports.loadSwaggerDocument = loadSwaggerDocument;
42
40
  exports.writeFile = writeFile;
43
41
  const fs = __importStar(require("fs"));
44
42
  const path = __importStar(require("path"));
45
- const axios_1 = __importDefault(require("axios"));
43
+ const OpenAPIParser = require("@apidevtools/swagger-parser");
44
+ const logger_1 = require("./logger");
46
45
  /**
47
46
  * 确保目录存在,如果不存在则创建
48
47
  * @param dirPath 目录路径
@@ -62,28 +61,59 @@ function removeDirectory(dirPath) {
62
61
  }
63
62
  }
64
63
  /**
65
- * 读取Swagger文档
66
- * @param input 文件路径或URL
67
- * @returns Swagger文档对象
64
+ * 判断目标路径是否严格位于父路径内部
65
+ * @param parentPath 父路径
66
+ * @param targetPath 目标路径
67
+ * @returns 目标路径是否位于父路径内部
68
+ */
69
+ function isPathInside(parentPath, targetPath) {
70
+ const relativePath = path.relative(path.resolve(parentPath), path.resolve(targetPath));
71
+ return (relativePath !== '' &&
72
+ relativePath !== '..' &&
73
+ !relativePath.startsWith(`..${path.sep}`) &&
74
+ !path.isAbsolute(relativePath));
75
+ }
76
+ /**
77
+ * 读取并打包 OpenAPI 3.0 文档
78
+ * @param input 文件路径或 URL
79
+ * @returns OpenAPI 文档对象
68
80
  */
69
81
  async function loadSwaggerDocument(input) {
70
82
  try {
71
- if (input.startsWith('http://') || input.startsWith('https://')) {
72
- const { data } = await axios_1.default.get(input);
73
- console.log('Loaded from URL:', input);
74
- if (data.components?.schemas) {
75
- console.log('Schemas count:', Object.keys(data.components.schemas).length);
76
- }
77
- else {
78
- console.log('No schemas in loaded data');
83
+ const isRemoteInput = /^https?:\/\//i.test(input);
84
+ const resolveOptions = isRemoteInput
85
+ ? {
86
+ file: false,
87
+ http: {
88
+ timeout: 15000,
89
+ safeUrlResolver: false
90
+ }
79
91
  }
80
- return data;
92
+ : {
93
+ http: {
94
+ timeout: 15000,
95
+ safeUrlResolver: false
96
+ }
97
+ };
98
+ const data = (await OpenAPIParser.bundle(input, {
99
+ resolve: resolveOptions
100
+ }));
101
+ if (isRemoteInput) {
102
+ logger_1.logger.debug(`从 URL 加载: ${input}`);
103
+ }
104
+ else {
105
+ logger_1.logger.debug(`从文件加载: ${path.resolve(input)}`);
106
+ }
107
+ if (data.components?.schemas) {
108
+ logger_1.logger.debugKV('schemas 数量', Object.keys(data.components.schemas).length);
109
+ }
110
+ else {
111
+ logger_1.logger.debug('加载的数据中未发现 schemas');
81
112
  }
82
- const content = fs.readFileSync(input, 'utf-8');
83
- return JSON.parse(content);
113
+ return data;
84
114
  }
85
115
  catch (error) {
86
- throw new Error(`Failed to load Swagger document from ${input}: ${error}`);
116
+ throw new Error(`Failed to load OpenAPI document from ${input}: ${error}`);
87
117
  }
88
118
  }
89
119
  /**
@@ -2,3 +2,4 @@ export * from './naming';
2
2
  export * from './type';
3
3
  export * from './file';
4
4
  export * from './comment';
5
+ export * from './logger';
@@ -18,3 +18,4 @@ __exportStar(require("./naming"), exports);
18
18
  __exportStar(require("./type"), exports);
19
19
  __exportStar(require("./file"), exports);
20
20
  __exportStar(require("./comment"), exports);
21
+ __exportStar(require("./logger"), exports);
@@ -0,0 +1,115 @@
1
+ /**
2
+ * 轻量级终端日志工具(零依赖,基于 ANSI 转义码)
3
+ */
4
+ /**
5
+ * 日志工具类
6
+ *
7
+ * 提供统一的、带颜色的终端输出格式。
8
+ * error 方法不使用颜色码,确保 console.error 的参数为纯文本,
9
+ * 兼容测试中对 console.error 的 spy 断言。
10
+ */
11
+ declare class Logger {
12
+ private step;
13
+ private totalSteps;
14
+ /**
15
+ * 写入标准输出
16
+ * @param message 输出内容
17
+ */
18
+ private log;
19
+ /**
20
+ * 写入错误输出
21
+ * @param message 输出内容
22
+ */
23
+ private fail;
24
+ /**
25
+ * 格式化未知错误
26
+ * @param error 错误对象
27
+ * @returns 错误文案
28
+ */
29
+ private formatError;
30
+ /**
31
+ * 输出带图标的状态行
32
+ * @param symbol 状态图标
33
+ * @param message 消息内容
34
+ */
35
+ private status;
36
+ /**
37
+ * 设置总步骤数
38
+ * @param n 步骤总数
39
+ */
40
+ setTotalSteps(n: number): void;
41
+ /**
42
+ * 输出步骤标题
43
+ * @param emoji 步骤图标
44
+ * @param title 步骤标题
45
+ */
46
+ stepTitle(emoji: string, title: string): void;
47
+ /**
48
+ * 输出成功信息
49
+ * @param message 消息内容
50
+ */
51
+ success(message: string): void;
52
+ /**
53
+ * 输出信息
54
+ * @param message 消息内容
55
+ */
56
+ info(message: string): void;
57
+ /**
58
+ * 输出文件/路径信息
59
+ * @param label 标签
60
+ * @param path 路径
61
+ */
62
+ path(label: string, path: string): void;
63
+ /**
64
+ * 输出警告信息
65
+ * @param message 消息内容
66
+ */
67
+ warn(message: string): void;
68
+ /**
69
+ * 输出错误信息(不使用颜色码,兼容测试断言)
70
+ * @param message 消息内容
71
+ * @param error 错误对象
72
+ */
73
+ error(message: string, error?: unknown): void;
74
+ /**
75
+ * 输出错误列表
76
+ * @param title 错误标题
77
+ * @param items 错误项列表
78
+ */
79
+ errorList(title: string, items: string[]): void;
80
+ /**
81
+ * 输出调试信息(灰色暗淡)
82
+ * @param message 消息内容
83
+ */
84
+ debug(message: string): void;
85
+ /**
86
+ * 输出调试键值对
87
+ * @param key 键名
88
+ * @param value 键值
89
+ */
90
+ debugKV(key: string, value: string | number | boolean): void;
91
+ /**
92
+ * 输出列表项
93
+ * @param label 列表项标签
94
+ * @param detail 列表项详情
95
+ */
96
+ listItem(label: string, detail: string): void;
97
+ /**
98
+ * 输出分隔线
99
+ */
100
+ divider(): void;
101
+ /**
102
+ * 输出横幅标题
103
+ * @param title 标题
104
+ * @param subtitle 副标题(可选)
105
+ */
106
+ banner(title: string, subtitle?: string): void;
107
+ /**
108
+ * 输出完成横幅
109
+ * @param message 完成消息
110
+ */
111
+ done(message: string): void;
112
+ }
113
+ /** 日志单例 */
114
+ export declare const logger: Logger;
115
+ export {};