jszy-swagger-doc-generator 1.1.0

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/OpenAPI.md ADDED
@@ -0,0 +1,298 @@
1
+ ## 1. 后端 OpenAPI 文档要求
2
+
3
+ 为了能够正确生成前端 SDK,后端需要提供符合特定格式的 OpenAPI 3.1 规范文档。以下是具体要求:
4
+
5
+ ### 1.1 OpenAPI 文档结构要求
6
+
7
+ 后端 API 文档必须遵循 OpenAPI 3.1 规范,并包含以下必要组成部分:
8
+
9
+ #### 1.1.1 文档根结构
10
+ ```json
11
+ {
12
+ "openapi": "3.1.0-rc0",
13
+ "info": {
14
+ "title": "服务名称",
15
+ "description": "服务描述",
16
+ "version": "API 版本"
17
+ },
18
+ "servers": [
19
+ {
20
+ "url": "服务地址",
21
+ "description": "环境描述",
22
+ "x-environment": "环境标识"
23
+ }
24
+ ],
25
+ "components": {
26
+ "schemas": {
27
+ // 数据模型定义
28
+ }
29
+ },
30
+ "paths": {
31
+ // API 端点定义
32
+ }
33
+ }
34
+ ```
35
+
36
+ #### 1.1.2 数据模型定义 (components.schemas)
37
+
38
+ 数据模型必须按照以下格式定义:
39
+
40
+ 1. **对象类型**:
41
+ ```json
42
+ "ModelName": {
43
+ "type": "object",
44
+ "properties": {
45
+ "field_name": {
46
+ "type": "数据类型", // string, integer, number, boolean, array 等
47
+ "format": "格式说明", // 如 timestamp, date 等
48
+ "pattern": "正则表达式", // 如 "^\\d{4}-\\d{2}-\\d{2}$"
49
+ "title": "字段标题" // 作为 TypeScript 注释
50
+ }
51
+ },
52
+ "required": ["必需字段列表"]
53
+ }
54
+ ```
55
+
56
+ 2. **枚举类型**:
57
+ ```json
58
+ "EnumName": {
59
+ "type": "string",
60
+ "enum": ["值1", "值2", "值3"]
61
+ }
62
+ ```
63
+
64
+ 3. **联合类型 (oneOf)**:
65
+ ```json
66
+ "UnionType": {
67
+ "oneOf": [
68
+ {
69
+ "type": "null"
70
+ },
71
+ {
72
+ "$ref": "#/components/schemas/OtherModel"
73
+ }
74
+ ]
75
+ }
76
+ ```
77
+
78
+ 4. **数组类型**:
79
+ ```json
80
+ "ArrayField": {
81
+ "type": "array",
82
+ "items": {
83
+ "$ref": "#/components/schemas/ItemType"
84
+ }
85
+ }
86
+ ```
87
+
88
+ 5. **可空类型处理**:
89
+ - 使用数组形式 `"type": ["string", "null"]`
90
+ - 或使用 `oneOf` 包含 null 类型
91
+
92
+ #### 1.1.3 API 端点定义 (paths)
93
+
94
+ API 端点必须按照以下格式定义:
95
+
96
+ 1. **路径和操作**:
97
+ ```json
98
+ "/api/path/{paramName}": {
99
+ "get|post|put|delete": {
100
+ "summary": "API 摘要", // 作为 TypeScript 注释
101
+ "description": "API 描述", // 作为 TypeScript 注释
102
+ "tags": ["分组名称"], // 用于 API 分组
103
+ "operationId": "apiMethodName", // 生成的 API 方法名
104
+ "parameters": [...], // 请求参数
105
+ "requestBody": {...}, // 请求体 (POST/PUT)
106
+ "responses": {...} // 响应定义
107
+ }
108
+ }
109
+ ```
110
+
111
+ 2. **参数定义**:
112
+ ```json
113
+ "parameters": [
114
+ {
115
+ "name": "参数名",
116
+ "in": "path|query|header|cookie", // 参数位置
117
+ "schema": {
118
+ "type": "参数类型",
119
+ "format": "格式"
120
+ },
121
+ "required": true|false, // 是否必需
122
+ "description": "参数描述"
123
+ }
124
+ ]
125
+ ```
126
+
127
+ 3. **请求体定义**:
128
+ ```json
129
+ "requestBody": {
130
+ "description": "请求体描述",
131
+ "content": {
132
+ "application/json": {
133
+ "schema": {
134
+ "$ref": "#/components/schemas/RequestBodyModel"
135
+ }
136
+ }
137
+ },
138
+ "required": true
139
+ }
140
+ ```
141
+
142
+ 4. **响应定义**:
143
+ ```json
144
+ "responses": {
145
+ "200": {
146
+ "description": "成功响应描述",
147
+ "content": {
148
+ "application/json": {
149
+ "schema": {
150
+ "$ref": "#/components/schemas/ResponseModel"
151
+ }
152
+ }
153
+ }
154
+ },
155
+ "201": {
156
+ // 创建成功响应
157
+ },
158
+ "204": {
159
+ "description": "No Content" // 无返回值
160
+ },
161
+ "500": {
162
+ // 错误响应
163
+ }
164
+ }
165
+ ```
166
+
167
+ ### 1.2 特殊类型处理
168
+
169
+ #### 1.2.1 引用处理
170
+ - 使用 `$ref` 引用定义在 `components.schemas` 中的模型
171
+ - 格式:`"$ref": "#/components/schemas/ModelName"`
172
+
173
+ #### 1.2.2 类型映射
174
+ - `integer` → TypeScript `number`
175
+ - `string` → TypeScript `string`
176
+ - `boolean` → TypeScript `boolean`
177
+ - `array` → TypeScript `Type[]`
178
+ - `null` → TypeScript 可空类型
179
+
180
+ #### 1.2.3 字段特性
181
+ - 使用 `title` 属性作为字段注释
182
+ - 使用 `description` 属性作为详细说明
183
+ - 使用 `pattern` 属性定义字符串格式验证
184
+
185
+ ### 1.3 生成器特殊处理
186
+
187
+ 当前 SDK 生成器会进行以下特殊处理:
188
+
189
+ 1. **路径参数转换**:将路径中的 `{paramName}` 转换为 `{paramName}` 并进行驼峰命名
190
+ 2. **参数展开**:对于可选参数,会将对象参数展开为多个独立参数
191
+ 3. **默认值处理**:为可空字段生成适当的默认值处理逻辑
192
+ 4. **导入检测**:自动分析类型依赖关系并生成相应的导入语句
193
+
194
+ ### 1.4 完整示例
195
+
196
+ 以下是一个完整的 API 端点定义示例:
197
+
198
+ ```json
199
+ {
200
+ "openapi": "3.1.0-rc0",
201
+ "info": {
202
+ "title": "Example Service",
203
+ "version": "1.0.0"
204
+ },
205
+ "paths": {
206
+ "/users/{userId}": {
207
+ "get": {
208
+ "summary": "获取用户信息",
209
+ "description": "根据用户ID获取用户详细信息",
210
+ "tags": ["User"],
211
+ "operationId": "getUserById",
212
+ "parameters": [
213
+ {
214
+ "name": "userId",
215
+ "in": "path",
216
+ "required": true,
217
+ "schema": {
218
+ "type": "integer"
219
+ },
220
+ "description": "用户ID"
221
+ },
222
+ {
223
+ "name": "includeProfile",
224
+ "in": "query",
225
+ "required": false,
226
+ "schema": {
227
+ "type": "boolean",
228
+ "default": false
229
+ },
230
+ "description": "是否包含用户档案信息"
231
+ }
232
+ ],
233
+ "responses": {
234
+ "200": {
235
+ "description": "成功获取用户信息",
236
+ "content": {
237
+ "application/json": {
238
+ "schema": {
239
+ "$ref": "#/components/schemas/User"
240
+ }
241
+ }
242
+ }
243
+ }
244
+ }
245
+ }
246
+ }
247
+ },
248
+ "components": {
249
+ "schemas": {
250
+ "User": {
251
+ "type": "object",
252
+ "properties": {
253
+ "id": {
254
+ "type": "integer",
255
+ "title": "用户ID"
256
+ },
257
+ "name": {
258
+ "type": "string",
259
+ "title": "用户名"
260
+ },
261
+ "email": {
262
+ "type": ["string", "null"],
263
+ "title": "邮箱地址"
264
+ },
265
+ "profile": {
266
+ "oneOf": [
267
+ {
268
+ "type": "null"
269
+ },
270
+ {
271
+ "$ref": "#/components/schemas/UserProfile"
272
+ }
273
+ ],
274
+ "title": "用户档案"
275
+ }
276
+ },
277
+ "required": ["id", "name"]
278
+ },
279
+ "UserProfile": {
280
+ "type": "object",
281
+ "properties": {
282
+ "age": {
283
+ "type": "integer",
284
+ "title": "年龄"
285
+ },
286
+ "bio": {
287
+ "type": "string",
288
+ "title": "个人简介"
289
+ }
290
+ },
291
+ "required": ["age", "bio"]
292
+ }
293
+ }
294
+ }
295
+ }
296
+ ```
297
+
298
+ 这个自动化 SDK 生成系统通过解析 OpenAPI 规范,使用模板引擎生成类型安全的 TypeScript 代码,大大减少了手动编写 API 客户端的工作量,同时确保了前端与后端 API 的一致性。
package/README.md ADDED
@@ -0,0 +1,193 @@
1
+ # Swagger Documentation Generator
2
+
3
+ A tool to generate frontend documentation, TypeScript types, and React Hooks from Swagger/OpenAPI JSON files.
4
+
5
+ ## Installation
6
+
7
+ You can install this package globally to use it as a CLI tool:
8
+
9
+ ```bash
10
+ npm install -g swagger-doc-generator
11
+ ```
12
+
13
+ Or you can use it without installing with npx:
14
+
15
+ ```bash
16
+ npx swagger-doc-generator
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ ### Command Line Interface
22
+
23
+ The tool can be used from the command line to generate documentation, TypeScript types, or React Hooks from a Swagger JSON file:
24
+
25
+ #### Generate Documentation (Default)
26
+ ```bash
27
+ # Generate documentation from a URL (output to ./generated/docs/api-documentation.md by default)
28
+ swagger-doc-generator --url https://petstore.swagger.io/v2/swagger.json
29
+
30
+ # Generate documentation from a local file (output to ./generated/docs/api-documentation.md by default)
31
+ swagger-doc-generator --input ./swagger.json
32
+
33
+ # Generate documentation and specify output file
34
+ swagger-doc-generator --url https://petstore.swagger.io/v2/swagger.json --output ./docs/api-documentation.md
35
+ ```
36
+
37
+ #### Generate TypeScript Types
38
+ ```bash
39
+ # Generate TypeScript types from a local file (output to ./generated/types/ by default)
40
+ swagger-doc-generator --input ./swagger.json --generate-types
41
+
42
+ # Generate TypeScript types with custom output directory
43
+ swagger-doc-generator --input ./swagger.json --generate-types --types-output ./src/types
44
+ ```
45
+
46
+ #### Generate React Hooks
47
+ ```bash
48
+ # Generate React hooks from a local file (output to ./generated/hooks/ by default)
49
+ swagger-doc-generator --input ./swagger.json --generate-hooks
50
+
51
+ # Generate React hooks with custom output directory
52
+ swagger-doc-generator --input ./swagger.json --generate-hooks --hooks-output ./src/hooks
53
+
54
+ # Generate both types and hooks (output to ./generated/ directories by default)
55
+ swagger-doc-generator --input ./swagger.json --generate-types --generate-hooks
56
+ ```
57
+
58
+ #### Default Output Location
59
+ By default, all generated content is placed in the `./generated` directory to keep your project clean and separate generated content from source code:
60
+
61
+ - Documentation: `./generated/docs/api-documentation.md`
62
+ - TypeScript types: `./generated/types/`
63
+ - React hooks: `./generated/hooks/`
64
+
65
+ ### Programmatic Usage
66
+
67
+ You can also use this package programmatically in your JavaScript/TypeScript code:
68
+
69
+ ```javascript
70
+ const { SwaggerDocGenerator } = require('swagger-doc-generator');
71
+
72
+ async function generate() {
73
+ const generator = new SwaggerDocGenerator();
74
+
75
+ // Load from URL
76
+ const swaggerDoc = await generator.fetchSwaggerJSON('https://api.example.com/swagger.json');
77
+
78
+ // Or load from file
79
+ // const swaggerDoc = generator.loadSwaggerFromFile('./swagger.json');
80
+
81
+ // Generate documentation
82
+ const documentation = generator.generateDocumentation(swaggerDoc);
83
+ generator.saveDocumentationToFile(documentation, './docs/api-documentation.md');
84
+
85
+ // Generate TypeScript types
86
+ const types = generator.generateTypeDefinitions(swaggerDoc);
87
+ generator.saveTypesToFile(types, './types/api-types.ts');
88
+
89
+ // Generate React hooks organized by tags
90
+ const hooksByTag = generator.generateReactHooks(swaggerDoc);
91
+ generator.saveHooksByTag(hooksByTag, './hooks');
92
+ }
93
+
94
+ generate().catch(console.error);
95
+ ```
96
+
97
+ ## Options
98
+
99
+ - `--url, -u`: URL to the Swagger JSON file
100
+ - `--input, -i`: Path to the local Swagger JSON file
101
+ - `--output, -o`: Output path for the generated documentation (default: ./docs/api-documentation.md)
102
+ - `--generate-types`: Generate TypeScript type definitions
103
+ - `--generate-hooks`: Generate React hooks
104
+ - `--types-output`: Output directory for TypeScript types (default: ./types)
105
+ - `--hooks-output`: Output directory for React hooks (default: ./hooks)
106
+ - `--help`: Show help information
107
+
108
+ ## Features
109
+
110
+ - Fetches Swagger JSON from a URL
111
+ - Loads Swagger JSON from a local file
112
+ - Generates comprehensive documentation in Markdown format
113
+ - Generates TypeScript type definitions from schemas
114
+ - Generates React Hooks for API endpoints, organized by tags
115
+ - Supports both Swagger 2.0 and OpenAPI 3.x specifications
116
+ - Handles API endpoints, parameters, request/response bodies, and responses
117
+ - Creates output directory if it doesn't exist
118
+ - Properly handles path and query parameters
119
+ - Generates proper TypeScript interfaces and type aliases
120
+ - Organizes generated code by API tags
121
+
122
+ ## Example Output
123
+
124
+ ### Generated TypeScript Types
125
+ ```typescript
126
+ // Auto-generated TypeScript types from Swagger schema
127
+
128
+ export interface User {
129
+ /** 用户ID */
130
+ id: number;
131
+ /** 用户名 */
132
+ name: string;
133
+ /** 用户邮箱 */
134
+ email: string;
135
+ /** 头像URL */
136
+ avatar?: string;
137
+ }
138
+
139
+ export interface UserConfig {
140
+ /** 用户ID */
141
+ userId: number;
142
+ /** 主题设置 */
143
+ theme: "light" | "dark";
144
+ /** 语言设置 */
145
+ language: string;
146
+ /** 通知设置 */
147
+ notifications: NotificationSettings;
148
+ /** 个性化设置 */
149
+ preferences: Preferences;
150
+ }
151
+ ```
152
+
153
+ ### Generated React Hooks
154
+ ```typescript
155
+ // Users API Hooks
156
+
157
+ export interface UserController_queryUserInfoParams {
158
+ id?: number;
159
+ name?: string;
160
+ }
161
+
162
+ export const useUserController_queryUserInfo = () => {
163
+ const apiCall = async (params: UserController_queryUserInfoParams) => {
164
+ const path = `${process.env.REACT_APP_API_BASE_URL || ''}/api/queryUserInfo`;
165
+ const queryParams = new URLSearchParams();
166
+ if (params.id) queryParams.append('id', params.id.toString());
167
+ if (params.name) queryParams.append('name', params.name.toString());
168
+ const queryString = queryParams.toString();
169
+ const url = `${path}${queryString ? '?' + queryString : ''}`;
170
+ const options: RequestInit = {
171
+ method: 'GET',
172
+ };
173
+
174
+ const result = await fetch(url, options);
175
+ return result.json() as Promise<User[]>;
176
+ };
177
+
178
+ return { userController_queryUserInfo: apiCall };
179
+ };
180
+ ```
181
+
182
+ ## Development
183
+
184
+ To run this project locally:
185
+
186
+ 1. Clone the repository
187
+ 2. Run `pnpm install` to install dependencies
188
+ 3. Run `pnpm run build` to compile TypeScript
189
+ 4. Run `pnpm run test` to run tests
190
+
191
+ ## License
192
+
193
+ MIT
@@ -0,0 +1,152 @@
1
+ import { SwaggerDocGenerator, SwaggerDoc } from '../src/index';
2
+ import axios from 'axios';
3
+ import * as fs from 'fs';
4
+
5
+ // Mock swagger doc for testing
6
+ const mockSwaggerDoc: SwaggerDoc = {
7
+ swagger: "2.0",
8
+ info: {
9
+ title: "Test API",
10
+ version: "1.0.0",
11
+ description: "A test API"
12
+ },
13
+ paths: {
14
+ "/test": {
15
+ get: {
16
+ summary: "Get test",
17
+ description: "Returns test data",
18
+ responses: {
19
+ "200": {
20
+ description: "Successful response",
21
+ content: {
22
+ "application/json": {
23
+ schema: {
24
+ type: "object",
25
+ properties: {
26
+ message: {
27
+ type: "string"
28
+ }
29
+ }
30
+ }
31
+ }
32
+ }
33
+ }
34
+ }
35
+ }
36
+ }
37
+ }
38
+ };
39
+
40
+ // Create a mock for the fs module
41
+ jest.mock('fs');
42
+
43
+ describe('SwaggerDocGenerator', () => {
44
+ let generator: SwaggerDocGenerator;
45
+
46
+ beforeEach(() => {
47
+ generator = new SwaggerDocGenerator();
48
+ // Reset all mocks before each test
49
+ jest.clearAllMocks();
50
+ });
51
+
52
+ describe('fetchSwaggerJSON', () => {
53
+ it('should fetch swagger JSON from a URL', async () => {
54
+ const mockUrl = 'https://api.example.com/swagger.json';
55
+
56
+ // Mock the axios response
57
+ jest.spyOn(axios, 'get').mockResolvedValueOnce({ data: mockSwaggerDoc });
58
+
59
+ const result = await generator.fetchSwaggerJSON(mockUrl);
60
+
61
+ expect(result).toEqual(mockSwaggerDoc);
62
+ expect(axios.get).toHaveBeenCalledWith(mockUrl);
63
+ });
64
+
65
+ it('should handle errors when fetching swagger JSON', async () => {
66
+ const mockUrl = 'https://api.example.com/invalid.json';
67
+
68
+ // Mock the axios response to reject
69
+ jest.spyOn(axios, 'get').mockRejectedValueOnce(new Error('Network error'));
70
+
71
+ await expect(generator.fetchSwaggerJSON(mockUrl)).rejects.toThrow(
72
+ `Failed to fetch Swagger JSON from ${mockUrl}: Network error`
73
+ );
74
+ });
75
+ });
76
+
77
+ describe('loadSwaggerFromFile', () => {
78
+ it('should load swagger JSON from a local file', () => {
79
+ const mockFilePath = 'test-swagger.json';
80
+
81
+ // Mock the file system
82
+ (fs.readFileSync as jest.Mock).mockReturnValue(JSON.stringify(mockSwaggerDoc));
83
+
84
+ const result = generator.loadSwaggerFromFile(mockFilePath);
85
+
86
+ expect(result).toEqual(mockSwaggerDoc);
87
+ expect(fs.readFileSync).toHaveBeenCalledWith(mockFilePath, 'utf8');
88
+ });
89
+
90
+ it('should handle errors when loading swagger JSON from file', () => {
91
+ const mockFilePath = 'nonexistent.json';
92
+
93
+ // Mock the file system to throw an error
94
+ (fs.readFileSync as jest.Mock).mockImplementation(() => {
95
+ throw new Error('File not found');
96
+ });
97
+
98
+ expect(() => generator.loadSwaggerFromFile(mockFilePath)).toThrow(
99
+ `Failed to load Swagger JSON from ${mockFilePath}: File not found`
100
+ );
101
+ });
102
+ });
103
+
104
+ describe('generateDocumentation', () => {
105
+ it('should generate documentation from swagger doc', () => {
106
+ const result = generator.generateDocumentation(mockSwaggerDoc);
107
+
108
+ expect(result).toContain('# Test API');
109
+ expect(result).toContain('A test API v1.0.0');
110
+ expect(result).toContain('## API Endpoints');
111
+ expect(result).toContain('### GET /test');
112
+ expect(result).toContain('**Summary:** Get test');
113
+ expect(result).toContain('**Description:** Returns test data');
114
+ expect(result).toContain('**Responses:**');
115
+ expect(result).toContain('- **200**: Successful response');
116
+ });
117
+ });
118
+
119
+ describe('saveDocumentationToFile', () => {
120
+ it('should save documentation to a file', () => {
121
+ const mockDocumentation = '# Test Documentation';
122
+ const mockOutputPath = './docs/test.md';
123
+
124
+ // Mock the file system methods
125
+ (fs.existsSync as jest.Mock).mockReturnValue(true);
126
+ (fs.writeFileSync as jest.Mock).mockImplementation();
127
+ (fs.mkdirSync as jest.Mock).mockImplementation();
128
+
129
+ generator.saveDocumentationToFile(mockDocumentation, mockOutputPath);
130
+
131
+ expect(fs.existsSync).toHaveBeenCalledWith('./docs');
132
+ expect(fs.writeFileSync).toHaveBeenCalledWith(mockOutputPath, mockDocumentation, 'utf8');
133
+ expect(fs.mkdirSync).not.toHaveBeenCalled(); // Because directory exists
134
+ });
135
+
136
+ it('should create directory if it does not exist', () => {
137
+ const mockDocumentation = '# Test Documentation';
138
+ const mockOutputPath = './newdir/test.md';
139
+
140
+ // Mock the file system methods
141
+ (fs.existsSync as jest.Mock).mockReturnValue(false);
142
+ (fs.writeFileSync as jest.Mock).mockImplementation();
143
+ (fs.mkdirSync as jest.Mock).mockImplementation();
144
+
145
+ generator.saveDocumentationToFile(mockDocumentation, mockOutputPath);
146
+
147
+ expect(fs.existsSync).toHaveBeenCalledWith('./newdir');
148
+ expect(fs.mkdirSync).toHaveBeenCalledWith('./newdir', { recursive: true });
149
+ expect(fs.writeFileSync).toHaveBeenCalledWith(mockOutputPath, mockDocumentation, 'utf8');
150
+ });
151
+ });
152
+ });
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};