@paulpugovkin/api-docs-axios-ts-generator 1.0.5
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 +310 -0
- package/bin/cli.js +6 -0
- package/package.json +53 -0
- package/src/clean-generated-folder/cleanGeneratedFolder.js +21 -0
- package/src/collect-genereted-files/collectGeneratedFiles.js +33 -0
- package/src/config/defaultConfig.ts +33 -0
- package/src/config/loadConfig.ts +112 -0
- package/src/config/validateConfig.ts +58 -0
- package/src/generate-class/generateClass.js +35 -0
- package/src/generate-index-file-with-public-api/generateIndexFileWithPublicApi.js +45 -0
- package/src/generate-interface/generateInterface.js +75 -0
- package/src/generate-js-doc/generateJSDoc.js +136 -0
- package/src/generate-main-index-file/generateMainIndexFile.js +21 -0
- package/src/generate-method/generateMethod.js +264 -0
- package/src/generators/axiosConfigGenerator.js +79 -0
- package/src/index.js +129 -0
- package/src/index.min.js +2 -0
- package/src/map-type/mapType.js +19 -0
- package/src/parse-and-generate/parseAndGenerate.js +189 -0
- package/src/resolve-type/resolveType.js +54 -0
- package/src/scripts/createConfigFile.js +59 -0
- package/src/templates/default.config.template.js +204 -0
- package/src/types/config.types.ts +183 -0
- package/src/types/index.d.ts +22 -0
- package/src/update-api-docs-json/updateApiDocsJson.js +33 -0
package/README.md
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
# API Docs Axios TS Generator
|
|
2
|
+
|
|
3
|
+
Generate TypeScript interfaces and axios classes from OpenAPI (Swagger) specifications.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- π Generate TypeScript interfaces from OpenAPI schemas
|
|
8
|
+
- π¦ Generate axios classes with API methods
|
|
9
|
+
- π§ Flexible configuration via TypeScript file
|
|
10
|
+
- π·οΈ Tag filtering (include/exclude)
|
|
11
|
+
- π Generation modes: single class or multiple by tags
|
|
12
|
+
- βοΈ Automatic axios configuration generation
|
|
13
|
+
- π― Support for spreading user axios configurations
|
|
14
|
+
- π JSDoc comments for methods
|
|
15
|
+
- π Backward compatibility with CLI arguments
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install -g @paulpugovkin/api-docs-axios-ts-generator
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
### 1. Install as project dependency
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install --save-dev @paulpugovkin/api-docs-axios-ts-generator
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Add script to your `package.json`:
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"scripts": {
|
|
36
|
+
"generate-services": "npx @paulpugovkin/api-docs-axios-ts-generator --config api-docs-generator.config.js"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Now you can run generation:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm run generate-services
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
### 2. Use as global tool
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# Install as global tool
|
|
52
|
+
npm install -g @paulpugovkin/api-docs-axios-ts-generator
|
|
53
|
+
|
|
54
|
+
# Run generation
|
|
55
|
+
api-docs-generator --config api-docs-generator.config.js
|
|
56
|
+
|
|
57
|
+
# Or via npx
|
|
58
|
+
npx @paulpugovkin/api-docs-axios-ts-generator --config api-docs-generator.config.js
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 3. Create configuration file
|
|
62
|
+
|
|
63
|
+
A default configuration file `api-docs-generator.config.js` is automatically created in your project root during installation. You can modify this file to suit your needs.
|
|
64
|
+
|
|
65
|
+
If you need to create it manually, create `api-docs-generator.config.js` file in the root of your project:
|
|
66
|
+
|
|
67
|
+
```javascript
|
|
68
|
+
module.exports = {
|
|
69
|
+
// URL to fetch OpenAPI specification from
|
|
70
|
+
apiDocsUrl: 'https://api.example.com/v3/api-docs.json',
|
|
71
|
+
|
|
72
|
+
// Output directory for generated files
|
|
73
|
+
outputDir: './src/api',
|
|
74
|
+
|
|
75
|
+
// Tag filtering options
|
|
76
|
+
tags: {
|
|
77
|
+
include: ['api_tag_users', 'api_tag_products'],
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
// Grouping mode for API methods
|
|
81
|
+
groupBy: 'tag',
|
|
82
|
+
|
|
83
|
+
// Class generation mode
|
|
84
|
+
classMode: 'multiple',
|
|
85
|
+
|
|
86
|
+
// Generation options
|
|
87
|
+
options: {
|
|
88
|
+
generateAxiosConfig: true,
|
|
89
|
+
allowAxiosConfigSpread: true,
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
// Axios configuration settings
|
|
93
|
+
axios: {
|
|
94
|
+
baseURL: 'https://api.example.com',
|
|
95
|
+
timeout: 30000,
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### 4. Use generated API
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import { UsersApi, ProductsApi } from './src/api';
|
|
104
|
+
|
|
105
|
+
// Use API
|
|
106
|
+
const usersApi = new UsersApi();
|
|
107
|
+
const productsApi = new ProductsApi();
|
|
108
|
+
|
|
109
|
+
// Get user
|
|
110
|
+
const user = await usersApi.getUserById(123);
|
|
111
|
+
|
|
112
|
+
// With custom axios configuration
|
|
113
|
+
const user = await usersApi.getUserById(123, {
|
|
114
|
+
headers: {
|
|
115
|
+
'X-Custom-Header': 'value',
|
|
116
|
+
},
|
|
117
|
+
timeout: 5000,
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Configuration
|
|
122
|
+
|
|
123
|
+
### Main parameters
|
|
124
|
+
|
|
125
|
+
| Parameter | Type | Required | Description |
|
|
126
|
+
|-----------|------|--------------|------------|
|
|
127
|
+
| `apiDocsUrl` | `string` | No* | URL to fetch OpenAPI specification |
|
|
128
|
+
| `apiDocsPath` | `string` | No* | Local path to OpenAPI specification file |
|
|
129
|
+
| `outputDir` | `string` | Yes | Output directory for generated files |
|
|
130
|
+
|
|
131
|
+
*One of `apiDocsUrl` or `apiDocsPath` is required.
|
|
132
|
+
|
|
133
|
+
### Tag filtering
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
tags: {
|
|
137
|
+
include?: string[]; // Tags to include
|
|
138
|
+
exclude?: string[]; // Tags to exclude
|
|
139
|
+
prefix?: string; // Tag prefix (default "api_tag_")
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Grouping modes
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
groupBy: 'tag' | 'all' | 'path';
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
- `'tag'` - grouping by tags (multiple classes)
|
|
150
|
+
- `'all'` - all methods in one class
|
|
151
|
+
- `'path'` - grouping by API paths
|
|
152
|
+
|
|
153
|
+
### Class generation modes
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
classMode: 'single' | 'multiple';
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
- `'single'` - one class for all methods
|
|
160
|
+
- `'multiple'` - multiple classes by grouping
|
|
161
|
+
|
|
162
|
+
### Naming options
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
naming: {
|
|
166
|
+
className?: (tag: string) => string;
|
|
167
|
+
interfaceName?: (name: string) => string;
|
|
168
|
+
methodName?: (operationId: string) => string;
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Import options
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
imports: {
|
|
176
|
+
axiosPath?: string; // Path to import axios (default 'axios')
|
|
177
|
+
baseUrlPath?: string; // Path to import BASE_URL
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Generation options
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
options: {
|
|
185
|
+
generateJSDoc?: boolean; // Generate JSDoc (default true)
|
|
186
|
+
generateIndexFiles?: boolean; // Generate index files (default true)
|
|
187
|
+
cleanOutputDir?: boolean; // Clean output directory (default true)
|
|
188
|
+
generateAxiosConfig?: boolean; // Generate axios configuration (default true)
|
|
189
|
+
allowAxiosConfigSpread?: boolean; // Allow spreading axios configs (default true)
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Axios settings
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
axios: {
|
|
197
|
+
baseURL?: string;
|
|
198
|
+
timeout?: number;
|
|
199
|
+
headers?: Record<string, string>;
|
|
200
|
+
withCredentials?: boolean;
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Spreading user axios configurations
|
|
205
|
+
|
|
206
|
+
When `allowAxiosConfigSpread: true` is enabled, API methods support passing user configurations:
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
// Generated method
|
|
210
|
+
async getUserById(id: number, config?: AxiosRequestConfig): Promise<AxiosResponse<User>>
|
|
211
|
+
|
|
212
|
+
// Usage
|
|
213
|
+
const response = await usersApi.getUserById(123, {
|
|
214
|
+
headers: {
|
|
215
|
+
'X-Custom-Header': 'value',
|
|
216
|
+
},
|
|
217
|
+
timeout: 5000,
|
|
218
|
+
params: {
|
|
219
|
+
fields: 'id,name,email',
|
|
220
|
+
},
|
|
221
|
+
});
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Generated files structure
|
|
225
|
+
|
|
226
|
+
```
|
|
227
|
+
outputDir/
|
|
228
|
+
βββ config/
|
|
229
|
+
β βββ axios/
|
|
230
|
+
β βββ axios.ts # Axios configuration with interceptors
|
|
231
|
+
βββ interfaces/
|
|
232
|
+
β βββ User.ts
|
|
233
|
+
β βββ Product.ts
|
|
234
|
+
β βββ index.ts
|
|
235
|
+
βββ classes/
|
|
236
|
+
β βββ UsersApi.ts
|
|
237
|
+
β βββ ProductsApi.ts
|
|
238
|
+
β βββ index.ts
|
|
239
|
+
βββ index.ts
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## CLI options
|
|
243
|
+
|
|
244
|
+
```
|
|
245
|
+
Usage: api-docs-generator [options]
|
|
246
|
+
|
|
247
|
+
Options:
|
|
248
|
+
-V, --version output the version number
|
|
249
|
+
-c, --config <path> Path to configuration file
|
|
250
|
+
--api-docs-url <url> URL to fetch OpenAPI documentation
|
|
251
|
+
--api-docs-path <path> Local path to OpenAPI documentation file
|
|
252
|
+
--output-dir <dir> Output directory for generated files
|
|
253
|
+
--clean Clean output directory before generation
|
|
254
|
+
-h, --help display help for command
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
## Configuration examples
|
|
258
|
+
|
|
259
|
+
### Generate by tags (multiple classes)
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
{
|
|
263
|
+
tags: {
|
|
264
|
+
include: ['api_tag_users', 'api_tag_products'],
|
|
265
|
+
},
|
|
266
|
+
groupBy: 'tag',
|
|
267
|
+
classMode: 'multiple',
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Generate single class
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
{
|
|
275
|
+
groupBy: 'all',
|
|
276
|
+
classMode: 'single',
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Exclude tags
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
{
|
|
284
|
+
tags: {
|
|
285
|
+
exclude: ['api_tag_internal', 'api_tag_admin'],
|
|
286
|
+
},
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Generate with axios configuration
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
{
|
|
294
|
+
options: {
|
|
295
|
+
generateAxiosConfig: true,
|
|
296
|
+
},
|
|
297
|
+
axios: {
|
|
298
|
+
baseURL: 'https://api.example.com',
|
|
299
|
+
timeout: 30000,
|
|
300
|
+
headers: {
|
|
301
|
+
'Content-Type': 'application/json',
|
|
302
|
+
'X-API-Key': 'your-api-key',
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
## License
|
|
309
|
+
|
|
310
|
+
MIT
|
package/bin/cli.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@paulpugovkin/api-docs-axios-ts-generator",
|
|
3
|
+
"version": "1.0.5",
|
|
4
|
+
"description": "Generate TypeScript interfaces and axios classes from OpenAPI documentation",
|
|
5
|
+
"main": "src/index.min.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"api-docs-generator": "./src/index.min.js"
|
|
8
|
+
},
|
|
9
|
+
"types": "src/types/index.d.ts",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"generate-services": "npx @paulpugovkin/api-docs-axios-ts-generator --config api-docs-generator.config.js",
|
|
12
|
+
"postinstall": "node src/scripts/createConfigFile.js"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"openapi",
|
|
16
|
+
"swagger",
|
|
17
|
+
"typescript",
|
|
18
|
+
"axios",
|
|
19
|
+
"generator",
|
|
20
|
+
"api-client",
|
|
21
|
+
"code-generation",
|
|
22
|
+
"api",
|
|
23
|
+
"rest"
|
|
24
|
+
],
|
|
25
|
+
"author": "",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"commander": "^11.0.0",
|
|
29
|
+
"node-fetch": "^2.7.0",
|
|
30
|
+
"axios": "^1.6.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^20.0.0",
|
|
34
|
+
"typescript": "^5.0.0"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=14.0.0"
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"src",
|
|
41
|
+
"bin",
|
|
42
|
+
"README.md",
|
|
43
|
+
"LICENSE"
|
|
44
|
+
],
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "git+https://github.com/PaulPugovkin/api-docs-axios-ts-generator.git"
|
|
48
|
+
},
|
|
49
|
+
"bugs": {
|
|
50
|
+
"url": "https://github.com/PaulPugovkin/api-docs-axios-ts-generator/issues"
|
|
51
|
+
},
|
|
52
|
+
"homepage": "https://github.com/PaulPugovkin/api-docs-axios-ts-generator#readme"
|
|
53
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Π£Π΄Π°Π»ΡΠ΅Ρ ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠ΅ ΠΏΠ°ΠΏΠΊΠΈ ΠΈ ΠΏΠ΅ΡΠ΅ΡΠΎΠ·Π΄Π°Π΅Ρ Π΅Ρ.
|
|
6
|
+
* @param {string} outputDir - ΠΡΡΡ ΠΊ ΠΏΠ°ΠΏΠΊΠ΅.
|
|
7
|
+
*/
|
|
8
|
+
function cleanGeneratedFolder(outputDir) {
|
|
9
|
+
if (fs.existsSync(outputDir)) {
|
|
10
|
+
fs.rmSync(outputDir, {recursive: true}); // Π£Π΄Π°Π»ΡΠ΅ΠΌ ΠΏΠ°ΠΏΠΊΡ ΠΈ Π²ΡΠ΅ Π²Π»ΠΎΠΆΠ΅Π½Π½ΡΠ΅ ΡΠ°ΠΉΠ»Ρ
|
|
11
|
+
console.log(`Deleted: ${outputDir}`);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
fs.mkdirSync(outputDir, {recursive: true}); // Π‘ΠΎΠ·Π΄Π°Π΅ΠΌ ΠΎΡΠ½ΠΎΠ²Π½ΡΡ ΠΏΠ°ΠΏΠΊΡ
|
|
15
|
+
fs.mkdirSync(path.join(outputDir, "interfaces"), {recursive: true});
|
|
16
|
+
fs.mkdirSync(path.join(outputDir, "classes"), {recursive: true});
|
|
17
|
+
|
|
18
|
+
console.log(`Recreated necessary directories.`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = {cleanGeneratedFolder};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
|
|
4
|
+
function collectGeneratedFiles(outputDir) {
|
|
5
|
+
const classFiles = [];
|
|
6
|
+
const interfaceFiles = [];
|
|
7
|
+
|
|
8
|
+
// Π Π΅ΠΊΡΡΡΠΈΠ²Π½ΡΠΉ ΠΎΠ±Ρ
ΠΎΠ΄ ΠΏΠ°ΠΏΠΎΠΊ
|
|
9
|
+
function walkDir(currentPath) {
|
|
10
|
+
const entries = fs.readdirSync(currentPath, {withFileTypes: true});
|
|
11
|
+
for (const entry of entries) {
|
|
12
|
+
const fullPath = path.join(currentPath, entry.name);
|
|
13
|
+
|
|
14
|
+
if (entry.isDirectory()) {
|
|
15
|
+
walkDir(fullPath); // Π Π΅ΠΊΡΡΡΠΈΠ²Π½ΡΠΉ Π²ΡΠ·ΠΎΠ² Π΄Π»Ρ ΠΏΠ°ΠΏΠΎΠΊ
|
|
16
|
+
} else if (
|
|
17
|
+
entry.isFile() &&
|
|
18
|
+
entry.name.endsWith(".ts") &&
|
|
19
|
+
entry.name !== "index.ts"
|
|
20
|
+
) {
|
|
21
|
+
if (fullPath.includes("interfaces")) {
|
|
22
|
+
interfaceFiles.push(fullPath);
|
|
23
|
+
} else {
|
|
24
|
+
classFiles.push(fullPath);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
walkDir(outputDir); // ΠΠ°ΠΏΡΡΠΊ ΠΎΠ±Ρ
ΠΎΠ΄Π° Ρ ΡΠΊΠ°Π·Π°Π½Π½ΠΎΠΉ ΠΏΠ°ΠΏΠΊΠΈ
|
|
31
|
+
|
|
32
|
+
return {classFiles, interfaceFiles};
|
|
33
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ΠΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ Π΄Π»Ρ Π³Π΅Π½Π΅ΡΠ°ΡΠΎΡΠ°
|
|
3
|
+
*/
|
|
4
|
+
import { GeneratorConfig } from '../types/config.types';
|
|
5
|
+
|
|
6
|
+
export const defaultConfig: Partial<GeneratorConfig> = {
|
|
7
|
+
outputDir: './generated',
|
|
8
|
+
interfacesDir: './generated/interfaces',
|
|
9
|
+
classesDir: './generated/classes',
|
|
10
|
+
groupBy: 'tag',
|
|
11
|
+
classMode: 'multiple',
|
|
12
|
+
tags: {
|
|
13
|
+
prefix: 'api_tag_',
|
|
14
|
+
},
|
|
15
|
+
imports: {
|
|
16
|
+
axiosPath: 'axios',
|
|
17
|
+
baseUrlPath: './config/axios/axios',
|
|
18
|
+
},
|
|
19
|
+
options: {
|
|
20
|
+
generateJSDoc: true,
|
|
21
|
+
generateIndexFiles: true,
|
|
22
|
+
cleanOutputDir: true,
|
|
23
|
+
generateAxiosConfig: true,
|
|
24
|
+
allowAxiosConfigSpread: true,
|
|
25
|
+
},
|
|
26
|
+
axios: {
|
|
27
|
+
baseURL: '',
|
|
28
|
+
timeout: 30000,
|
|
29
|
+
headers: {
|
|
30
|
+
'Content-Type': 'application/json',
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ΠΠ°Π³ΡΡΠ·ΠΊΠ° ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ Π³Π΅Π½Π΅ΡΠ°ΡΠΎΡΠ°
|
|
3
|
+
*/
|
|
4
|
+
// @ts-ignore - Node.js types
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
// @ts-ignore - Node.js types
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import { GeneratorConfig } from '../types/config.types';
|
|
9
|
+
import { defaultConfig } from './defaultConfig';
|
|
10
|
+
import { validateConfig } from './validateConfig';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* ΠΠ°Π³ΡΡΠΆΠ°Π΅Ρ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ ΠΈΠ· ΡΠ°ΠΉΠ»Π°
|
|
14
|
+
* @param configPath - ΠΡΡΡ ΠΊ ΡΠ°ΠΉΠ»Ρ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ
|
|
15
|
+
* @returns ΠΠ±ΡΠ΅ΠΊΡ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ
|
|
16
|
+
*/
|
|
17
|
+
async function loadConfigFile(configPath: string): Promise<Partial<GeneratorConfig>> {
|
|
18
|
+
const resolvedPath = path.resolve(configPath);
|
|
19
|
+
|
|
20
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
21
|
+
throw new Error(`Configuration file not found: ${resolvedPath}`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Π£Π΄Π°Π»ΡΠ΅ΠΌ ΠΊΡΡ Π΄Π»Ρ ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΊΠΈ Π³ΠΎΡΡΡΠ΅ΠΉ ΠΏΠ΅ΡΠ΅Π·Π°Π³ΡΡΠ·ΠΊΠΈ
|
|
25
|
+
// @ts-ignore
|
|
26
|
+
delete require.cache[require.resolve(resolvedPath)];
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
// @ts-ignore
|
|
30
|
+
const userConfig = require(resolvedPath);
|
|
31
|
+
return userConfig.default || userConfig;
|
|
32
|
+
} catch (error) {
|
|
33
|
+
throw new Error(`Failed to load configuration from ${resolvedPath}: ${error}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* ΠΠ±ΡΠ΅Π΄ΠΈΠ½ΡΠ΅Ρ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ (ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»ΡΡΠΊΠ°Ρ ΠΏΠ΅ΡΠ΅ΠΊΡΡΠ²Π°Π΅Ρ Π΄Π΅ΡΠΎΠ»ΡΠ½ΡΡ)
|
|
39
|
+
* @param defaults - ΠΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ
|
|
40
|
+
* @param userConfig - ΠΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»ΡΡΠΊΠ°Ρ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ
|
|
41
|
+
* @returns ΠΠ±ΡΠ΅Π΄ΠΈΠ½ΡΠ½Π½Π°Ρ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ
|
|
42
|
+
*/
|
|
43
|
+
function mergeConfigs(
|
|
44
|
+
defaults: Partial<GeneratorConfig>,
|
|
45
|
+
userConfig: Partial<GeneratorConfig>
|
|
46
|
+
): GeneratorConfig {
|
|
47
|
+
const merged: any = {
|
|
48
|
+
...defaults,
|
|
49
|
+
...userConfig,
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// ΠΠ»ΡΠ±ΠΎΠΊΠΎΠ΅ ΡΠ»ΠΈΡΠ½ΠΈΠ΅ Π΄Π»Ρ Π²Π»ΠΎΠΆΠ΅Π½Π½ΡΡ
ΠΎΠ±ΡΠ΅ΠΊΡΠΎΠ²
|
|
53
|
+
if (defaults.tags && userConfig.tags) {
|
|
54
|
+
merged.tags = { ...defaults.tags, ...userConfig.tags };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (defaults.naming && userConfig.naming) {
|
|
58
|
+
merged.naming = { ...defaults.naming, ...userConfig.naming };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (defaults.imports && userConfig.imports) {
|
|
62
|
+
merged.imports = { ...defaults.imports, ...userConfig.imports };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (defaults.options && userConfig.options) {
|
|
66
|
+
merged.options = { ...defaults.options, ...userConfig.options };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (defaults.axios && userConfig.axios) {
|
|
70
|
+
merged.axios = { ...defaults.axios, ...userConfig.axios };
|
|
71
|
+
// ΠΠ»ΡΠ±ΠΎΠΊΠΎΠ΅ ΡΠ»ΠΈΡΠ½ΠΈΠ΅ Π΄Π»Ρ headers
|
|
72
|
+
if (defaults.axios.headers && userConfig.axios.headers) {
|
|
73
|
+
merged.axios.headers = { ...defaults.axios.headers, ...userConfig.axios.headers };
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return merged as GeneratorConfig;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* ΠΠ°Π³ΡΡΠΆΠ°Π΅Ρ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ Π³Π΅Π½Π΅ΡΠ°ΡΠΎΡΠ°
|
|
82
|
+
* @param configPath - ΠΡΡΡ ΠΊ ΡΠ°ΠΉΠ»Ρ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ (ΠΎΠΏΡΠΈΠΎΠ½Π°Π»ΡΠ½ΠΎ)
|
|
83
|
+
* @param cliOverrides - ΠΠ΅ΡΠ΅ΠΎΠΏΡΠ΅Π΄Π΅Π»Π΅Π½ΠΈΡ ΠΈΠ· CLI Π°ΡΠ³ΡΠΌΠ΅Π½ΡΠΎΠ²
|
|
84
|
+
* @returns ΠΠΎΠ»Π½Π°Ρ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ Π³Π΅Π½Π΅ΡΠ°ΡΠΎΡΠ°
|
|
85
|
+
*/
|
|
86
|
+
export async function loadConfig(
|
|
87
|
+
configPath?: string,
|
|
88
|
+
cliOverrides?: Partial<GeneratorConfig>
|
|
89
|
+
): Promise<GeneratorConfig> {
|
|
90
|
+
let userConfig: Partial<GeneratorConfig> = {};
|
|
91
|
+
|
|
92
|
+
// ΠΠ°Π³ΡΡΠ·ΠΊΠ° ΠΈΠ· ΡΠ°ΠΉΠ»Π°, Π΅ΡΠ»ΠΈ ΡΠΊΠ°Π·Π°Π½
|
|
93
|
+
if (configPath) {
|
|
94
|
+
console.log(`Loading configuration from: ${configPath}`);
|
|
95
|
+
userConfig = await loadConfigFile(configPath);
|
|
96
|
+
} else {
|
|
97
|
+
console.log('No configuration file specified, using default configuration');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Π‘Π»ΠΈΡΠ½ΠΈΠ΅ Ρ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠ΅ΠΉ ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ
|
|
101
|
+
let mergedConfig = mergeConfigs(defaultConfig, userConfig);
|
|
102
|
+
|
|
103
|
+
// ΠΡΠΈΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ ΠΏΠ΅ΡΠ΅ΠΎΠΏΡΠ΅Π΄Π΅Π»Π΅Π½ΠΈΠΉ ΠΈΠ· CLI
|
|
104
|
+
if (cliOverrides) {
|
|
105
|
+
mergedConfig = mergeConfigs(mergedConfig, cliOverrides);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ΠΠ°Π»ΠΈΠ΄Π°ΡΠΈΡ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ
|
|
109
|
+
validateConfig(mergedConfig);
|
|
110
|
+
|
|
111
|
+
return mergedConfig;
|
|
112
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ΠΠ°Π»ΠΈΠ΄Π°ΡΠΈΡ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ Π³Π΅Π½Π΅ΡΠ°ΡΠΎΡΠ°
|
|
3
|
+
*/
|
|
4
|
+
import { GeneratorConfig } from '../types/config.types';
|
|
5
|
+
|
|
6
|
+
export function validateConfig(config: GeneratorConfig): void {
|
|
7
|
+
// ΠΡΠΎΠ²Π΅ΡΠΊΠ° ΠΎΠ±ΡΠ·Π°ΡΠ΅Π»ΡΠ½ΠΎΠ³ΠΎ ΠΏΠΎΠ»Ρ outputDir
|
|
8
|
+
if (!config.outputDir) {
|
|
9
|
+
throw new Error('Configuration error: outputDir is required');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// ΠΡΠΎΠ²Π΅ΡΠΊΠ°, ΡΡΠΎ ΡΠΊΠ°Π·Π°Π½ Π»ΠΈΠ±ΠΎ apiDocsUrl, Π»ΠΈΠ±ΠΎ apiDocsPath
|
|
13
|
+
if (!config.apiDocsUrl && !config.apiDocsPath) {
|
|
14
|
+
throw new Error('Configuration error: either apiDocsUrl or apiDocsPath must be specified');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// ΠΡΠΎΠ²Π΅ΡΠΊΠ° groupBy
|
|
18
|
+
if (config.groupBy && !['tag', 'all', 'path'].includes(config.groupBy)) {
|
|
19
|
+
throw new Error(`Configuration error: invalid groupBy value "${config.groupBy}". Must be one of: tag, all, path`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// ΠΡΠΎΠ²Π΅ΡΠΊΠ° classMode
|
|
23
|
+
if (config.classMode && !['single', 'multiple'].includes(config.classMode)) {
|
|
24
|
+
throw new Error(`Configuration error: invalid classMode value "${config.classMode}". Must be one of: single, multiple`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// ΠΡΠΎΠ²Π΅ΡΠΊΠ° ΡΠΈΠ»ΡΡΡΠ°ΡΠΈΠΈ ΠΏΠΎ ΡΠ΅Π³Π°ΠΌ
|
|
28
|
+
if (config.tags) {
|
|
29
|
+
if (config.tags.include && config.tags.exclude) {
|
|
30
|
+
console.warn('Warning: Both include and exclude tags are specified. Include will take precedence.');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (config.tags.include && config.tags.include.length === 0) {
|
|
34
|
+
console.warn('Warning: tags.include is empty. No tags will be filtered.');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ΠΡΠΎΠ²Π΅ΡΠΊΠ° ΡΠΎΠ²ΠΌΠ΅ΡΡΠΈΠΌΠΎΡΡΠΈ groupBy ΠΈ classMode
|
|
39
|
+
if (config.groupBy === 'all' && config.classMode === 'multiple') {
|
|
40
|
+
console.warn('Warning: groupBy="all" with classMode="multiple" will generate only one class. Consider using classMode="single".');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ΠΡΠΎΠ²Π΅ΡΠΊΠ° Π½Π°ΡΡΡΠΎΠ΅ΠΊ axios
|
|
44
|
+
if (config.axios) {
|
|
45
|
+
if (config.axios.timeout !== undefined && config.axios.timeout < 0) {
|
|
46
|
+
throw new Error('Configuration error: axios.timeout must be a positive number');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ΠΡΠΎΠ²Π΅ΡΠΊΠ° ΠΎΠΏΡΠΈΠΉ Π³Π΅Π½Π΅ΡΠ°ΡΠΈΠΈ
|
|
51
|
+
if (config.options) {
|
|
52
|
+
if (config.options.generateAxiosConfig && !config.axios) {
|
|
53
|
+
console.warn('Warning: generateAxiosConfig is true but no axios config provided. Using default values.');
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
console.log('Configuration validated successfully.');
|
|
58
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ΠΠ΅Π½Π΅ΡΠ°ΡΠΈΡ ΠΊΠ»Π°ΡΡΠΎΠ² TypeScript
|
|
3
|
+
* @param {string} name - ΠΠ°Π·Π²Π°Π½ΠΈΠ΅ ΠΊΠ»Π°ΡΡΠ°
|
|
4
|
+
* @param {string[]} methods - Π‘ΠΏΠΈΡΠΎΠΊ ΠΌΠ΅ΡΠΎΠ΄ΠΎΠ² ΠΊΠ»Π°ΡΡΠ°
|
|
5
|
+
* @param {Set<string>} usedInterfaces - ΠΠ°Π±ΠΎΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌΡΡ
ΠΈΠ½ΡΠ΅ΡΡΠ΅ΠΉΡΠΎΠ²
|
|
6
|
+
* @param {Object} config - ΠΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ Π³Π΅Π½Π΅ΡΠ°ΡΠΎΡΠ°
|
|
7
|
+
* @returns {string} - Π‘Π³Π΅Π½Π΅ΡΠΈΡΠΎΠ²Π°Π½Π½ΡΠΉ ΠΊΠΎΠ΄ ΠΊΠ»Π°ΡΡΠ° TypeScript
|
|
8
|
+
*/
|
|
9
|
+
function generateClass(name, methods, usedInterfaces, config) {
|
|
10
|
+
const axiosResponseImport =
|
|
11
|
+
'import { AxiosResponse, AxiosRequestConfig } from "axios";\n';
|
|
12
|
+
|
|
13
|
+
// ΠΠΏΡΠ΅Π΄Π΅Π»ΡΠ΅ΠΌ ΠΏΡΡΠΈ ΠΈΠΌΠΏΠΎΡΡΠ° ΠΈΠ· ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ
|
|
14
|
+
const axiosPath = config?.imports?.axiosPath || 'axios';
|
|
15
|
+
const baseUrlPath = config?.imports?.baseUrlPath || './config/axios/axios';
|
|
16
|
+
|
|
17
|
+
// ΠΠΌΠΏΠΎΡΡΡ ΠΈΠ½ΡΠ΅ΡΡΠ΅ΠΉΡΠΎΠ²
|
|
18
|
+
let interfaceImport = '';
|
|
19
|
+
if (usedInterfaces && usedInterfaces.size > 0) {
|
|
20
|
+
interfaceImport = Array.from(usedInterfaces)
|
|
21
|
+
.sort()
|
|
22
|
+
.map((interfaceName) => `import { ${interfaceName} } from '../../';`)
|
|
23
|
+
.join("\n");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ΠΠΌΠΏΠΎΡΡΡ axios
|
|
27
|
+
const axiosImport = `\nimport axios from '${axiosPath}'`;
|
|
28
|
+
const baseApiUrl = `\nimport { BASE_URL } from '${baseUrlPath}'`;
|
|
29
|
+
|
|
30
|
+
const imports = `${axiosResponseImport}${interfaceImport}${axiosImport}${baseApiUrl}`;
|
|
31
|
+
const tsMethods = methods.join("\n");
|
|
32
|
+
return `${imports}\n\nexport class ${name} {\n${tsMethods}\n}\n`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = {generateClass};
|