swagger2api-v3 1.1.9 → 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/README.md CHANGED
@@ -44,6 +44,7 @@ The tool generates a `.swagger.config.json` configuration file:
44
44
  "generator": "typescript",
45
45
  "requestStyle": "generic",
46
46
  "groupByTags": true,
47
+ "multiTagStrategy": "first",
47
48
  "overwrite": true,
48
49
  "prefix": "",
49
50
  "lint": "prettier --write",
@@ -79,6 +80,7 @@ npx swagger2api-v3 generate
79
80
  | `output` | string | `'./src/api'` | Output directory for generated code |
80
81
  | `generator` | string | `'typescript'` | Code generator type. Supports `'typescript'` and `'javascript'`. `'javascript'` outputs `.js` files and skips type file generation |
81
82
  | `groupByTags` | boolean | `true` | Whether to group files by tags |
83
+ | `multiTagStrategy` | 'first' \| 'all' | `'first'` | Grouping strategy for operations with multiple tags. `first` uses only the first tag, `all` combines all tags into one group name |
82
84
  | `overwrite` | boolean | `true` | Whether to overwrite existing files |
83
85
  | `prefix` | string | `''` | Common prefix for API paths |
84
86
  | `importTemplate` | string | - | Import statement template for request function |
@@ -24,6 +24,10 @@
24
24
  "type": "boolean",
25
25
  "description": "是否按 tags 分组生成文件"
26
26
  },
27
+ "multiTagStrategy": {
28
+ "enum": ["first", "all"],
29
+ "description": "多 tag 接口分组策略。first 只使用第一个 tag,all 将所有 tags 合成一个分组"
30
+ },
27
31
  "overwrite": {
28
32
  "type": "boolean",
29
33
  "description": "生成前是否覆盖输出目录"
package/dist/cli/index.js CHANGED
@@ -38,6 +38,7 @@ const commander_1 = require("commander");
38
38
  const path = __importStar(require("path"));
39
39
  const fs = __importStar(require("fs"));
40
40
  const index_1 = require("../index");
41
+ const utils_1 = require("../utils");
41
42
  const program = new commander_1.Command();
42
43
  // 版本信息
43
44
  const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, '../../package.json'), 'utf-8'));
@@ -73,7 +74,7 @@ program
73
74
  prettify: true
74
75
  }
75
76
  };
76
- const { generate } = await Promise.resolve().then(() => __importStar(require('../index')));
77
+ const { generate } = await import('../index.js');
77
78
  await generate(config);
78
79
  }
79
80
  else {
@@ -82,7 +83,7 @@ program
82
83
  }
83
84
  }
84
85
  catch (error) {
85
- console.error('❌ 生成失败:', error);
86
+ utils_1.logger.error('生成失败', error);
86
87
  process.exit(1);
87
88
  }
88
89
  });
@@ -98,11 +99,11 @@ program
98
99
  const config = createInitConfig(configPath, options.force);
99
100
  const successMessage = getInitSuccessMessage(existsBeforeInit, options.force);
100
101
  fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
101
- console.log(successMessage, configPath);
102
- console.log('💡 请根据需要修改配置文件,然后运行 swagger2api-v3 generate');
102
+ utils_1.logger.success(`${successMessage} ${configPath}`);
103
+ utils_1.logger.info('请根据需要修改配置文件,然后运行 swagger2api-v3 generate');
103
104
  }
104
105
  catch (error) {
105
- console.error('❌ 创建配置文件失败:', error);
106
+ utils_1.logger.error('创建配置文件失败', error);
106
107
  process.exit(1);
107
108
  }
108
109
  });
@@ -115,29 +116,28 @@ program
115
116
  const configPath = path.resolve(process.cwd(), options.config);
116
117
  try {
117
118
  if (!fs.existsSync(configPath)) {
118
- console.error(`❌ 找不到配置文件: ${configPath}`);
119
+ utils_1.logger.error(`找不到配置文件: ${configPath}`);
119
120
  process.exit(1);
120
121
  }
121
122
  const configContent = fs.readFileSync(configPath, 'utf-8');
122
123
  const config = JSON.parse(configContent);
123
- const { validateSwaggerConfig } = await Promise.resolve().then(() => __importStar(require('../config/validator')));
124
+ const { validateSwaggerConfig } = await import('../config/validator.js');
124
125
  const errors = validateSwaggerConfig(config);
125
126
  if (errors.length === 0) {
126
- console.log('配置文件验证通过');
127
+ utils_1.logger.success('配置文件验证通过');
127
128
  }
128
129
  else {
129
- console.error('配置验证失败:');
130
- errors.forEach((error) => console.error(` - ${error}`));
130
+ utils_1.logger.errorList('配置验证失败:', errors);
131
131
  process.exit(1);
132
132
  }
133
133
  }
134
134
  catch (error) {
135
135
  if (error instanceof SyntaxError) {
136
- console.error(`❌ 配置文件 JSON 格式错误: ${configPath}`);
137
- console.error('错误详情:', error.message);
136
+ utils_1.logger.error(`配置文件 JSON 格式错误: ${configPath}`);
137
+ utils_1.logger.error(`错误详情: ${error.message}`);
138
138
  }
139
139
  else {
140
- console.error('❌ 配置文件验证失败:', error);
140
+ utils_1.logger.error('配置文件验证失败', error);
141
141
  }
142
142
  process.exit(1);
143
143
  }
@@ -175,6 +175,7 @@ function createDefaultConfig() {
175
175
  generator: 'typescript',
176
176
  requestStyle: 'generic',
177
177
  groupByTags: true,
178
+ multiTagStrategy: 'first',
178
179
  overwrite: true,
179
180
  prefix: '',
180
181
  lint: 'prettier --write',
@@ -202,12 +203,12 @@ function createDefaultConfig() {
202
203
  */
203
204
  function getInitSuccessMessage(existsBeforeInit, force) {
204
205
  if (existsBeforeInit && force) {
205
- return '配置文件已覆盖:';
206
+ return '配置文件已覆盖:';
206
207
  }
207
208
  if (existsBeforeInit) {
208
- return '配置文件已补全:';
209
+ return '配置文件已补全:';
209
210
  }
210
- return '配置文件已创建:';
211
+ return '配置文件已创建:';
211
212
  }
212
213
  /**
213
214
  * 读取已存在的配置文件
@@ -221,11 +222,11 @@ function readExistingConfig(configPath) {
221
222
  }
222
223
  catch (error) {
223
224
  if (error instanceof SyntaxError) {
224
- console.error(`❌ 配置文件 JSON 格式错误: ${configPath}`);
225
- console.error('错误详情:', error.message);
225
+ utils_1.logger.error(`配置文件 JSON 格式错误: ${configPath}`);
226
+ utils_1.logger.error(`错误详情: ${error.message}`);
226
227
  }
227
228
  else {
228
- console.error('❌ 读取配置文件失败:', error);
229
+ utils_1.logger.error('读取配置文件失败', error);
229
230
  }
230
231
  process.exit(1);
231
232
  throw error;
@@ -7,6 +7,7 @@ const CONFIG_KEYS = [
7
7
  'output',
8
8
  'generator',
9
9
  'groupByTags',
10
+ 'multiTagStrategy',
10
11
  'overwrite',
11
12
  'prefix',
12
13
  'requestStyle',
@@ -46,6 +47,7 @@ function validateSwaggerConfig(config) {
46
47
  validateRequiredString(config.output, 'output', errors);
47
48
  validateEnum(config.generator, 'generator', ['typescript', 'javascript'], errors);
48
49
  validateBoolean(config.groupByTags, 'groupByTags', errors);
50
+ validateOptionalEnum(config.multiTagStrategy, 'multiTagStrategy', ['first', 'all'], errors);
49
51
  validateOptionalBoolean(config.overwrite, 'overwrite', errors);
50
52
  validateOptionalString(config.prefix, 'prefix', errors);
51
53
  validateOptionalString(config.importTemplate, 'importTemplate', errors);
@@ -61,6 +61,12 @@ export declare class CodeGenerator {
61
61
  * @returns 类型字符串
62
62
  */
63
63
  private getTypeFromSchema;
64
+ /**
65
+ * 获取当前配置下可安全输出的类型
66
+ * @param type 类型字符串
67
+ * @returns 安全类型字符串
68
+ */
69
+ private getSafeGeneratedType;
64
70
  /**
65
71
  * 收集API数组中实际使用的类型
66
72
  * @param apis API接口数组
@@ -93,11 +99,23 @@ export declare class CodeGenerator {
93
99
  * @returns 请求配置代码
94
100
  */
95
101
  private generateRequestConfig;
102
+ /**
103
+ * 生成 params 对象字段访问表达式
104
+ * @param name 参数名
105
+ * @returns 字段访问表达式
106
+ */
107
+ private getParamAccessExpression;
96
108
  /**
97
109
  * 生成入口文件
98
110
  * @param groupedApis 按标签分组的API
99
111
  */
100
112
  private generateIndexFile;
113
+ /**
114
+ * 写入生成文件,overwrite=false 时保留已存在文件
115
+ * @param filePath 文件路径
116
+ * @param content 文件内容
117
+ */
118
+ private writeGeneratedFile;
101
119
  /**
102
120
  * 获取标签对应的文件名
103
121
  * @param tag 标签名
@@ -35,6 +35,7 @@ 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 fs = __importStar(require("fs"));
38
39
  const child_process_1 = require("child_process");
39
40
  const util_1 = require("util");
40
41
  const utils_1 = require("../utils");
@@ -90,7 +91,7 @@ class CodeGenerator {
90
91
  async generateTypesFile(types) {
91
92
  const content = this.generateTypesContent(types);
92
93
  const filePath = path.join(this.config.output, 'types.ts');
93
- (0, utils_1.writeFile)(filePath, content);
94
+ this.writeGeneratedFile(filePath, content);
94
95
  }
95
96
  /**
96
97
  * 生成类型文件内容
@@ -136,7 +137,7 @@ class CodeGenerator {
136
137
  const ext = this.config.generator === 'javascript' ? 'js' : 'ts';
137
138
  const filePath = path.join(tagFolderPath, `index.${ext}`);
138
139
  const content = this.generateApiFileContent(apis, types, tag);
139
- (0, utils_1.writeFile)(filePath, content);
140
+ this.writeGeneratedFile(filePath, content);
140
141
  }
141
142
  }
142
143
  /**
@@ -148,7 +149,7 @@ class CodeGenerator {
148
149
  const ext = this.config.generator === 'javascript' ? 'js' : 'ts';
149
150
  const filePath = path.join(this.config.output, `api.${ext}`);
150
151
  const content = this.generateApiFileContent(apis, types);
151
- (0, utils_1.writeFile)(filePath, content);
152
+ this.writeGeneratedFile(filePath, content);
152
153
  }
153
154
  /**
154
155
  * 生成API文件内容
@@ -200,7 +201,7 @@ class CodeGenerator {
200
201
  type: p.type,
201
202
  description: p.description
202
203
  }));
203
- const comment = (0, utils_1.generateApiComment)({ summary: api.description, deprecated: false }, swaggerParams);
204
+ const comment = (0, utils_1.generateApiComment)({ summary: api.description, deprecated: api.deprecated }, swaggerParams);
204
205
  parts.push(comment);
205
206
  }
206
207
  // 生成函数签名
@@ -213,7 +214,9 @@ class CodeGenerator {
213
214
  }));
214
215
  // 生成直接参数形式
215
216
  const functionParams = this.generateDirectParameters(swaggerParameters, this.config.generator === 'javascript');
216
- const responseType = api.responseType || 'any';
217
+ const responseType = this.config.options?.generateModels === false
218
+ ? 'any'
219
+ : api.responseType || 'any';
217
220
  const functionName = (0, utils_1.toCamelCase)(api.name);
218
221
  parts.push(`export const ${functionName} = (${functionParams}) => {`);
219
222
  // 生成请求配置
@@ -254,7 +257,8 @@ class CodeGenerator {
254
257
  const paramType = allParams
255
258
  .map((p) => {
256
259
  const optional = p.required ? '' : '?';
257
- return `${p.name}${optional}: ${p.type}`;
260
+ const type = this.getSafeGeneratedType(p.type);
261
+ return `${(0, utils_1.formatTsPropertyName)(p.name)}${optional}: ${type}`;
258
262
  })
259
263
  .join(', ');
260
264
  // 检查是否所有参数都是可选的
@@ -273,7 +277,7 @@ class CodeGenerator {
273
277
  const bodyType = bodyParam.schema
274
278
  ? this.getTypeFromSchema(bodyParam.schema)
275
279
  : bodyParam.type;
276
- params.push(`data: ${bodyType}`);
280
+ params.push(`data: ${this.getSafeGeneratedType(bodyType)}`);
277
281
  }
278
282
  }
279
283
  // 添加可选的config参数
@@ -286,8 +290,23 @@ class CodeGenerator {
286
290
  * @returns 类型字符串
287
291
  */
288
292
  getTypeFromSchema(schema) {
293
+ if (this.config.options?.generateModels === false) {
294
+ return 'any';
295
+ }
289
296
  return (0, utils_1.swaggerTypeToTsType)(schema);
290
297
  }
298
+ /**
299
+ * 获取当前配置下可安全输出的类型
300
+ * @param type 类型字符串
301
+ * @returns 安全类型字符串
302
+ */
303
+ getSafeGeneratedType(type) {
304
+ if (!type)
305
+ return 'any';
306
+ if (this.config.options?.generateModels !== false)
307
+ return type;
308
+ return this.isPrimitiveType(type) ? type : 'any';
309
+ }
291
310
  /**
292
311
  * 收集API数组中实际使用的类型
293
312
  * @param apis API接口数组
@@ -409,8 +428,14 @@ class CodeGenerator {
409
428
  }
410
429
  // 查询参数和路径参数都从params对象中获取
411
430
  if (queryParams.length > 0) {
412
- // 直接传递params对象,让axios自动过滤undefined值
413
- config.push('params');
431
+ if (pathParams.length > 0) {
432
+ const queryEntries = queryParams.map((param) => `${JSON.stringify(param.name)}: ${this.getParamAccessExpression(param.name)}`);
433
+ config.push(`params: { ${queryEntries.join(', ')} }`);
434
+ }
435
+ else {
436
+ // 直接传递params对象,让axios自动过滤undefined值
437
+ config.push('params');
438
+ }
414
439
  }
415
440
  // 请求体数据
416
441
  const bodyParams = api.parameters.filter((p) => p.in === 'body');
@@ -425,6 +450,16 @@ class CodeGenerator {
425
450
  config.push('...config');
426
451
  return `{\n ${config.join(',\n ')}\n }`;
427
452
  }
453
+ /**
454
+ * 生成 params 对象字段访问表达式
455
+ * @param name 参数名
456
+ * @returns 字段访问表达式
457
+ */
458
+ getParamAccessExpression(name) {
459
+ return (0, utils_1.isValidIdentifier)(name)
460
+ ? `params.${name}`
461
+ : `params[${JSON.stringify(name)}]`;
462
+ }
428
463
  /**
429
464
  * 生成入口文件
430
465
  * @param groupedApis 按标签分组的API
@@ -452,6 +487,17 @@ class CodeGenerator {
452
487
  const content = [this.generateHeader(), '', ...exports, ''].join('\n');
453
488
  const ext = this.config.generator === 'javascript' ? 'js' : 'ts';
454
489
  const filePath = path.join(this.config.output, `index.${ext}`);
490
+ this.writeGeneratedFile(filePath, content);
491
+ }
492
+ /**
493
+ * 写入生成文件,overwrite=false 时保留已存在文件
494
+ * @param filePath 文件路径
495
+ * @param content 文件内容
496
+ */
497
+ writeGeneratedFile(filePath, content) {
498
+ if (this.config.overwrite === false && fs.existsSync(filePath)) {
499
+ return;
500
+ }
455
501
  (0, utils_1.writeFile)(filePath, content);
456
502
  }
457
503
  /**
@@ -492,15 +538,15 @@ class CodeGenerator {
492
538
  if (!this.config.lint)
493
539
  return;
494
540
  try {
495
- console.log(`🎨 Running lint command: ${this.config.lint} ${this.config.output}`);
541
+ utils_1.logger.info(`执行 Lint 命令: ${this.config.lint} ${this.config.output}`);
496
542
  const result = await execPromise(`${this.config.lint} ${this.config.output}`);
497
543
  if (result.stdout) {
498
- console.log(result.stdout);
544
+ utils_1.logger.info(result.stdout.trim());
499
545
  }
500
- console.log('Lint completed successfully');
546
+ utils_1.logger.success('Lint 执行完成');
501
547
  }
502
548
  catch (error) {
503
- console.warn(`⚠️ Failed to run lint command:`, error.message);
549
+ utils_1.logger.warn(`Lint 命令执行失败: ${error.message}`);
504
550
  }
505
551
  }
506
552
  }
@@ -51,6 +51,12 @@ export declare class SwaggerParser {
51
51
  * @returns 引用解析后的对象
52
52
  */
53
53
  private resolveReference;
54
+ /**
55
+ * 合并同名同位置参数,后出现的 operation 级参数覆盖 path 级参数
56
+ * @param parameters 参数数组
57
+ * @returns 合并后的参数数组
58
+ */
59
+ private mergeParameters;
54
60
  /**
55
61
  * 按标签分组API
56
62
  * @param apis API接口数组
@@ -11,6 +11,7 @@ const HTTP_METHODS = [
11
11
  'head',
12
12
  'options'
13
13
  ];
14
+ const MAX_REF_RESOLVE_DEPTH = 20;
14
15
  /**
15
16
  * Swagger文档解析器
16
17
  */
@@ -54,10 +55,7 @@ class SwaggerParser {
54
55
  */
55
56
  parseOperation(method, path, operation, globalParameters) {
56
57
  // 合并全局参数和操作参数
57
- const allParameters = [
58
- ...(globalParameters || []),
59
- ...(operation.parameters || [])
60
- ].map((parameter) => this.resolveReference(parameter));
58
+ const allParameters = this.mergeParameters([...(globalParameters || []), ...(operation.parameters || [])].map((parameter) => this.resolveReference(parameter)));
61
59
  // 处理 OpenAPI 3.0 requestBody
62
60
  if (operation.requestBody) {
63
61
  const requestBody = this.resolveReference(operation.requestBody);
@@ -98,7 +96,7 @@ class SwaggerParser {
98
96
  // 获取请求体类型
99
97
  const bodyParam = allParameters.find((p) => p.in === 'body');
100
98
  const requestBodyType = bodyParam?.schema
101
- ? (0, utils_1.swaggerTypeToTsType)(bodyParam.schema)
99
+ ? (0, utils_1.swaggerTypeToTsType)(bodyParam.schema, this.getAllSchemas())
102
100
  : undefined;
103
101
  // 解析参数信息
104
102
  const parameters = allParameters.map((param) => {
@@ -118,6 +116,7 @@ class SwaggerParser {
118
116
  path,
119
117
  description: operation.summary || operation.description,
120
118
  tags: operation.tags || [],
119
+ deprecated: operation.deprecated,
121
120
  parameters,
122
121
  responseType,
123
122
  requestBodyType
@@ -130,12 +129,12 @@ class SwaggerParser {
130
129
  parseTypes() {
131
130
  const types = [];
132
131
  // Debug log
133
- console.log('解析类型定义...');
134
- console.log('Has components:', !!this.document.components);
132
+ utils_1.logger.debug('解析类型定义...');
133
+ utils_1.logger.debugKV('components', !!this.document.components);
135
134
  if (this.document.components) {
136
- console.log('Has schemas:', !!this.document.components.schemas);
135
+ utils_1.logger.debugKV('schemas', !!this.document.components.schemas);
137
136
  if (this.document.components.schemas) {
138
- console.log('Schema keys:', Object.keys(this.document.components.schemas));
137
+ utils_1.logger.debugKV('schema 列表', Object.keys(this.document.components.schemas).join(', '));
139
138
  }
140
139
  }
141
140
  // 解析 OpenAPI 3.x components.schemas
@@ -170,7 +169,7 @@ class SwaggerParser {
170
169
  const comment = value.description
171
170
  ? ` /** ${value.description} */`
172
171
  : '';
173
- return `${comment}\n ${key}${optional}: ${type};`;
172
+ return `${comment}\n ${(0, utils_1.formatTsPropertyName)(key)}${optional}: ${type};`;
174
173
  })
175
174
  .join('\n');
176
175
  definition = `export interface ${typeName} {\n${properties}\n}`;
@@ -186,7 +185,7 @@ class SwaggerParser {
186
185
  const enumValues = schema.enum
187
186
  .map((value, index) => {
188
187
  const key = this.getEnumMemberName(schema, value, index);
189
- return ` ${key} = '${value}'`;
188
+ return ` ${key} = ${JSON.stringify(String(value))}`;
190
189
  })
191
190
  .join(',\n');
192
191
  definition = `export enum ${typeName} {\n${enumValues}\n}`;
@@ -225,19 +224,45 @@ class SwaggerParser {
225
224
  * @param value 可能带有 $ref 的对象
226
225
  * @returns 引用解析后的对象
227
226
  */
228
- resolveReference(value) {
227
+ resolveReference(value, seenRefs = new Set(), depth = 0) {
229
228
  if (!value || !value.$ref) {
230
229
  return value;
231
230
  }
232
- const refPath = value.$ref.replace(/^#\//, '').split('/');
231
+ const ref = value.$ref;
232
+ if (!ref.startsWith('#/')) {
233
+ throw new Error(`暂不支持外部 $ref 引用: ${ref}`);
234
+ }
235
+ if (depth >= MAX_REF_RESOLVE_DEPTH) {
236
+ throw new Error(`$ref 解析超过最大深度: ${ref}`);
237
+ }
238
+ if (seenRefs.has(ref)) {
239
+ throw new Error(`检测到循环 $ref 引用: ${ref}`);
240
+ }
241
+ seenRefs.add(ref);
242
+ const refPath = ref
243
+ .replace(/^#\//, '')
244
+ .split('/')
245
+ .map((segment) => decodeURIComponent(segment).replace(/~1/g, '/').replace(/~0/g, '~'));
233
246
  let current = this.document;
234
247
  for (const segment of refPath) {
235
248
  current = current?.[segment];
236
- if (!current) {
237
- return value;
249
+ if (current === undefined) {
250
+ throw new Error(`无法解析 $ref 引用: ${ref}`);
238
251
  }
239
252
  }
240
- return current;
253
+ return this.resolveReference(current, seenRefs, depth + 1);
254
+ }
255
+ /**
256
+ * 合并同名同位置参数,后出现的 operation 级参数覆盖 path 级参数
257
+ * @param parameters 参数数组
258
+ * @returns 合并后的参数数组
259
+ */
260
+ mergeParameters(parameters) {
261
+ const merged = new Map();
262
+ for (const parameter of parameters) {
263
+ merged.set(`${parameter.in}:${parameter.name}`, parameter);
264
+ }
265
+ return Array.from(merged.values());
241
266
  }
242
267
  /**
243
268
  * 按标签分组API
@@ -248,7 +273,9 @@ class SwaggerParser {
248
273
  const groupedApis = new Map();
249
274
  for (const api of apis) {
250
275
  const tags = api.tags.length > 0 ? api.tags : ['default'];
251
- for (const tag of tags) {
276
+ const strategy = this.config.multiTagStrategy ?? 'first';
277
+ const groupTags = strategy === 'all' ? [tags.join('-')] : [tags[0]];
278
+ for (const tag of groupTags) {
252
279
  if (!groupedApis.has(tag)) {
253
280
  groupedApis.set(tag, []);
254
281
  }
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
  /** 接口路径公共前缀,默认为空字符串 */
@@ -373,6 +375,8 @@ export interface ApiInfo {
373
375
  description?: string;
374
376
  /** 标签 */
375
377
  tags: string[];
378
+ /** 是否废弃 */
379
+ deprecated?: boolean;
376
380
  /** 参数 */
377
381
  parameters: ParameterInfo[];
378
382
  /** 响应类型 */
@@ -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
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 类型字符串
@@ -1,5 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isValidIdentifier = isValidIdentifier;
4
+ exports.formatTsPropertyName = formatTsPropertyName;
3
5
  exports.stripNullFromUnion = stripNullFromUnion;
4
6
  exports.swaggerTypeToTsType = swaggerTypeToTsType;
5
7
  exports.getRefName = getRefName;
@@ -7,6 +9,22 @@ exports.swaggerParameterToSchema = swaggerParameterToSchema;
7
9
  exports.getResponseType = getResponseType;
8
10
  exports.getSchemaFromContent = getSchemaFromContent;
9
11
  const naming_1 = require("./naming");
12
+ /**
13
+ * 判断字符串是否为合法 TypeScript 标识符
14
+ * @param name 属性名
15
+ * @returns 是否为合法标识符
16
+ */
17
+ function isValidIdentifier(name) {
18
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name);
19
+ }
20
+ /**
21
+ * 格式化 TypeScript 属性名
22
+ * @param name 属性名
23
+ * @returns 可用于类型声明的属性名
24
+ */
25
+ function formatTsPropertyName(name) {
26
+ return isValidIdentifier(name) ? name : JSON.stringify(name);
27
+ }
10
28
  /**
11
29
  * 移除联合类型中的顶层 null 类型
12
30
  * @param typeStr 类型字符串
@@ -78,7 +96,7 @@ function swaggerTypeToTsType(schema, schemas) {
78
96
  else if (schema.allOf) {
79
97
  const refSchema = schema.allOf.find((item) => item.$ref);
80
98
  const secondSchema = schema.allOf.find((item) => !item.$ref);
81
- if (refSchema && secondSchema) {
99
+ if (refSchema && secondSchema && schema.allOf.length === 2) {
82
100
  const refName = getRefName(refSchema.$ref || '');
83
101
  const sanitizedRefName = (0, naming_1.sanitizeTypeName)(refName || '');
84
102
  if (secondSchema.properties) {
@@ -97,7 +115,7 @@ function swaggerTypeToTsType(schema, schemas) {
97
115
  let type = swaggerTypeToTsType(value, schemas);
98
116
  if (optional === '?')
99
117
  type = stripNullFromUnion(type);
100
- return `${key}${optional}: ${type}`;
118
+ return `${formatTsPropertyName(key)}${optional}: ${type}`;
101
119
  })
102
120
  .join('; ')} }`;
103
121
  baseType = `${sanitizedRefName}<${combinedType}>`;
@@ -112,9 +130,10 @@ function swaggerTypeToTsType(schema, schemas) {
112
130
  }
113
131
  else {
114
132
  const types = schema.allOf
115
- .map((item) => swaggerTypeToTsType(item))
133
+ .map((item) => swaggerTypeToTsType(item, schemas))
116
134
  .filter((type) => type !== 'any');
117
- baseType = types.length > 0 ? types[0] : 'any';
135
+ const uniqueTypes = Array.from(new Set(types));
136
+ baseType = uniqueTypes.length > 0 ? uniqueTypes.join(' & ') : 'any';
118
137
  }
119
138
  }
120
139
  else if (schema.anyOf || schema.oneOf) {
@@ -162,7 +181,7 @@ function swaggerTypeToTsType(schema, schemas) {
162
181
  let type = swaggerTypeToTsType(value, schemas);
163
182
  if (optional === '?')
164
183
  type = stripNullFromUnion(type);
165
- return ` ${key}${optional}: ${type};`;
184
+ return ` ${formatTsPropertyName(key)}${optional}: ${type};`;
166
185
  })
167
186
  .join('\n');
168
187
  baseType = `{\n${properties}\n}`;
@@ -228,7 +247,7 @@ function toLiteralType(value) {
228
247
  if (value === null)
229
248
  return 'null';
230
249
  if (typeof value === 'string')
231
- return `'${value}'`;
250
+ return JSON.stringify(value);
232
251
  return String(value);
233
252
  }
234
253
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "swagger2api-v3",
3
- "version": "1.1.9",
3
+ "version": "1.1.10",
4
4
  "description": "A command-line tool for generating TypeScript API interfaces from OpenAPI 3.x / Swagger 3.0 documentation",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -18,20 +18,21 @@
18
18
  "test:watch": "jest --watch",
19
19
  "test:coverage": "jest --coverage",
20
20
  "test:ci": "jest --ci --coverage --watchAll=false",
21
- "build": "tsc && npm run copy:schema",
21
+ "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0",
22
+ "build": "tsc && pnpm run copy:schema",
22
23
  "copy:schema": "node build/copy-schema.js",
23
24
  "clean": "rimraf dist",
24
- "prebuild": "npm run clean",
25
+ "prebuild": "pnpm run clean",
25
26
  "start": "node dist/cli/index.js",
26
- "dev": "npm run build && npm start",
27
+ "dev": "pnpm run build && pnpm start",
27
28
  "lint": "prettier --write .",
28
- "generate": "npm run build && node dist/cli/index.js generate",
29
- "init": "npm run build && node dist/cli/index.js init",
30
- "validate": "npm run build && node dist/cli/index.js validate",
31
- "swagger:generate": "npm run generate",
32
- "swagger:run": "npm run generate",
33
- "swagger:init": "npm run init",
34
- "swagger:validate": "npm run validate"
29
+ "generate": "pnpm run build && node dist/cli/index.js generate",
30
+ "init": "pnpm run build && node dist/cli/index.js init",
31
+ "validate": "pnpm run build && node dist/cli/index.js validate",
32
+ "swagger:generate": "pnpm run generate",
33
+ "swagger:run": "pnpm run generate",
34
+ "swagger:init": "pnpm run init",
35
+ "swagger:validate": "pnpm run validate"
35
36
  },
36
37
  "keywords": [
37
38
  "swagger",
@@ -55,15 +56,16 @@
55
56
  "packageManager": "pnpm@10.11.0",
56
57
  "devDependencies": {
57
58
  "@types/jest": "^30.0.0",
58
- "@types/node": "^24.3.1",
59
+ "@types/node": "^24.13.2",
60
+ "conventional-changelog-cli": "^5.0.0",
59
61
  "jest": "^30.4.2",
60
- "prettier": "^3.6.2",
61
- "rimraf": "^6.0.1",
62
+ "prettier": "^3.8.4",
63
+ "rimraf": "^6.1.3",
62
64
  "ts-jest": "^29.4.11",
63
- "typescript": "^5.9.2"
65
+ "typescript": "^6.0.3"
64
66
  },
65
67
  "dependencies": {
66
- "axios": "^1.11.0",
67
- "commander": "^12.0.0"
68
+ "axios": "^1.18.1",
69
+ "commander": "^12.1.0"
68
70
  }
69
71
  }