@paulpugovkin/api-docs-axios-ts-generator 1.0.17 → 1.2.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/README.md +237 -310
- package/dist/index.js +78 -0
- package/package.json +11 -22
- package/bin/cli.js +0 -6
- package/src/clean-generated-folder/cleanGeneratedFolder.js +0 -21
- package/src/collect-genereted-files/collectGeneratedFiles.js +0 -33
- package/src/generate-class/generateClass.js +0 -42
- package/src/generate-index-file-with-public-api/generateIndexFileWithPublicApi.js +0 -45
- package/src/generate-interface/generateInterface.js +0 -81
- package/src/generate-js-doc/generateJSDoc.js +0 -136
- package/src/generate-main-index-file/generateMainIndexFile.js +0 -21
- package/src/generate-method/generateMethod.js +0 -264
- package/src/generators/axiosConfigGenerator.js +0 -79
- package/src/index.js +0 -187
- package/src/map-type/mapType.js +0 -19
- package/src/parse-and-generate/parseAndGenerate.js +0 -190
- package/src/resolve-type/resolveType.js +0 -54
- package/src/update-api-docs-json/updateApiDocsJson.js +0 -33
|
@@ -1,264 +0,0 @@
|
|
|
1
|
-
const path = require("path");
|
|
2
|
-
const {resolveType} = require("../resolve-type/resolveType");
|
|
3
|
-
const {generateJSDoc} = require("../generate-js-doc/generateJSDoc");
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Генерирует метод TypeScript на основе спецификации API.
|
|
7
|
-
* @param {string} name - Имя метода.
|
|
8
|
-
* @param {Object} method - Описание метода API (из OpenAPI).
|
|
9
|
-
* @param {string} path - Путь API.
|
|
10
|
-
* @param {string} methodType - HTTP-метод (GET, POST и т.д.).
|
|
11
|
-
* @param {Object<string, string>} schemaRefs - Ссылки на схемы для разрешения типов.
|
|
12
|
-
* @param {Set<string>} usedInterfaces - Набор интерфейсов, используемых в методе.
|
|
13
|
-
* @param {Object} config - Конфигурация генератора.
|
|
14
|
-
* @returns {string} - Сгенерированный метод TypeScript.
|
|
15
|
-
*/
|
|
16
|
-
function generateMethod(
|
|
17
|
-
name,
|
|
18
|
-
method,
|
|
19
|
-
path,
|
|
20
|
-
methodType,
|
|
21
|
-
schemaRefs,
|
|
22
|
-
usedInterfaces,
|
|
23
|
-
config = {}
|
|
24
|
-
) {
|
|
25
|
-
const allowConfigSpread = config?.options?.allowAxiosConfigSpread !== false;
|
|
26
|
-
const pathParams = [...path.matchAll(/\{(\w+)\}/g)].map((match) => match[1]);
|
|
27
|
-
pathParams.forEach((param) => {
|
|
28
|
-
path = path.replace(`{${param}}`, `\${${param}}`);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
const requiredParams = [];
|
|
32
|
-
const requiredQueryParams = [];
|
|
33
|
-
const optionalQueryParams = [];
|
|
34
|
-
const nestedParams = {};
|
|
35
|
-
let bodyParam = null;
|
|
36
|
-
let bodyString = "";
|
|
37
|
-
let isMultipart = false;
|
|
38
|
-
|
|
39
|
-
// Обработка параметров запроса
|
|
40
|
-
for (const param of method.parameters || []) {
|
|
41
|
-
const paramName = param.name;
|
|
42
|
-
const paramLocation = param.in;
|
|
43
|
-
const paramRequired = param.required || false;
|
|
44
|
-
const paramSchema = param.schema || {};
|
|
45
|
-
|
|
46
|
-
// Проверяем, является ли параметр сложным объектом (имеет $ref или properties)
|
|
47
|
-
const isComplexObject = paramSchema.$ref || paramSchema.properties;
|
|
48
|
-
|
|
49
|
-
if (paramLocation === "path") {
|
|
50
|
-
const paramType = resolveType(paramSchema, schemaRefs, usedInterfaces);
|
|
51
|
-
requiredParams.push(`${paramName}: ${paramType}`);
|
|
52
|
-
} else if (paramLocation === "query") {
|
|
53
|
-
if (isComplexObject) {
|
|
54
|
-
// Для сложных объектов добавляем распыление
|
|
55
|
-
const paramType = resolveType(paramSchema, schemaRefs, usedInterfaces);
|
|
56
|
-
if (paramRequired) {
|
|
57
|
-
requiredParams.push(`${paramName}: ${paramType}`);
|
|
58
|
-
// Вместо добавления в requiredQueryParams, просто отмечаем что есть сложный объект
|
|
59
|
-
// который нужно распылить
|
|
60
|
-
requiredQueryParams.push(`...${paramName}`);
|
|
61
|
-
} else {
|
|
62
|
-
optionalQueryParams.push(`${paramName}?: ${paramType}`);
|
|
63
|
-
// Для опциональных сложных объектов тоже добавляем распыление
|
|
64
|
-
requiredQueryParams.push(`...(${paramName} || {})`);
|
|
65
|
-
}
|
|
66
|
-
} else if (paramName.includes(".")) {
|
|
67
|
-
const [nestedName, subfield] = paramName.split(".");
|
|
68
|
-
nestedParams[nestedName] = nestedParams[nestedName] || [];
|
|
69
|
-
const paramType = resolveType(paramSchema, schemaRefs, usedInterfaces);
|
|
70
|
-
nestedParams[nestedName].push(`${subfield}?: ${paramType}`);
|
|
71
|
-
} else {
|
|
72
|
-
const paramType = resolveType(paramSchema, schemaRefs, usedInterfaces);
|
|
73
|
-
if (paramRequired) {
|
|
74
|
-
requiredQueryParams.push(paramName);
|
|
75
|
-
requiredParams.push(`${paramName}: ${paramType}`);
|
|
76
|
-
} else {
|
|
77
|
-
optionalQueryParams.push(`${paramName}?: ${paramType}`);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Обрабатываем вложенные параметры (только для простых типов)
|
|
84
|
-
for (const [nestedName, fields] of Object.entries(nestedParams)) {
|
|
85
|
-
optionalQueryParams.push(`${nestedName}?: { ${fields.join(", ")} }`);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Проверяем наличие тела запроса (только для методов, которые могут иметь тело)
|
|
89
|
-
const methodsWithBody = ['post', 'put', 'patch', 'delete'];
|
|
90
|
-
const hasRequestBody = methodsWithBody.includes(methodType.toLowerCase()) &&
|
|
91
|
-
method.requestBody &&
|
|
92
|
-
method.requestBody.content &&
|
|
93
|
-
Object.keys(method.requestBody.content).length > 0;
|
|
94
|
-
|
|
95
|
-
if (hasRequestBody) {
|
|
96
|
-
const content = method.requestBody.content || {};
|
|
97
|
-
if (content["application/json"]) {
|
|
98
|
-
const schema = content["application/json"].schema || {};
|
|
99
|
-
if (schema.oneOf) {
|
|
100
|
-
const refName = resolveType(schema, schemaRefs, usedInterfaces);
|
|
101
|
-
bodyParam = `values: ${refName}`;
|
|
102
|
-
} else if (schema.$ref) {
|
|
103
|
-
const refName = schema.$ref.split("/").pop();
|
|
104
|
-
usedInterfaces.add(refName);
|
|
105
|
-
bodyParam = `values: ${refName}`;
|
|
106
|
-
} else if (schema.properties) {
|
|
107
|
-
const props = Object.entries(schema.properties)
|
|
108
|
-
.map(
|
|
109
|
-
([key, value]) =>
|
|
110
|
-
`${key}: ${resolveType(value, schemaRefs, usedInterfaces)}`
|
|
111
|
-
)
|
|
112
|
-
.join(", ");
|
|
113
|
-
bodyParam = `values: { ${props} }`;
|
|
114
|
-
} else {
|
|
115
|
-
bodyParam = 'values: any'
|
|
116
|
-
}
|
|
117
|
-
bodyString = "values";
|
|
118
|
-
} else if (content["multipart/form-data"]) {
|
|
119
|
-
isMultipart = true;
|
|
120
|
-
const schema = content["multipart/form-data"].schema || {};
|
|
121
|
-
if (schema.properties) {
|
|
122
|
-
for (const [fieldName, fieldDetails] of Object.entries(
|
|
123
|
-
schema.properties
|
|
124
|
-
)) {
|
|
125
|
-
let tsType = resolveType(fieldDetails, schemaRefs, usedInterfaces);
|
|
126
|
-
if (fieldDetails.format === "binary") {
|
|
127
|
-
tsType = "File";
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
bodyParam = `data: FormData`;
|
|
132
|
-
bodyString = "data";
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
let responseType = "void";
|
|
137
|
-
if (method.responses) {
|
|
138
|
-
for (const [status, response] of Object.entries(method.responses)) {
|
|
139
|
-
if (response.content) {
|
|
140
|
-
const contentSchema = Object.values(response.content)[0].schema || {};
|
|
141
|
-
if (contentSchema.$ref) {
|
|
142
|
-
const refName = contentSchema.$ref.split("/").pop();
|
|
143
|
-
responseType = refName;
|
|
144
|
-
usedInterfaces.add(refName);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const returnType = `Promise<AxiosResponse<${responseType}>>`;
|
|
151
|
-
const optionalArgsName = optionalQueryParams.length ? "queryParams" : null;
|
|
152
|
-
const optionalArgsDefinition = optionalArgsName
|
|
153
|
-
? `${optionalArgsName}: { ${optionalQueryParams.join(", ")} }`
|
|
154
|
-
: "";
|
|
155
|
-
|
|
156
|
-
// Определяем, есть ли параметры запроса (query params)
|
|
157
|
-
const hasQueryParams = requiredQueryParams.length > 0 || optionalQueryParams.length > 0;
|
|
158
|
-
|
|
159
|
-
const jsDocComment = generateJSDoc(method);
|
|
160
|
-
|
|
161
|
-
// Строки для аргументов функции
|
|
162
|
-
const functionArgs = `(${[
|
|
163
|
-
...requiredParams,
|
|
164
|
-
bodyParam,
|
|
165
|
-
optionalArgsDefinition,
|
|
166
|
-
"config?: AxiosRequestConfig",
|
|
167
|
-
]
|
|
168
|
-
.filter(Boolean)
|
|
169
|
-
.join(", ")}): ${returnType}`;
|
|
170
|
-
const functionString = `${name}${functionArgs}`;
|
|
171
|
-
|
|
172
|
-
// Строка url для запроса
|
|
173
|
-
const fullUrlString = `const fullURL = \`\${BASE_URL}${path}\`;`;
|
|
174
|
-
|
|
175
|
-
// Формируем строку для axios вызова
|
|
176
|
-
let axiosString = '';
|
|
177
|
-
|
|
178
|
-
// Функция для формирования объекта параметров
|
|
179
|
-
const buildParamsObject = () => {
|
|
180
|
-
const parts = [];
|
|
181
|
-
|
|
182
|
-
// Добавляем обязательные параметры (простые и распыленные сложные)
|
|
183
|
-
if (requiredQueryParams.length > 0) {
|
|
184
|
-
parts.push(...requiredQueryParams);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// Добавляем опциональные параметры
|
|
188
|
-
if (optionalArgsName) {
|
|
189
|
-
if (parts.length > 0) {
|
|
190
|
-
parts.push(`...${optionalArgsName}`);
|
|
191
|
-
} else {
|
|
192
|
-
parts.push(`...${optionalArgsName}`);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
return parts.length > 0 ? `{ ${parts.join(', ')} }` : '{}';
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
if (methodType.toLowerCase() === "delete") {
|
|
200
|
-
// DELETE запросы могут иметь и тело, и параметры запроса
|
|
201
|
-
if (bodyParam) {
|
|
202
|
-
// Есть тело запроса
|
|
203
|
-
if (hasQueryParams) {
|
|
204
|
-
// Есть и тело, и параметры запроса
|
|
205
|
-
const paramsObject = buildParamsObject();
|
|
206
|
-
axiosString = `return axios.delete(fullURL, { data: values, ...config, params: ${paramsObject} });`;
|
|
207
|
-
} else {
|
|
208
|
-
// Только тело запроса, без параметров
|
|
209
|
-
axiosString = `return axios.delete(fullURL, { data: values, ...config });`;
|
|
210
|
-
}
|
|
211
|
-
} else {
|
|
212
|
-
// Нет тела запроса
|
|
213
|
-
if (hasQueryParams) {
|
|
214
|
-
// Только параметры запроса
|
|
215
|
-
const paramsObject = buildParamsObject();
|
|
216
|
-
axiosString = `return axios.delete(fullURL, { ...config, params: ${paramsObject} });`;
|
|
217
|
-
} else {
|
|
218
|
-
// Нет ни тела, ни параметров
|
|
219
|
-
axiosString = `return axios.delete(fullURL, config);`;
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
} else if (methodType.toLowerCase() === "get") {
|
|
223
|
-
// GET запросы никогда не имеют тела
|
|
224
|
-
if (hasQueryParams) {
|
|
225
|
-
const paramsObject = buildParamsObject();
|
|
226
|
-
axiosString = `return axios.get(fullURL, { ...config, params: ${paramsObject} });`;
|
|
227
|
-
} else {
|
|
228
|
-
axiosString = `return axios.get(fullURL, config);`;
|
|
229
|
-
}
|
|
230
|
-
} else {
|
|
231
|
-
// PUT/POST/PATCH методы
|
|
232
|
-
if (hasRequestBody) {
|
|
233
|
-
// Есть тело запроса
|
|
234
|
-
if (hasQueryParams) {
|
|
235
|
-
// Есть и тело, и параметры запроса
|
|
236
|
-
const paramsObject = buildParamsObject();
|
|
237
|
-
axiosString = `return axios.${methodType}(fullURL, ${isMultipart ? 'data' : bodyString}, { ...config, params: ${paramsObject} });`;
|
|
238
|
-
} else {
|
|
239
|
-
// Только тело запроса, без параметров
|
|
240
|
-
axiosString = `return axios.${methodType}(fullURL, ${isMultipart ? 'data' : bodyString}, config);`;
|
|
241
|
-
}
|
|
242
|
-
} else {
|
|
243
|
-
// Нет тела запроса, но могут быть параметры
|
|
244
|
-
if (hasQueryParams) {
|
|
245
|
-
// Только параметры запроса
|
|
246
|
-
const paramsObject = buildParamsObject();
|
|
247
|
-
axiosString = `return axios.${methodType}(fullURL, null, { ...config, params: ${paramsObject} });`;
|
|
248
|
-
} else {
|
|
249
|
-
// Нет ни тела, ни параметров
|
|
250
|
-
axiosString = `return axios.${methodType}(fullURL, null, config);`;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
return `
|
|
256
|
-
${jsDocComment}
|
|
257
|
-
${functionString} {
|
|
258
|
-
${fullUrlString}
|
|
259
|
-
${axiosString}
|
|
260
|
-
}
|
|
261
|
-
`;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
module.exports = {generateMethod};
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Генератор конфигурации axios
|
|
3
|
-
*/
|
|
4
|
-
const fs = require('fs');
|
|
5
|
-
const path = require('path');
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Генерирует конфигурацию axios для сгенерированного API клиента
|
|
9
|
-
* @param {Object} config - Конфигурация генератора
|
|
10
|
-
* @param {string} outputDir - Директория вывода
|
|
11
|
-
*/
|
|
12
|
-
function generateAxiosConfig(config, outputDir) {
|
|
13
|
-
const axiosDir = path.join(outputDir, 'config', 'axios');
|
|
14
|
-
fs.mkdirSync(axiosDir, { recursive: true });
|
|
15
|
-
|
|
16
|
-
const axiosConfig = config.axios || {};
|
|
17
|
-
const baseURL = axiosConfig.baseURL || '';
|
|
18
|
-
const timeout = axiosConfig.timeout || 30000;
|
|
19
|
-
const headers = axiosConfig.headers || { 'Content-Type': 'application/json' };
|
|
20
|
-
const withCredentials = axiosConfig.withCredentials || false;
|
|
21
|
-
|
|
22
|
-
// Генерация файла axios.ts
|
|
23
|
-
const axiosTsContent = `import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
|
|
24
|
-
|
|
25
|
-
export const BASE_URL = '${baseURL}';
|
|
26
|
-
|
|
27
|
-
const axiosConfig: AxiosRequestConfig = {
|
|
28
|
-
baseURL: BASE_URL,
|
|
29
|
-
timeout: ${timeout},
|
|
30
|
-
headers: ${JSON.stringify(headers, null, 2)},
|
|
31
|
-
withCredentials: ${withCredentials},
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const axiosInstance: AxiosInstance = axios.create(axiosConfig);
|
|
35
|
-
|
|
36
|
-
// Перехватчик запросов
|
|
37
|
-
axiosInstance.interceptors.request.use(
|
|
38
|
-
(config) => {
|
|
39
|
-
// Можно добавить токен авторизации
|
|
40
|
-
// const token = localStorage.getItem('token');
|
|
41
|
-
// if (token) {
|
|
42
|
-
// config.headers.Authorization = \`Bearer \${token}\`;
|
|
43
|
-
// }
|
|
44
|
-
return config;
|
|
45
|
-
},
|
|
46
|
-
(error) => {
|
|
47
|
-
return Promise.reject(error);
|
|
48
|
-
}
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
// Перехватчик ответов
|
|
52
|
-
axiosInstance.interceptors.response.use(
|
|
53
|
-
(response) => {
|
|
54
|
-
return response;
|
|
55
|
-
},
|
|
56
|
-
(error) => {
|
|
57
|
-
// Обработка ошибок
|
|
58
|
-
if (error.response) {
|
|
59
|
-
// Сервер ответил со статусом, отличным от 2xx
|
|
60
|
-
console.error('Response error:', error.response.status, error.response.data);
|
|
61
|
-
} else if (error.request) {
|
|
62
|
-
// Запрос был сделан, но ответ не получен
|
|
63
|
-
console.error('Request error:', error.request);
|
|
64
|
-
} else {
|
|
65
|
-
// Произошла ошибка при настройке запроса
|
|
66
|
-
console.error('Error:', error.message);
|
|
67
|
-
}
|
|
68
|
-
return Promise.reject(error);
|
|
69
|
-
}
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
export default axiosInstance;
|
|
73
|
-
`;
|
|
74
|
-
|
|
75
|
-
fs.writeFileSync(path.join(axiosDir, 'axios.ts'), axiosTsContent, { encoding: 'utf-8' });
|
|
76
|
-
console.log(`Generated axios config at: ${axiosDir}/axios.ts`);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
module.exports = { generateAxiosConfig };
|
package/src/index.js
DELETED
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
const {program} = require("commander");
|
|
2
|
-
|
|
3
|
-
// Модули генератора
|
|
4
|
-
const {
|
|
5
|
-
cleanGeneratedFolder,
|
|
6
|
-
} = require("./clean-generated-folder/cleanGeneratedFolder");
|
|
7
|
-
const {
|
|
8
|
-
updateApiDocsJson,
|
|
9
|
-
} = require("./update-api-docs-json/updateApiDocsJson");
|
|
10
|
-
const {parseAndGenerate} = require("./parse-and-generate/parseAndGenerate");
|
|
11
|
-
const {
|
|
12
|
-
generateIndexFileWithOpenApi,
|
|
13
|
-
} = require("./generate-index-file-with-public-api/generateIndexFileWithPublicApi");
|
|
14
|
-
const {
|
|
15
|
-
generateMainIndexFile,
|
|
16
|
-
} = require("./generate-main-index-file/generateMainIndexFile");
|
|
17
|
-
const {generateAxiosConfig} = require("./generators/axiosConfigGenerator");
|
|
18
|
-
|
|
19
|
-
// Simple configuration loader
|
|
20
|
-
const fs = require('fs');
|
|
21
|
-
const path = require('path');
|
|
22
|
-
|
|
23
|
-
async function loadConfig(configPath, cliOptions = {}) {
|
|
24
|
-
const resolvedPath = path.resolve(configPath);
|
|
25
|
-
|
|
26
|
-
if (!fs.existsSync(resolvedPath)) {
|
|
27
|
-
throw new Error(`Configuration file not found: ${resolvedPath}`);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const ext = path.extname(resolvedPath);
|
|
31
|
-
let config;
|
|
32
|
-
|
|
33
|
-
try {
|
|
34
|
-
if (ext === '.js') {
|
|
35
|
-
const modulePath = resolvedPath;
|
|
36
|
-
config = require(modulePath);
|
|
37
|
-
} else if (ext === '.json') {
|
|
38
|
-
const content = fs.readFileSync(resolvedPath, 'utf8');
|
|
39
|
-
config = JSON.parse(content);
|
|
40
|
-
} else if (ext === '.ts') {
|
|
41
|
-
const ts = require('typescript');
|
|
42
|
-
const content = fs.readFileSync(resolvedPath, 'utf8');
|
|
43
|
-
const result = ts.transpileModule(content, {
|
|
44
|
-
compilerOptions: {
|
|
45
|
-
module: 1, // CommonJS
|
|
46
|
-
target: 99, // ESNext
|
|
47
|
-
esModuleInterop: true,
|
|
48
|
-
},
|
|
49
|
-
});
|
|
50
|
-
const modulePath = resolvedPath.replace('.ts', '.js');
|
|
51
|
-
fs.writeFileSync(modulePath, result.outputText);
|
|
52
|
-
config = require(modulePath);
|
|
53
|
-
} else {
|
|
54
|
-
throw new Error(`Unsupported configuration file format. Use .js, .json, or .ts`);
|
|
55
|
-
}
|
|
56
|
-
} catch (error) {
|
|
57
|
-
throw new Error(`Failed to load configuration: ${error.message}`);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Merge CLI options with config, filtering out undefined values
|
|
61
|
-
const mergedConfig = { ...config };
|
|
62
|
-
Object.keys(cliOptions).forEach(key => {
|
|
63
|
-
if (cliOptions[key] !== undefined) {
|
|
64
|
-
mergedConfig[key] = cliOptions[key];
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
return mergedConfig;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Основной инструмент CLI
|
|
72
|
-
program
|
|
73
|
-
.version("1.0.0")
|
|
74
|
-
.description("Generate TypeScript API client from OpenAPI documentation")
|
|
75
|
-
.option("-c, --config <path>", "Path to configuration file")
|
|
76
|
-
.option("--api-docs-url <url>", "URL to fetch the OpenAPI documentation")
|
|
77
|
-
.option("--api-docs-path <path>", "Local path to OpenAPI documentation file")
|
|
78
|
-
.option("--output-dir <dir>", "Output directory for generated files")
|
|
79
|
-
.option("--clean", "Clean output directory before generation")
|
|
80
|
-
.parse(process.argv);
|
|
81
|
-
|
|
82
|
-
// Основной вызов
|
|
83
|
-
async function main() {
|
|
84
|
-
const options = program.opts();
|
|
85
|
-
let configPath = options.config;
|
|
86
|
-
|
|
87
|
-
// Автоматическое обнаружение конфигурационного файла, если он не указан явно
|
|
88
|
-
if (!configPath) {
|
|
89
|
-
const defaultConfigs = [
|
|
90
|
-
'api-docs-generator.config.js',
|
|
91
|
-
'api-docs-generator.config.json',
|
|
92
|
-
'api-docs-generator.config.ts'
|
|
93
|
-
];
|
|
94
|
-
|
|
95
|
-
for (const file of defaultConfigs) {
|
|
96
|
-
const filePath = path.join(process.cwd(), file);
|
|
97
|
-
if (fs.existsSync(filePath)) {
|
|
98
|
-
configPath = filePath;
|
|
99
|
-
console.log(`Auto-detected configuration file: ${file}`);
|
|
100
|
-
break;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
let config = null;
|
|
106
|
-
|
|
107
|
-
// Загрузка конфигурации из файла или использование CLI аргументов
|
|
108
|
-
if (configPath) {
|
|
109
|
-
try {
|
|
110
|
-
config = await loadConfig(configPath, {
|
|
111
|
-
apiDocsUrl: options.apiDocsUrl,
|
|
112
|
-
apiDocsPath: options.apiDocsPath,
|
|
113
|
-
outputDir: options.outputDir,
|
|
114
|
-
});
|
|
115
|
-
} catch (error) {
|
|
116
|
-
console.error(`Failed to load configuration: ${error.message}`);
|
|
117
|
-
process.exit(1);
|
|
118
|
-
}
|
|
119
|
-
} else {
|
|
120
|
-
// Режим обратной совместимости - использование CLI аргументов
|
|
121
|
-
const apiDocsUrl = options.apiDocsUrl;
|
|
122
|
-
const outputDir = options.outputDir || path.resolve(process.cwd(), "generated");
|
|
123
|
-
|
|
124
|
-
// Проверяем, указан ли URL для API документации
|
|
125
|
-
if (!apiDocsUrl) {
|
|
126
|
-
console.error('Error: API documentation URL is required.');
|
|
127
|
-
console.error('Please specify --api-docs-url or --api-docs-path, or create a configuration file.');
|
|
128
|
-
console.error('Run "api-docs-generator --help" for more information.');
|
|
129
|
-
process.exit(1);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
config = {
|
|
133
|
-
apiDocsUrl: apiDocsUrl,
|
|
134
|
-
outputDir: outputDir,
|
|
135
|
-
interfacesDir: path.join(outputDir, "interfaces"),
|
|
136
|
-
classesDir: path.join(outputDir, "classes"),
|
|
137
|
-
groupBy: "tag",
|
|
138
|
-
classMode: "multiple",
|
|
139
|
-
options: {
|
|
140
|
-
cleanOutputDir: options.clean !== undefined ? options.clean : true,
|
|
141
|
-
generateAxiosConfig: false,
|
|
142
|
-
generateIndexFiles: true,
|
|
143
|
-
},
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
console.log(`Using API Docs URL: ${config.apiDocsUrl}`);
|
|
148
|
-
console.log(`Output directory: ${config.outputDir || 'Not specified - using default'}`);
|
|
149
|
-
|
|
150
|
-
// Определяем пути
|
|
151
|
-
const outputDir = config.outputDir || path.resolve(process.cwd(), "generated");
|
|
152
|
-
const interfacesDir = config.interfacesDir || path.join(outputDir, "interfaces");
|
|
153
|
-
const classesDir = config.classesDir || path.join(outputDir, "classes");
|
|
154
|
-
const interfacesOpenApi = path.join(interfacesDir, "index.ts");
|
|
155
|
-
const classesOpenApi = path.join(classesDir, "index.ts");
|
|
156
|
-
|
|
157
|
-
// Очищаем папку, если нужно
|
|
158
|
-
if (config.options?.cleanOutputDir !== false) {
|
|
159
|
-
cleanGeneratedFolder(outputDir);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Обновляем API-документацию
|
|
163
|
-
if (config.apiDocsUrl) {
|
|
164
|
-
await updateApiDocsJson(config.apiDocsUrl, "api-docs.json");
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Генерация кода на основе спецификаций OpenAPI
|
|
168
|
-
const apiDocsPath = config.apiDocsPath || "api-docs.json";
|
|
169
|
-
await parseAndGenerate(apiDocsPath, config);
|
|
170
|
-
|
|
171
|
-
// Генерация конфигурации axios, если нужно
|
|
172
|
-
if (config.options?.generateAxiosConfig) {
|
|
173
|
-
generateAxiosConfig(config, outputDir);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Генерация файлов index.ts для интерфейсов
|
|
177
|
-
if (config.options?.generateIndexFiles !== false) {
|
|
178
|
-
await generateIndexFileWithOpenApi(interfacesDir, interfacesOpenApi);
|
|
179
|
-
await generateIndexFileWithOpenApi(classesDir, classesOpenApi);
|
|
180
|
-
await generateMainIndexFile(outputDir);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
console.log("Generation completed successfully.");
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// Вызов
|
|
187
|
-
main().catch((err) => console.error("Error during generation:", err));
|
package/src/map-type/mapType.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Маппинг типов OpenAPI в TypeScript.
|
|
3
|
-
* @param {string} openapiType - Тип из спецификации OpenAPI.
|
|
4
|
-
* @returns {string} - Соответствующий тип TypeScript.
|
|
5
|
-
*/
|
|
6
|
-
function mapType(openapiType) {
|
|
7
|
-
const typeMapping = {
|
|
8
|
-
string: "string",
|
|
9
|
-
integer: "number",
|
|
10
|
-
boolean: "boolean",
|
|
11
|
-
array: "any[]",
|
|
12
|
-
object: "Record<string, any>",
|
|
13
|
-
binary: "File",
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
return typeMapping[openapiType] || "any";
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
module.exports = {mapType};
|