@diagramers/cli
Version:
Diagramers CLI - Command-line tools for managing Diagramers projects
482 lines (455 loc) • 22 kB
JavaScript
;
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