UNPKG

@diagramers/cli

Version:

Diagramers CLI - Command-line tools for managing Diagramers projects

1,396 lines (1,276 loc) 59.8 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.ProjectExtender = void 0; const fs = __importStar(require("fs-extra")); const path = __importStar(require("path")); const chalk_1 = __importDefault(require("chalk")); class ProjectExtender { constructor() { this.features = [ { name: 'auth', description: 'Authentication system with JWT', files: ['src/features/auth/**/*'], dependencies: ['jsonwebtoken', 'bcryptjs'] }, { name: 'email', description: 'Email notification system', files: ['src/features/email/**/*'], dependencies: ['nodemailer', 'handlebars'] }, { name: 'socket', description: 'Real-time WebSocket communication', files: ['src/features/socket/**/*'], dependencies: ['socket.io'] }, { name: 'cron', description: 'Scheduled task system', files: ['src/features/cron/**/*'], dependencies: ['node-cron'] }, { name: 'audit', description: 'Audit logging system', files: ['src/features/audit/**/*'] } ]; } async listFeatures() { console.log(chalk_1.default.blue('�� Available features and templates:')); console.log(''); console.log(chalk_1.default.green('🔧 Features:')); this.features.forEach(feature => { console.log(chalk_1.default.green(` ${feature.name}`)); console.log(chalk_1.default.gray(` ${feature.description}`)); if (feature.dependencies && feature.dependencies.length > 0) { console.log(chalk_1.default.yellow(` Dependencies: ${feature.dependencies.join(', ')}`)); } console.log(''); }); console.log(chalk_1.default.blue('📦 Module Templates:')); console.log(chalk_1.default.green(' --module <name> --crud --fields field1,field2')); console.log(chalk_1.default.gray(' Generate complete module with CRUD operations')); console.log(''); console.log(chalk_1.default.blue('🗄️ Database Templates:')); console.log(chalk_1.default.green(' --table <name> --fields field1,field2 --type mongodb')); console.log(chalk_1.default.gray(' Generate database table/collection schema')); console.log(''); console.log(chalk_1.default.blue('🔗 Relation Templates:')); console.log(chalk_1.default.green(' --relation <name> --type mongodb')); console.log(chalk_1.default.gray(' Generate database relations and references')); console.log(''); } async addFeature(featureName) { const feature = this.features.find(f => f.name === featureName); if (!feature) { throw new Error(`Feature '${featureName}' not found. Use --list to see available features.`); } const projectPath = process.cwd(); // Check if feature already exists const featurePath = path.join(projectPath, 'src', 'features', featureName); if (await fs.pathExists(featurePath)) { throw new Error(`Feature '${featureName}' already exists in this project.`); } // Create feature directory structure await this.createFeatureStructure(projectPath, feature); // Add dependencies if any if (feature.dependencies && feature.dependencies.length > 0) { await this.addDependencies(feature.dependencies); } // Update main configuration to use the extended feature await this.updateMainConfiguration(projectPath, feature); console.log(chalk_1.default.green(`✅ Feature '${featureName}' added successfully!`)); console.log(chalk_1.default.blue(`📝 Main configuration updated to include ${featureName} feature`)); } async generateModule(moduleName, options = {}) { const projectPath = process.cwd(); // Check if module already exists const modulePath = path.join(projectPath, 'src', 'modules', moduleName); if (await fs.pathExists(modulePath)) { throw new Error(`Module '${moduleName}' already exists in this project.`); } console.log(chalk_1.default.blue(`📁 Creating module structure for: ${moduleName}`)); // Create module directory structure await this.createModuleStructure(projectPath, moduleName, options); // Generate CRUD operations if requested if (options.crud) { await this.generateCRUDOperations(projectPath, moduleName, options); } // Update main application to include the module await this.registerModule(projectPath, moduleName); console.log(chalk_1.default.green(`✅ Module '${moduleName}' generated successfully!`)); } async generateTable(tableName, options = {}) { const projectPath = process.cwd(); console.log(chalk_1.default.blue(`🗄️ Generating table schema for: ${tableName}`)); // Create table schema based on database type await this.createTableSchema(projectPath, tableName, options); console.log(chalk_1.default.green(`✅ Table '${tableName}' generated successfully!`)); } async generateRelations(relationName, options = {}) { const projectPath = process.cwd(); console.log(chalk_1.default.blue(`🔗 Generating relations for: ${relationName}`)); // Create relation definitions await this.createRelations(projectPath, relationName, options); console.log(chalk_1.default.green(`✅ Relations '${relationName}' generated successfully!`)); } async createFeatureStructure(projectPath, feature) { const featurePath = path.join(projectPath, 'src', 'features', feature.name); // Create basic feature structure const structure = { 'controllers': 'Feature controllers', 'services': 'Feature business logic', 'schemas': 'Feature validation schemas', 'routes': 'Feature API routes', 'types': 'Feature TypeScript types', 'providers': 'Feature providers and implementations' }; for (const [dir, description] of Object.entries(structure)) { const dirPath = path.join(featurePath, dir); await fs.ensureDir(dirPath); // Create index file const indexContent = `// ${description} for ${feature.name} feature export * from './${feature.name}-${dir.slice(0, -1)}'; `; await fs.writeFile(path.join(dirPath, 'index.ts'), indexContent); } // Create main feature file with core functions const mainFeatureContent = await this.generateFeatureCore(feature.name); await fs.writeFile(path.join(featurePath, `${feature.name}.feature.ts`), mainFeatureContent); // Create feature configuration const configContent = this.generateFeatureConfig(feature.name); await fs.writeFile(path.join(featurePath, 'config.ts'), configContent); // Create controllers, services, and other core files await this.createFeatureCoreFiles(featurePath, feature.name); // Track path refinements await this.trackPathRefinements(projectPath, feature.name, 'feature'); } async createFeatureCoreFiles(featurePath, featureName) { const capitalizedName = this.capitalizeFirst(featureName); // Create controller const controllerContent = this.generateFeatureController(featureName, capitalizedName); await fs.writeFile(path.join(featurePath, 'controllers', `${featureName}-controller.ts`), controllerContent); // Create service const serviceContent = this.generateFeatureService(featureName, capitalizedName); await fs.writeFile(path.join(featurePath, 'services', `${featureName}-service.ts`), serviceContent); // Create types const typesContent = this.generateFeatureTypes(featureName, capitalizedName); await fs.writeFile(path.join(featurePath, 'types', `${featureName}-types.ts`), typesContent); // Create schemas const schemasContent = this.generateFeatureSchemas(featureName, capitalizedName); await fs.writeFile(path.join(featurePath, 'schemas', `${featureName}-schemas.ts`), schemasContent); } generateFeatureController(featureName, capitalizedName) { switch (featureName) { case 'auth': return `import { Request, Response } from 'express'; import { AuthService } from '../services/auth-service'; import { Result } from '../../../shared/types/result'; import { logger } from '../../../core/logging'; export class AuthController { constructor(private authService: AuthService) {} async login(req: Request, res: Response): Promise<void> { try { const { email, password } = req.body; const result = await this.authService.login(email, password); res.json(result); } catch (error) { logger.error('Login error:', error); res.status(500).json(Result.error('Login failed')); } } async register(req: Request, res: Response): Promise<void> { try { const userData = req.body; const result = await this.authService.register(userData); res.json(result); } catch (error) { logger.error('Registration error:', error); res.status(500).json(Result.error('Registration failed')); } } async logout(req: Request, res: Response): Promise<void> { try { const token = req.headers.authorization?.replace('Bearer ', ''); const result = await this.authService.logout(token); res.json(result); } catch (error) { logger.error('Logout error:', error); res.status(500).json(Result.error('Logout failed')); } } async refreshToken(req: Request, res: Response): Promise<void> { try { const { refreshToken } = req.body; const result = await this.authService.refreshToken(refreshToken); res.json(result); } catch (error) { logger.error('Token refresh error:', error); res.status(500).json(Result.error('Token refresh failed')); } } async forgotPassword(req: Request, res: Response): Promise<void> { try { const { email } = req.body; const result = await this.authService.forgotPassword(email); res.json(result); } catch (error) { logger.error('Forgot password error:', error); res.status(500).json(Result.error('Password reset failed')); } } async resetPassword(req: Request, res: Response): Promise<void> { try { const { token, newPassword } = req.body; const result = await this.authService.resetPassword(token, newPassword); res.json(result); } catch (error) { logger.error('Reset password error:', error); res.status(500).json(Result.error('Password reset failed')); } } async verifyEmail(req: Request, res: Response): Promise<void> { try { const { token } = req.body; const result = await this.authService.verifyEmail(token); res.json(result); } catch (error) { logger.error('Email verification error:', error); res.status(500).json(Result.error('Email verification failed')); } } }`; case 'email': return `import { Request, Response } from 'express'; import { EmailService } from '../services/email-service'; import { Result } from '../../../shared/types/result'; import { logger } from '../../../core/logging'; export class EmailController { constructor(private emailService: EmailService) {} async sendEmail(req: Request, res: Response): Promise<void> { try { const { to, subject, content, template } = req.body; const result = await this.emailService.sendEmail({ to, subject, content, template }); res.json(result); } catch (error) { logger.error('Send email error:', error); res.status(500).json(Result.error('Email sending failed')); } } async sendTemplateEmail(req: Request, res: Response): Promise<void> { try { const { to, templateName, data } = req.body; const result = await this.emailService.sendTemplateEmail(to, templateName, data); res.json(result); } catch (error) { logger.error('Template email error:', error); res.status(500).json(Result.error('Template email failed')); } } async getTemplates(req: Request, res: Response): Promise<void> { try { const result = await this.emailService.getTemplates(); res.json(result); } catch (error) { logger.error('Get templates error:', error); res.status(500).json(Result.error('Failed to get templates')); } } async createTemplate(req: Request, res: Response): Promise<void> { try { const templateData = req.body; const result = await this.emailService.createTemplate(templateData); res.json(result); } catch (error) { logger.error('Create template error:', error); res.status(500).json(Result.error('Template creation failed')); } } }`; default: return `import { Request, Response } from 'express'; import { ${capitalizedName}Service } from '../services/${featureName}-service'; import { Result } from '../../../shared/types/result'; import { logger } from '../../../core/logging'; export class ${capitalizedName}Controller { constructor(private ${featureName}Service: ${capitalizedName}Service) {} async index(req: Request, res: Response): Promise<void> { try { const result = await this.${featureName}Service.getStatus(); res.json(result); } catch (error) { logger.error('${capitalizedName} index error:', error); res.status(500).json(Result.error('${capitalizedName} service error')); } } // Add your custom controller methods here async customMethod(req: Request, res: Response): Promise<void> { try { const result = await this.${featureName}Service.customMethod(req.body); res.json(result); } catch (error) { logger.error('${capitalizedName} custom method error:', error); res.status(500).json(Result.error('Custom method failed')); } } }`; } } generateFeatureService(featureName, capitalizedName) { switch (featureName) { case 'auth': return `import { Result } from '../../../shared/types/result'; import { logger } from '../../../core/logging'; import { AuthConfig } from '../config'; export class AuthService { constructor(private config: AuthConfig) {} async login(email: string, password: string): Promise<Result> { try { // Implement login logic here logger.info('Login attempt for:', email); // Example implementation if (!email || !password) { return Result.badRequest('Email and password are required'); } // Add your authentication logic here // - Validate credentials // - Generate JWT tokens // - Return user data and tokens return Result.success({ message: 'Login successful' }); } catch (error) { logger.error('Login service error:', error); return Result.error('Login failed'); } } async register(userData: any): Promise<Result> { try { logger.info('Registration attempt for:', userData.email); // Add your registration logic here // - Validate user data // - Hash password // - Create user record // - Send verification email return Result.success({ message: 'Registration successful' }); } catch (error) { logger.error('Registration service error:', error); return Result.error('Registration failed'); } } async logout(token: string): Promise<Result> { try { // Add your logout logic here // - Invalidate token // - Clear session return Result.success({ message: 'Logout successful' }); } catch (error) { logger.error('Logout service error:', error); return Result.error('Logout failed'); } } async refreshToken(refreshToken: string): Promise<Result> { try { // Add your token refresh logic here // - Validate refresh token // - Generate new access token return Result.success({ message: 'Token refreshed' }); } catch (error) { logger.error('Token refresh service error:', error); return Result.error('Token refresh failed'); } } async forgotPassword(email: string): Promise<Result> { try { // Add your forgot password logic here // - Validate email // - Generate reset token // - Send reset email return Result.success({ message: 'Password reset email sent' }); } catch (error) { logger.error('Forgot password service error:', error); return Result.error('Password reset failed'); } } async resetPassword(token: string, newPassword: string): Promise<Result> { try { // Add your password reset logic here // - Validate token // - Update password return Result.success({ message: 'Password reset successful' }); } catch (error) { logger.error('Reset password service error:', error); return Result.error('Password reset failed'); } } async verifyEmail(token: string): Promise<Result> { try { // Add your email verification logic here // - Validate token // - Mark email as verified return Result.success({ message: 'Email verified' }); } catch (error) { logger.error('Email verification service error:', error); return Result.error('Email verification failed'); } } }`; default: return `import { Result } from '../../../shared/types/result'; import { logger } from '../../../core/logging'; import { ${capitalizedName}Config } from '../config'; export class ${capitalizedName}Service { constructor(private config: ${capitalizedName}Config) {} async getStatus(): Promise<Result> { try { return Result.success({ status: 'ok', feature: '${featureName}', enabled: this.config.enabled }); } catch (error) { logger.error('${capitalizedName} service error:', error); return Result.error('${capitalizedName} service error'); } } async customMethod(data: any): Promise<Result> { try { // Add your custom business logic here logger.info('${capitalizedName} custom method called with:', data); return Result.success({ message: 'Custom method executed' }); } catch (error) { logger.error('${capitalizedName} custom method error:', error); return Result.error('Custom method failed'); } } // Add more service methods as needed }`; } } generateFeatureTypes(featureName, capitalizedName) { return `// TypeScript types for ${featureName} feature export interface ${capitalizedName}Config { enabled: boolean; // Add your configuration interface properties here } export interface ${capitalizedName}Data { // Add your data interface properties here } export interface ${capitalizedName}Options { // Add your options interface properties here } // Add more type definitions as needed `; } generateFeatureSchemas(featureName, capitalizedName) { return `// Validation schemas for ${featureName} feature import Joi from 'joi'; export const ${featureName}ValidationSchema = Joi.object({ // Add your validation schema here // Example: // name: Joi.string().required(), // email: Joi.string().email().required(), }); export const ${featureName}UpdateSchema = Joi.object({ // Add your update validation schema here }); // Add more validation schemas as needed `; } async generateFeatureCore(featureName) { const capitalizedName = this.capitalizeFirst(featureName); switch (featureName) { case 'auth': return `import { Router } from 'express'; import { AuthController } from './controllers'; import { AuthService } from './services'; import { AuthConfig } from './config'; export class ${capitalizedName}Feature { private router: Router; private controller: AuthController; private service: AuthService; private config: AuthConfig; constructor() { this.router = Router(); this.config = new AuthConfig(); this.service = new AuthService(); this.controller = new AuthController(this.service); this.setupRoutes(); } private setupRoutes(): void { // Authentication routes this.router.post('/login', this.controller.login.bind(this.controller)); this.router.post('/register', this.controller.register.bind(this.controller)); this.router.post('/logout', this.controller.logout.bind(this.controller)); this.router.post('/refresh', this.controller.refreshToken.bind(this.controller)); this.router.post('/forgot-password', this.controller.forgotPassword.bind(this.controller)); this.router.post('/reset-password', this.controller.resetPassword.bind(this.controller)); this.router.post('/verify-email', this.controller.verifyEmail.bind(this.controller)); } getRouter(): Router { return this.router; } getService(): AuthService { return this.service; } getConfig(): AuthConfig { return this.config; } }`; case 'email': return `import { Router } from 'express'; import { EmailController } from './controllers'; import { EmailService } from './services'; import { EmailConfig } from './config'; export class ${capitalizedName}Feature { private router: Router; private controller: EmailController; private service: EmailService; private config: EmailConfig; constructor() { this.router = Router(); this.config = new EmailConfig(); this.service = new EmailService(this.config); this.controller = new EmailController(this.service); this.setupRoutes(); } private setupRoutes(): void { // Email routes this.router.post('/send', this.controller.sendEmail.bind(this.controller)); this.router.post('/send-template', this.controller.sendTemplateEmail.bind(this.controller)); this.router.get('/templates', this.controller.getTemplates.bind(this.controller)); this.router.post('/templates', this.controller.createTemplate.bind(this.controller)); } getRouter(): Router { return this.router; } getService(): EmailService { return this.service; } getConfig(): EmailConfig { return this.config; } }`; case 'socket': return `import { Server as SocketIOServer } from 'socket.io'; import { SocketController } from './controllers'; import { SocketService } from './services'; import { SocketConfig } from './config'; export class ${capitalizedName}Feature { private io: SocketIOServer; private controller: SocketController; private service: SocketService; private config: SocketConfig; constructor(io: SocketIOServer) { this.io = io; this.config = new SocketConfig(); this.service = new SocketService(this.io, this.config); this.controller = new SocketController(this.service); this.setupEvents(); } private setupEvents(): void { this.io.on('connection', (socket) => { this.controller.handleConnection(socket); // Register event handlers socket.on('join-room', this.controller.handleJoinRoom.bind(this.controller, socket)); socket.on('leave-room', this.controller.handleLeaveRoom.bind(this.controller, socket)); socket.on('send-message', this.controller.handleSendMessage.bind(this.controller, socket)); socket.on('disconnect', this.controller.handleDisconnect.bind(this.controller, socket)); }); } getService(): SocketService { return this.service; } getConfig(): SocketConfig { return this.config; } }`; case 'cron': return `import { CronController } from './controllers'; import { CronService } from './services'; import { CronConfig } from './config'; export class ${capitalizedName}Feature { private controller: CronController; private service: CronService; private config: CronConfig; constructor() { this.config = new CronConfig(); this.service = new CronService(this.config); this.controller = new CronController(this.service); this.initializeJobs(); } private initializeJobs(): void { // Initialize scheduled jobs this.service.initializeJobs(); } getService(): CronService { return this.service; } getConfig(): CronConfig { return this.config; } // Method to manually trigger jobs async triggerJob(jobName: string): Promise<void> { await this.service.triggerJob(jobName); } }`; case 'audit': return `import { Router } from 'express'; import { AuditController } from './controllers'; import { AuditService } from './services'; import { AuditConfig } from './config'; export class ${capitalizedName}Feature { private router: Router; private controller: AuditController; private service: AuditService; private config: AuditConfig; constructor() { this.router = Router(); this.config = new AuditConfig(); this.service = new AuditService(this.config); this.controller = new AuditController(this.service); this.setupRoutes(); } private setupRoutes(): void { // Audit routes this.router.get('/logs', this.controller.getLogs.bind(this.controller)); this.router.post('/logs', this.controller.createLog.bind(this.controller)); this.router.get('/logs/:id', this.controller.getLogById.bind(this.controller)); this.router.delete('/logs/:id', this.controller.deleteLog.bind(this.controller)); } getRouter(): Router { return this.router; } getService(): AuditService { return this.service; } getConfig(): AuditConfig { return this.config; } // Method to log audit events async logEvent(event: any): Promise<void> { await this.service.logEvent(event); } }`; default: return `import { Router } from 'express'; import { ${capitalizedName}Controller } from './controllers'; import { ${capitalizedName}Service } from './services'; import { ${capitalizedName}Config } from './config'; export class ${capitalizedName}Feature { private router: Router; private controller: ${capitalizedName}Controller; private service: ${capitalizedName}Service; private config: ${capitalizedName}Config; constructor() { this.router = Router(); this.config = new ${capitalizedName}Config(); this.service = new ${capitalizedName}Service(this.config); this.controller = new ${capitalizedName}Controller(this.service); this.setupRoutes(); } private setupRoutes(): void { // Add your feature routes here this.router.get('/', this.controller.index.bind(this.controller)); } getRouter(): Router { return this.router; } getService(): ${capitalizedName}Service { return this.service; } getConfig(): ${capitalizedName}Config { return this.config; } }`; } } generateFeatureConfig(featureName) { const capitalizedName = this.capitalizeFirst(featureName); return `export class ${capitalizedName}Config { constructor() { // Initialize configuration from environment variables this.loadFromEnv(); } private loadFromEnv(): void { // Load configuration from environment variables // Override this method to add specific configuration loading } // Add getter methods for configuration values get enabled(): boolean { return process.env.${featureName.toUpperCase()}_ENABLED === 'true'; } }`; } async trackPathRefinements(projectPath, featureName, type) { const refinementsPath = path.join(projectPath, '.diagramers-refinements.json'); let refinements = {}; if (await fs.pathExists(refinementsPath)) { refinements = JSON.parse(await fs.readFile(refinementsPath, 'utf8')); } if (!refinements.paths) { refinements.paths = []; } refinements.paths.push({ type: type, name: featureName, path: `src/features/${featureName}`, addedAt: new Date().toISOString(), version: '1.0.0' }); await fs.writeFile(refinementsPath, JSON.stringify(refinements, null, 2)); } async createModuleStructure(projectPath, moduleName, options) { const modulePath = path.join(projectPath, 'src', 'modules', moduleName); // Create module directory structure const structure = { 'entities': 'Data models and interfaces', 'schemas': 'Database schemas and validation', 'services': 'Business logic and data access', 'controllers': 'HTTP request handlers', 'routes': 'API route definitions' }; for (const [dir, description] of Object.entries(structure)) { const dirPath = path.join(modulePath, dir); await fs.ensureDir(dirPath); // Create index file with correct file extensions let fileName = ''; switch (dir) { case 'entities': fileName = `${moduleName}.entity`; break; case 'schemas': fileName = `${moduleName}.schema`; break; case 'services': fileName = `${moduleName}.service`; break; case 'controllers': fileName = `${moduleName}.controller`; break; case 'routes': fileName = `${moduleName}.routes`; break; default: fileName = `${moduleName}.${dir.slice(0, -1)}`; } const indexContent = `// ${description} for ${moduleName} module export * from './${fileName}'; `; await fs.writeFile(path.join(dirPath, 'index.ts'), indexContent); } // Create main entity await this.createEntity(modulePath, moduleName, options); // Create schema await this.createSchema(modulePath, moduleName, options); // Create service await this.createService(modulePath, moduleName, options); // Create controller await this.createController(modulePath, moduleName, options); // Create routes await this.createRoutes(modulePath, moduleName, options); } async createEntity(modulePath, moduleName, options) { const entityName = this.capitalizeFirst(moduleName); const fields = options.fields || ['name', 'description']; const entityContent = `import { BaseEntity } from '../../../shared/types/base-entity'; export interface I${entityName} extends BaseEntity { ${fields.map((field) => ` ${field}: string;`).join('\n')} } export interface I${entityName}Create { ${fields.map((field) => ` ${field}: string;`).join('\n')} } export interface I${entityName}Update { ${fields.map((field) => ` ${field}?: string;`).join('\n')} } `; await fs.writeFile(path.join(modulePath, 'entities', `${moduleName}.entity.ts`), entityContent); } async createSchema(modulePath, moduleName, options) { const entityName = this.capitalizeFirst(moduleName); const fields = options.fields || ['name', 'description']; const schemaContent = `import mongoose, { Schema, Document } from 'mongoose'; import { I${entityName} } from '../entities/${moduleName}.entity'; export interface I${entityName}Document extends Omit<I${entityName}, '_id'>, Document {} const ${moduleName}Schema = new Schema({ ${fields.map((field) => ` ${field}: { type: String, required: true, trim: true }`).join(',\n')} }, { timestamps: true, toJSON: { transform: function(doc, ret) { return ret; } } }); // Indexes ${fields.map((field) => `${moduleName}Schema.index({ ${field}: 1 });`).join('\n')} export const ${entityName}Model = mongoose.model<I${entityName}Document>('${entityName}', ${moduleName}Schema); `; await fs.writeFile(path.join(modulePath, 'schemas', `${moduleName}.schema.ts`), schemaContent); } async createService(modulePath, moduleName, options) { const entityName = this.capitalizeFirst(moduleName); const serviceContent = `import { Result } from '../../../shared/types/result'; import { ${entityName}Model, I${entityName}Document } from '../schemas/${moduleName}.schema'; import { I${entityName}, I${entityName}Create, I${entityName}Update } from '../entities/${moduleName}.entity'; export class ${entityName}Service { /** * Get all ${moduleName}s */ async getAll(): Promise<Result<I${entityName}[]>> { try { const ${moduleName}s = await ${entityName}Model.find({ status: 1 }).lean(); const converted${entityName}s = ${moduleName}s.map(${moduleName} => ({ ...${moduleName}, _id: ${moduleName}._id?.toString() || '' })) as I${entityName}[]; return Result.success(converted${entityName}s); } catch (error: any) { return Result.error('Failed to fetch ${moduleName}s', error.message); } } /** * Get ${moduleName} by ID */ async getById(id: string): Promise<Result<I${entityName} | null>> { try { const ${moduleName} = await ${entityName}Model.findOne({ _id: id, status: 1 }); if (!${moduleName}) { return Result.error('${entityName} not found', '${entityName} with the specified ID does not exist'); } const ${moduleName}Obj = ${moduleName}.toObject(); const converted${entityName} = { ...${moduleName}Obj, _id: ${moduleName}Obj._id?.toString() || '' } as I${entityName}; return Result.success(converted${entityName}); } catch (error: any) { return Result.error('Failed to fetch ${moduleName}', error.message); } } /** * Create new ${moduleName} */ async create(${moduleName}Data: I${entityName}Create): Promise<Result<I${entityName}>> { try { const ${moduleName} = new ${entityName}Model({ ...${moduleName}Data, status: 1 }); const saved${entityName} = await ${moduleName}.save(); const ${moduleName}Obj = saved${entityName}.toObject(); const converted${entityName} = { ...${moduleName}Obj, _id: ${moduleName}Obj._id?.toString() || '' } as I${entityName}; return Result.success(converted${entityName}); } catch (error: any) { return Result.error('Failed to create ${moduleName}', error.message); } } /** * Update ${moduleName} */ async update(id: string, ${moduleName}Data: I${entityName}Update): Promise<Result<I${entityName} | null>> { try { const ${moduleName} = await ${entityName}Model.findOne({ _id: id, status: 1 }); if (!${moduleName}) { return Result.error('${entityName} not found', '${entityName} with the specified ID does not exist'); } Object.assign(${moduleName}, ${moduleName}Data); const updated${entityName} = await ${moduleName}.save(); const ${moduleName}Obj = updated${entityName}.toObject(); const converted${entityName} = { ...${moduleName}Obj, _id: ${moduleName}Obj._id?.toString() || '' } as I${entityName}; return Result.success(converted${entityName}); } catch (error: any) { return Result.error('Failed to update ${moduleName}', error.message); } } /** * Delete ${moduleName} (soft delete) */ async delete(id: string): Promise<Result<boolean>> { try { const ${moduleName} = await ${entityName}Model.findOne({ _id: id, status: 1 }); if (!${moduleName}) { return Result.error('${entityName} not found', '${entityName} with the specified ID does not exist'); } ${moduleName}.status = 0; await ${moduleName}.save(); return Result.success(true); } catch (error: any) { return Result.error('Failed to delete ${moduleName}', error.message); } } } `; await fs.writeFile(path.join(modulePath, 'services', `${moduleName}.service.ts`), serviceContent); } async createController(modulePath, moduleName, options) { const entityName = this.capitalizeFirst(moduleName); const controllerContent = `import { Request, Response } from 'express'; import { ${entityName}Service } from '../services/${moduleName}.service'; import { I${entityName}Create, I${entityName}Update } from '../entities/${moduleName}.entity'; /** * @swagger * components: * schemas: * ${entityName}: * type: object * properties: * _id: * type: string * name: * type: string * description: * type: string * createdAt: * type: string * format: date-time * updatedAt: * type: string * format: date-time */ export class ${entityName}Controller { constructor(private service: ${entityName}Service) {} /** * @swagger * /api/${moduleName}s: * get: * summary: Get all ${moduleName}s * tags: [${entityName}s] * responses: * 200: * description: List of ${moduleName}s * content: * application/json: * schema: * type: object * properties: * Data: * type: array * items: * $ref: '#/components/schemas/${entityName}' * StatusCode: * type: number * Message: * type: string */ async getAll(req: Request, res: Response): Promise<void> { try { const result = await this.service.getAll(); res.status(result.StatusCode === 1000 ? 200 : 400).json(result); } catch (error: any) { res.status(500).json({ error: error.message }); } } /** * @swagger * /api/${moduleName}s/{id}: * get: * summary: Get ${moduleName} by ID * tags: [${entityName}s] * parameters: * - in: path * name: id * required: true * schema: * type: string * responses: * 200: * description: ${entityName} details * content: * application/json: * schema: * type: object * properties: * Data: * $ref: '#/components/schemas/${entityName}' * StatusCode: * type: number * Message: * type: string */ async getById(req: Request, res: Response): Promise<void> { try { const { id } = req.params; const result = await this.service.getById(id); res.status(result.StatusCode === 1000 ? 200 : 400).json(result); } catch (error: any) { res.status(500).json({ error: error.message }); } } /** * @swagger * /api/${moduleName}s: * post: * summary: Create new ${moduleName} * tags: [${entityName}s] * requestBody: * required: true * content: * application/json: * schema: * type: object * properties: * name: * type: string * description: * type: string * responses: * 201: * description: ${entityName} created successfully * content: * application/json: * schema: * type: object * properties: * Data: * $ref: '#/components/schemas/${entityName}' * StatusCode: * type: number * Message: * type: string */ async create(req: Request, res: Response): Promise<void> { try { const ${moduleName}Data: I${entityName}Create = req.body; const result = await this.service.create(${moduleName}Data); res.status(result.StatusCode === 1000 ? 201 : 400).json(result); } catch (error: any) { res.status(500).json({ error: error.message }); } } /** * @swagger * /api/${moduleName}s/{id}: * put: * summary: Update ${moduleName} * tags: [${entityName}s] * parameters: * - in: path * name: id * required: true * schema: * type: string * requestBody: * required: true * content: * application/json: * schema: * type: object * properties: * name: * type: string * description: * type: string * responses: * 200: * description: ${entityName} updated successfully * content: * application/json: * schema: * type: object * properties: * Data: * $ref: '#/components/schemas/${entityName}' * StatusCode: * type: number * Message: * type: string */ async update(req: Request, res: Response): Promise<void> { try { const { id } = req.params; const ${moduleName}Data: I${entityName}Update = req.body; const result = await this.service.update(id, ${moduleName}Data); res.status(result.StatusCode === 1000 ? 200 : 400).json(result); } catch (error: any) { res.status(500).json({ error: error.message }); } } /** * @swagger * /api/${moduleName}s/{id}: * delete: * summary: Delete ${moduleName} * tags: [${entityName}s] * parameters: * - in: path * name: id * required: true * schema: * type: string * responses: * 200: * description: ${entityName} deleted successfully * content: * application/json: * schema: * type: object * properties: * Data: * type: boolean * StatusCode: * type: number * Message: * type: string */ async delete(req: Request, res: Response): Promise<void> { try { const { id } = req.params; const result = await this.service.delete(id); res.status(result.StatusCode === 1000 ? 200 : 400).json(result); } catch (error: any) { res.status(500).json({ error: error.message }); } } } `; await fs.writeFile(path.join(modulePath, 'controllers', `${moduleName}.controller.ts`), controllerContent); } async createRoutes(modulePath, moduleName, options) { const entityName = this.capitalizeFirst(moduleName); const routesContent = `import { Router } from 'express'; import { ${entityName}Controller } from '../controllers/${moduleName}.controller'; import { ${entityName}Service } from '../services/${moduleName}.service'; const router = Router(); const service = new ${entityName}Service(); const controller = new ${entityName}Controller(service); // ${entityName} routes router.get('/', controller.getAll.bind(controller)); router.get('/:id', controller.getById.bind(controller)); router.post('/', controller.create.bind(controller)); router.put('/:id', controller.update.bind(controller)); router.delete('/:id', controller.delete.bind(controller)); export default router; `; await fs.writeFile(path.join(modulePath, 'routes', `${moduleName}.routes.ts`), routesContent); } async generateCRUDOperations(projectPath, moduleName, options) { console.log(chalk_1.default.blue(`🔄 Generating CRUD operations for ${moduleName}...`)); // CRUD operations are already included in the service and controller generation } async registerModule(projectPath, moduleName) { console.log(chalk_1.default.blue(`📝 Registering ${moduleName} module in main application...`)); // Update main routes index to include the new module const routesIndexPath = path.join(projectPath, 'src', 'modules', 'index.ts'); if (await fs.pathExists(routesIndexPath)) { let content = await fs.readFile(routesIndexPath, 'utf8'); // Add import for the new module const importStatement = `import ${moduleName}Routes from './${moduleName}/routes/${moduleName}.routes';`; if (!content.includes(importStatement)) { // Find the last import statement and add after it const importRegex = /import.*from.*['"];?\s*$/gm; const matches = [...content.matchAll(importRegex)]; if (matches.length > 0) { const lastImport = matches[matches.length - 1]; const insertIndex = lastImport.index + lastImport[0].length; content = content.slice(0, insertIndex) + '\n' + importStatement + content.slice(insertIndex); } } // Add route registration const routeRegistration = `app.use('/api/${moduleName}s', ${moduleName}Routes);`; if (!content.includes(routeRegistration)) { // Find where routes are registered and add after const routeRegex = /app\.use\(.*\);?\s*$/gm; const matches = [...content.matchAll(routeRegex)]; if (matches.length > 0) { const lastRoute = matches[matches.length - 1]; const insertIndex = lastRoute.index + lastRoute[0].length; content = content.slice(0, insertIndex) + '\n' + routeRegistration + content.slice(insertIndex); } } await fs.writeFile(routesIndexPath, content); } } async createTableSchema(projectPath, tableName, options) { const entityName = this.capitalizeFirst(tableName); const fields = options.fields || ['name', 'description']; const dbType = options.type || 'mongodb'; if (dbType === 'mongodb') { await this.createMongoDBSchema(projectPath, tableName, fields); } else { await this.createSQLSchema(projectPath, tableName, fields, dbType); } } async createMongoDBSchema(projectPath, tableName, fields) { const schemaPath = path.join(projectPath, 'src', 'schemas', `${tableName}.schema.ts`); await fs.ensureDir(path.dirname(schemaPath)); const entityName = this.capitalizeFirst(tableName); const schemaContent = `import mongoose, { Schema, Document } from 'mongoose'; export interface I${entityName}Document extends Document { ${fields.map(field => ` ${field}: string;`).join('\n')} createdAt: Date; updatedAt: Date; } const ${tableName}Schema = new Schema({ ${fields.map(field => ` ${field}: { type: String, required: true, trim: true }`).join(',\n')} }, { timestamps: true }); // Indexes ${fields.map(field => `${tableName}Schema.index({ ${field}: 1 });`).join('\n')} export const ${entityName}Model = mongoose.model<I${entityName}Document>('${entityName}', ${tableName}Schema); `; await fs.writeFile(schemaPath, schemaContent); } async createSQLSchema(projectPath, tableName, fields, dbType) { const schemaPath = path.join(projectPath, 'src', 'schemas', `${tableName}.sql`); await fs.ensureDir(path.dirname(schemaPath)); const entityName = this.capitalizeFirst(tableName); const schemaContent = `-- ${entityName} table schema for ${dbType.toUpperCase()} CREATE TABLE ${tableName}s ( id ${dbType === 'postgres' ? 'SERIAL' : 'INT AUTO_INCREMENT'} PRIMARY KEY, ${fields.map(field => ` ${field} VARCHAR(255) NOT NULL,`).join('\n')} created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDAT