UNPKG

weaver-frontend-cli

Version:

🕷️ Weaver CLI - Generador completo de arquitectura Clean Architecture con parser OpenAPI avanzado para entidades CRUD y flujos de negocio complejos

899 lines (833 loc) 53.3 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.createCorrectEntityFlow = createCorrectEntityFlow; const fs = __importStar(require("fs-extra")); const path = __importStar(require("path")); const chalk_1 = __importDefault(require("chalk")); // Ruta base donde se ejecutará el comando const DEFAULT_BASE_PATH = process.cwd(); // Directorio actual por defecto const LOCAL_TEST_PATH = './test-output'; async function createCorrectEntityFlow(entityName, basePath = DEFAULT_BASE_PATH, schema, targetApiName = 'platform') { console.log(chalk_1.default.blue(`📁 Generando flujo completo para ${entityName} en: ${basePath}`)); const entityNameLower = entityName.toLowerCase(); const entityNameKebab = entityName.replace(/([A-Z])/g, '-$1').toLowerCase().substring(1); // Verificar que el directorio base existe if (!await fs.pathExists(basePath)) { throw new Error(`El directorio base no existe: ${basePath}`); } // SIEMPRE usar el directorio base seleccionado, NUNCA crear carpetas adicionales // El targetApiName se usa solo para la estructura apis/{targetApiName}/ const apiPrefix = ''; const paths = { // Domain DTOs domainModels: path.join(basePath, `${apiPrefix}domain/models/apis/${targetApiName}/entities/${entityNameLower}`), // Domain Repository Interface domainRepository: path.join(basePath, `${apiPrefix}domain/services/repositories/apis/${targetApiName}/entities`), // Domain Use Cases domainUseCases: path.join(basePath, `${apiPrefix}domain/services/use_cases/apis/${targetApiName}/entities/${entityNameLower}`), // Infrastructure Entities infraEntities: path.join(basePath, `${apiPrefix}infrastructure/entities/apis/${targetApiName}/entities/${entityNameLower}`), // Infrastructure Mappers infraMappers: path.join(basePath, `${apiPrefix}infrastructure/mappers/apis/${targetApiName}/entities/${entityNameLower}`), // Infrastructure Repository infraRepository: path.join(basePath, `${apiPrefix}infrastructure/repositories/apis/${targetApiName}/repositories/entities/${entityNameLower}`), // Facade facade: path.join(basePath, `${apiPrefix}facade/apis/${targetApiName}/entities`), // Injection folders useCaseInjection: path.join(basePath, `${apiPrefix}domain/services/use_cases/apis/${targetApiName}/injection/entities`), mapperInjection: path.join(basePath, `${apiPrefix}infrastructure/mappers/apis/${targetApiName}/injection/entities`), repositoryInjection: path.join(basePath, `${apiPrefix}infrastructure/repositories/apis/${targetApiName}/repositories/injection/entities`), facadeInjection: path.join(basePath, `${apiPrefix}facade/apis/${targetApiName}/injection/entities`) }; try { // Crear directorios necesarios for (const dirPath of Object.values(paths)) { await fs.ensureDir(dirPath); console.log(chalk_1.default.gray(`📂 Directorio creado/verificado: ${dirPath}`)); } // Generar archivos según el patrón correcto await generateDomainDTOs(entityName, paths, schema, targetApiName); await generateDomainRepositoryInterface(entityName, paths, schema, targetApiName); await generateDomainUseCases(entityName, paths, schema, targetApiName); await generateInfrastructureEntities(entityName, paths, schema, targetApiName); await generateInfrastructureMappers(entityName, paths, schema, targetApiName); await generateInfrastructureRepository(entityName, paths, schema, targetApiName); await generateFacade(entityName, paths, schema, targetApiName); await generateInjectionFiles(entityName, paths, schema, targetApiName); await generateFacadeInjection(entityName, paths.facadeInjection, targetApiName); console.log(chalk_1.default.green(`✨ Flujo ${entityName} generado exitosamente siguiendo el patrón correcto!`)); } catch (error) { console.error(chalk_1.default.red('❌ Error generando archivos:'), error); throw error; } } async function generateDomainDTOs(entityName, paths, schema, apiName = 'platform') { const entityNameLower = entityName.toLowerCase(); const entityNameKebab = entityName.replace(/([A-Z])/g, '-$1').toLowerCase().substring(1); const fields = schema?.fields || getDefaultFields(); // 1. IEntityDTO - Modelo principal const dtoInterface = generateDTOInterface(entityName, fields, 'main'); await fs.writeFile(path.join(paths.domainModels, `i-${entityNameKebab}-dto.ts`), dtoInterface); console.log(chalk_1.default.green(`✅ DTO principal: i-${entityNameKebab}-dto.ts`)); // 2. IEntitySaveDTO const saveDtoInterface = generateDTOInterface(entityName, fields, 'save'); await fs.writeFile(path.join(paths.domainModels, `i-${entityNameKebab}-save-dto.ts`), saveDtoInterface); console.log(chalk_1.default.green(`✅ Save DTO: i-${entityNameKebab}-save-dto.ts`)); // 3. IEntityReadDTO const readDtoInterface = generateDTOInterface(entityName, fields, 'read'); await fs.writeFile(path.join(paths.domainModels, `i-${entityNameKebab}-read-dto.ts`), readDtoInterface); console.log(chalk_1.default.green(`✅ Read DTO: i-${entityNameKebab}-read-dto.ts`)); // 4. IEntityUpdateDTO const updateDtoInterface = generateDTOInterface(entityName, fields, 'update'); await fs.writeFile(path.join(paths.domainModels, `i-${entityNameKebab}-update-dto.ts`), updateDtoInterface); console.log(chalk_1.default.green(`✅ Update DTO: i-${entityNameKebab}-update-dto.ts`)); // 5. IEntityDeleteDTO const deleteDtoInterface = generateDTOInterface(entityName, fields, 'delete'); await fs.writeFile(path.join(paths.domainModels, `i-${entityNameKebab}-delete-dto.ts`), deleteDtoInterface); console.log(chalk_1.default.green(`✅ Delete DTO: i-${entityNameKebab}-delete-dto.ts`)); // 6. Index file para exportar todos los DTOs const indexContent = `export { I${entityName}DTO } from './i-${entityNameKebab}-dto'; export { I${entityName}SaveDTO } from './i-${entityNameKebab}-save-dto'; export { I${entityName}ReadDTO } from './i-${entityNameKebab}-read-dto'; export { I${entityName}UpdateDTO } from './i-${entityNameKebab}-update-dto'; export { I${entityName}DeleteDTO } from './i-${entityNameKebab}-delete-dto';`; await fs.writeFile(path.join(paths.domainModels, 'index.ts'), indexContent); console.log(chalk_1.default.green(`✅ Index DTO: index.ts`)); } async function generateDomainRepositoryInterface(entityName, paths, schema, apiName = 'platform') { const entityNameKebab = entityName.replace(/([A-Z])/g, '-$1').toLowerCase().substring(1); const repositoryInterface = `import { IConfigDTO } from "@core/interfaces/i-config-repository-dto"; import { IPaginationBackendDTO } from "@core/interfaces/i-pagination-backend-dto"; import { I${entityName}DTO } from "@${apiName}/domain/models/apis/${apiName}/entities/${entityName.toLowerCase()}"; import { I${entityName}DeleteEntity, I${entityName}ReadEntity, I${entityName}SaveEntity, I${entityName}UpdateEntity, } from "@${apiName}/infrastructure/entities/apis/${apiName}/entities/${entityName.toLowerCase()}"; export abstract class I${entityName}Repository { abstract read(params: I${entityName}ReadEntity, config: IConfigDTO): Promise<I${entityName}DTO | null>; abstract save(params: I${entityName}SaveEntity, config: IConfigDTO): Promise<I${entityName}DTO | null>; abstract update(params: I${entityName}UpdateEntity, config: IConfigDTO): Promise<I${entityName}DTO | null>; abstract delete(params: I${entityName}DeleteEntity, config: IConfigDTO): Promise<I${entityName}DTO | null>; abstract list(params: IPaginationBackendDTO, config: IConfigDTO): Promise<I${entityName}DTO[] | null>; }`; await fs.writeFile(path.join(paths.domainRepository, `i-${entityNameKebab}-repository.ts`), repositoryInterface); console.log(chalk_1.default.green(`✅ Repository interface: i-${entityNameKebab}-repository.ts`)); } async function generateDomainUseCases(entityName, paths, schema, apiName = 'platform') { const entityNameLower = entityName.toLowerCase(); const entityNameKebab = entityName.replace(/([A-Z])/g, '-$1').toLowerCase().substring(1); // 1. Save Use Case const saveUseCase = `import { IConfigDTO } from "@core/interfaces/i-config-repository-dto"; import { UseCase } from "@core/interfaces/use-case"; import { I${entityName}DTO, I${entityName}SaveDTO } from "@${apiName}/domain/models/apis/${apiName}/entities/${entityNameLower}"; import { InjectionPlatformEntities${entityName}Mapper } from "@${apiName}/infrastructure/mappers/apis/${apiName}/injection/entities/injection-${apiName}-entities-${entityNameKebab}-mapper"; import { InjectionPlatformEntitiesRepository } from "@${apiName}/infrastructure/repositories/apis/${apiName}/repositories/injection/entities/injection-${apiName}-entities-repository"; export class ${entityName}SaveUseCase implements UseCase<I${entityName}SaveDTO, I${entityName}DTO | null> { private static instance: ${entityName}SaveUseCase; private repository = InjectionPlatformEntitiesRepository.${entityName}Repository(); private mapper = InjectionPlatformEntities${entityName}Mapper.${entityName}SaveMapper(); public static getInstance(): ${entityName}SaveUseCase { if (!${entityName}SaveUseCase.instance) ${entityName}SaveUseCase.instance = new ${entityName}SaveUseCase(); return ${entityName}SaveUseCase.instance; } public async execute(params: I${entityName}SaveDTO, config?: IConfigDTO): Promise<I${entityName}DTO | null> { const paramsEntity = this.mapper.mapTo(params); return await this.repository.save(paramsEntity, config).then((data) => data ?? null); } }`; await fs.writeFile(path.join(paths.domainUseCases, `${entityNameKebab}-save-use-case.ts`), saveUseCase); console.log(chalk_1.default.green(`✅ Save Use Case: ${entityNameKebab}-save-use-case.ts`)); // 2. Read Use Case const readUseCase = `import { IConfigDTO } from "@core/interfaces/i-config-repository-dto"; import { UseCase } from "@core/interfaces/use-case"; import { I${entityName}DTO, I${entityName}ReadDTO } from "@${apiName}/domain/models/apis/${apiName}/entities/${entityNameLower}"; import { InjectionPlatformEntities${entityName}Mapper } from "@${apiName}/infrastructure/mappers/apis/${apiName}/injection/entities/injection-${apiName}-entities-${entityNameKebab}-mapper"; import { InjectionPlatformEntitiesRepository } from "@${apiName}/infrastructure/repositories/apis/${apiName}/repositories/injection/entities/injection-${apiName}-entities-repository"; export class ${entityName}ReadUseCase implements UseCase<I${entityName}ReadDTO, I${entityName}DTO | null> { private static instance: ${entityName}ReadUseCase; private repository = InjectionPlatformEntitiesRepository.${entityName}Repository(); private mapper = InjectionPlatformEntities${entityName}Mapper.${entityName}ReadMapper(); public static getInstance(): ${entityName}ReadUseCase { if (!${entityName}ReadUseCase.instance) ${entityName}ReadUseCase.instance = new ${entityName}ReadUseCase(); return ${entityName}ReadUseCase.instance; } public async execute(params: I${entityName}ReadDTO, config?: IConfigDTO): Promise<I${entityName}DTO | null> { const paramsEntity = this.mapper.mapTo(params); return await this.repository.read(paramsEntity, config).then((data) => data ?? null); } }`; await fs.writeFile(path.join(paths.domainUseCases, `${entityNameKebab}-read-use-case.ts`), readUseCase); console.log(chalk_1.default.green(`✅ Read Use Case: ${entityNameKebab}-read-use-case.ts`)); // 3. Update Use Case const updateUseCase = `import { IConfigDTO } from "@core/interfaces/i-config-repository-dto"; import { UseCase } from "@core/interfaces/use-case"; import { I${entityName}DTO, I${entityName}UpdateDTO } from "@${apiName}/domain/models/apis/${apiName}/entities/${entityNameLower}"; import { InjectionPlatformEntities${entityName}Mapper } from "@${apiName}/infrastructure/mappers/apis/${apiName}/injection/entities/injection-${apiName}-entities-${entityNameKebab}-mapper"; import { InjectionPlatformEntitiesRepository } from "@${apiName}/infrastructure/repositories/apis/${apiName}/repositories/injection/entities/injection-${apiName}-entities-repository"; export class ${entityName}UpdateUseCase implements UseCase<I${entityName}UpdateDTO, I${entityName}DTO | null> { private static instance: ${entityName}UpdateUseCase; private repository = InjectionPlatformEntitiesRepository.${entityName}Repository(); private mapper = InjectionPlatformEntities${entityName}Mapper.${entityName}UpdateMapper(); public static getInstance(): ${entityName}UpdateUseCase { if (!${entityName}UpdateUseCase.instance) ${entityName}UpdateUseCase.instance = new ${entityName}UpdateUseCase(); return ${entityName}UpdateUseCase.instance; } public async execute(params: I${entityName}UpdateDTO, config?: IConfigDTO): Promise<I${entityName}DTO | null> { const paramsEntity = this.mapper.mapTo(params); return await this.repository.update(paramsEntity, config).then((data) => data ?? null); } }`; await fs.writeFile(path.join(paths.domainUseCases, `${entityNameKebab}-update-use-case.ts`), updateUseCase); console.log(chalk_1.default.green(`✅ Update Use Case: ${entityNameKebab}-update-use-case.ts`)); // 4. Delete Use Case const deleteUseCase = `import { IConfigDTO } from "@core/interfaces/i-config-repository-dto"; import { UseCase } from "@core/interfaces/use-case"; import { I${entityName}DTO, I${entityName}DeleteDTO } from "@${apiName}/domain/models/apis/${apiName}/entities/${entityNameLower}"; import { InjectionPlatformEntities${entityName}Mapper } from "@${apiName}/infrastructure/mappers/apis/${apiName}/injection/entities/injection-${apiName}-entities-${entityNameKebab}-mapper"; import { InjectionPlatformEntitiesRepository } from "@${apiName}/infrastructure/repositories/apis/${apiName}/repositories/injection/entities/injection-${apiName}-entities-repository"; export class ${entityName}DeleteUseCase implements UseCase<I${entityName}DeleteDTO, I${entityName}DTO | null> { private static instance: ${entityName}DeleteUseCase; private repository = InjectionPlatformEntitiesRepository.${entityName}Repository(); private mapper = InjectionPlatformEntities${entityName}Mapper.${entityName}DeleteMapper(); public static getInstance(): ${entityName}DeleteUseCase { if (!${entityName}DeleteUseCase.instance) ${entityName}DeleteUseCase.instance = new ${entityName}DeleteUseCase(); return ${entityName}DeleteUseCase.instance; } public async execute(params: I${entityName}DeleteDTO, config?: IConfigDTO): Promise<I${entityName}DTO | null> { const paramsEntity = this.mapper.mapTo(params); return await this.repository.delete(paramsEntity, config).then((data) => data ?? null); } }`; await fs.writeFile(path.join(paths.domainUseCases, `${entityNameKebab}-delete-use-case.ts`), deleteUseCase); console.log(chalk_1.default.green(`✅ Delete Use Case: ${entityNameKebab}-delete-use-case.ts`)); // 5. List Use Case const listUseCase = `import { IConfigDTO } from "@core/interfaces/i-config-repository-dto"; import { UseCase } from "@core/interfaces/use-case"; import { I${entityName}DTO } from "@${apiName}/domain/models/apis/${apiName}/entities/${entityNameLower}"; import { InjectionPlatformEntitiesRepository } from "@${apiName}/infrastructure/repositories/apis/${apiName}/repositories/injection/entities/injection-${apiName}-entities-repository"; import { IPaginationBackendDTO } from "@core/interfaces/i-pagination-backend-dto"; export class ${entityName}ListUseCase implements UseCase<IPaginationBackendDTO, I${entityName}DTO[] | null> { private static instance: ${entityName}ListUseCase; private repository = InjectionPlatformEntitiesRepository.${entityName}Repository(); public static getInstance(): ${entityName}ListUseCase { if (!${entityName}ListUseCase.instance) ${entityName}ListUseCase.instance = new ${entityName}ListUseCase(); return ${entityName}ListUseCase.instance; } public async execute(params: IPaginationBackendDTO, config?: IConfigDTO): Promise<I${entityName}DTO[] | null> { return await this.repository.list(params, config).then((data) => data ?? null); } }`; await fs.writeFile(path.join(paths.domainUseCases, `${entityNameKebab}-list-use-case.ts`), listUseCase); console.log(chalk_1.default.green(`✅ List Use Case: ${entityNameKebab}-list-use-case.ts`)); } async function generateInfrastructureEntities(entityName, paths, schema, apiName = 'platform') { const entityNameKebab = entityName.replace(/([A-Z])/g, '-$1').toLowerCase().substring(1); const fields = schema?.fields || getDefaultFields(); // 1. IEntityEntity - Entidad principal const entityInterface = generateEntityInterface(entityName, fields, 'main'); await fs.writeFile(path.join(paths.infraEntities, `i-${entityNameKebab}-entity.ts`), entityInterface); console.log(chalk_1.default.green(`✅ Entity principal: i-${entityNameKebab}-entity.ts`)); // 2. IEntitySaveEntity const saveEntityInterface = generateEntityInterface(entityName, fields, 'save'); await fs.writeFile(path.join(paths.infraEntities, `i-${entityNameKebab}-save-entity.ts`), saveEntityInterface); console.log(chalk_1.default.green(`✅ Save Entity: i-${entityNameKebab}-save-entity.ts`)); // 3. IEntityReadEntity const readEntityInterface = generateEntityInterface(entityName, fields, 'read'); await fs.writeFile(path.join(paths.infraEntities, `i-${entityNameKebab}-read-entity.ts`), readEntityInterface); console.log(chalk_1.default.green(`✅ Read Entity: i-${entityNameKebab}-read-entity.ts`)); // 4. IEntityUpdateEntity const updateEntityInterface = generateEntityInterface(entityName, fields, 'update'); await fs.writeFile(path.join(paths.infraEntities, `i-${entityNameKebab}-update-entity.ts`), updateEntityInterface); console.log(chalk_1.default.green(`✅ Update Entity: i-${entityNameKebab}-update-entity.ts`)); // 5. IEntityDeleteEntity const deleteEntityInterface = generateEntityInterface(entityName, fields, 'delete'); await fs.writeFile(path.join(paths.infraEntities, `i-${entityNameKebab}-delete-entity.ts`), deleteEntityInterface); console.log(chalk_1.default.green(`✅ Delete Entity: i-${entityNameKebab}-delete-entity.ts`)); // 6. Index file para exportar todas las entidades const indexContent = `export { I${entityName}Entity } from './i-${entityNameKebab}-entity'; export { I${entityName}SaveEntity } from './i-${entityNameKebab}-save-entity'; export { I${entityName}ReadEntity } from './i-${entityNameKebab}-read-entity'; export { I${entityName}UpdateEntity } from './i-${entityNameKebab}-update-entity'; export { I${entityName}DeleteEntity } from './i-${entityNameKebab}-delete-entity';`; await fs.writeFile(path.join(paths.infraEntities, 'index.ts'), indexContent); console.log(chalk_1.default.green(`✅ Index Entity: index.ts`)); } // Continúo en la siguiente función... async function generateInfrastructureMappers(entityName, paths, schema, apiName = 'platform') { const entityNameKebab = entityName.replace(/([A-Z])/g, '-$1').toLowerCase().substring(1); const entityNameLower = entityName.toLowerCase(); const fields = schema?.fields || getDefaultFields(); // 1. Entity Mapper (principal) const entityMapper = generateEntityMapper(entityName, fields, apiName); await fs.writeFile(path.join(paths.infraMappers, `${entityNameKebab}-entity-mapper.ts`), entityMapper); console.log(chalk_1.default.green(`✅ Entity Mapper: ${entityNameKebab}-entity-mapper.ts`)); // 2. Save Mapper const saveMapper = generateSpecificMapper(entityName, fields, 'save', apiName); await fs.writeFile(path.join(paths.infraMappers, `${entityNameKebab}-save-mapper.ts`), saveMapper); console.log(chalk_1.default.green(`✅ Save Mapper: ${entityNameKebab}-save-mapper.ts`)); // 3. Read Mapper const readMapper = generateSpecificMapper(entityName, fields, 'read', apiName); await fs.writeFile(path.join(paths.infraMappers, `${entityNameKebab}-read-mapper.ts`), readMapper); console.log(chalk_1.default.green(`✅ Read Mapper: ${entityNameKebab}-read-mapper.ts`)); // 4. Update Mapper const updateMapper = generateSpecificMapper(entityName, fields, 'update', apiName); await fs.writeFile(path.join(paths.infraMappers, `${entityNameKebab}-update-mapper.ts`), updateMapper); console.log(chalk_1.default.green(`✅ Update Mapper: ${entityNameKebab}-update-mapper.ts`)); // 5. Delete Mapper const deleteMapper = generateSpecificMapper(entityName, fields, 'delete', apiName); await fs.writeFile(path.join(paths.infraMappers, `${entityNameKebab}-delete-mapper.ts`), deleteMapper); console.log(chalk_1.default.green(`✅ Delete Mapper: ${entityNameKebab}-delete-mapper.ts`)); } async function generateInfrastructureRepository(entityName, paths, schema, apiName = 'platform') { const entityNameKebab = entityName.replace(/([A-Z])/g, '-$1').toLowerCase().substring(1); const entityNameLower = entityName.toLowerCase(); const entityNameUpper = entityName.toUpperCase(); const repositoryContent = `import { IConfigDTO } from "@core/interfaces/i-config-repository-dto"; import ${apiName}Axios from "@core/axios/${apiName}-axios"; import { CONST_${apiName.toUpperCase()}_API_ROUTES } from "@core/const"; import { CONST_CORE_DTO } from "@core/const/const-core"; import { InjectionCore } from "@core/injection/injection-core"; import { I${entityName}Repository } from "@${apiName}/domain/services/repositories/apis/${apiName}/entities/i-${entityNameKebab}-repository"; import { IPaginationBackendDTO } from "@core/interfaces/i-pagination-backend-dto"; import { I${entityName}DTO } from "@${apiName}/domain/models/apis/${apiName}/entities/${entityNameLower}"; import { I${entityName}DeleteEntity, I${entityName}Entity, I${entityName}ReadEntity, I${entityName}SaveEntity, I${entityName}UpdateEntity } from "@${apiName}/infrastructure/entities/apis/${apiName}/entities/${entityNameLower}"; import { InjectionPlatformEntities${entityName}Mapper } from "@${apiName}/infrastructure/mappers/apis/${apiName}/injection/entities/injection-${apiName}-entities-${entityNameKebab}-mapper"; export class ${entityName}Repository extends I${entityName}Repository { private static instance: ${entityName}Repository; private readonly resolve = InjectionCore.Resolve(); private readonly ${entityNameLower}EntityMapper = InjectionPlatformEntities${entityName}Mapper.${entityName}EntityMapper(); private constructor() { super(); } public static getInstance(): ${entityName}Repository { if (!${entityName}Repository.instance) ${entityName}Repository.instance = new ${entityName}Repository(); return ${entityName}Repository.instance; } public async read( params: I${entityName}ReadEntity, config: IConfigDTO = CONST_CORE_DTO.CONFIG ): Promise<I${entityName}DTO | null> { if (config.loadService) return ${apiName}Axios .get(\`\${CONST_${apiName.toUpperCase()}_API_ROUTES.${entityNameUpper}}/\${params.id}\`) .then(({ data }) => { const entity = this.resolve.ResolveRequest<I${entityName}Entity>(data); if (entity) return this.${entityNameLower}EntityMapper.mapFrom(entity); return null; }); return null; } public async save( params: I${entityName}SaveEntity, config: IConfigDTO = CONST_CORE_DTO.CONFIG ): Promise<I${entityName}DTO | null> { if (config.loadService) return ${apiName}Axios .post(CONST_${apiName.toUpperCase()}_API_ROUTES.${entityNameUpper}, params) .then(({ data }) => { const entity = this.resolve.ResolveRequest<I${entityName}Entity>(data); if (entity) return this.${entityNameLower}EntityMapper.mapFrom(entity); return null; }); return null; } public async update( params: I${entityName}UpdateEntity, config: IConfigDTO = CONST_CORE_DTO.CONFIG ): Promise<I${entityName}DTO | null> { if (config.loadService) return ${apiName}Axios .put(CONST_${apiName.toUpperCase()}_API_ROUTES.${entityNameUpper}, params) .then(({ data }) => { const entity = this.resolve.ResolveRequest<I${entityName}Entity>(data); if (entity) return this.${entityNameLower}EntityMapper.mapFrom(entity); return null; }); return null; } public async delete( params: I${entityName}DeleteEntity, config: IConfigDTO = CONST_CORE_DTO.CONFIG ): Promise<I${entityName}DTO | null> { if (config.loadService) return ${apiName}Axios .delete(\`\${CONST_${apiName.toUpperCase()}_API_ROUTES.${entityNameUpper}}/\${params.id}\`) .then(({ data }) => { const entity = this.resolve.ResolveRequest<I${entityName}Entity>(data); if (entity) return this.${entityNameLower}EntityMapper.mapFrom(entity); return null; }); return null; } public async list( params: IPaginationBackendDTO, config: IConfigDTO = CONST_CORE_DTO.CONFIG ): Promise<I${entityName}DTO[] | null> { if (config.loadService) return ${apiName}Axios .post(CONST_${apiName.toUpperCase()}_API_ROUTES.${entityNameUpper}_LIST, params) .then(({ data }) => { const entities = this.resolve.ResolveRequest<I${entityName}Entity[]>(data); if (entities) return this.${entityNameLower}EntityMapper.mapFromList(entities); return null; }); return null; } }`; await fs.writeFile(path.join(paths.infraRepository, `${entityNameKebab}-repository.ts`), repositoryContent); console.log(chalk_1.default.green(`✅ Repository: ${entityNameKebab}-repository.ts`)); } async function generateFacade(entityName, paths, schema, apiName = 'platform') { const entityNameKebab = entityName.replace(/([A-Z])/g, '-$1').toLowerCase().substring(1); const entityNameLower = entityName.toLowerCase(); const facadeContent = `import { IConfigDTO } from "@core/interfaces/i-config-repository-dto"; import { I${entityName}DTO, I${entityName}DeleteDTO, I${entityName}ReadDTO, I${entityName}SaveDTO, I${entityName}UpdateDTO, } from "@${apiName}/domain/models/apis/${apiName}/entities/${entityNameLower}"; import { IPaginationBackendDTO } from "@core/interfaces/i-pagination-backend-dto"; import { InjectionPlatformEntities${entityName}UseCase } from "@${apiName}/domain/services/use_cases/apis/${apiName}/injection/entities/injection-${apiName}-entities-${entityNameKebab}-use-case"; export class ${entityName}Facade { private static instance: ${entityName}Facade; private readonly readUseCase = InjectionPlatformEntities${entityName}UseCase.${entityName}ReadUseCase(); private readonly saveUseCase = InjectionPlatformEntities${entityName}UseCase.${entityName}SaveUseCase(); private readonly updateUseCase = InjectionPlatformEntities${entityName}UseCase.${entityName}UpdateUseCase(); private readonly deleteUseCase = InjectionPlatformEntities${entityName}UseCase.${entityName}DeleteUseCase(); private readonly listUseCase = InjectionPlatformEntities${entityName}UseCase.${entityName}ListUseCase(); public static getInstance(): ${entityName}Facade { if (!${entityName}Facade.instance) ${entityName}Facade.instance = new ${entityName}Facade(); return ${entityName}Facade.instance; } public async read(params: I${entityName}ReadDTO, config?: IConfigDTO): Promise<I${entityName}DTO | null> { return await this.readUseCase.execute(params, config); } public async save(params: I${entityName}SaveDTO, config?: IConfigDTO): Promise<I${entityName}DTO | null> { return await this.saveUseCase.execute(params, config); } public async update(params: I${entityName}UpdateDTO, config?: IConfigDTO): Promise<I${entityName}DTO | null> { return await this.updateUseCase.execute(params, config); } public async delete(params: I${entityName}DeleteDTO, config?: IConfigDTO): Promise<I${entityName}DTO | null> { return await this.deleteUseCase.execute(params, config); } public async list(params: IPaginationBackendDTO, config?: IConfigDTO): Promise<I${entityName}DTO[] | null> { return await this.listUseCase.execute(params, config); } }`; await fs.writeFile(path.join(paths.facade, `${entityNameKebab}-facade.ts`), facadeContent); console.log(chalk_1.default.green(`✅ Facade: ${entityNameKebab}-facade.ts`)); } async function generateInjectionFiles(entityName, paths, schema, apiName = 'platform') { const entityNameKebab = entityName.replace(/([A-Z])/g, '-$1').toLowerCase().substring(1); // 1. Use Case Injection const useCaseInjection = `import { ${entityName}DeleteUseCase } from "@${apiName}/domain/services/use_cases/apis/${apiName}/entities/${entityName.toLowerCase()}/${entityNameKebab}-delete-use-case"; import { ${entityName}ListUseCase } from "@${apiName}/domain/services/use_cases/apis/${apiName}/entities/${entityName.toLowerCase()}/${entityNameKebab}-list-use-case"; import { ${entityName}ReadUseCase } from "@${apiName}/domain/services/use_cases/apis/${apiName}/entities/${entityName.toLowerCase()}/${entityNameKebab}-read-use-case"; import { ${entityName}SaveUseCase } from "@${apiName}/domain/services/use_cases/apis/${apiName}/entities/${entityName.toLowerCase()}/${entityNameKebab}-save-use-case"; import { ${entityName}UpdateUseCase } from "@${apiName}/domain/services/use_cases/apis/${apiName}/entities/${entityName.toLowerCase()}/${entityNameKebab}-update-use-case"; export class InjectionPlatformEntities${entityName}UseCase { public static ${entityName}ReadUseCase(): ${entityName}ReadUseCase { return ${entityName}ReadUseCase.getInstance(); } public static ${entityName}SaveUseCase(): ${entityName}SaveUseCase { return ${entityName}SaveUseCase.getInstance(); } public static ${entityName}UpdateUseCase(): ${entityName}UpdateUseCase { return ${entityName}UpdateUseCase.getInstance(); } public static ${entityName}DeleteUseCase(): ${entityName}DeleteUseCase { return ${entityName}DeleteUseCase.getInstance(); } public static ${entityName}ListUseCase(): ${entityName}ListUseCase { return ${entityName}ListUseCase.getInstance(); } }`; await fs.writeFile(path.join(paths.useCaseInjection, `injection-platform-entities-${entityNameKebab}-use-case.ts`), useCaseInjection); console.log(chalk_1.default.green(`✅ Use Case Injection: injection-platform-entities-${entityNameKebab}-use-case.ts`)); // 2. Mapper Injection const mapperInjection = `import { ${entityName}DeleteMapper } from "@${apiName}/infrastructure/mappers/apis/${apiName}/entities/${entityName.toLowerCase()}/${entityNameKebab}-delete-mapper"; import { ${entityName}EntityMapper } from "@${apiName}/infrastructure/mappers/apis/${apiName}/entities/${entityName.toLowerCase()}/${entityNameKebab}-entity-mapper"; import { ${entityName}ReadMapper } from "@${apiName}/infrastructure/mappers/apis/${apiName}/entities/${entityName.toLowerCase()}/${entityNameKebab}-read-mapper"; import { ${entityName}SaveMapper } from "@${apiName}/infrastructure/mappers/apis/${apiName}/entities/${entityName.toLowerCase()}/${entityNameKebab}-save-mapper"; import { ${entityName}UpdateMapper } from "@${apiName}/infrastructure/mappers/apis/${apiName}/entities/${entityName.toLowerCase()}/${entityNameKebab}-update-mapper"; export class InjectionPlatformEntities${entityName}Mapper { public static ${entityName}EntityMapper(): ${entityName}EntityMapper { return ${entityName}EntityMapper.getInstance(); } public static ${entityName}SaveMapper(): ${entityName}SaveMapper { return ${entityName}SaveMapper.getInstance(); } public static ${entityName}ReadMapper(): ${entityName}ReadMapper { return ${entityName}ReadMapper.getInstance(); } public static ${entityName}UpdateMapper(): ${entityName}UpdateMapper { return ${entityName}UpdateMapper.getInstance(); } public static ${entityName}DeleteMapper(): ${entityName}DeleteMapper { return ${entityName}DeleteMapper.getInstance(); } }`; await fs.writeFile(path.join(paths.mapperInjection, `injection-platform-entities-${entityNameKebab}-mapper.ts`), mapperInjection); console.log(chalk_1.default.green(`✅ Mapper Injection: injection-platform-entities-${entityNameKebab}-mapper.ts`)); // 3. Repository Injection await generateRepositoryInjection(entityName, paths.repositoryInjection, apiName); console.log(chalk_1.default.green(`✅ Repository Injection: injection-platform-entities-repository.ts`)); } // Helper functions function getDefaultFields() { return [ { name: 'id', type: 'string', required: false, format: 'uuid4' }, { name: 'name', type: 'string', required: true }, { name: 'state', type: 'boolean', required: false } ]; } function generateDTOInterface(entityName, fields, type) { const interfaceName = type === 'main' ? `I${entityName}DTO` : type === 'save' ? `I${entityName}SaveDTO` : type === 'read' ? `I${entityName}ReadDTO` : type === 'update' ? `I${entityName}UpdateDTO` : `I${entityName}DeleteDTO`; let content = `export interface ${interfaceName} {\n`; // Verificar si el campo 'id' viene del Swagger const hasIdField = fields.some(field => field.name === 'id'); const idField = fields.find(field => field.name === 'id'); if (type === 'delete' || type === 'read') { // Para delete y read, siempre necesitamos el ID content += ` id: string;\n`; } else { // Solo agregar el campo id si viene del Swagger y es main o update if ((type === 'main' || type === 'update') && hasIdField) { const isOptional = !idField?.required; content += ` id${isOptional ? '?' : ''}: string;\n`; } // Procesar el resto de campos, excluyendo 'id' para evitar duplicados fields.forEach(field => { if (field.name === 'id') return; // Ya lo procesamos arriba o lo omitimos const fieldName = convertToCamelCase(field.name); const isOptional = getFieldOptionalStatus(field, type); const tsType = getTypeScriptType(field); content += ` ${fieldName}${isOptional ? '?' : ''}: ${tsType};\n`; }); } content += '}'; return content; } function generateEntityInterface(entityName, fields, type) { const interfaceName = type === 'main' ? `I${entityName}Entity` : type === 'save' ? `I${entityName}SaveEntity` : type === 'read' ? `I${entityName}ReadEntity` : type === 'update' ? `I${entityName}UpdateEntity` : `I${entityName}DeleteEntity`; let content = `export interface ${interfaceName} {\n`; // Verificar si el campo 'id' viene del Swagger const hasIdField = fields.some(field => field.name === 'id'); const idField = fields.find(field => field.name === 'id'); if (type === 'delete' || type === 'read') { // Para delete y read, siempre necesitamos el ID content += ` id: string;\n`; } else { // Solo agregar el campo id si viene del Swagger y es main o update if ((type === 'main' || type === 'update') && hasIdField) { const isOptional = !idField?.required; content += ` id${isOptional ? '?' : ''}: string;\n`; } // Procesar el resto de campos, excluyendo 'id' para evitar duplicados fields.forEach(field => { if (field.name === 'id') return; // Ya lo procesamos arriba o lo omitimos const fieldName = field.name; // Mantener snake_case para entities const isOptional = getFieldOptionalStatus(field, type); const tsType = getTypeScriptType(field); content += ` ${fieldName}${isOptional ? '?' : ''}: ${tsType};\n`; }); } content += '}'; return content; } function generateEntityMapper(entityName, fields, apiName = 'platform') { const entityNameLower = entityName.toLowerCase(); // Agregar siempre el campo id al principio const mapFromFields = [ ' id: param.id', ...fields.map(field => { const dtoField = convertToCamelCase(field.name); const entityField = field.name; return ` ${dtoField}: param.${entityField}`; }) ].join(',\n'); const mapToFields = [ ' id: param.id', ...fields.map(field => { const dtoField = convertToCamelCase(field.name); const entityField = field.name; return ` ${entityField}: param.${dtoField}`; }) ].join(',\n'); return `import { Mapper } from "@core/classes"; import { I${entityName}DTO } from "@${apiName}/domain/models/apis/${apiName}/entities/${entityNameLower}"; import { I${entityName}Entity } from "@${apiName}/infrastructure/entities/apis/${apiName}/entities/${entityNameLower}"; export class ${entityName}EntityMapper extends Mapper<I${entityName}Entity, I${entityName}DTO> { private static instance: ${entityName}EntityMapper; public constructor() { super(); } public static getInstance(): ${entityName}EntityMapper { if (!${entityName}EntityMapper.instance) ${entityName}EntityMapper.instance = new ${entityName}EntityMapper(); return ${entityName}EntityMapper.instance; } public mapFrom(param: I${entityName}Entity): I${entityName}DTO { return { ${mapFromFields} }; } public mapFromList(params: I${entityName}Entity[]): I${entityName}DTO[] { return params.map((param) => this.mapFrom(param)); } public mapTo(param: I${entityName}DTO): I${entityName}Entity { return { ${mapToFields} }; } public mapToList(params: I${entityName}DTO[]): I${entityName}Entity[] { return params.map((param) => this.mapTo(param)); } }`; } function generateSpecificMapper(entityName, fields, operation, apiName = 'platform') { const entityNameLower = entityName.toLowerCase(); const operationCap = operation.charAt(0).toUpperCase() + operation.slice(1); if (operation === 'delete' || operation === 'read') { return `import { Mapper } from "@core/classes"; import { I${entityName}${operationCap}DTO } from "@${apiName}/domain/models/apis/${apiName}/entities/${entityNameLower}"; import { I${entityName}${operationCap}Entity } from "@${apiName}/infrastructure/entities/apis/${apiName}/entities/${entityNameLower}"; export class ${entityName}${operationCap}Mapper extends Mapper<I${entityName}${operationCap}Entity, I${entityName}${operationCap}DTO> { private static instance: ${entityName}${operationCap}Mapper; public constructor() { super(); } public static getInstance(): ${entityName}${operationCap}Mapper { if (!${entityName}${operationCap}Mapper.instance) ${entityName}${operationCap}Mapper.instance = new ${entityName}${operationCap}Mapper(); return ${entityName}${operationCap}Mapper.instance; } public mapFrom(param: I${entityName}${operationCap}Entity): I${entityName}${operationCap}DTO { return { id: param.id }; } public mapFromList(params: I${entityName}${operationCap}Entity[]): I${entityName}${operationCap}DTO[] { return params.map((param) => this.mapFrom(param)); } public mapTo(param: I${entityName}${operationCap}DTO): I${entityName}${operationCap}Entity { return { id: param.id }; } public mapToList(params: I${entityName}${operationCap}DTO[]): I${entityName}${operationCap}Entity[] { return params.map((param) => this.mapTo(param)); } }`; } // Para save y update, generar mappers más complejos const relevantFields = fields.filter(field => operation === 'save' ? !['id', 'created_date', 'updated_date'].includes(field.name) : operation === 'update' ? true : true); const mapFromFields = relevantFields.map(field => { const dtoField = convertToCamelCase(field.name); const entityField = field.name; if (field.name === 'id' && operation === 'update') { return ` ${dtoField}: param.${entityField}`; } else if (['created_date', 'updated_date', 'id'].includes(field.name) && operation === 'save') { return null; // No incluir estos campos en save } return ` ${dtoField}: param.${entityField}`; }).filter(Boolean).join(',\n'); const mapToFields = relevantFields.map(field => { const dtoField = convertToCamelCase(field.name); const entityField = field.name; if (field.name === 'id' && operation === 'update') { return ` ${entityField}: param.${dtoField}`; } else if (['created_date', 'updated_date', 'id'].includes(field.name) && operation === 'save') { return null; } // Agregar valores por defecto para campos opcionales if (!field.required && operation === 'save') { if (field.type === 'boolean') { return ` ${entityField}: param.${dtoField} ?? ${field.name === 'state' ? 'true' : 'false'}`; } } return ` ${entityField}: param.${dtoField}`; }).filter(Boolean).join(',\n'); return `import { Mapper } from "@core/classes"; import { I${entityName}${operationCap}DTO } from "@${apiName}/domain/models/apis/${apiName}/entities/${entityNameLower}"; import { I${entityName}${operationCap}Entity } from "@${apiName}/infrastructure/entities/apis/${apiName}/entities/${entityNameLower}"; export class ${entityName}${operationCap}Mapper extends Mapper<I${entityName}${operationCap}Entity, I${entityName}${operationCap}DTO> { private static instance: ${entityName}${operationCap}Mapper; public constructor() { super(); } public static getInstance(): ${entityName}${operationCap}Mapper { if (!${entityName}${operationCap}Mapper.instance) ${entityName}${operationCap}Mapper.instance = new ${entityName}${operationCap}Mapper(); return ${entityName}${operationCap}Mapper.instance; } public mapFrom(param: I${entityName}${operationCap}Entity): I${entityName}${operationCap}DTO { return { ${mapFromFields} }; } public mapFromList(params: I${entityName}${operationCap}Entity[]): I${entityName}${operationCap}DTO[] { return params.map((param) => this.mapFrom(param)); } public mapTo(param: I${entityName}${operationCap}DTO): I${entityName}${operationCap}Entity { return { ${mapToFields} }; } public mapToList(params: I${entityName}${operationCap}DTO[]): I${entityName}${operationCap}Entity[] { return params.map((param) => this.mapTo(param)); } }`; } function convertToCamelCase(str) { return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase()); } function getFieldOptionalStatus(field, type) { if (type === 'save') { return !field.required || ['id', 'created_date', 'updated_date'].includes(field.name); } if (type === 'update') { return field.name !== 'id'; } return !field.required; } function getTypeScriptType(field) { if (field.format === 'date-time' || field.format === 'date') return 'string'; if (field.format === 'uuid4') return 'string'; switch (field.type) { case 'integer': return 'number'; case 'number': return 'number'; case 'boolean': return 'boolean'; case 'array': return 'any[]'; default: return 'string'; } } /** * Genera o actualiza el archivo de injection para facades */ async function generateFacadeInjection(entityName, facadeInjectionPath, apiName = 'platform') { const injectionFileName = `injection-${apiName}-entities-facade.ts`; const injectionFilePath = path.join(facadeInjectionPath, injectionFileName); const entityNameCapitalized = entityName.charAt(0).toUpperCase() + entityName.slice(1); const entityNameKebab = entityName.replace(/([A-Z])/g, '-$1').toLowerCase().substring(1); // Verificar si el archivo ya existe const fileExists = await fs.pathExists(injectionFilePath); if (fileExists) { // Leer archivo existente const existingContent = await fs.readFile(injectionFilePath, 'utf8'); // Verificar si la entidad ya está incluida const facadeMethodPattern = new RegExp(`\\s+public\\s+static\\s+${entityNameCapitalized}Facade\\(\\)`, 'g'); const importPattern = new RegExp(`import.*${entityNameCapitalized}Facade.*from.*${entityNameKebab}-facade`, 'g'); if (!facadeMethodPattern.test(existingContent) || !importPattern.test(existingContent)) { // Agregar import si no existe let updatedContent = existingContent; if (!importPattern.test(existingContent)) { const importLine = `import { ${entityNameCapitalized}Facade } from "@${apiName}/facade/apis/${apiName}/entities/${entityNameKebab}-facade";`; // Buscar la última línea de import const importLines = existingContent.split('\n').filter(line => line.trim().startsWith('import')); const lastImportIndex = existingContent.lastIndexOf(importLines[importLines.length - 1]) + importLines[importLines.length - 1].length; updatedContent = existingContent.slice(0, lastImportIndex) + '\n' + importLine + existingContent.slice(lastImportIndex); } // Agregar método si no existe if (!facadeMethodPattern.test(updatedContent)) { const methodLine = ` public static ${entityNameCapitalized}Facade() { return ${entityNameCapitalized}Facade.getInstance(); }`; // Buscar el final de la clase (antes del último }) const classEndIndex = updatedContent.lastIndexOf('}'); const beforeClassEnd = updatedContent.slice(0, classEndIndex); const afterClassEnd = updatedContent.slice(classEndIndex); updatedContent = beforeClassEnd + methodLine + '\n' + afterClassEnd; } await fs.writeFile(injectionFilePath, updatedContent); console.log(chalk_1.default.green(`✅ Facade Injection actualizado: ${injectionFileName} (agregado ${entityNameCapitalized}Facade)`)); } else { console.log(chalk_1.default.yellow(`⚠️ Facade Injection: ${entityNameCapitalized}Facade ya existe en ${injectionFileName}`)); } } else { // Crear archivo nuevo const apiNameCapitalized = apiName.charAt(0).toUpperCase() + apiName.slice(1); const facadeInjectionContent = `import { ${entityNameCapitalized}Facade } from "@${apiName}/facade/apis/${apiName}/entities/${entityNameKebab}-facade"; export class Injection${apiNameCapitalized}EntitiesFacade { public static ${entityNameCapitalized}Facade() { return ${entityNameCapitalized}Facade.getInstance(); } } `; await fs.writeFile(injectionFilePath, facadeInjectionContent); console.log(chalk_1.default.green(`✅ Facade Injection creado: ${injectionFileName}`)); } } /** * Genera o actualiza el archivo de injection para repositories */ async function generateRepositoryInjection(entityName, repositoryInjectionPath, apiName = 'platform') { const injectionFileName = `injection-${apiName}-entities-repository.ts`; const injectionFilePath = path.join(repositoryInjectionPath, injectionFileName); const entityNameCapitalized = entityName.charAt(0).toUpperCase() + entityName.slice(1); const entityNameKebab = entityName.replace(/([A-Z])/g, '-$1').toLowerCase().substring(1); // Verificar si el archivo ya existe const fileExis