swagger2api-v3 1.0.10 → 1.1.1

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
@@ -75,12 +75,14 @@ npx swagger2api-v3 generate
75
75
  |--------|------|---------|-------------|
76
76
  | `input` | string | - | Swagger JSON file path or URL |
77
77
  | `output` | string | `'./src/api'` | Output directory for generated code |
78
- | `generator` | string | `'typescript'` | Code generator type |
78
+ | `generator` | string | `'typescript'` | Code generator type. Supports `'typescript'` and `'javascript'`. `'javascript'` outputs `.js` files and skips type file generation |
79
79
  | `groupByTags` | boolean | `true` | Whether to group files by tags |
80
80
  | `overwrite` | boolean | `true` | Whether to overwrite existing files |
81
81
  | `prefix` | string | `''` | Common prefix for API paths |
82
82
  | `importTemplate` | string | - | Import statement template for request function |
83
+ | `requestStyle` | 'method' \| 'generic' | `'generic'` | Request call style: `method` uses `request.get/post`, `generic` uses `request({ method })` |
83
84
  | `lint` | string | - | Code formatting command (optional) |
85
+ | `methodNameIgnorePrefix` | string[] | `[]` | Array of prefixes to ignore when generating method names. For example, `['api', 'auth']` will transform `apiGetName` to `getName` and `authUserInfo` to `userInfo` |
84
86
  | `options.addComments` | boolean | `true` | Whether to add detailed comments |
85
87
 
86
88
  ## 📁 Generated File Structure
@@ -89,7 +91,7 @@ npx swagger2api-v3 generate
89
91
 
90
92
  ```
91
93
  src/api/
92
- ├── types.ts # Data type definitions
94
+ ├── types.ts # Data type definitions (TypeScript mode only)
93
95
  ├── user/ # User-related APIs
94
96
  │ └── index.ts
95
97
  ├── auth/ # Auth-related APIs
@@ -97,6 +99,30 @@ src/api/
97
99
  └── index.ts # Entry file
98
100
  ```
99
101
 
102
+ ### JavaScript Output
103
+
104
+ When `generator: 'javascript'` is set:
105
+
106
+ - Outputs `.js` files (`index.js`, `api.js`, `user/index.js`, etc.)
107
+ - Does not generate a `types.ts` file
108
+ - Removes TypeScript-specific syntax (types, `import type`, generics like `<T>`)
109
+
110
+ Example generated API function (method style):
111
+
112
+ ```javascript
113
+ export const codeAuth = (data, config) => {
114
+ return request.post({ url: '/api/auth/codeAuth', data, ...config });
115
+ };
116
+ ```
117
+
118
+ Example generated API function (generic style):
119
+
120
+ ```javascript
121
+ export const codeAuth = (data, config) => {
122
+ return request({ url: '/api/auth/codeAuth', method: 'POST', data, ...config });
123
+ };
124
+ ```
125
+
100
126
  ### Not Grouped
101
127
 
102
128
  ```
@@ -146,6 +172,16 @@ export const authControllerLoginPost = (data: LoginDto, config?: any) => {
146
172
  ...config
147
173
  });
148
174
  };
175
+
176
+ // When requestStyle is set to 'generic':
177
+ export const authControllerLoginPost2 = (data: LoginDto, config?: any) => {
178
+ return request<LoginRespDto>({
179
+ url: '/admin/auth/login',
180
+ data,
181
+ method: 'POST',
182
+ ...config
183
+ });
184
+ };
149
185
  ```
150
186
 
151
187
  ## 🔧 CLI Commands
package/dist/cli/index.js CHANGED
@@ -125,7 +125,10 @@ const config = {
125
125
  importTemplate: "import { request } from '@/utils/request';",
126
126
 
127
127
  // 生成器类型
128
- generator: 'typescript',
128
+ generator: 'typescript', // 可选 'javascript'(JS 模式输出 .js 文件且不生成类型文件)
129
+
130
+ // 请求调用风格:'method' 使用 request.get/post;'generic' 使用 request({ method })
131
+ requestStyle: 'generic',
129
132
 
130
133
  // 按标签分组生成文件
131
134
  groupByTags: true,
@@ -139,6 +142,11 @@ const config = {
139
142
  // 代码格式化命令(可选)
140
143
  lint: 'prettier --write',
141
144
 
145
+ // 生成方法名时需要忽略的前缀(可选)
146
+ // 例如:配置 ['api', 'auth'] 后,apiGetName 会变成 getName,authUserInfo 会变成 userInfo
147
+ // 如果方法名是 apiAuthGetName,会依次移除所有匹配的前缀,最终变成 getName
148
+ methodNameIgnorePrefix: [],
149
+
142
150
  // 生成选项
143
151
  options: {
144
152
  // 是否添加注释
@@ -57,8 +57,9 @@ class CodeGenerator {
57
57
  }
58
58
  // 确保输出目录存在
59
59
  (0, utils_1.ensureDirectoryExists)(this.config.output);
60
- // 生成类型文件
61
- if (this.config.options?.generateModels !== false) {
60
+ // 生成类型文件(仅在 TypeScript 模式下生成)
61
+ if (this.config.generator === 'typescript' &&
62
+ this.config.options?.generateModels !== false) {
62
63
  await this.generateTypesFile(types);
63
64
  }
64
65
  // 生成API文件
@@ -97,7 +98,7 @@ class CodeGenerator {
97
98
  const header = [
98
99
  '/**',
99
100
  ' * API 类型定义',
100
- ' * 此文件由 swagger2api 自动生成,请勿手动修改',
101
+ ' * 此文件由 swagger2api-v3 自动生成,请勿手动修改',
101
102
  ' */',
102
103
  ''
103
104
  ].join('\n');
@@ -130,7 +131,8 @@ class CodeGenerator {
130
131
  const tagFolderPath = path.join(this.config.output, folderName);
131
132
  // 确保tag文件夹存在
132
133
  (0, utils_1.ensureDirectoryExists)(tagFolderPath);
133
- const filePath = path.join(tagFolderPath, 'index.ts');
134
+ const ext = this.config.generator === 'javascript' ? 'js' : 'ts';
135
+ const filePath = path.join(tagFolderPath, `index.${ext}`);
134
136
  const content = this.generateApiFileContent(apis, types, tag);
135
137
  (0, utils_1.writeFile)(filePath, content);
136
138
  }
@@ -141,7 +143,8 @@ class CodeGenerator {
141
143
  * @param types 类型定义数组
142
144
  */
143
145
  async generateSingleApiFile(apis, types) {
144
- const filePath = path.join(this.config.output, 'api.ts');
146
+ const ext = this.config.generator === 'javascript' ? 'js' : 'ts';
147
+ const filePath = path.join(this.config.output, `api.${ext}`);
145
148
  const content = this.generateApiFileContent(apis, types);
146
149
  (0, utils_1.writeFile)(filePath, content);
147
150
  }
@@ -157,15 +160,17 @@ class CodeGenerator {
157
160
  const header = [
158
161
  '/**',
159
162
  ` * ${tag ? `${tag} ` : ''}API 接口`,
160
- ' * 此文件由 swagger2api 自动生成,请勿手动修改',
163
+ ' * 此文件由 swagger2api-v3 自动生成,请勿手动修改',
161
164
  ' */',
162
165
  '',
163
166
  importTemplate + ';'
164
167
  ];
165
168
  // 收集当前文件实际使用的类型
166
169
  const usedTypes = this.collectUsedTypes(apis);
167
- // 添加类型导入
168
- if (usedTypes.length > 0) {
170
+ // 添加类型导入(仅在 TypeScript 且生成类型文件时)
171
+ if (this.config.generator === 'typescript' &&
172
+ this.config.options?.generateModels !== false &&
173
+ usedTypes.length > 0) {
169
174
  const typeNames = usedTypes.join(', ');
170
175
  const typesPath = tag ? '../types' : './types';
171
176
  header.push(`import type { ${typeNames} } from '${typesPath}';`);
@@ -204,13 +209,25 @@ class CodeGenerator {
204
209
  schema: p.schema || { type: p.type }
205
210
  }));
206
211
  // 生成直接参数形式
207
- const functionParams = this.generateDirectParameters(swaggerParameters);
212
+ const functionParams = this.generateDirectParameters(swaggerParameters, this.config.generator === 'javascript');
208
213
  const responseType = api.responseType || 'any';
209
214
  const functionName = (0, utils_1.toCamelCase)(api.name);
210
215
  parts.push(`export const ${functionName} = (${functionParams}) => {`);
211
216
  // 生成请求配置
212
- const requestConfig = this.generateRequestConfig(api);
213
- parts.push(` return request.${api.method.toLowerCase()}<${responseType}>(${requestConfig});`);
217
+ const useGenericRequest = this.config.requestStyle === 'generic';
218
+ const requestConfig = this.generateRequestConfig(api, useGenericRequest);
219
+ const isJS = this.config.generator === 'javascript';
220
+ if (useGenericRequest) {
221
+ parts.push(isJS
222
+ ? ` return request(${requestConfig});`
223
+ : ` return request<${responseType}>(${requestConfig});`);
224
+ }
225
+ else {
226
+ const method = api.method.toLowerCase();
227
+ parts.push(isJS
228
+ ? ` return request.${method}(${requestConfig});`
229
+ : ` return request.${method}<${responseType}>(${requestConfig});`);
230
+ }
214
231
  parts.push('}');
215
232
  return parts.join('\n');
216
233
  }
@@ -219,7 +236,7 @@ class CodeGenerator {
219
236
  * @param parameters Swagger参数数组
220
237
  * @returns 函数参数字符串
221
238
  */
222
- generateDirectParameters(parameters) {
239
+ generateDirectParameters(parameters, isJavaScript = false) {
223
240
  const params = [];
224
241
  const queryParams = parameters.filter((p) => p.in === 'query');
225
242
  const pathParams = parameters.filter((p) => p.in === 'path');
@@ -228,37 +245,52 @@ class CodeGenerator {
228
245
  // 合并路径参数和查询参数为一个params对象
229
246
  const allParams = [...pathParams, ...queryParams];
230
247
  if (allParams.length > 0) {
231
- const paramType = allParams
232
- .map((p) => {
233
- const optional = p.required ? '' : '?';
234
- return `${p.name}${optional}: ${p.type}`;
235
- })
236
- .join(', ');
237
- // 检查是否所有参数都是可选的
238
- const allOptional = allParams.every((p) => !p.required);
239
- const optionalModifier = allOptional ? '?' : '';
240
- params.push(`params${optionalModifier}: { ${paramType} }`);
248
+ if (isJavaScript) {
249
+ params.push('params');
250
+ }
251
+ else {
252
+ const paramType = allParams
253
+ .map((p) => {
254
+ const optional = p.required ? '' : '?';
255
+ return `${p.name}${optional}: ${p.type}`;
256
+ })
257
+ .join(', ');
258
+ // 检查是否所有参数都是可选的
259
+ const allOptional = allParams.every((p) => !p.required);
260
+ const optionalModifier = allOptional ? '?' : '';
261
+ params.push(`params${optionalModifier}: { ${paramType} }`);
262
+ }
241
263
  }
242
264
  // 请求体参数
243
265
  if (bodyParams.length > 0) {
244
- const bodyParam = bodyParams[0];
245
- const bodyType = bodyParam.schema
246
- ? this.getTypeFromSchema(bodyParam.schema)
247
- : bodyParam.type;
248
- params.push(`data: ${bodyType}`);
266
+ if (isJavaScript) {
267
+ params.push('data');
268
+ }
269
+ else {
270
+ const bodyParam = bodyParams[0];
271
+ const bodyType = bodyParam.schema
272
+ ? this.getTypeFromSchema(bodyParam.schema)
273
+ : bodyParam.type;
274
+ params.push(`data: ${bodyType}`);
275
+ }
249
276
  }
250
277
  // 表单参数
251
278
  if (formParams.length > 0) {
252
- const formType = formParams
253
- .map((p) => {
254
- const optional = p.required ? '' : '?';
255
- return `${p.name}${optional}: ${p.type}`;
256
- })
257
- .join(', ');
258
- params.push(`data: { ${formType} }`);
279
+ if (isJavaScript) {
280
+ params.push('data');
281
+ }
282
+ else {
283
+ const formType = formParams
284
+ .map((p) => {
285
+ const optional = p.required ? '' : '?';
286
+ return `${p.name}${optional}: ${p.type}`;
287
+ })
288
+ .join(', ');
289
+ params.push(`data: { ${formType} }`);
290
+ }
259
291
  }
260
292
  // 添加可选的config参数
261
- params.push('config?: any');
293
+ params.push(isJavaScript ? 'config' : 'config?: any');
262
294
  return params.join(', ');
263
295
  }
264
296
  /**
@@ -363,7 +395,7 @@ class CodeGenerator {
363
395
  * @param api API接口信息
364
396
  * @returns 请求配置代码
365
397
  */
366
- generateRequestConfig(api) {
398
+ generateRequestConfig(api, includeMethod = false) {
367
399
  const config = [];
368
400
  // URL处理
369
401
  let url = api.path;
@@ -397,6 +429,10 @@ class CodeGenerator {
397
429
  else if (formParams.length > 0) {
398
430
  config.push('data');
399
431
  }
432
+ // 在通用请求风格下添加 method 字段
433
+ if (includeMethod) {
434
+ config.push(`method: '${api.method}'`);
435
+ }
400
436
  // 添加config参数合并
401
437
  config.push('...config');
402
438
  return `{\n ${config.join(',\n ')}\n }`;
@@ -407,8 +443,11 @@ class CodeGenerator {
407
443
  */
408
444
  async generateIndexFile(groupedApis) {
409
445
  const exports = [];
410
- // 导出类型
411
- exports.push("export * from './types';");
446
+ // 导出类型(仅在 TypeScript 且生成类型文件时)
447
+ if (this.config.generator === 'typescript' &&
448
+ this.config.options?.generateModels !== false) {
449
+ exports.push("export * from './types';");
450
+ }
412
451
  if (this.config.groupByTags) {
413
452
  // 按标签导出
414
453
  for (const tag of groupedApis.keys()) {
@@ -423,13 +462,14 @@ class CodeGenerator {
423
462
  const content = [
424
463
  '/**',
425
464
  ' * API 入口文件',
426
- ' * 此文件由 swagger2api 自动生成,请勿手动修改',
465
+ ' * 此文件由 swagger2api-v3 自动生成,请勿手动修改',
427
466
  ' */',
428
467
  '',
429
468
  ...exports,
430
469
  ''
431
470
  ].join('\n');
432
- const filePath = path.join(this.config.output, 'index.ts');
471
+ const ext = this.config.generator === 'javascript' ? 'js' : 'ts';
472
+ const filePath = path.join(this.config.output, `index.${ext}`);
433
473
  (0, utils_1.writeFile)(filePath, content);
434
474
  }
435
475
  /**
@@ -76,6 +76,8 @@ class SwaggerParser {
76
76
  const methodSuffix = method.charAt(0).toUpperCase() + method.slice(1).toLowerCase();
77
77
  functionName = functionName + methodSuffix;
78
78
  }
79
+ // 应用前缀忽略规则
80
+ functionName = (0, utils_1.stripMethodNamePrefixes)(functionName, this.config.methodNameIgnorePrefix);
79
81
  // 获取响应类型
80
82
  const responseType = (0, utils_1.getResponseType)(operation.responses);
81
83
  // 获取请求体类型
package/dist/index.js CHANGED
@@ -48,7 +48,12 @@ const utils_1 = require("./utils");
48
48
  */
49
49
  class Swagger2API {
50
50
  constructor(config) {
51
- this.config = config;
51
+ // 规范化配置:设置默认值
52
+ this.config = {
53
+ ...config,
54
+ // 默认请求风格为 generic
55
+ requestStyle: config.requestStyle ?? 'generic'
56
+ };
52
57
  }
53
58
  /**
54
59
  * 生成API接口文件
@@ -95,8 +100,10 @@ class Swagger2API {
95
100
  if (!this.config.output) {
96
101
  errors.push('output 配置项不能为空');
97
102
  }
98
- if (this.config.generator !== 'typescript') {
99
- errors.push('目前只支持 typescript 生成器');
103
+ // 支持 typescript 与 javascript 两种生成器
104
+ if (this.config.generator !== 'typescript' &&
105
+ this.config.generator !== 'javascript') {
106
+ errors.push('目前只支持 typescript 或 javascript 生成器');
100
107
  }
101
108
  if (errors.length > 0) {
102
109
  console.error('❌ 配置验证失败:');
@@ -10,13 +10,15 @@ export interface SwaggerConfig {
10
10
  /** 输出目录 */
11
11
  output: string;
12
12
  /** 生成器类型 */
13
- generator: 'typescript';
13
+ generator: 'typescript' | 'javascript';
14
14
  /** 按 tags 分组生成文件 */
15
15
  groupByTags: boolean;
16
16
  /** 是否覆盖更新,默认为true。为true时会先删除输出目录下的所有文件 */
17
17
  overwrite?: boolean;
18
18
  /** 接口路径公共前缀,默认为空字符串 */
19
19
  prefix?: string;
20
+ /** 请求调用风格:'method' 使用 request.get/post;'generic' 使用 request({ method }) */
21
+ requestStyle?: 'method' | 'generic';
20
22
  /** 标签分组配置 */
21
23
  tagGrouping?: TagGroupingConfig;
22
24
  /** 生成选项 */
@@ -27,6 +29,8 @@ export interface SwaggerConfig {
27
29
  importTemplate?: string;
28
30
  /** 格式化代码命令 */
29
31
  lint?: string;
32
+ /** 生成方法名时需要忽略的前缀数组,如 ['api', 'auth'] */
33
+ methodNameIgnorePrefix?: string[];
30
34
  }
31
35
  /**
32
36
  * 标签分组配置
@@ -27,6 +27,13 @@ export declare function toPascalCase(str: string): string;
27
27
  * @returns camelCase字符串
28
28
  */
29
29
  export declare function toCamelCase(str: string): string;
30
+ /**
31
+ * 从方法名中移除指定的前缀
32
+ * @param methodName 方法名
33
+ * @param prefixes 需要移除的前缀数组
34
+ * @returns 移除前缀后的方法名
35
+ */
36
+ export declare function stripMethodNamePrefixes(methodName: string, prefixes?: string[]): string;
30
37
  /**
31
38
  * 将Swagger类型转换为TypeScript类型
32
39
  * @param schema Swagger模式
@@ -40,6 +40,7 @@ exports.pathToFunctionName = pathToFunctionName;
40
40
  exports.toKebabCase = toKebabCase;
41
41
  exports.toPascalCase = toPascalCase;
42
42
  exports.toCamelCase = toCamelCase;
43
+ exports.stripMethodNamePrefixes = stripMethodNamePrefixes;
43
44
  exports.swaggerTypeToTsType = swaggerTypeToTsType;
44
45
  exports.generateParameterTypes = generateParameterTypes;
45
46
  exports.ensureDirectoryExists = ensureDirectoryExists;
@@ -111,6 +112,44 @@ function toCamelCase(str) {
111
112
  const pascalCase = toPascalCase(str);
112
113
  return pascalCase.charAt(0).toLowerCase() + pascalCase.slice(1);
113
114
  }
115
+ /**
116
+ * 从方法名中移除指定的前缀
117
+ * @param methodName 方法名
118
+ * @param prefixes 需要移除的前缀数组
119
+ * @returns 移除前缀后的方法名
120
+ */
121
+ function stripMethodNamePrefixes(methodName, prefixes) {
122
+ if (!prefixes || prefixes.length === 0) {
123
+ return methodName;
124
+ }
125
+ let result = methodName;
126
+ // 循环移除所有匹配的前缀,直到没有前缀可以移除
127
+ let changed = true;
128
+ while (changed) {
129
+ changed = false;
130
+ for (const prefix of prefixes) {
131
+ if (!prefix)
132
+ continue;
133
+ // 将前缀转换为小驼峰格式进行匹配
134
+ const camelPrefix = toCamelCase(prefix);
135
+ // 检查方法名是否以该前缀开头(不区分大小写)
136
+ const lowerMethodName = result.toLowerCase();
137
+ const lowerPrefix = camelPrefix.toLowerCase();
138
+ if (lowerMethodName.startsWith(lowerPrefix)) {
139
+ // 移除前缀,保持后续字符的大小写
140
+ const remaining = result.substring(camelPrefix.length);
141
+ // 如果移除前缀后还有内容,则更新结果
142
+ if (remaining.length > 0) {
143
+ // 确保首字母小写
144
+ result = remaining.charAt(0).toLowerCase() + remaining.slice(1);
145
+ changed = true;
146
+ break; // 重新开始检查
147
+ }
148
+ }
149
+ }
150
+ }
151
+ return result;
152
+ }
114
153
  /**
115
154
  * 将Swagger类型转换为TypeScript类型
116
155
  * @param schema Swagger模式
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "swagger2api-v3",
3
- "version": "1.0.10",
4
- "description": " Swagger(OAS3.0) 文档生成 TypeScript API 接口的命令行工具",
3
+ "version": "1.1.1",
4
+ "description": "A command-line tool for generating TypeScript API interfaces from Swagger (OAS 3.0) documentation",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "type": "commonjs",
@@ -65,4 +65,4 @@
65
65
  "axios": "^1.11.0",
66
66
  "commander": "^12.0.0"
67
67
  }
68
- }
68
+ }
@@ -1,17 +0,0 @@
1
- import { type AxiosRequestConfig, type AxiosResponse, type CreateAxiosDefaults, type InternalAxiosRequestConfig } from 'axios';
2
- export interface IReqBoay<T> {
3
- code: number;
4
- data: T;
5
- message: string;
6
- }
7
- declare class HttpRequest {
8
- private instance;
9
- constructor(options: CreateAxiosDefaults);
10
- requestInterceptors(config: InternalAxiosRequestConfig): InternalAxiosRequestConfig<any>;
11
- responseInterceptors(response: AxiosResponse): any;
12
- private request;
13
- get<T = any>({ ...config }: AxiosRequestConfig): Promise<IReqBoay<T>>;
14
- post<T = any>({ ...config }: AxiosRequestConfig): Promise<IReqBoay<T>>;
15
- }
16
- export declare const request: HttpRequest;
17
- export {};
@@ -1,49 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.request = void 0;
7
- const axios_1 = __importDefault(require("axios"));
8
- class HttpRequest {
9
- constructor(options) {
10
- this.instance = axios_1.default.create(options);
11
- this.instance.interceptors.request.use(this.requestInterceptors);
12
- this.instance.interceptors.response.use(this.responseInterceptors);
13
- }
14
- requestInterceptors(config) {
15
- return config;
16
- }
17
- responseInterceptors(response) {
18
- return response.data;
19
- }
20
- request(config) {
21
- return new Promise((resolve, reject) => {
22
- this.instance
23
- .request(config)
24
- .then((res) => {
25
- console.log('request:', res);
26
- resolve(res);
27
- })
28
- .catch((err) => {
29
- reject(err.message);
30
- });
31
- });
32
- }
33
- get({ ...config }) {
34
- return this.request({
35
- ...config,
36
- method: 'GET'
37
- });
38
- }
39
- post({ ...config }) {
40
- return this.request({
41
- ...config,
42
- method: 'POST'
43
- });
44
- }
45
- }
46
- exports.request = new HttpRequest({
47
- baseURL: ``,
48
- timeout: 20000
49
- });