From 21c7ddfd54346eff382ac649c791ca51a993b240 Mon Sep 17 00:00:00 2001 From: "S.Gromov" Date: Tue, 28 Oct 2025 09:18:08 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=B0=D0=B2=D1=82=D0=BE=D0=BC=D0=B0?= =?UTF-8?q?=D1=82=D0=B8=D1=87=D0=B5=D1=81=D0=BA=D0=BE=D0=B5=20=D0=BE=D0=BF?= =?UTF-8?q?=D1=80=D0=B5=D0=B4=D0=B5=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20baseUr?= =?UTF-8?q?l,=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B0=20?= =?UTF-8?q?=D0=B4=D0=BE=D0=BA=D1=83=D0=BC=D0=B5=D0=BD=D1=82=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AI-PROJECT-OVERVIEW.md | 107 +++++++++++++++++++++++++++++++++++++++++ src/cli.ts | 2 - src/config.ts | 20 -------- src/generator.ts | 36 +++----------- 4 files changed, 115 insertions(+), 50 deletions(-) create mode 100644 AI-PROJECT-OVERVIEW.md diff --git a/AI-PROJECT-OVERVIEW.md b/AI-PROJECT-OVERVIEW.md new file mode 100644 index 0000000..0c4811a --- /dev/null +++ b/AI-PROJECT-OVERVIEW.md @@ -0,0 +1,107 @@ +# API CodeGen - Описание проекта для AI агента + +## Назначение +CLI утилита для автоматической генерации TypeScript API клиента из OpenAPI/Swagger спецификации. + +## Архитектура + +### Основные компоненты +1. **CLI** ([`src/cli.ts`](src/cli.ts:1)) - точка входа, парсинг аргументов командной строки +2. **Generator** ([`src/generator.ts`](src/generator.ts:1)) - логика генерации кода через swagger-typescript-api +3. **Config** ([`src/config.ts`](src/config.ts:1)) - валидация конфигурации +4. **Templates** (`src/templates/*.ejs`) - EJS шаблоны для генерации кода + +### Технологический стек +- **Runtime**: Bun +- **Языки**: TypeScript +- **Зависимости**: + - `swagger-typescript-api` - основной генератор + - `commander` - парсинг CLI аргументов + - `ejs` - шаблонизатор + - `js-yaml` - парсинг YAML + - `chalk` - цветной вывод в консоль + +## Рабочий процесс + +1. **Входные данные**: OpenAPI спецификация (JSON/YAML файл или URL) +2. **Обработка**: + - Валидация конфигурации + - Чтение OpenAPI спецификации + - Применение кастомных шаблонов EJS + - Генерация TypeScript кода +3. **Выходные данные**: 3 файла в указанной директории: + - `{FileName}.ts` - API endpoints с методами + - `http-client.ts` - HTTP клиент с настройками + - `data-contracts.ts` - TypeScript типы + +## Ключевые особенности + +### Кастомизация имен методов +Хук [`onFormatRouteName`](src/generator.ts:76) убирает суффикс "Controller" из имен методов: +- `projectControllerUpdate` → `update` +- `authControllerLogin` → `login` + +### Извлечение baseUrl +Хук [`onInit`](src/generator.ts:91) автоматически извлекает baseUrl из OpenAPI спецификации (`servers[0].url`) + +### Поддержка источников +- Локальные файлы: `./openapi.json` +- URL: `https://api.example.com/openapi.json` + +## Использование + +```bash +api-codegen -i <путь-к-openapi> -o <выходная-директория> [-n <имя-файла>] +``` + +### Аргументы +- `-i, --input` - путь к OpenAPI файлу (локальный или URL) +- `-o, --output` - директория для сохранения +- `-n, --name` - опциональное имя файла (по умолчанию из `spec.info.title`) + +## Структура проекта + +``` +src/ +├── cli.ts # CLI интерфейс +├── config.ts # Типы и валидация конфигурации +├── generator.ts # Основная логика генерации +├── templates/ # EJS шаблоны +│ ├── api.ejs +│ ├── http-client.ejs +│ ├── data-contracts.ejs +│ └── ... +└── utils/ + └── file.ts # Утилиты для работы с файлами +``` + +## Конфигурация генератора + +[`swaggerGenerateApi`](src/generator.ts:38) настроен с параметрами: +- `httpClientType: 'fetch'` - использование Fetch API +- `unwrapResponseData: true` - автоматическая распаковка данных ответа +- `singleHttpClient: true` - единый HTTP клиент +- `extractRequestParams: true` - извлечение параметров запросов +- `extractRequestBody: true` - извлечение тел запросов +- `extractEnums: true` - извлечение enum типов + +## Примеры использования сгенерированного кода + +```typescript +import { Api, HttpClient } from './Api'; + +const httpClient = new HttpClient(); +httpClient.setSecurityData({ token: 'jwt-token' }); + +const api = new Api(httpClient); + +// Вызов API методов +const user = await api.auth.getProfile(); +const result = await api.auth.login({ email, password }); +``` + +## Точки расширения + +1. **Шаблоны** - можно модифицировать EJS шаблоны в `src/templates/` +2. **Хуки генератора** - можно добавить новые хуки в [`hooks`](src/generator.ts:75) +3. **Конфигурация** - можно расширить [`GeneratorConfig`](src/config.ts:4) для новых опций \ No newline at end of file diff --git a/src/cli.ts b/src/cli.ts index b42bffb..5b46bdd 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -12,7 +12,6 @@ program .name('api-codegen') .description('Generate TypeScript API client from OpenAPI specification') .version('1.0.0') - .requiredOption('-u, --url ', 'Base API URL (e.g., https://api.example.com)') .requiredOption('-i, --input ', 'Path to OpenAPI specification file (JSON or YAML)') .requiredOption('-o, --output ', 'Output directory for generated files') .option('-n, --name ', 'Name of generated file (without extension)') @@ -20,7 +19,6 @@ program try { // Создание конфигурации const config: Partial = { - apiUrl: options.url, inputPath: options.input, outputPath: options.output, fileName: options.name, diff --git a/src/config.ts b/src/config.ts index 87a55d8..b7424eb 100644 --- a/src/config.ts +++ b/src/config.ts @@ -2,8 +2,6 @@ * Конфигурация генератора API */ export interface GeneratorConfig { - /** Базовый URL API */ - apiUrl: string; /** Путь к файлу OpenAPI спецификации */ inputPath: string; /** Путь для сохранения сгенерированных файлов */ @@ -18,12 +16,6 @@ export interface GeneratorConfig { export function validateConfig(config: Partial): config is GeneratorConfig { const errors: string[] = []; - if (!config.apiUrl) { - errors.push('API URL is required (--url)'); - } else if (!isValidUrl(config.apiUrl)) { - errors.push('API URL must be a valid URL'); - } - if (!config.inputPath) { errors.push('Input path is required (--input)'); } @@ -39,15 +31,3 @@ export function validateConfig(config: Partial): config is Gene return true; } -/** - * Проверка валидности URL - */ -function isValidUrl(url: string): boolean { - try { - new URL(url); - return true; - } catch { - return false; - } -} - diff --git a/src/generator.ts b/src/generator.ts index 89f223e..f014b57 100644 --- a/src/generator.ts +++ b/src/generator.ts @@ -1,10 +1,9 @@ import { generateApi as swaggerGenerateApi } from 'swagger-typescript-api'; -import { resolve, join } from 'path'; +import { resolve } from 'path'; import { fileURLToPath } from 'url'; import { dirname } from 'path'; -import { unlink } from 'fs/promises'; import type { GeneratorConfig } from './config.js'; -import { ensureDir, readJsonFile, writeFileWithDirs } from './utils/file.js'; +import { ensureDir, readJsonFile } from './utils/file.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); @@ -20,7 +19,7 @@ export async function generate(config: GeneratorConfig): Promise { const templatesPath = resolve(__dirname, '../src/templates'); - // Читаем и модифицируем OpenAPI спецификацию + // Читаем OpenAPI спецификацию const inputPath = config.inputPath.startsWith('http://') || config.inputPath.startsWith('https://') ? config.inputPath : resolve(config.inputPath); @@ -34,17 +33,10 @@ export async function generate(config: GeneratorConfig): Promise { ? spec.info.title.replace(/[^a-zA-Z0-9]/g, '') : 'Api'; } - - // Добавляем или обновляем servers с baseUrl - spec.servers = [{ url: config.apiUrl }]; - - // Сохраняем модифицированную спецификацию во временный файл - const tempSpecPath = join(config.outputPath, '.openapi-temp.json'); - await writeFileWithDirs(tempSpecPath, JSON.stringify(spec, null, 2)); try { await swaggerGenerateApi({ - input: tempSpecPath, + input: inputPath, output: resolve(config.outputPath), fileName: `${fileName}.ts`, httpClientType: 'fetch', @@ -96,10 +88,11 @@ export async function generate(config: GeneratorConfig): Promise { return templateRouteName; }, - onInit: (configuration, codeGenProcess) => { - // Передаём baseUrl в конфигурацию для шаблонов + onInit: (configuration) => { + // Получаем дефолтный baseUrl из OpenAPI спецификации + const defaultBaseUrl = spec.servers?.[0]?.url || ''; (configuration as any).apiConfig = (configuration as any).apiConfig || {}; - (configuration as any).apiConfig.baseUrl = config.apiUrl; + (configuration as any).apiConfig.baseUrl = defaultBaseUrl; return configuration; }, }, @@ -109,21 +102,8 @@ export async function generate(config: GeneratorConfig): Promise { console.log(` - ${fileName}.ts (API endpoints)`); console.log(' - http-client.ts (HTTP client)'); console.log(' - data-contracts.ts (TypeScript types)'); - - // Удаляем временный файл - try { - await unlink(tempSpecPath); - } catch (e) { - // Игнорируем ошибки удаления - } } catch (error) { console.error('❌ Generation failed:', error); - // Удаляем временный файл даже при ошибке - try { - await unlink(tempSpecPath); - } catch (e) { - // Игнорируем ошибки удаления - } throw error; } }