first commit
This commit is contained in:
311
generate.js
Executable file
311
generate.js
Executable file
@@ -0,0 +1,311 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { execSync } = require('child_process');
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const yaml = require('js-yaml');
|
||||
const { program } = require('commander');
|
||||
|
||||
// Colores para console (sin dependencias externas)
|
||||
const colors = {
|
||||
reset: '\x1b[0m',
|
||||
bright: '\x1b[1m',
|
||||
green: '\x1b[32m',
|
||||
blue: '\x1b[34m',
|
||||
yellow: '\x1b[33m',
|
||||
red: '\x1b[31m',
|
||||
cyan: '\x1b[36m'
|
||||
};
|
||||
|
||||
function log(message, color = 'reset') {
|
||||
console.log(`${colors[color]}${message}${colors.reset}`);
|
||||
}
|
||||
|
||||
function logSuccess(message) {
|
||||
log(`✅ ${message}`, 'green');
|
||||
}
|
||||
|
||||
function logInfo(message) {
|
||||
log(`ℹ️ ${message}`, 'blue');
|
||||
}
|
||||
|
||||
function logWarning(message) {
|
||||
log(`⚠️ ${message}`, 'yellow');
|
||||
}
|
||||
|
||||
function logError(message) {
|
||||
log(`❌ ${message}`, 'red');
|
||||
}
|
||||
|
||||
function logStep(message) {
|
||||
log(`\n🚀 ${message}`, 'cyan');
|
||||
}
|
||||
|
||||
// Configuración del CLI
|
||||
program
|
||||
.name('generate-clean-arch')
|
||||
.description('Generador de código Angular con Clean Architecture desde OpenAPI/Swagger')
|
||||
.version('1.0.0')
|
||||
.option('-i, --input <file>', 'Archivo OpenAPI/Swagger (yaml o json)', 'swagger.yaml')
|
||||
.option('-o, --output <dir>', 'Directorio de salida', './src/app')
|
||||
.option('-t, --templates <dir>', 'Directorio de templates personalizados', './templates')
|
||||
.option('--skip-install', 'No instalar dependencias')
|
||||
.option('--dry-run', 'Simular sin generar archivos')
|
||||
.parse(process.argv);
|
||||
|
||||
const options = program.opts();
|
||||
|
||||
// Validar que existe openapi-generator-cli
|
||||
function checkOpenApiGenerator() {
|
||||
try {
|
||||
execSync('openapi-generator-cli version', { stdio: 'ignore' });
|
||||
return true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Instalar openapi-generator-cli
|
||||
function installOpenApiGenerator() {
|
||||
logStep('Instalando @openapitools/openapi-generator-cli...');
|
||||
try {
|
||||
execSync('npm install -g @openapitools/openapi-generator-cli', { stdio: 'inherit' });
|
||||
logSuccess('OpenAPI Generator CLI instalado correctamente');
|
||||
} catch (error) {
|
||||
logError('Error al instalar OpenAPI Generator CLI');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Crear estructura de directorios
|
||||
function createDirectoryStructure(baseDir) {
|
||||
const dirs = [
|
||||
path.join(baseDir, 'data/dtos'),
|
||||
path.join(baseDir, 'data/repositories'),
|
||||
path.join(baseDir, 'data/mappers'),
|
||||
path.join(baseDir, 'domain/repositories'),
|
||||
path.join(baseDir, 'domain/use-cases'),
|
||||
path.join(baseDir, 'di/repositories'),
|
||||
path.join(baseDir, 'di/use-cases'),
|
||||
path.join(baseDir, 'entities/models')
|
||||
];
|
||||
|
||||
dirs.forEach(dir => {
|
||||
fs.ensureDirSync(dir);
|
||||
});
|
||||
|
||||
logSuccess('Estructura de directorios creada');
|
||||
}
|
||||
|
||||
// Analizar el swagger para extraer tags y dominios
|
||||
function analyzeSwagger(swaggerFile) {
|
||||
logStep('Analizando archivo OpenAPI...');
|
||||
|
||||
try {
|
||||
const fileContent = fs.readFileSync(swaggerFile, 'utf8');
|
||||
const swagger = yaml.load(fileContent);
|
||||
|
||||
const tags = swagger.tags || [];
|
||||
const paths = swagger.paths || {};
|
||||
|
||||
logInfo(`Encontrados ${tags.length} tags en el API`);
|
||||
logInfo(`Encontrados ${Object.keys(paths).length} endpoints`);
|
||||
|
||||
tags.forEach(tag => {
|
||||
logInfo(` - ${tag.name}: ${tag.description || 'Sin descripción'}`);
|
||||
});
|
||||
|
||||
return { tags, paths, swagger };
|
||||
} catch (error) {
|
||||
logError(`Error al leer el archivo Swagger: ${error.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Generar código con OpenAPI Generator
|
||||
function generateCode(swaggerFile, templatesDir) {
|
||||
logStep('Generando código desde OpenAPI...');
|
||||
|
||||
const tempDir = path.join(process.cwd(), '.temp-generated');
|
||||
|
||||
// Limpiar directorio temporal
|
||||
if (fs.existsSync(tempDir)) {
|
||||
fs.removeSync(tempDir);
|
||||
}
|
||||
|
||||
try {
|
||||
const command = `openapi-generator-cli generate \
|
||||
-i "${swaggerFile}" \
|
||||
-g typescript-angular \
|
||||
-t "${templatesDir}" \
|
||||
-o "${tempDir}" \
|
||||
--additional-properties=ngVersion=17.0.0,withInterfaces=true,providedInRoot=false,supportsES6=true,modelPropertyNaming=camelCase`;
|
||||
|
||||
execSync(command, { stdio: 'inherit' });
|
||||
logSuccess('Código generado correctamente');
|
||||
|
||||
return tempDir;
|
||||
} catch (error) {
|
||||
logError('Error al generar código');
|
||||
if (fs.existsSync(tempDir)) {
|
||||
fs.removeSync(tempDir);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Organizar archivos según Clean Architecture
|
||||
function organizeFiles(tempDir, outputDir) {
|
||||
logStep('Organizando archivos en estructura Clean Architecture...');
|
||||
|
||||
const moves = [
|
||||
{ from: 'model', to: path.join(outputDir, 'data/dtos'), pattern: '*.dto.ts' },
|
||||
{ from: 'model', to: path.join(outputDir, 'entities/models'), pattern: '*.model.ts' },
|
||||
{ from: 'api', to: path.join(outputDir, 'domain/repositories'), pattern: '*.repository.contract.ts' },
|
||||
{ from: 'api', to: path.join(outputDir, 'data/repositories'), pattern: '*.repository.impl.ts' },
|
||||
{ from: 'api', to: path.join(outputDir, 'data/repositories'), pattern: '*.repository.mock.ts' },
|
||||
{ from: 'api', to: path.join(outputDir, 'data/mappers'), pattern: '*.mapper.ts' },
|
||||
{ from: 'api', to: path.join(outputDir, 'domain/use-cases'), pattern: '*.use-cases.contract.ts' },
|
||||
{ from: 'api', to: path.join(outputDir, 'domain/use-cases'), pattern: '*.use-cases.impl.ts' },
|
||||
{ from: 'providers', to: path.join(outputDir, 'di/repositories'), pattern: '*.repository.provider.ts' },
|
||||
{ from: 'providers', to: path.join(outputDir, 'di/use-cases'), pattern: '*.use-cases.provider.ts' }
|
||||
];
|
||||
|
||||
let filesMoved = 0;
|
||||
|
||||
moves.forEach(({ from, to, pattern }) => {
|
||||
const sourceDir = path.join(tempDir, from);
|
||||
|
||||
if (fs.existsSync(sourceDir)) {
|
||||
fs.ensureDirSync(to);
|
||||
|
||||
const files = fs.readdirSync(sourceDir).filter(file => {
|
||||
if (pattern.includes('*')) {
|
||||
const regex = new RegExp(pattern.replace('*', '.*'));
|
||||
return regex.test(file);
|
||||
}
|
||||
return file.endsWith(pattern);
|
||||
});
|
||||
|
||||
files.forEach(file => {
|
||||
const sourcePath = path.join(sourceDir, file);
|
||||
const destPath = path.join(to, file);
|
||||
|
||||
fs.copySync(sourcePath, destPath);
|
||||
filesMoved++;
|
||||
logInfo(` ${file} → ${path.relative(process.cwd(), destPath)}`);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
logSuccess(`${filesMoved} archivos organizados correctamente`);
|
||||
}
|
||||
|
||||
// Limpiar directorio temporal
|
||||
function cleanup(tempDir) {
|
||||
if (fs.existsSync(tempDir)) {
|
||||
fs.removeSync(tempDir);
|
||||
logInfo('Archivos temporales eliminados');
|
||||
}
|
||||
}
|
||||
|
||||
// Generar reporte
|
||||
function generateReport(outputDir, analysis) {
|
||||
logStep('Generando reporte de generación...');
|
||||
|
||||
const report = {
|
||||
timestamp: new Date().toISOString(),
|
||||
tags: analysis.tags.length,
|
||||
endpoints: Object.keys(analysis.paths).length,
|
||||
outputDirectory: outputDir,
|
||||
structure: {
|
||||
dtos: fs.readdirSync(path.join(outputDir, 'data/dtos')).length,
|
||||
repositories: fs.readdirSync(path.join(outputDir, 'data/repositories')).length,
|
||||
mappers: fs.readdirSync(path.join(outputDir, 'data/mappers')).length,
|
||||
useCases: fs.readdirSync(path.join(outputDir, 'domain/use-cases')).length
|
||||
}
|
||||
};
|
||||
|
||||
const reportPath = path.join(process.cwd(), 'generation-report.json');
|
||||
fs.writeJsonSync(reportPath, report, { spaces: 2 });
|
||||
|
||||
logSuccess(`Reporte guardado en: ${reportPath}`);
|
||||
|
||||
return report;
|
||||
}
|
||||
|
||||
// Función principal
|
||||
async function main() {
|
||||
console.log('\n' + '='.repeat(60));
|
||||
log(' OpenAPI Clean Architecture Generator', 'bright');
|
||||
log(' Angular + Clean Architecture Code Generator', 'cyan');
|
||||
console.log('='.repeat(60) + '\n');
|
||||
|
||||
// Validar archivo de entrada
|
||||
if (!fs.existsSync(options.input)) {
|
||||
logError(`Archivo no encontrado: ${options.input}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
logInfo(`Archivo de entrada: ${options.input}`);
|
||||
logInfo(`Directorio de salida: ${options.output}`);
|
||||
logInfo(`Templates: ${options.templates}`);
|
||||
|
||||
if (options.dryRun) {
|
||||
logWarning('Modo DRY RUN - No se generarán archivos');
|
||||
}
|
||||
|
||||
// Verificar/Instalar OpenAPI Generator
|
||||
if (!checkOpenApiGenerator()) {
|
||||
logWarning('OpenAPI Generator CLI no encontrado');
|
||||
if (!options.skipInstall) {
|
||||
installOpenApiGenerator();
|
||||
} else {
|
||||
logError('Instala openapi-generator-cli con: npm install -g @openapitools/openapi-generator-cli');
|
||||
process.exit(1);
|
||||
}
|
||||
} else {
|
||||
logSuccess('OpenAPI Generator CLI encontrado');
|
||||
}
|
||||
|
||||
// Analizar Swagger
|
||||
const analysis = analyzeSwagger(options.input);
|
||||
|
||||
if (options.dryRun) {
|
||||
logInfo('Finalizando en modo DRY RUN');
|
||||
return;
|
||||
}
|
||||
|
||||
// Crear estructura de directorios
|
||||
createDirectoryStructure(options.output);
|
||||
|
||||
// Generar código
|
||||
const tempDir = generateCode(options.input, options.templates);
|
||||
|
||||
// Organizar archivos
|
||||
organizeFiles(tempDir, options.output);
|
||||
|
||||
// Limpiar
|
||||
cleanup(tempDir);
|
||||
|
||||
// Generar reporte
|
||||
const report = generateReport(options.output, analysis);
|
||||
|
||||
// Resumen final
|
||||
console.log('\n' + '='.repeat(60));
|
||||
log(' ✨ Generación completada con éxito', 'green');
|
||||
console.log('='.repeat(60));
|
||||
console.log(`\n📊 Resumen:`);
|
||||
console.log(` - DTOs generados: ${report.structure.dtos}`);
|
||||
console.log(` - Repositories: ${report.structure.repositories}`);
|
||||
console.log(` - Mappers: ${report.structure.mappers}`);
|
||||
console.log(` - Use Cases: ${report.structure.useCases}`);
|
||||
console.log(`\n📁 Archivos generados en: ${colors.cyan}${options.output}${colors.reset}\n`);
|
||||
}
|
||||
|
||||
// Ejecutar
|
||||
main().catch(error => {
|
||||
logError(`Error fatal: ${error.message}`);
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user