swagger2api-v3 1.1.8 → 1.1.10

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.js CHANGED
@@ -62,32 +62,36 @@ class Swagger2API {
62
62
  */
63
63
  async generate() {
64
64
  try {
65
- console.log('🚀 开始生成API接口文件...');
65
+ utils_1.logger.banner('🚀 swagger2api-v3', '开始生成 API 接口文件');
66
+ utils_1.logger.setTotalSteps(4);
66
67
  // 1. 加载Swagger文档
67
- console.log('📖 加载Swagger文档...');
68
+ utils_1.logger.stepTitle('📖', '加载 Swagger 文档');
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
  }
@@ -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
  /** 接口路径公共前缀,默认为空字符串 */
@@ -286,6 +288,10 @@ export interface SwaggerSchema {
286
288
  externalDocs?: SwaggerExternalDocs;
287
289
  nullable?: boolean;
288
290
  deprecated?: boolean;
291
+ /** OpenAPI 扩展字段:枚举变量名 */
292
+ 'x-enum-varnames'?: string[];
293
+ /** OpenAPI 扩展字段:枚举名称 */
294
+ 'x-enumNames'?: string[];
289
295
  }
290
296
  /**
291
297
  * Swagger项目
@@ -369,6 +375,8 @@ export interface ApiInfo {
369
375
  description?: string;
370
376
  /** 标签 */
371
377
  tags: string[];
378
+ /** 是否废弃 */
379
+ deprecated?: boolean;
372
380
  /** 参数 */
373
381
  parameters: ParameterInfo[];
374
382
  /** 响应类型 */
@@ -1,8 +1,23 @@
1
- import { SwaggerParameter } from '../types';
1
+ import { ParameterInfo } from '../types';
2
+ /**
3
+ * 注释生成需要的操作信息
4
+ */
5
+ export interface ApiCommentOperation {
6
+ /** 接口摘要 */
7
+ summary?: string;
8
+ /** 接口描述 */
9
+ description?: string;
10
+ /** 是否废弃 */
11
+ deprecated?: boolean;
12
+ }
13
+ /**
14
+ * 注释生成需要的参数信息
15
+ */
16
+ export type ApiCommentParameter = Pick<ParameterInfo, 'in' | 'description'>;
2
17
  /**
3
18
  * 生成接口注释
4
- * @param operation Swagger操作对象
19
+ * @param operation 接口操作信息
5
20
  * @param parameters 参数列表
6
21
  * @returns 注释字符串
7
22
  */
8
- export declare function generateApiComment(operation: any, parameters: SwaggerParameter[]): string;
23
+ export declare function generateApiComment(operation: ApiCommentOperation, parameters: ApiCommentParameter[]): string;
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.generateApiComment = generateApiComment;
4
4
  /**
5
5
  * 生成接口注释
6
- * @param operation Swagger操作对象
6
+ * @param operation 接口操作信息
7
7
  * @param parameters 参数列表
8
8
  * @returns 注释字符串
9
9
  */
@@ -43,6 +43,7 @@ exports.writeFile = writeFile;
43
43
  const fs = __importStar(require("fs"));
44
44
  const path = __importStar(require("path"));
45
45
  const axios_1 = __importDefault(require("axios"));
46
+ const logger_1 = require("./logger");
46
47
  /**
47
48
  * 确保目录存在,如果不存在则创建
48
49
  * @param dirPath 目录路径
@@ -70,12 +71,12 @@ async function loadSwaggerDocument(input) {
70
71
  try {
71
72
  if (input.startsWith('http://') || input.startsWith('https://')) {
72
73
  const { data } = await axios_1.default.get(input);
73
- console.log('Loaded from URL:', input);
74
+ logger_1.logger.debug(`从 URL 加载: ${input}`);
74
75
  if (data.components?.schemas) {
75
- console.log('Schemas count:', Object.keys(data.components.schemas).length);
76
+ logger_1.logger.debugKV('schemas 数量', Object.keys(data.components.schemas).length);
76
77
  }
77
78
  else {
78
- console.log('No schemas in loaded data');
79
+ logger_1.logger.debug('加载的数据中未发现 schemas');
79
80
  }
80
81
  return data;
81
82
  }
@@ -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 {};
@@ -0,0 +1,261 @@
1
+ "use strict";
2
+ /**
3
+ * 轻量级终端日志工具(零依赖,基于 ANSI 转义码)
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.logger = void 0;
7
+ /** 终端是否支持颜色输出 */
8
+ const supportsColor = process.stdout.isTTY &&
9
+ process.env.NODE_ENV !== 'test' &&
10
+ process.env.NO_COLOR === undefined;
11
+ /** 日志左侧缩进 */
12
+ const INDENT = ' ';
13
+ /** 列表标签默认宽度 */
14
+ const LIST_LABEL_WIDTH = 22;
15
+ /** 日志图标 */
16
+ const icon = {
17
+ success: '✅',
18
+ info: 'ℹ',
19
+ warn: '⚠',
20
+ error: '❌',
21
+ debug: '·',
22
+ path: '📂',
23
+ bullet: '•'
24
+ };
25
+ /**
26
+ * 包裹 ANSI 颜色码
27
+ * @param code ANSI 转义码
28
+ * @param text 文本内容
29
+ * @returns 带颜色的字符串(不支持颜色时返回原文)
30
+ */
31
+ function wrap(code, text) {
32
+ return supportsColor ? `\x1b[${code}m${text}\x1b[0m` : text;
33
+ }
34
+ /** 颜色与样式快捷方法 */
35
+ const style = {
36
+ /**
37
+ * 加粗文本
38
+ * @param text 文本内容
39
+ * @returns 格式化后的文本
40
+ */
41
+ bold(text) {
42
+ return wrap('1', text);
43
+ },
44
+ /**
45
+ * 弱化文本
46
+ * @param text 文本内容
47
+ * @returns 格式化后的文本
48
+ */
49
+ dim(text) {
50
+ return wrap('2', text);
51
+ },
52
+ /**
53
+ * 绿色文本
54
+ * @param text 文本内容
55
+ * @returns 格式化后的文本
56
+ */
57
+ green(text) {
58
+ return wrap('32', text);
59
+ },
60
+ /**
61
+ * 黄色文本
62
+ * @param text 文本内容
63
+ * @returns 格式化后的文本
64
+ */
65
+ yellow(text) {
66
+ return wrap('33', text);
67
+ },
68
+ /**
69
+ * 紫色文本
70
+ * @param text 文本内容
71
+ * @returns 格式化后的文本
72
+ */
73
+ magenta(text) {
74
+ return wrap('35', text);
75
+ },
76
+ /**
77
+ * 青色文本
78
+ * @param text 文本内容
79
+ * @returns 格式化后的文本
80
+ */
81
+ cyan(text) {
82
+ return wrap('36', text);
83
+ },
84
+ /**
85
+ * 灰色文本
86
+ * @param text 文本内容
87
+ * @returns 格式化后的文本
88
+ */
89
+ gray(text) {
90
+ return wrap('90', text);
91
+ }
92
+ };
93
+ /**
94
+ * 日志工具类
95
+ *
96
+ * 提供统一的、带颜色的终端输出格式。
97
+ * error 方法不使用颜色码,确保 console.error 的参数为纯文本,
98
+ * 兼容测试中对 console.error 的 spy 断言。
99
+ */
100
+ class Logger {
101
+ constructor() {
102
+ this.step = 0;
103
+ this.totalSteps = 0;
104
+ }
105
+ /**
106
+ * 写入标准输出
107
+ * @param message 输出内容
108
+ */
109
+ log(message = '') {
110
+ console.log(message);
111
+ }
112
+ /**
113
+ * 写入错误输出
114
+ * @param message 输出内容
115
+ */
116
+ fail(message) {
117
+ console.error(message);
118
+ }
119
+ /**
120
+ * 格式化未知错误
121
+ * @param error 错误对象
122
+ * @returns 错误文案
123
+ */
124
+ formatError(error) {
125
+ if (error instanceof Error) {
126
+ return error.message ? `${error.name}: ${error.message}` : error.name;
127
+ }
128
+ return String(error);
129
+ }
130
+ /**
131
+ * 输出带图标的状态行
132
+ * @param symbol 状态图标
133
+ * @param message 消息内容
134
+ */
135
+ status(symbol, message) {
136
+ this.log(`${INDENT}${symbol} ${message}`);
137
+ }
138
+ /**
139
+ * 设置总步骤数
140
+ * @param n 步骤总数
141
+ */
142
+ setTotalSteps(n) {
143
+ this.totalSteps = n;
144
+ this.step = 0;
145
+ }
146
+ /**
147
+ * 输出步骤标题
148
+ * @param emoji 步骤图标
149
+ * @param title 步骤标题
150
+ */
151
+ stepTitle(emoji, title) {
152
+ this.step++;
153
+ const progress = this.totalSteps > 0
154
+ ? style.dim(`[${this.step}/${this.totalSteps}] `)
155
+ : '';
156
+ this.log();
157
+ this.log(`${INDENT}${progress}${emoji} ${style.bold(title)}`);
158
+ }
159
+ /**
160
+ * 输出成功信息
161
+ * @param message 消息内容
162
+ */
163
+ success(message) {
164
+ this.status(style.green(icon.success), message);
165
+ }
166
+ /**
167
+ * 输出信息
168
+ * @param message 消息内容
169
+ */
170
+ info(message) {
171
+ this.status(style.cyan(icon.info), message);
172
+ }
173
+ /**
174
+ * 输出文件/路径信息
175
+ * @param label 标签
176
+ * @param path 路径
177
+ */
178
+ path(label, path) {
179
+ this.status(icon.path, `${style.dim(label)}: ${style.bold(path)}`);
180
+ }
181
+ /**
182
+ * 输出警告信息
183
+ * @param message 消息内容
184
+ */
185
+ warn(message) {
186
+ this.status(style.yellow(icon.warn), message);
187
+ }
188
+ /**
189
+ * 输出错误信息(不使用颜色码,兼容测试断言)
190
+ * @param message 消息内容
191
+ * @param error 错误对象
192
+ */
193
+ error(message, error) {
194
+ const detail = error === undefined ? message : `${message}: ${this.formatError(error)}`;
195
+ this.fail(`${icon.error} ${detail}`);
196
+ }
197
+ /**
198
+ * 输出错误列表
199
+ * @param title 错误标题
200
+ * @param items 错误项列表
201
+ */
202
+ errorList(title, items) {
203
+ this.fail(`${icon.error} ${title}`);
204
+ items.forEach((item) => this.fail(`${INDENT} - ${item}`));
205
+ }
206
+ /**
207
+ * 输出调试信息(灰色暗淡)
208
+ * @param message 消息内容
209
+ */
210
+ debug(message) {
211
+ this.status(style.gray(icon.debug), style.gray(message));
212
+ }
213
+ /**
214
+ * 输出调试键值对
215
+ * @param key 键名
216
+ * @param value 键值
217
+ */
218
+ debugKV(key, value) {
219
+ this.status(style.gray(icon.debug), `${style.gray(key)}: ${style.gray(String(value))}`);
220
+ }
221
+ /**
222
+ * 输出列表项
223
+ * @param label 列表项标签
224
+ * @param detail 列表项详情
225
+ */
226
+ listItem(label, detail) {
227
+ const paddedLabel = label.padEnd(LIST_LABEL_WIDTH, ' ');
228
+ this.log(`${INDENT} ${style.dim(icon.bullet)} ${style.cyan(paddedLabel)} ${style.dim(detail)}`);
229
+ }
230
+ /**
231
+ * 输出分隔线
232
+ */
233
+ divider() {
234
+ const line = '─'.repeat(48);
235
+ this.log(`${INDENT}${style.dim(line)}`);
236
+ }
237
+ /**
238
+ * 输出横幅标题
239
+ * @param title 标题
240
+ * @param subtitle 副标题(可选)
241
+ */
242
+ banner(title, subtitle) {
243
+ this.log();
244
+ this.log(`${INDENT}${style.bold(style.magenta(title))}`);
245
+ if (subtitle) {
246
+ this.log(`${INDENT}${style.dim(subtitle)}`);
247
+ }
248
+ this.log();
249
+ }
250
+ /**
251
+ * 输出完成横幅
252
+ * @param message 完成消息
253
+ */
254
+ done(message) {
255
+ this.log();
256
+ this.log(`${INDENT}${style.green(icon.success)} ${style.bold(message)}`);
257
+ this.log();
258
+ }
259
+ }
260
+ /** 日志单例 */
261
+ exports.logger = new Logger();
@@ -1,4 +1,16 @@
1
- import { SwaggerParameter } from '../types';
1
+ import { SwaggerParameter, SwaggerSchema, SwaggerResponses } from '../types';
2
+ /**
3
+ * 判断字符串是否为合法 TypeScript 标识符
4
+ * @param name 属性名
5
+ * @returns 是否为合法标识符
6
+ */
7
+ export declare function isValidIdentifier(name: string): boolean;
8
+ /**
9
+ * 格式化 TypeScript 属性名
10
+ * @param name 属性名
11
+ * @returns 可用于类型声明的属性名
12
+ */
13
+ export declare function formatTsPropertyName(name: string): string;
2
14
  /**
3
15
  * 移除联合类型中的顶层 null 类型
4
16
  * @param typeStr 类型字符串
@@ -11,29 +23,31 @@ export declare function stripNullFromUnion(typeStr: string): string;
11
23
  * @param schemas 可选的 schemas 上下文,用于查找被引用的类型定义
12
24
  * @returns TypeScript类型字符串
13
25
  */
14
- export declare function swaggerTypeToTsType(schema: any, schemas?: any): string;
26
+ export declare function swaggerTypeToTsType(schema: SwaggerSchema | undefined | null, schemas?: Record<string, SwaggerSchema>): string;
27
+ /**
28
+ * 从 $ref 字符串中提取引用名称
29
+ * @param ref OpenAPI $ref 字符串
30
+ * @returns 引用名称
31
+ */
32
+ export declare function getRefName(ref: string): string;
15
33
  /**
16
34
  * 将 OpenAPI 参数转换为 schema 对象
17
35
  * @param parameter OpenAPI 参数
18
36
  * @returns 参数对应的 schema
19
37
  */
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;
38
+ export declare function swaggerParameterToSchema(parameter: SwaggerParameter): SwaggerSchema;
27
39
  /**
28
40
  * 获取响应类型
29
41
  * @param responses OpenAPI 响应对象
30
42
  * @param schemas OpenAPI components.schemas
31
43
  * @returns TypeScript类型字符串
32
44
  */
33
- export declare function getResponseType(responses: any, schemas?: any): string;
45
+ export declare function getResponseType(responses: SwaggerResponses, schemas?: Record<string, SwaggerSchema>): string;
34
46
  /**
35
47
  * 从 OpenAPI content 对象中获取最合适的 schema
36
48
  * @param content OpenAPI content 对象
37
49
  * @returns 匹配到的 schema
38
50
  */
39
- export declare function getSchemaFromContent(content: any): any;
51
+ export declare function getSchemaFromContent(content: Record<string, {
52
+ schema?: SwaggerSchema;
53
+ }> | undefined): SwaggerSchema | undefined;