swagger2api-v3 1.1.6 → 1.1.8

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
@@ -37,7 +37,8 @@ The tool generates a `.swagger.config.json` configuration file:
37
37
 
38
38
  ```json
39
39
  {
40
- "input": "https://petstore.swagger.io/v2/swagger.json",
40
+ "$schema": "./node_modules/swagger2api-v3/dist/.swagger2api.schema.json",
41
+ "input": "https://petstore3.swagger.io/api/v3/openapi.json",
41
42
  "output": "./src/api",
42
43
  "importTemplate": "import { request } from '@/utils/request';",
43
44
  "generator": "typescript",
@@ -48,6 +49,15 @@ The tool generates a `.swagger.config.json` configuration file:
48
49
  "lint": "prettier --write",
49
50
  "methodNameIgnorePrefix": [],
50
51
  "addMethodSuffix": true,
52
+ "headerComment": "",
53
+ "filter": {
54
+ "include": {
55
+ "tags": []
56
+ },
57
+ "exclude": {
58
+ "tags": []
59
+ }
60
+ },
51
61
  "options": {
52
62
  "addComments": true
53
63
  }
@@ -64,6 +74,7 @@ npx swagger2api-v3 generate
64
74
 
65
75
  | Option | Type | Default | Description |
66
76
  | ------------------------ | --------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
77
+ | `$schema` | string | - | Local JSON Schema path for editor completion. The default points to `node_modules/swagger2api-v3/dist/.swagger2api.schema.json` |
67
78
  | `input` | string | - | Swagger JSON file path or URL |
68
79
  | `output` | string | `'./src/api'` | Output directory for generated code |
69
80
  | `generator` | string | `'typescript'` | Code generator type. Supports `'typescript'` and `'javascript'`. `'javascript'` outputs `.js` files and skips type file generation |
@@ -75,6 +86,9 @@ npx swagger2api-v3 generate
75
86
  | `lint` | string | - | Code formatting command (optional) |
76
87
  | `methodNameIgnorePrefix` | string[] | `[]` | Array of prefixes to ignore when generating method names. For example, `['api', 'auth']` will transform `apiGetName` to `getName` and `authUserInfo` to `userInfo` |
77
88
  | `addMethodSuffix` | boolean | `true` | Whether to add HTTP method suffix to generated function names. `true` generates `userListPost`, `false` generates `userList` |
89
+ | `headerComment` | string | - | Custom header comment for generated `types`, API, and index files |
90
+ | `filter.include.tags` | string[] | `[]` | Only generate APIs whose tags match this list. Empty means include all |
91
+ | `filter.exclude.tags` | string[] | `[]` | Skip APIs whose tags match this list. Exclude rules take priority over include rules |
78
92
  | `options.addComments` | boolean | `true` | Whether to add detailed comments |
79
93
 
80
94
  ## 📁 Generated File Structure
@@ -0,0 +1,120 @@
1
+ {
2
+ "title": "swagger2api-v3 config",
3
+ "type": "object",
4
+ "additionalProperties": false,
5
+ "required": ["input", "output", "generator", "groupByTags"],
6
+ "properties": {
7
+ "$schema": {
8
+ "type": "string",
9
+ "description": "本地 JSON Schema 路径,用于编辑器提示"
10
+ },
11
+ "input": {
12
+ "type": "string",
13
+ "description": "OpenAPI JSON 文件路径或 URL"
14
+ },
15
+ "output": {
16
+ "type": "string",
17
+ "description": "生成代码输出目录"
18
+ },
19
+ "generator": {
20
+ "enum": ["typescript", "javascript"],
21
+ "description": "生成器类型"
22
+ },
23
+ "groupByTags": {
24
+ "type": "boolean",
25
+ "description": "是否按 tags 分组生成文件"
26
+ },
27
+ "overwrite": {
28
+ "type": "boolean",
29
+ "description": "生成前是否覆盖输出目录"
30
+ },
31
+ "prefix": {
32
+ "type": "string",
33
+ "description": "接口路径公共前缀"
34
+ },
35
+ "requestStyle": {
36
+ "enum": ["method", "generic"],
37
+ "description": "请求调用风格"
38
+ },
39
+ "importTemplate": {
40
+ "type": "string",
41
+ "description": "request 导入语句"
42
+ },
43
+ "lint": {
44
+ "type": "string",
45
+ "description": "生成后执行的格式化命令"
46
+ },
47
+ "methodNameIgnorePrefix": {
48
+ "type": "array",
49
+ "items": { "type": "string" },
50
+ "description": "生成方法名时需要忽略的前缀"
51
+ },
52
+ "addMethodSuffix": {
53
+ "type": "boolean",
54
+ "description": "是否在方法名中追加 HTTP method 后缀"
55
+ },
56
+ "headerComment": {
57
+ "type": "string",
58
+ "description": "自定义生成文件头部注释"
59
+ },
60
+ "filter": {
61
+ "type": "object",
62
+ "additionalProperties": false,
63
+ "properties": {
64
+ "include": {
65
+ "type": "object",
66
+ "additionalProperties": false,
67
+ "properties": {
68
+ "tags": {
69
+ "type": "array",
70
+ "items": { "type": "string" }
71
+ }
72
+ }
73
+ },
74
+ "exclude": {
75
+ "type": "object",
76
+ "additionalProperties": false,
77
+ "properties": {
78
+ "tags": {
79
+ "type": "array",
80
+ "items": { "type": "string" }
81
+ }
82
+ }
83
+ }
84
+ }
85
+ },
86
+ "tagGrouping": {
87
+ "type": "object",
88
+ "additionalProperties": false,
89
+ "properties": {
90
+ "enabled": { "type": "boolean" },
91
+ "createSubDirectories": { "type": "boolean" },
92
+ "fileNaming": {
93
+ "enum": ["tag", "kebab-case", "camelCase"]
94
+ }
95
+ }
96
+ },
97
+ "options": {
98
+ "type": "object",
99
+ "additionalProperties": false,
100
+ "properties": {
101
+ "generateModels": { "type": "boolean" },
102
+ "generateApis": { "type": "boolean" },
103
+ "generateIndex": { "type": "boolean" },
104
+ "useAxios": { "type": "boolean" },
105
+ "addComments": { "type": "boolean" },
106
+ "prettify": { "type": "boolean" }
107
+ }
108
+ },
109
+ "comments": {
110
+ "type": "object",
111
+ "additionalProperties": false,
112
+ "properties": {
113
+ "includeDescription": { "type": "boolean" },
114
+ "includeParameters": { "type": "boolean" },
115
+ "includeResponses": { "type": "boolean" },
116
+ "includeExamples": { "type": "boolean" }
117
+ }
118
+ }
119
+ }
120
+ }
package/dist/cli/index.js CHANGED
@@ -93,29 +93,12 @@ program
93
93
  .option('-f, --force', '强制覆盖已存在的配置文件')
94
94
  .action(async (options) => {
95
95
  const configPath = path.resolve(process.cwd(), '.swagger.config.json');
96
- if (fs.existsSync(configPath) && !options.force) {
97
- console.error('❌ 配置文件已存在,使用 --force 参数强制覆盖');
98
- process.exit(1);
99
- }
100
- const config = {
101
- input: 'http://localhost:3000/admin/docs/json',
102
- output: './src/api',
103
- importTemplate: "import { request } from '@/utils/request';",
104
- generator: 'typescript',
105
- requestStyle: 'generic',
106
- groupByTags: true,
107
- overwrite: true,
108
- prefix: '',
109
- lint: 'prettier --write',
110
- methodNameIgnorePrefix: [],
111
- addMethodSuffix: true,
112
- options: {
113
- addComments: true
114
- }
115
- };
116
96
  try {
97
+ const existsBeforeInit = fs.existsSync(configPath);
98
+ const config = createInitConfig(configPath, options.force);
99
+ const successMessage = getInitSuccessMessage(existsBeforeInit, options.force);
117
100
  fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
118
- console.log('✅ 配置文件已创建:', configPath);
101
+ console.log(successMessage, configPath);
119
102
  console.log('💡 请根据需要修改配置文件,然后运行 swagger2api-v3 generate');
120
103
  }
121
104
  catch (error) {
@@ -137,21 +120,14 @@ program
137
120
  }
138
121
  const configContent = fs.readFileSync(configPath, 'utf-8');
139
122
  const config = JSON.parse(configContent);
140
- const { Swagger2API } = await Promise.resolve().then(() => __importStar(require('../index')));
141
- const swagger2api = new Swagger2API(config);
142
- if (swagger2api.validateConfig()) {
123
+ const { validateSwaggerConfig } = await Promise.resolve().then(() => __importStar(require('../config/validator')));
124
+ const errors = validateSwaggerConfig(config);
125
+ if (errors.length === 0) {
143
126
  console.log('✅ 配置文件验证通过');
144
- // 尝试加载 Swagger 文档
145
- try {
146
- const { loadSwaggerDocument } = await Promise.resolve().then(() => __importStar(require('../utils')));
147
- const document = await loadSwaggerDocument(config.input);
148
- console.log(`✅ Swagger 文档加载成功: ${document.info.title} v${document.info.version}`);
149
- }
150
- catch (error) {
151
- console.warn('⚠️ Swagger 文档加载失败:', error);
152
- }
153
127
  }
154
128
  else {
129
+ console.error('❌ 配置验证失败:');
130
+ errors.forEach((error) => console.error(` - ${error}`));
155
131
  process.exit(1);
156
132
  }
157
133
  }
@@ -166,23 +142,119 @@ program
166
142
  process.exit(1);
167
143
  }
168
144
  });
169
- // run 命令(别名,兼容性)
170
- program
171
- .command('run')
172
- .description('运行生成器(generate 命令的别名)')
173
- .option('-c, --config <path>', '配置文件路径', '.swagger.config.json')
174
- .action(async (options) => {
175
- try {
176
- await (0, index_1.generateFromConfig)(options.config);
177
- }
178
- catch (error) {
179
- console.error('❌ 生成失败:', error);
180
- process.exit(1);
181
- }
182
- });
183
145
  // 解析命令行参数
184
146
  program.parse();
185
147
  // 如果没有提供命令,显示帮助信息
186
148
  if (!process.argv.slice(2).length) {
187
149
  program.outputHelp();
188
150
  }
151
+ /**
152
+ * 创建 init 命令需要写入的配置
153
+ * @param configPath 配置文件路径
154
+ * @param force 是否强制覆盖
155
+ * @returns 初始化配置对象
156
+ */
157
+ function createInitConfig(configPath, force) {
158
+ const defaultConfig = createDefaultConfig();
159
+ if (!fs.existsSync(configPath) || force) {
160
+ return defaultConfig;
161
+ }
162
+ const existingConfig = readExistingConfig(configPath);
163
+ return mergeMissingConfig(existingConfig, defaultConfig);
164
+ }
165
+ /**
166
+ * 创建默认配置对象
167
+ * @returns 默认配置对象
168
+ */
169
+ function createDefaultConfig() {
170
+ return {
171
+ $schema: './node_modules/swagger2api-v3/dist/.swagger2api.schema.json',
172
+ input: 'http://localhost:3000/admin/docs/json',
173
+ output: './src/api',
174
+ importTemplate: "import { request } from '@/utils/request';",
175
+ generator: 'typescript',
176
+ requestStyle: 'generic',
177
+ groupByTags: true,
178
+ overwrite: true,
179
+ prefix: '',
180
+ lint: 'prettier --write',
181
+ methodNameIgnorePrefix: [],
182
+ addMethodSuffix: true,
183
+ headerComment: '',
184
+ filter: {
185
+ include: {
186
+ tags: []
187
+ },
188
+ exclude: {
189
+ tags: []
190
+ }
191
+ },
192
+ options: {
193
+ addComments: true
194
+ }
195
+ };
196
+ }
197
+ /**
198
+ * 获取 init 命令成功提示
199
+ * @param existsBeforeInit 执行 init 前配置文件是否存在
200
+ * @param force 是否强制覆盖
201
+ * @returns 成功提示文案
202
+ */
203
+ function getInitSuccessMessage(existsBeforeInit, force) {
204
+ if (existsBeforeInit && force) {
205
+ return '✅ 配置文件已覆盖:';
206
+ }
207
+ if (existsBeforeInit) {
208
+ return '✅ 配置文件已补全:';
209
+ }
210
+ return '✅ 配置文件已创建:';
211
+ }
212
+ /**
213
+ * 读取已存在的配置文件
214
+ * @param configPath 配置文件路径
215
+ * @returns 已存在的配置对象
216
+ */
217
+ function readExistingConfig(configPath) {
218
+ try {
219
+ const configContent = fs.readFileSync(configPath, 'utf-8');
220
+ return JSON.parse(configContent);
221
+ }
222
+ catch (error) {
223
+ if (error instanceof SyntaxError) {
224
+ console.error(`❌ 配置文件 JSON 格式错误: ${configPath}`);
225
+ console.error('错误详情:', error.message);
226
+ }
227
+ else {
228
+ console.error('❌ 读取配置文件失败:', error);
229
+ }
230
+ process.exit(1);
231
+ throw error;
232
+ }
233
+ }
234
+ /**
235
+ * 合并缺失的配置字段,保留已有配置值
236
+ * @param current 当前配置
237
+ * @param defaults 默认配置
238
+ * @returns 补全后的配置
239
+ */
240
+ function mergeMissingConfig(current, defaults) {
241
+ const result = { ...current };
242
+ for (const [key, value] of Object.entries(defaults)) {
243
+ if (result[key] === undefined) {
244
+ result[key] = value;
245
+ continue;
246
+ }
247
+ if (isPlainObject(result[key]) && isPlainObject(value)) {
248
+ result[key] = mergeMissingConfig(result[key], value);
249
+ }
250
+ }
251
+ return result;
252
+ }
253
+ /**
254
+ * 判断是否为普通对象
255
+ * @param value 待判断的值
256
+ * @returns 是否为普通对象
257
+ */
258
+ function isPlainObject(value) {
259
+ return !!value && typeof value === 'object' && !Array.isArray(value);
260
+ }
@@ -0,0 +1,7 @@
1
+ import { SwaggerConfig } from '../types';
2
+ /**
3
+ * 验证配置对象
4
+ * @param config 配置对象
5
+ * @returns 错误信息数组
6
+ */
7
+ export declare function validateSwaggerConfig(config: SwaggerConfig): string[];
@@ -0,0 +1,218 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateSwaggerConfig = validateSwaggerConfig;
4
+ const CONFIG_KEYS = [
5
+ '$schema',
6
+ 'input',
7
+ 'output',
8
+ 'generator',
9
+ 'groupByTags',
10
+ 'overwrite',
11
+ 'prefix',
12
+ 'requestStyle',
13
+ 'tagGrouping',
14
+ 'options',
15
+ 'comments',
16
+ 'importTemplate',
17
+ 'lint',
18
+ 'methodNameIgnorePrefix',
19
+ 'addMethodSuffix',
20
+ 'filter',
21
+ 'headerComment'
22
+ ];
23
+ const OPTIONS_KEYS = [
24
+ 'generateModels',
25
+ 'generateApis',
26
+ 'generateIndex',
27
+ 'useAxios',
28
+ 'addComments',
29
+ 'prettify'
30
+ ];
31
+ const TAG_GROUPING_KEYS = ['enabled', 'createSubDirectories', 'fileNaming'];
32
+ const COMMENT_KEYS = [
33
+ 'includeDescription',
34
+ 'includeParameters',
35
+ 'includeResponses',
36
+ 'includeExamples'
37
+ ];
38
+ /**
39
+ * 验证配置对象
40
+ * @param config 配置对象
41
+ * @returns 错误信息数组
42
+ */
43
+ function validateSwaggerConfig(config) {
44
+ const errors = [];
45
+ validateRequiredString(config.input, 'input', errors);
46
+ validateRequiredString(config.output, 'output', errors);
47
+ validateEnum(config.generator, 'generator', ['typescript', 'javascript'], errors);
48
+ validateBoolean(config.groupByTags, 'groupByTags', errors);
49
+ validateOptionalBoolean(config.overwrite, 'overwrite', errors);
50
+ validateOptionalString(config.prefix, 'prefix', errors);
51
+ validateOptionalString(config.importTemplate, 'importTemplate', errors);
52
+ validateOptionalString(config.lint, 'lint', errors);
53
+ validateOptionalString(config.headerComment, 'headerComment', errors);
54
+ validateOptionalString(config.$schema, '$schema', errors);
55
+ validateOptionalBoolean(config.addMethodSuffix, 'addMethodSuffix', errors);
56
+ validateOptionalEnum(config.requestStyle, 'requestStyle', ['method', 'generic'], errors);
57
+ validateStringArray(config.methodNameIgnorePrefix, 'methodNameIgnorePrefix', errors);
58
+ validateFilter(config, errors);
59
+ validateOptions(config, errors);
60
+ validateTagGrouping(config, errors);
61
+ validateComments(config, errors);
62
+ validateKnownKeys(config, CONFIG_KEYS, 'config', errors);
63
+ return errors;
64
+ }
65
+ /**
66
+ * 验证必填字符串
67
+ * @param value 字段值
68
+ * @param field 字段名
69
+ * @param errors 错误信息数组
70
+ */
71
+ function validateRequiredString(value, field, errors) {
72
+ if (typeof value !== 'string' || !value) {
73
+ errors.push(`${field} 配置项不能为空,且必须是字符串`);
74
+ }
75
+ }
76
+ /**
77
+ * 验证可选字符串
78
+ * @param value 字段值
79
+ * @param field 字段名
80
+ * @param errors 错误信息数组
81
+ */
82
+ function validateOptionalString(value, field, errors) {
83
+ if (value !== undefined && typeof value !== 'string') {
84
+ errors.push(`${field} 必须是字符串`);
85
+ }
86
+ }
87
+ /**
88
+ * 验证布尔值
89
+ * @param value 字段值
90
+ * @param field 字段名
91
+ * @param errors 错误信息数组
92
+ */
93
+ function validateBoolean(value, field, errors) {
94
+ if (typeof value !== 'boolean') {
95
+ errors.push(`${field} 必须是布尔值`);
96
+ }
97
+ }
98
+ /**
99
+ * 验证可选布尔值
100
+ * @param value 字段值
101
+ * @param field 字段名
102
+ * @param errors 错误信息数组
103
+ */
104
+ function validateOptionalBoolean(value, field, errors) {
105
+ if (value !== undefined && typeof value !== 'boolean') {
106
+ errors.push(`${field} 必须是布尔值`);
107
+ }
108
+ }
109
+ /**
110
+ * 验证枚举值
111
+ * @param value 字段值
112
+ * @param field 字段名
113
+ * @param values 允许值
114
+ * @param errors 错误信息数组
115
+ */
116
+ function validateEnum(value, field, values, errors) {
117
+ if (typeof value !== 'string' || !values.includes(value)) {
118
+ errors.push(`${field} 必须是 ${values.join(' 或 ')}`);
119
+ }
120
+ }
121
+ /**
122
+ * 验证可选枚举值
123
+ * @param value 字段值
124
+ * @param field 字段名
125
+ * @param values 允许值
126
+ * @param errors 错误信息数组
127
+ */
128
+ function validateOptionalEnum(value, field, values, errors) {
129
+ if (value !== undefined) {
130
+ validateEnum(value, field, values, errors);
131
+ }
132
+ }
133
+ /**
134
+ * 验证字符串数组
135
+ * @param value 字段值
136
+ * @param field 字段名
137
+ * @param errors 错误信息数组
138
+ */
139
+ function validateStringArray(value, field, errors) {
140
+ if (value === undefined)
141
+ return;
142
+ if (!Array.isArray(value) || value.some((item) => typeof item !== 'string')) {
143
+ errors.push(`${field} 必须是字符串数组`);
144
+ }
145
+ }
146
+ /**
147
+ * 验证过滤配置
148
+ * @param config 配置对象
149
+ * @param errors 错误信息数组
150
+ */
151
+ function validateFilter(config, errors) {
152
+ if (!config.filter)
153
+ return;
154
+ validateKnownKeys(config.filter, ['include', 'exclude'], 'filter', errors);
155
+ validateKnownKeys(config.filter.include, ['tags'], 'filter.include', errors);
156
+ validateKnownKeys(config.filter.exclude, ['tags'], 'filter.exclude', errors);
157
+ validateStringArray(config.filter.include?.tags, 'filter.include.tags', errors);
158
+ validateStringArray(config.filter.exclude?.tags, 'filter.exclude.tags', errors);
159
+ }
160
+ /**
161
+ * 验证生成选项配置
162
+ * @param config 配置对象
163
+ * @param errors 错误信息数组
164
+ */
165
+ function validateOptions(config, errors) {
166
+ if (!config.options)
167
+ return;
168
+ validateKnownKeys(config.options, OPTIONS_KEYS, 'options', errors);
169
+ validateOptionalBoolean(config.options.generateModels, 'options.generateModels', errors);
170
+ validateOptionalBoolean(config.options.generateApis, 'options.generateApis', errors);
171
+ validateOptionalBoolean(config.options.generateIndex, 'options.generateIndex', errors);
172
+ validateOptionalBoolean(config.options.useAxios, 'options.useAxios', errors);
173
+ validateOptionalBoolean(config.options.addComments, 'options.addComments', errors);
174
+ validateOptionalBoolean(config.options.prettify, 'options.prettify', errors);
175
+ }
176
+ /**
177
+ * 验证标签分组选项
178
+ * @param config 配置对象
179
+ * @param errors 错误信息数组
180
+ */
181
+ function validateTagGrouping(config, errors) {
182
+ if (!config.tagGrouping)
183
+ return;
184
+ validateKnownKeys(config.tagGrouping, TAG_GROUPING_KEYS, 'tagGrouping', errors);
185
+ validateOptionalBoolean(config.tagGrouping.enabled, 'tagGrouping.enabled', errors);
186
+ validateOptionalBoolean(config.tagGrouping.createSubDirectories, 'tagGrouping.createSubDirectories', errors);
187
+ validateOptionalEnum(config.tagGrouping.fileNaming, 'tagGrouping.fileNaming', ['tag', 'kebab-case', 'camelCase'], errors);
188
+ }
189
+ /**
190
+ * 验证注释配置
191
+ * @param config 配置对象
192
+ * @param errors 错误信息数组
193
+ */
194
+ function validateComments(config, errors) {
195
+ if (!config.comments)
196
+ return;
197
+ validateKnownKeys(config.comments, COMMENT_KEYS, 'comments', errors);
198
+ validateOptionalBoolean(config.comments.includeDescription, 'comments.includeDescription', errors);
199
+ validateOptionalBoolean(config.comments.includeParameters, 'comments.includeParameters', errors);
200
+ validateOptionalBoolean(config.comments.includeResponses, 'comments.includeResponses', errors);
201
+ validateOptionalBoolean(config.comments.includeExamples, 'comments.includeExamples', errors);
202
+ }
203
+ /**
204
+ * 验证未知字段
205
+ * @param value 对象值
206
+ * @param knownKeys 允许字段列表
207
+ * @param path 对象路径
208
+ * @param errors 错误信息数组
209
+ */
210
+ function validateKnownKeys(value, knownKeys, path, errors) {
211
+ if (!value || typeof value !== 'object' || Array.isArray(value))
212
+ return;
213
+ for (const key of Object.keys(value)) {
214
+ if (!knownKeys.includes(key)) {
215
+ errors.push(`${path}.${key} 不是支持的配置项`);
216
+ }
217
+ }
218
+ }
@@ -51,7 +51,7 @@ export declare class CodeGenerator {
51
51
  private generateApiFunction;
52
52
  /**
53
53
  * 生成直接参数形式
54
- * @param parameters Swagger参数数组
54
+ * @param parameters OpenAPI 参数数组
55
55
  * @returns 函数参数字符串
56
56
  */
57
57
  private generateDirectParameters;
@@ -104,6 +104,12 @@ export declare class CodeGenerator {
104
104
  * @returns 文件名
105
105
  */
106
106
  private getTagFileName;
107
+ /**
108
+ * 生成文件头部注释
109
+ * @param defaultHeader 默认头部注释
110
+ * @returns 文件头部注释内容
111
+ */
112
+ private generateHeader;
107
113
  /**
108
114
  * 在所有文件生成完成后运行lint命令
109
115
  */