arela
Version:
AI-powered CTO with multi-agent orchestration, code summarization, visual testing (web + mobile) for blazing fast development.
113 lines • 4.94 kB
JavaScript
/**
* Contract-driven client generator
* Generates type-safe API clients from OpenAPI 3.0 specifications
*/
import path from 'path';
import fs from 'fs-extra';
import { glob } from 'glob';
import ora from 'ora';
import colors from 'picocolors';
import { generateTypeScriptClient } from './typescript-generator.js';
export { generateTypeScriptClient } from './typescript-generator.js';
export * from './types.js';
export async function generateClient(options) {
const { language, contract, contractDir, outputDir = 'src/api', baseURL, dryRun } = options;
if (!contract && !contractDir) {
throw new Error('Either --contract or --contract-dir must be specified');
}
const outputPath = path.resolve(outputDir);
if (language === 'typescript') {
await generateTypeScriptClients(contract, contractDir, outputPath, baseURL, dryRun);
}
else if (language === 'python') {
throw new Error('Python client generation not yet implemented');
}
else {
throw new Error(`Unsupported language: ${language}`);
}
}
async function generateTypeScriptClients(contract, contractDir, outputDir, baseURL, dryRun) {
let specs = [];
if (contract) {
const resolvedPath = path.resolve(contract);
if (!fs.existsSync(resolvedPath)) {
throw new Error(`Contract file not found: ${contract}`);
}
specs = [resolvedPath];
}
else if (contractDir) {
const resolvedDir = path.resolve(contractDir);
if (!fs.existsSync(resolvedDir)) {
throw new Error(`Contract directory not found: ${contractDir}`);
}
specs = await glob(`${resolvedDir}/**/*.{yaml,yml,json}`, {
ignore: '**/node_modules/**',
});
if (specs.length === 0) {
throw new Error(`No OpenAPI specs found in ${contractDir}`);
}
}
console.log(`\n${colors.cyan('🎨 Generating TypeScript API Clients...')}\n`);
if (dryRun) {
console.log(colors.yellow('📋 DRY RUN - No files will be written\n'));
}
let totalFilesGenerated = 0;
let totalLinesOfCode = 0;
const errors = [];
for (const spec of specs) {
const spinner = ora({
text: `Processing ${path.basename(spec)}...`,
color: 'cyan',
}).start();
try {
if (dryRun) {
spinner.succeed(`${colors.green('✓')} Would generate client for ${path.basename(spec)}`);
}
else {
const result = await generateTypeScriptClient(spec, outputDir, baseURL);
if (result.success) {
totalFilesGenerated += result.filesGenerated.length;
totalLinesOfCode += result.linesOfCode;
spinner.succeed(`${colors.green('✓')} Generated ${result.filesGenerated.length} files (${result.linesOfCode} lines)`);
// Print file details
result.filesGenerated.forEach((file) => {
const relativePath = path.relative(process.cwd(), file);
console.log(` ${colors.gray('📝')} ${relativePath}`);
});
}
else {
spinner.fail(`Failed to generate client for ${path.basename(spec)}`);
if (result.errors) {
result.errors.forEach((err) => console.log(` ${colors.red('✗')} ${err}`));
}
errors.push(`${spec}: ${result.errors?.join(', ')}`);
}
}
}
catch (error) {
spinner.fail(`Error processing ${path.basename(spec)}`);
const errorMsg = error instanceof Error ? error.message : String(error);
console.log(` ${colors.red('✗')} ${errorMsg}`);
errors.push(`${spec}: ${errorMsg}`);
}
}
// Summary
console.log(`\n${colors.cyan('✨ Generation Summary')}`);
console.log(` ${colors.green('✓')} Specs processed: ${specs.length}`);
console.log(` ${colors.green('✓')} Files generated: ${totalFilesGenerated}`);
console.log(` ${colors.green('✓')} Lines of code: ${totalLinesOfCode}`);
if (errors.length > 0) {
console.log(`\n${colors.red('⚠ Errors encountered:')}`);
errors.forEach((err) => console.log(` ${colors.red('✗')} ${err}`));
}
if (!dryRun && totalFilesGenerated > 0) {
console.log(`\n${colors.green('💡 Usage:')}`);
console.log(` import { WorkoutApiClient } from '${path.relative(process.cwd(), outputDir)}/workout';`);
console.log(`\n const client = new WorkoutApiClient({`);
console.log(` baseURL: 'https://api.example.com'${baseURL ? ` // or '${baseURL}'` : ''}`);
console.log(` token: 'your-auth-token'`);
console.log(` });\n`);
}
console.log('');
}
//# sourceMappingURL=index.js.map