UNPKG

crud-generator-ts

Version:

CRUD generator for TypeScript Express applications

217 lines (192 loc) 8.21 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.run = void 0; const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const generateFiles = (resourceName) => { var _a, _b; const className = resourceName.charAt(0).toUpperCase() + resourceName.slice(1); const fileName = resourceName .replace(/([A-Z])/g, '-$1') .toLowerCase() .replace(/^-/, ''); const entityTemplate = `import { Entity, Column, PrimaryColumn, BeforeInsert, BeforeUpdate, DeleteDateColumn, PrimaryGeneratedColumn } from 'typeorm'; @Entity({ name: '${resourceName.replace(/([A-Z])/g, '_$1').toLowerCase()}', schema: process.env.DB_CONNECTION_SCHEMA, }) export class ${className} { @PrimaryGeneratedColumn({ type: 'int' }) id: number; // add your columns here @Column({ type: 'timestamp', nullable: true, default: () => 'CURRENT_TIMESTAMP', }) created_at: Date; @Column({ type: 'timestamp', nullable: true, default: () => 'CURRENT_TIMESTAMP', }) updated_at?: Date; @DeleteDateColumn({ type: 'timestamp' }) deleted_at?: Date; }`; const controllerTemplate = ` import { Body, Controller, Delete, Get, Path, Post, Put, Route, Tags } from 'tsoa'; import { AppDataSource } from '../infra/database/main'; import { ${className}Repository } from '../repositories/${fileName}-repository'; @Route('${fileName}') @Tags('${className}') export class ${className}Controller extends Controller { private repository = new ${className}Repository(AppDataSource); @Get('/') public async getAll(): Promise<any> { return await this.repository.findAll(); } @Get('/{id}') public async getById(@Path() id: number): Promise<any> { return await this.repository.findById(id); } @Post('/') public async create(@Body() body: any): Promise<any> { return await this.repository.create(body); } @Put('/{id}') public async update(@Path() id: number, @Body() body: any): Promise<any> { return await this.repository.update(id, body); } @Delete('/{id}') public async delete(@Path() id: number): Promise<any> { await this.repository.deleteById(id); return { message: 'Deletado com sucesso' }; } } `; const repositoryTemplate = ` import { DataSource, Repository } from 'typeorm'; import { ${className} } from '../infra/entity/${fileName}'; export class ${className}Repository { private repository: Repository<${className}>; constructor(dataSource: DataSource) { this.repository = dataSource.getRepository(${className}); } async findAll(): Promise<${className}[]> { return await this.repository.find(); } async findById(id: number): Promise<${className} | null> { return await this.repository.findOne({ where: { id } }); } async create(entity: Partial<${className}>): Promise<${className}> { const newEntity = this.repository.create(entity); return await this.repository.save(newEntity); } async update(id: number, updatedData: Partial<${className}>): Promise<${className} | null> { const entity = await this.findById(id); if (!entity) { return null; } Object.assign(entity, updatedData); return await this.repository.save(entity); } async deleteById(id: number): Promise<void> { await this.repository.delete(id); } } `; const routeTemplate = `import { Router } from 'express'; import { ${className}Controller } from '../controllers/${fileName}-controller'; export const ${fileName.replace(/-/g, '')}Routes = Router(); const ${resourceName}Controller = new ${className}Controller(); ${fileName.replace(/-/g, '')}Routes.get('/', async (_req, res) => { const response = await ${resourceName}Controller.getAll(); res.status(200).json(response); }); ${fileName.replace(/-/g, '')}Routes.get('/:id', async (req, res) => { const response = await ${resourceName}Controller.getById(parseInt(req.params.id)); res.status(200).json(response); }); ${fileName.replace(/-/g, '')}Routes.post('/', async (req, res) => { const response = await ${resourceName}Controller.create(req.body); res.status(201).json(response); }); ${fileName.replace(/-/g, '')}Routes.put('/:id', async (req, res) => { const response = await ${resourceName}Controller.update(parseInt(req.params.id), req.body); res.status(200).json(response); }); ${fileName.replace(/-/g, '')}Routes.delete('/:id', async (req, res) => { await ${resourceName}Controller.delete(parseInt(req.params.id)); res.status(200).json({ message: 'Registro deletado com sucesso' }); });`; const dirs = [ 'src/infra/entity', 'src/controllers', 'src/repositories', 'src/routes' ].map(dir => dir.split('/').join(path_1.default.sep)); dirs.forEach(dir => { if (!fs_1.default.existsSync(dir)) { fs_1.default.mkdirSync(dir, { recursive: true }); } }); fs_1.default.writeFileSync(`src/infra/entity/${fileName}.ts`, entityTemplate.trim()); fs_1.default.writeFileSync(`src/controllers/${fileName}-controller.ts`, controllerTemplate.trim()); fs_1.default.writeFileSync(`src/repositories/${fileName}-repository.ts`, repositoryTemplate.trim()); fs_1.default.writeFileSync(`src/routes/${fileName}.routes.ts`, routeTemplate.trim()); const entityIndexPath = 'src/infra/entity/index.ts'; let entityIndexContent = fs_1.default.existsSync(entityIndexPath) ? fs_1.default.readFileSync(entityIndexPath, 'utf8') : ''; const importLine = `import { ${className} } from './${fileName}';`; const exportLine = ` ${className}`; if (!entityIndexContent.includes(importLine)) { if (!entityIndexContent) { entityIndexContent = `${importLine}\n\nexport {\n${exportLine}\n};`; } else { let imports = ((_a = entityIndexContent.match(/(import.*;\n)*/g)) === null || _a === void 0 ? void 0 : _a[0]) || ''; let exports = ((_b = entityIndexContent.match(/export {([\s\S]*?)};/)) === null || _b === void 0 ? void 0 : _b[1]) || ''; imports = `${importLine}\n${imports}`.trim(); exports = exports .split(',') .filter(Boolean) .map(e => e.trim()) .concat([exportLine]) .join(',\n '); entityIndexContent = `${imports}\n\nexport {\n ${exports}\n};`; } fs_1.default.writeFileSync(entityIndexPath, entityIndexContent); } const routesIndexPath = 'src/routes/index.ts'; let routesIndexContent = fs_1.default.existsSync(routesIndexPath) ? fs_1.default.readFileSync(routesIndexPath, 'utf8') : `import { isAuthenticated } from '../middlewares/auth';\nimport { Router } from 'express';\n\nconst routes = Router();\n\nexport default routes;`; const routeImportLine = `import { ${fileName.replace(/-/g, '')}Routes } from './${fileName}.routes';`; const routeUseLine = `routes.use('/${fileName}', isAuthenticated, ${fileName.replace(/-/g, '')}Routes);`; if (!routesIndexContent.includes(routeImportLine)) { let [imports, rest] = routesIndexContent.split('\nconst routes'); imports = `${routeImportLine}\n${imports}`.trim(); let [declaration, uses, export_] = rest.split('\nexport'); uses = uses.split('\n') .filter(line => line.trim()) .concat([routeUseLine]) .join('\n'); routesIndexContent = `${imports}\n\nconst routes${declaration}\n${uses}\n\nexport${export_}`; fs_1.default.writeFileSync(routesIndexPath, routesIndexContent); } console.log(`Resource ${className} created successfully!`); }; const run = () => { const resourceName = process.argv[2]; if (!resourceName) { console.error('Please provide a resource name!'); process.exit(1); } generateFiles(resourceName); }; exports.run = run;