UNPKG

@diagramers/cli

Version:

Diagramers CLI - Command-line tools for managing Diagramers projects

482 lines (455 loc) 22 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; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.generateModule = generateModule; const fs = __importStar(require("fs")); const path = __importStar(require("path")); async function generateModule(moduleName) { // Validate module name if (!moduleName || !/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(moduleName)) { throw new Error('Invalid module name. Use only letters, numbers, hyphens, and underscores. Must start with a letter.'); } const moduleNameCapitalized = moduleName.charAt(0).toUpperCase() + moduleName.slice(1); const currentDir = process.cwd(); // Check for required API project structure const requiredDirs = ['src/modules']; const missingDirs = requiredDirs.filter(dir => !fs.existsSync(path.join(currentDir, dir))); if (missingDirs.length > 0) { throw new Error(`This command should be run from a diagramers API project. Missing directories: ${missingDirs.join(', ')}`); } // Create module directory structure const modulePath = path.join(currentDir, 'src/modules', moduleName); if (!fs.existsSync(modulePath)) { fs.mkdirSync(modulePath, { recursive: true }); fs.mkdirSync(path.join(modulePath, 'entities'), { recursive: true }); fs.mkdirSync(path.join(modulePath, 'schemas'), { recursive: true }); fs.mkdirSync(path.join(modulePath, 'services'), { recursive: true }); fs.mkdirSync(path.join(modulePath, 'controllers'), { recursive: true }); fs.mkdirSync(path.join(modulePath, 'routes'), { recursive: true }); } console.log('📝 Creating entity...'); const entityContent = generateEntityContent(moduleName, moduleNameCapitalized); fs.writeFileSync(path.join(modulePath, 'entities', `${moduleName}.entity.ts`), entityContent); console.log('📋 Creating schema...'); const schemaContent = generateSchemaContent(moduleName, moduleNameCapitalized); fs.writeFileSync(path.join(modulePath, 'schemas', `${moduleName}.schema.ts`), schemaContent); console.log('🔧 Creating service...'); const serviceContent = generateServiceContent(moduleName, moduleNameCapitalized); fs.writeFileSync(path.join(modulePath, 'services', `${moduleName}.service.ts`), serviceContent); console.log('🎮 Creating controller...'); const controllerContent = generateControllerContent(moduleName, moduleNameCapitalized); fs.writeFileSync(path.join(modulePath, 'controllers', `${moduleName}.controller.ts`), controllerContent); console.log('🛣️ Creating routes...'); const routesContent = generateRoutesContent(moduleName, moduleNameCapitalized); fs.writeFileSync(path.join(modulePath, 'routes', `${moduleName}.routes.ts`), routesContent); // Register routes in server manager console.log('🔗 Registering routes...'); await registerModuleRoutes(currentDir, moduleName, moduleNameCapitalized); // Create database table/collection console.log('🗄️ Creating database table...'); await createDatabaseTable(currentDir, moduleName, moduleNameCapitalized); console.log('✅ Module generation completed!'); console.log(`📁 Module: src/modules/${moduleName}/`); console.log(`🔗 Routes: /api/${moduleName}s`); console.log(`🗄️ Database: ${moduleName} collection`); } function generateEntityContent(moduleName, moduleNameCapitalized) { return `import * as mongoose from 'mongoose'; import { ObjectId } from "bson"; export interface I${moduleNameCapitalized} extends mongoose.Document { _id: ObjectId, name: string, description?: string, status: number, createdAt: Date, updatedAt: Date }`; } function generateSchemaContent(moduleName, moduleNameCapitalized) { return `import * as mongoose from 'mongoose'; import { I${moduleNameCapitalized} } from '../entities/${moduleName}'; export const ${moduleName}Schema = new mongoose.Schema( { name: { type: mongoose.SchemaTypes.String, required: true, }, description: { type: mongoose.SchemaTypes.String, required: false, }, status: { type: mongoose.SchemaTypes.Number, default: 1, } }, { timestamps: true, suppressReservedKeysWarning: true }, ); export const ${moduleNameCapitalized}Entity = mongoose.model<I${moduleNameCapitalized}>('${moduleName}', ${moduleName}Schema);`; } function generateServiceContent(moduleName, moduleNameCapitalized) { return `import { ObjectId } from "bson"; import dbcontext from "../helpers/dbcontext"; import { ResponseCode } from "../helpers/enums"; import { Result } from "../helpers/result"; import { ${moduleNameCapitalized}Entity } from "../schemas/${moduleName}"; export class ${moduleNameCapitalized}Service { result: Result; className = '${moduleNameCapitalized}Service'; constructor(result: Result) { this.result = result; } async getAll(): Promise<Result> { try { const ${moduleName}s = await dbcontext.${moduleNameCapitalized}Entity.find({ status: 1 }); this.result.Data = ${moduleName}s; this.result.Status = ResponseCode.Ok; return this.result; } catch (ex) { this.result.addException(this.className, 'GetAll', ex); return this.result; } } async getById(id: string): Promise<Result> { try { const ${moduleName} = await dbcontext.${moduleNameCapitalized}Entity.findById(id); if (${moduleName}) { this.result.Data = ${moduleName}; this.result.Status = ResponseCode.Ok; } else { this.result.Status = ResponseCode.NotExist; } return this.result; } catch (ex) { this.result.addException(this.className, 'GetById', ex); return this.result; } } async create(${moduleName}Data: any): Promise<Result> { try { const ${moduleName}Entity = new ${moduleNameCapitalized}Entity({ _id: new ObjectId(), ...${moduleName}Data, status: 1 }); const created${moduleNameCapitalized} = await dbcontext.${moduleNameCapitalized}Entity.create(${moduleName}Entity); if (created${moduleNameCapitalized}) { this.result.Data = created${moduleNameCapitalized}; this.result.Status = ResponseCode.Ok; } else { this.result.Status = ResponseCode.Error; this.result.Errors.push('Failed to create ${moduleName}'); } return this.result; } catch (ex) { this.result.addException(this.className, 'Create', ex); return this.result; } } async update(id: string, ${moduleName}Data: any): Promise<Result> { try { const updated${moduleNameCapitalized} = await dbcontext.${moduleNameCapitalized}Entity.findByIdAndUpdate( id, { ...${moduleName}Data }, { new: true } ); if (updated${moduleNameCapitalized}) { this.result.Data = updated${moduleNameCapitalized}; this.result.Status = ResponseCode.Ok; } else { this.result.Status = ResponseCode.NotExist; } return this.result; } catch (ex) { this.result.addException(this.className, 'Update', ex); return this.result; } } async delete(id: string): Promise<Result> { try { const deleted${moduleNameCapitalized} = await dbcontext.${moduleNameCapitalized}Entity.findByIdAndUpdate( id, { status: 0 }, { new: true } ); if (deleted${moduleNameCapitalized}) { this.result.Data = deleted${moduleNameCapitalized}; this.result.Status = ResponseCode.Ok; } else { this.result.Status = ResponseCode.NotExist; } return this.result; } catch (ex) { this.result.addException(this.className, 'Delete', ex); return this.result; } } }`; } function generateControllerContent(moduleName, moduleNameCapitalized) { return `import { AuditMessageType, ResponseCode } from "../helpers/enums"; import handleResponse from "../helpers/handle-response"; import { Result } from "../helpers/result"; import { ${moduleNameCapitalized}Service } from "../services/${moduleName}-service"; export default class ${moduleNameCapitalized}Controller { async getAll(req, res) { const result = res.locals.result; try { result.addMessage(AuditMessageType.info, req.baseUrl, 'getAll', 'Started'); const ${moduleName}Service = new ${moduleNameCapitalized}Service(result); const serviceResult = await ${moduleName}Service.getAll(); result.addMessage(AuditMessageType.info, req.baseUrl, 'getAll', 'Finished'); return handleResponse(req, res, serviceResult); } catch (ex) { result.addException(req.baseUrl, 'getAll', ex); return handleResponse(req, res, result); } } async getById(req, res) { const result = res.locals.result; try { result.addMessage(AuditMessageType.info, req.baseUrl, 'getById', 'Started'); const ${moduleName}Service = new ${moduleNameCapitalized}Service(result); const serviceResult = await ${moduleName}Service.getById(req.params.id); result.addMessage(AuditMessageType.info, req.baseUrl, 'getById', 'Finished'); return handleResponse(req, res, serviceResult); } catch (ex) { result.addException(req.baseUrl, 'getById', ex); return handleResponse(req, res, result); } } async create(req, res) { const result = res.locals.result; try { result.addMessage(AuditMessageType.info, req.baseUrl, 'create', 'Started'); const ${moduleName}Service = new ${moduleNameCapitalized}Service(result); const serviceResult = await ${moduleName}Service.create(req.body); result.addMessage(AuditMessageType.info, req.baseUrl, 'create', 'Finished'); return handleResponse(req, res, serviceResult); } catch (ex) { result.addException(req.baseUrl, 'create', ex); return handleResponse(req, res, result); } } async update(req, res) { const result = res.locals.result; try { result.addMessage(AuditMessageType.info, req.baseUrl, 'update', 'Started'); const ${moduleName}Service = new ${moduleNameCapitalized}Service(result); const serviceResult = await ${moduleName}Service.update(req.params.id, req.body); result.addMessage(AuditMessageType.info, req.baseUrl, 'update', 'Finished'); return handleResponse(req, res, serviceResult); } catch (ex) { result.addException(req.baseUrl, 'update', ex); return handleResponse(req, res, result); } } async delete(req, res) { const result = res.locals.result; try { result.addMessage(AuditMessageType.info, req.baseUrl, 'delete', 'Started'); const ${moduleName}Service = new ${moduleNameCapitalized}Service(result); const serviceResult = await ${moduleName}Service.delete(req.params.id); result.addMessage(AuditMessageType.info, req.baseUrl, 'delete', 'Finished'); return handleResponse(req, res, serviceResult); } catch (ex) { result.addException(req.baseUrl, 'delete', ex); return handleResponse(req, res, result); } } }`; } function generateRoutesContent(moduleName, moduleNameCapitalized) { return `import express from 'express'; import ${moduleNameCapitalized}Controller from '../controllers/${moduleName}-controller'; const router = express.Router(); const ${moduleName}Controller = new ${moduleNameCapitalized}Controller(); // GET /api/${moduleName}s router.get('/', ${moduleName}Controller.getAll.bind(${moduleName}Controller)); // GET /api/${moduleName}s/:id router.get('/:id', ${moduleName}Controller.getById.bind(${moduleName}Controller)); // POST /api/${moduleName}s router.post('/', ${moduleName}Controller.create.bind(${moduleName}Controller)); // PUT /api/${moduleName}s/:id router.put('/:id', ${moduleName}Controller.update.bind(${moduleName}Controller)); // DELETE /api/${moduleName}s/:id router.delete('/:id', ${moduleName}Controller.delete.bind(${moduleName}Controller)); export default router;`; } function updateDbContext(currentDir, moduleName, moduleNameCapitalized) { const dbcontextPath = path.join(currentDir, 'src/helpers/dbcontext.ts'); if (!fs.existsSync(dbcontextPath)) { console.log('⚠️ dbcontext.ts not found, skipping dbcontext update'); return; } let dbcontextContent = fs.readFileSync(dbcontextPath, 'utf8'); // Add import const importStatement = `import { ${moduleNameCapitalized}Entity } from '../schemas/${moduleName}';`; if (!dbcontextContent.includes(importStatement)) { // Find the last import statement and add after it const importRegex = /import.*from.*['"];?\s*$/gm; const matches = [...dbcontextContent.matchAll(importRegex)]; if (matches.length > 0) { const lastImport = matches[matches.length - 1]; const insertIndex = lastImport.index + lastImport[0].length; dbcontextContent = dbcontextContent.slice(0, insertIndex) + '\n' + importStatement + dbcontextContent.slice(insertIndex); } } // Add to dbcontext object const dbcontextObjectRegex = /export\s+default\s*\{([^}]*)\}/s; const match = dbcontextContent.match(dbcontextObjectRegex); if (match) { const objectContent = match[1]; if (!objectContent.includes(`${moduleNameCapitalized}Entity`)) { const newObjectContent = objectContent.trim() + `,\n ${moduleNameCapitalized}Entity`; dbcontextContent = dbcontextContent.replace(dbcontextObjectRegex, `export default {$1${newObjectContent}\n}`); } } fs.writeFileSync(dbcontextPath, dbcontextContent); console.log('📊 Updated dbcontext.ts'); } function updateRoutesIndex(currentDir, moduleName) { const routesIndexPath = path.join(currentDir, 'src/routes/index.ts'); if (!fs.existsSync(routesIndexPath)) { console.log('⚠️ routes/index.ts not found, skipping routes index update'); return; } let routesContent = fs.readFileSync(routesIndexPath, 'utf8'); // Add import const importStatement = `import ${moduleName}Routes from './${moduleName}-routes';`; if (!routesContent.includes(importStatement)) { // Find the last import statement and add after it const importRegex = /import.*from.*['"];?\s*$/gm; const matches = [...routesContent.matchAll(importRegex)]; if (matches.length > 0) { const lastImport = matches[matches.length - 1]; const insertIndex = lastImport.index + lastImport[0].length; routesContent = routesContent.slice(0, insertIndex) + '\n' + importStatement + routesContent.slice(insertIndex); } } // Add route registration const routeRegistration = `app.use('/api/${moduleName}s', ${moduleName}Routes);`; if (!routesContent.includes(routeRegistration)) { // Find where routes are registered (usually after imports) const routeRegex = /app\.use\('\/api\/.*',.*\);?\s*$/gm; const matches = [...routesContent.matchAll(routeRegex)]; if (matches.length > 0) { const lastRoute = matches[matches.length - 1]; const insertIndex = lastRoute.index + lastRoute[0].length; routesContent = routesContent.slice(0, insertIndex) + '\n' + routeRegistration + routesContent.slice(insertIndex); } } fs.writeFileSync(routesIndexPath, routesContent); console.log('🛣️ Updated routes/index.ts'); } async function registerModuleRoutes(currentDir, moduleName, moduleNameCapitalized) { const serverManagerPath = path.join(currentDir, 'src/core/server/manager.ts'); if (!fs.existsSync(serverManagerPath)) { console.warn('⚠️ Server manager not found, routes will need to be registered manually'); return; } let serverManagerContent = fs.readFileSync(serverManagerPath, 'utf8'); // Add import for the new module routes const importStatement = `const ${moduleName}Routes = require('../../modules/${moduleName}/routes/${moduleName}.routes').default;`; // Find the registerPluginRoutes method const methodRegex = /private registerPluginRoutes\(\): void \{([^}]*)\}/s; const match = serverManagerContent.match(methodRegex); if (match) { const methodContent = match[1]; // Add import if not already present if (!methodContent.includes(importStatement)) { const updatedMethodContent = methodContent + `\n ${importStatement}`; serverManagerContent = serverManagerContent.replace(methodRegex, `private registerPluginRoutes(): void {$1${updatedMethodContent}\n }`); } // Add route registration const routeRegistration = `this.app.use('/api', ${moduleName}Routes);`; if (!methodContent.includes(routeRegistration)) { const updatedMethodContent = methodContent + `\n ${routeRegistration}`; serverManagerContent = serverManagerContent.replace(methodRegex, `private registerPluginRoutes(): void {$1${updatedMethodContent}\n }`); } fs.writeFileSync(serverManagerPath, serverManagerContent); console.log('✅ Routes registered in server manager'); } else { console.warn('⚠️ Could not find registerPluginRoutes method, routes will need to be registered manually'); } } async function createDatabaseTable(currentDir, moduleName, moduleNameCapitalized) { // Create a database seeder script for the new module const seederScript = `import { ${moduleNameCapitalized}Entity } from '../schemas/${moduleName}.schema'; import { logger } from '../../core/logging'; export async function seed${moduleNameCapitalized}Table(): Promise<void> { try { // Create collection and indexes await ${moduleNameCapitalized}Entity.createCollection(); // Create indexes if needed await ${moduleNameCapitalized}Entity.collection.createIndex({ name: 1 }); await ${moduleNameCapitalized}Entity.collection.createIndex({ createdAt: -1 }); logger.info(\`✅ ${moduleNameCapitalized} table/collection created successfully\`); } catch (error) { logger.error(\`❌ Failed to create ${moduleNameCapitalized} table:\`, error); throw error; } } // Auto-run when imported seed${moduleNameCapitalized}Table().catch(console.error); `; const seederPath = path.join(currentDir, 'src/scripts', `seed-${moduleName}.ts`); fs.writeFileSync(seederPath, seederScript); // Add to main seeder const mainSeederPath = path.join(currentDir, 'src/core/database/seeder.ts'); if (fs.existsSync(mainSeederPath)) { let seederContent = fs.readFileSync(mainSeederPath, 'utf8'); // Add import const importStatement = `import { seed${moduleNameCapitalized}Table } from '../../scripts/seed-${moduleName}';`; if (!seederContent.includes(importStatement)) { seederContent = seederContent.replace(/import.*from.*['"];?\s*$/gm, `$&\n${importStatement}`); } // Add to seeding methods const seedMethodRegex = /async seedAll\(\): Promise<void> \{([^}]*)\}/s; const match = seederContent.match(seedMethodRegex); if (match) { const methodContent = match[1]; const seedCall = `await seed${moduleNameCapitalized}Table();`; if (!methodContent.includes(seedCall)) { const updatedMethodContent = methodContent + `\n ${seedCall}`; seederContent = seederContent.replace(seedMethodRegex, `async seedAll(): Promise<void> {$1${updatedMethodContent}\n }`); } } fs.writeFileSync(mainSeederPath, seederContent); } console.log('✅ Database table creation script added'); } //# sourceMappingURL=api-generator.js.map