nestjs-reverse-engineering
Version:
A powerful TypeScript/NestJS library for database reverse engineering, entity generation, and CRUD operations
930 lines ⢠42.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CrudGenerator = void 0;
/* eslint-disable prettier/prettier */
const fs = require("fs");
const path = require("path");
const naming_utils_1 = require("../utils/naming-utils");
const type_mapper_1 = require("../utils/type-mapper");
class CrudGenerator {
constructor(config) {
this.config = config;
}
/**
* Helper method to create a file with content
*/
async createFile(filePath, content) {
const dir = path.dirname(filePath);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
fs.writeFileSync(filePath, content, 'utf8');
}
/**
* Filter tables based on configuration
*/
filterTables(tables) {
let filteredTables = tables;
// Apply included tables filter first (if specified)
if (this.config.crud.includedTables && this.config.crud.includedTables.length > 0) {
filteredTables = filteredTables.filter(table => this.config.crud.includedTables.includes(table.tableName));
}
// Apply excluded tables filter
if (this.config.crud.excludedTables && this.config.crud.excludedTables.length > 0) {
filteredTables = filteredTables.filter(table => !this.config.crud.excludedTables.includes(table.tableName));
}
return filteredTables;
}
/**
* Generate CRUD operations for all tables
*/
async generateCrudForTables(tables) {
const filteredTables = this.filterTables(tables);
const result = {
tablesProcessed: 0,
filesGenerated: 0,
outputPaths: [],
modules: [],
appModulePath: ''
};
console.log(`š Generating CRUD operations for ${filteredTables.length} tables...`);
if (tables.length !== filteredTables.length) {
console.log(`ā” Filtered from ${tables.length} to ${filteredTables.length} tables based on configuration`);
}
for (const table of filteredTables) {
try {
console.log(`š Processing table: ${table.tableName}`);
const moduleInfo = await this.generateCrudForTable(table);
result.tablesProcessed++;
result.filesGenerated += Object.keys(moduleInfo.files).length;
result.outputPaths.push(...Object.values(moduleInfo.files).flat());
result.modules.push(moduleInfo);
console.log(` ā
Generated ${Object.keys(moduleInfo.files).length} files`);
}
catch (error) {
console.error(` ā Failed to process ${table.tableName}:`, error);
}
}
// Generate or update main app module with all CRUD modules
result.appModulePath = await this.updateAppModule(result.modules);
result.outputPaths.push(result.appModulePath);
result.filesGenerated++;
console.log(`\nā
CRUD generation completed!`);
console.log(`š ${result.tablesProcessed} tables processed`);
console.log(`š ${result.filesGenerated} files generated`);
return result;
}
/**
* Generate CRUD operations for a single table
*/
async generateCrudForTable(table) {
const entityName = naming_utils_1.NamingUtils.toPascalCase(table.tableName);
const moduleName = naming_utils_1.NamingUtils.toCamelCase(table.tableName);
const moduleDir = path.join(this.config.paths.crud, moduleName);
// Ensure module directory exists
if (!fs.existsSync(moduleDir)) {
fs.mkdirSync(moduleDir, { recursive: true });
}
const moduleInfo = {
tableName: table.tableName,
entityName,
moduleName,
files: {
entity: '',
dto: [],
controller: '',
service: '',
repository: '',
module: '',
tests: []
}
};
// Generate entity file (only if not exists in entities folder)
moduleInfo.files.entity = await this.generateOrReuseEntity(table, moduleDir);
// Generate DTOs
if (this.config.crud.useDTO) {
moduleInfo.files.dto = await this.generateDTOs(table, moduleDir);
}
// Generate repository
moduleInfo.files.repository = await this.generateRepository(table, moduleDir);
// Generate service
moduleInfo.files.service = await this.generateService(table, moduleDir);
// Generate controller
moduleInfo.files.controller = await this.generateController(table, moduleDir);
// Generate module
moduleInfo.files.module = await this.generateModule(table, moduleDir);
// Generate tests if requested
if (this.config.crud.generateTests) {
moduleInfo.files.tests = await this.generateTests(table, moduleDir);
}
return moduleInfo;
}
/**
* Generate entity or reuse existing entity from entities folder
*/
async generateOrReuseEntity(table, moduleDir) {
const entityName = naming_utils_1.NamingUtils.toPascalCase(table.tableName);
const entityFileName = `${naming_utils_1.NamingUtils.toKebabCase(table.tableName)}.entity.ts`;
// Check if entity exists in the entities folder
const entitiesPath = path.join(this.config.paths.entities, entityFileName);
if (fs.existsSync(entitiesPath)) {
console.log(` š Reusing existing entity from: ${entitiesPath}`);
return entitiesPath;
}
// Generate new entity in the module folder
const filePath = path.join(moduleDir, 'entities', entityFileName);
const content = this.buildEntityContent(table);
await this.createFile(filePath, content);
return filePath;
}
/**
* Build entity content
*/
buildEntityContent(table) {
const entityName = naming_utils_1.NamingUtils.toPascalCase(table.tableName);
const lines = [];
// Imports
lines.push("import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm';");
if (this.config.crud.includeSwagger) {
lines.push("import { ApiProperty } from '@nestjs/swagger';");
}
if (this.config.crud.includeValidation) {
lines.push("import { IsString, IsNumber, IsBoolean, IsOptional, IsDate } from 'class-validator';");
}
lines.push('');
// Entity decorator and class
lines.push(``);
lines.push(`export class ${entityName} {`);
// Generate columns
table.columns.forEach(column => {
lines.push('');
lines.push(...this.buildColumnDecorators(column));
lines.push(` ${this.buildColumnProperty(column)}`);
});
lines.push('}');
return lines.join('\n');
}
/**
* Build column decorators
*/
buildColumnDecorators(column) {
const decorators = [];
// TypeORM decorators
if (column.columnName === 'id' && column.isAutoIncrement) {
decorators.push(' @PrimaryGeneratedColumn()');
}
else if (column.columnName.includes('created_at') || column.columnName.includes('createdAt')) {
decorators.push(' @CreateDateColumn()');
}
else if (column.columnName.includes('updated_at') || column.columnName.includes('updatedAt')) {
decorators.push(' @UpdateDateColumn()');
}
else {
const columnOptions = [];
if (column.columnName !== naming_utils_1.NamingUtils.toCamelCase(column.columnName)) {
columnOptions.push(`name: '${column.columnName}'`);
}
if (column.characterMaximumLength) {
columnOptions.push(`length: ${column.characterMaximumLength}`);
}
if (column.defaultValue) {
// Handle SQL constants and functions
if (typeof column.defaultValue === 'string' &&
(column.defaultValue.includes('CURRENT_') ||
column.defaultValue.includes('NOW()') ||
column.defaultValue.includes('UUID()'))) {
columnOptions.push(`default: () => "${column.defaultValue}"`);
}
else {
columnOptions.push(`default: ${column.defaultValue}`);
}
}
if (column.isNullable) {
columnOptions.push('nullable: true');
}
const optionsStr = columnOptions.length > 0 ? `{ ${columnOptions.join(', ')} }` : '';
decorators.push(` `);
}
// Swagger decorators
if (this.config.crud.includeSwagger) {
const swaggerOptions = [];
if (column.columnComment) {
swaggerOptions.push(`description: '${column.columnComment}'`);
}
if (column.isNullable) {
swaggerOptions.push('required: false');
}
const swaggerStr = swaggerOptions.length > 0 ? `{ ${swaggerOptions.join(', ')} }` : '';
decorators.push(` `);
}
// Validation decorators
if (this.config.crud.includeValidation) {
const tsType = this.getTypeScriptType(column.dataType);
if (column.isNullable) {
decorators.push(' @IsOptional()');
}
switch (tsType) {
case 'string':
decorators.push(' @IsString()');
break;
case 'number':
decorators.push(' @IsNumber()');
break;
case 'boolean':
decorators.push(' @IsBoolean()');
break;
case 'Date':
decorators.push(' @IsDate()');
break;
}
}
return decorators;
}
/**
* Build column property
*/
buildColumnProperty(column) {
const propertyName = naming_utils_1.NamingUtils.toCamelCase(column.columnName);
const tsType = this.getTypeScriptType(column.dataType);
const nullable = column.isNullable ? '?' : '';
return `${propertyName}${nullable}: ${tsType}${column.isNullable ? ' | null' : ''};`;
}
/**
* Generate DTOs
*/
async generateDTOs(table, moduleDir) {
const dtoDir = path.join(moduleDir, 'dto');
const entityName = naming_utils_1.NamingUtils.toPascalCase(table.tableName);
const filePaths = [];
// Create DTO
const createDtoPath = path.join(dtoDir, `create-${naming_utils_1.NamingUtils.toKebabCase(table.tableName)}.dto.ts`);
const createDtoContent = this.buildCreateDtoContent(table);
await this.createFile(createDtoPath, createDtoContent);
filePaths.push(createDtoPath);
// Update DTO
const updateDtoPath = path.join(dtoDir, `update-${naming_utils_1.NamingUtils.toKebabCase(table.tableName)}.dto.ts`);
const updateDtoContent = this.buildUpdateDtoContent(table);
await this.createFile(updateDtoPath, updateDtoContent);
filePaths.push(updateDtoPath);
// Query DTO (for filtering)
if (this.config.crud.includeFiltering) {
const queryDtoPath = path.join(dtoDir, `query-${naming_utils_1.NamingUtils.toKebabCase(table.tableName)}.dto.ts`);
const queryDtoContent = this.buildQueryDtoContent(table);
await this.createFile(queryDtoPath, queryDtoContent);
filePaths.push(queryDtoPath);
}
return filePaths;
}
/**
* Build Create DTO content
*/
buildCreateDtoContent(table) {
const entityName = naming_utils_1.NamingUtils.toPascalCase(table.tableName);
const lines = [];
// Imports
if (this.config.crud.includeSwagger) {
lines.push("import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';");
}
if (this.config.crud.includeValidation) {
lines.push("import { IsString, IsNumber, IsBoolean, IsOptional, IsDate } from 'class-validator';");
}
lines.push('');
// Class
lines.push(`export class Create${entityName}Dto {`);
// Generate properties (exclude id, created_at, updated_at)
const createColumns = table.columns.filter(col => !col.isAutoIncrement &&
!col.columnName.includes('created_at') &&
!col.columnName.includes('updated_at'));
createColumns.forEach(column => {
lines.push('');
lines.push(...this.buildDtoPropertyDecorators(column));
lines.push(` ${this.buildDtoProperty(column)}`);
});
lines.push('}');
return lines.join('\n');
}
/**
* Build Update DTO content
*/
buildUpdateDtoContent(table) {
const entityName = naming_utils_1.NamingUtils.toPascalCase(table.tableName);
const lines = [];
// Imports
lines.push(`import { PartialType } from '@nestjs/mapped-types';`);
lines.push(`import { Create${entityName}Dto } from './create-${naming_utils_1.NamingUtils.toKebabCase(table.tableName)}.dto';`);
lines.push('');
// Class
lines.push(`export class Update${entityName}Dto extends PartialType(Create${entityName}Dto) {}`);
return lines.join('\n');
}
/**
* Build Query DTO content
*/
buildQueryDtoContent(table) {
const entityName = naming_utils_1.NamingUtils.toPascalCase(table.tableName);
const lines = [];
// Imports
if (this.config.crud.includeSwagger) {
lines.push("import { ApiPropertyOptional } from '@nestjs/swagger';");
}
if (this.config.crud.includeValidation) {
lines.push("import { IsOptional, IsNumber, IsString } from 'class-validator';");
lines.push("import { Transform } from 'class-transformer';");
}
lines.push('');
// Class
lines.push(`export class Query${entityName}Dto {`);
// Pagination properties
if (this.config.crud.includePagination) {
lines.push('');
if (this.config.crud.includeSwagger) {
lines.push(" @ApiPropertyOptional({ description: 'Page number for pagination' })");
}
if (this.config.crud.includeValidation) {
lines.push(' @IsOptional()');
lines.push(' @IsNumber()');
lines.push(' @Transform(({ value }) => parseInt(value))');
}
lines.push(' page?: number;');
lines.push('');
if (this.config.crud.includeSwagger) {
lines.push(" @ApiPropertyOptional({ description: 'Number of items per page' })");
}
if (this.config.crud.includeValidation) {
lines.push(' @IsOptional()');
lines.push(' @IsNumber()');
lines.push(' @Transform(({ value }) => parseInt(value))');
}
lines.push(' limit?: number;');
}
// Sorting properties
if (this.config.crud.includeSorting) {
lines.push('');
if (this.config.crud.includeSwagger) {
lines.push(" @ApiPropertyOptional({ description: 'Field to sort by' })");
}
if (this.config.crud.includeValidation) {
lines.push(' @IsOptional()');
lines.push(' @IsString()');
}
lines.push(' sortBy?: string;');
lines.push('');
if (this.config.crud.includeSwagger) {
lines.push(" @ApiPropertyOptional({ description: 'Sort direction', enum: ['ASC', 'DESC'] })");
}
if (this.config.crud.includeValidation) {
lines.push(' @IsOptional()');
lines.push(' @IsString()');
}
lines.push(" sortOrder?: 'ASC' | 'DESC';");
}
lines.push('}');
return lines.join('\n');
}
/**
* Build DTO property decorators
*/
buildDtoPropertyDecorators(column) {
const decorators = [];
// Swagger decorators
if (this.config.crud.includeSwagger) {
const swaggerOptions = [];
if (column.columnComment) {
swaggerOptions.push(`description: '${column.columnComment}'`);
}
const swaggerStr = swaggerOptions.length > 0 ? `{ ${swaggerOptions.join(', ')} }` : '';
if (column.isNullable) {
decorators.push(` `);
}
else {
decorators.push(` `);
}
}
// Validation decorators
if (this.config.crud.includeValidation) {
const tsType = this.getTypeScriptType(column.dataType);
if (column.isNullable) {
decorators.push(' @IsOptional()');
}
switch (tsType) {
case 'string':
decorators.push(' @IsString()');
break;
case 'number':
decorators.push(' @IsNumber()');
break;
case 'boolean':
decorators.push(' @IsBoolean()');
break;
case 'Date':
decorators.push(' @IsDate()');
break;
}
}
return decorators;
}
/**
* Build DTO property
*/
buildDtoProperty(column) {
const propertyName = naming_utils_1.NamingUtils.toCamelCase(column.columnName);
const tsType = this.getTypeScriptType(column.dataType);
const nullable = column.isNullable ? '?' : '';
return `${propertyName}${nullable}: ${tsType}${column.isNullable ? ' | null' : ''};`;
}
/**
* Generate repository
*/
async generateRepository(table, moduleDir) {
const entityName = naming_utils_1.NamingUtils.toPascalCase(table.tableName);
const fileName = `${naming_utils_1.NamingUtils.toKebabCase(table.tableName)}.repository.ts`;
const filePath = path.join(moduleDir, fileName);
const content = this.buildRepositoryContent(table);
await this.createFile(filePath, content);
return filePath;
}
/**
* Build repository content
*/
buildRepositoryContent(table) {
const entityName = naming_utils_1.NamingUtils.toPascalCase(table.tableName);
const entityPath = this.getEntityImportPath(table);
const lines = [];
// Imports
lines.push("import { Injectable } from '@nestjs/common';");
lines.push("import { InjectRepository } from '@nestjs/typeorm';");
lines.push("import { Repository, FindManyOptions } from 'typeorm';");
lines.push(`import { ${entityName} } from '${entityPath}';`);
if (this.config.crud.useDTO) {
lines.push(`import { Create${entityName}Dto } from './dto/create-${naming_utils_1.NamingUtils.toKebabCase(table.tableName)}.dto';`);
lines.push(`import { Update${entityName}Dto } from './dto/update-${naming_utils_1.NamingUtils.toKebabCase(table.tableName)}.dto';`);
if (this.config.crud.includeFiltering) {
lines.push(`import { Query${entityName}Dto } from './dto/query-${naming_utils_1.NamingUtils.toKebabCase(table.tableName)}.dto';`);
}
}
lines.push('');
// Repository class
lines.push('@Injectable()');
lines.push(`export class ${entityName}Repository {`);
lines.push(` constructor(`);
lines.push(` `);
lines.push(` private readonly repository: Repository<${entityName}>,`);
lines.push(` ) {}`);
// Create method
lines.push('');
const createDtoType = this.config.crud.useDTO ? `Create${entityName}Dto` : `Partial<${entityName}>`;
lines.push(` async create(create${entityName}Dto: ${createDtoType}): Promise<${entityName}> {`);
lines.push(` const entity = this.repository.create(create${entityName}Dto);`);
lines.push(` return await this.repository.save(entity);`);
lines.push(` }`);
// Find all method
lines.push('');
if (this.config.crud.includeFiltering && this.config.crud.useDTO) {
lines.push(` async findAll(queryDto?: Query${entityName}Dto): Promise<{ data: ${entityName}[], total: number }> {`);
lines.push(` const { page = 1, limit = 10, sortBy = 'id', sortOrder = 'DESC', ...filters } = queryDto || {};`);
lines.push(` `);
lines.push(` const options: FindManyOptions<${entityName}> = {`);
lines.push(` order: { [sortBy]: sortOrder },`);
if (this.config.crud.includePagination) {
lines.push(` skip: (page - 1) * limit,`);
lines.push(` take: limit,`);
}
lines.push(` };`);
lines.push(` `);
lines.push(` const [data, total] = await this.repository.findAndCount(options);`);
lines.push(` return { data, total };`);
}
else {
lines.push(` async findAll(): Promise<${entityName}[]> {`);
lines.push(` return await this.repository.find();`);
}
lines.push(` }`);
// Find one method
lines.push('');
lines.push(` async findOne(id: number): Promise<${entityName}> {`);
lines.push(` const entity = await this.repository.findOne({ where: { id } as any });`);
lines.push(` if (!entity) {`);
lines.push(` throw new Error(\`${entityName} with ID \${id} not found\`);`);
lines.push(` }`);
lines.push(` return entity;`);
lines.push(` }`);
// Update method
lines.push('');
const updateDtoType = this.config.crud.useDTO ? `Update${entityName}Dto` : `Partial<${entityName}>`;
lines.push(` async update(id: number, update${entityName}Dto: ${updateDtoType}): Promise<${entityName}> {`);
lines.push(` const entity = await this.findOne(id);`);
lines.push(` Object.assign(entity, update${entityName}Dto);`);
lines.push(` return await this.repository.save(entity);`);
lines.push(` }`);
// Remove method
lines.push('');
lines.push(` async remove(id: number): Promise<void> {`);
lines.push(` const entity = await this.findOne(id);`);
lines.push(` await this.repository.remove(entity);`);
lines.push(` }`);
lines.push('}');
return lines.join('\n');
}
/**
* Get the correct import path for entity based on configuration
*/
getEntityImportPath(table) {
const entityFileName = `${naming_utils_1.NamingUtils.toKebabCase(table.tableName)}.entity`;
// Check if entity exists in global entities folder
const globalEntityPath = path.join(this.config.paths.entities, `${entityFileName}.ts`);
if (fs.existsSync(globalEntityPath)) {
// Calculate relative path from module to entities folder
const relativePath = path.relative(path.join(this.config.paths.crud, naming_utils_1.NamingUtils.toCamelCase(table.tableName)), this.config.paths.entities);
return `${relativePath}/${entityFileName}`.replace(/\\/g, '/');
}
// Use local entity in module
return `./entities/${entityFileName}`;
}
/**
* Generate service
*/
async generateService(table, moduleDir) {
const entityName = naming_utils_1.NamingUtils.toPascalCase(table.tableName);
const fileName = `${naming_utils_1.NamingUtils.toKebabCase(table.tableName)}.service.ts`;
const filePath = path.join(moduleDir, fileName);
const content = this.buildServiceContent(table);
await this.createFile(filePath, content);
return filePath;
}
/**
* Build service content
*/
buildServiceContent(table) {
const entityName = naming_utils_1.NamingUtils.toPascalCase(table.tableName);
const lines = [];
// Imports
lines.push("import { Injectable } from '@nestjs/common';");
lines.push(`import { ${entityName}Repository } from './${naming_utils_1.NamingUtils.toKebabCase(table.tableName)}.repository';`);
if (this.config.crud.useDTO) {
lines.push(`import { Create${entityName}Dto } from './dto/create-${naming_utils_1.NamingUtils.toKebabCase(table.tableName)}.dto';`);
lines.push(`import { Update${entityName}Dto } from './dto/update-${naming_utils_1.NamingUtils.toKebabCase(table.tableName)}.dto';`);
if (this.config.crud.includeFiltering) {
lines.push(`import { Query${entityName}Dto } from './dto/query-${naming_utils_1.NamingUtils.toKebabCase(table.tableName)}.dto';`);
}
}
lines.push('');
// Service class
lines.push('@Injectable()');
lines.push(`export class ${entityName}Service {`);
lines.push(` constructor(private readonly ${naming_utils_1.NamingUtils.toCamelCase(table.tableName)}Repository: ${entityName}Repository) {}`);
// Create method
lines.push('');
const createDtoType = this.config.crud.useDTO ? `Create${entityName}Dto` : `any`;
lines.push(` async create(create${entityName}Dto: ${createDtoType}) {`);
lines.push(` return await this.${naming_utils_1.NamingUtils.toCamelCase(table.tableName)}Repository.create(create${entityName}Dto);`);
lines.push(` }`);
// Find all method
lines.push('');
const queryParam = this.config.crud.includeFiltering && this.config.crud.useDTO ? `queryDto?: Query${entityName}Dto` : '';
lines.push(` async findAll(${queryParam}) {`);
if (this.config.crud.includeFiltering && this.config.crud.useDTO) {
lines.push(` return await this.${naming_utils_1.NamingUtils.toCamelCase(table.tableName)}Repository.findAll(queryDto);`);
}
else {
lines.push(` return await this.${naming_utils_1.NamingUtils.toCamelCase(table.tableName)}Repository.findAll();`);
}
lines.push(` }`);
// Find one method
lines.push('');
lines.push(` async findOne(id: number) {`);
lines.push(` return await this.${naming_utils_1.NamingUtils.toCamelCase(table.tableName)}Repository.findOne(id);`);
lines.push(` }`);
// Update method
lines.push('');
const updateDtoType = this.config.crud.useDTO ? `Update${entityName}Dto` : `any`;
lines.push(` async update(id: number, update${entityName}Dto: ${updateDtoType}) {`);
lines.push(` return await this.${naming_utils_1.NamingUtils.toCamelCase(table.tableName)}Repository.update(id, update${entityName}Dto);`);
lines.push(` }`);
// Remove method
lines.push('');
lines.push(` async remove(id: number) {`);
lines.push(` await this.${naming_utils_1.NamingUtils.toCamelCase(table.tableName)}Repository.remove(id);`);
lines.push(` return { message: \`${entityName} deleted successfully\` };`);
lines.push(` }`);
lines.push('}');
return lines.join('\n');
}
/**
* Generate controller
*/
async generateController(table, moduleDir) {
const entityName = naming_utils_1.NamingUtils.toPascalCase(table.tableName);
const fileName = `${naming_utils_1.NamingUtils.toKebabCase(table.tableName)}.controller.ts`;
const filePath = path.join(moduleDir, fileName);
const content = this.buildControllerContent(table);
await this.createFile(filePath, content);
return filePath;
}
/**
* Build controller content
*/
buildControllerContent(table) {
const entityName = naming_utils_1.NamingUtils.toPascalCase(table.tableName);
const controllerPath = naming_utils_1.NamingUtils.toKebabCase(table.tableName);
const lines = [];
// Imports
lines.push("import { Controller, Get, Post, Body, Patch, Param, Delete, Query } from '@nestjs/common';");
if (this.config.crud.includeSwagger) {
lines.push("import { ApiTags, ApiOperation, ApiResponse, ApiParam } from '@nestjs/swagger';");
}
if (this.config.crud.includeValidation) {
lines.push("import { ParseIntPipe } from '@nestjs/common';");
}
if (this.config.crud.authGuards) {
lines.push("import { UseGuards } from '@nestjs/common';");
lines.push("import { JwtAuthGuard } from '../auth/jwt-auth.guard';");
}
lines.push(`import { ${entityName}Service } from './${naming_utils_1.NamingUtils.toKebabCase(table.tableName)}.service';`);
if (this.config.crud.useDTO) {
lines.push(`import { Create${entityName}Dto } from './dto/create-${naming_utils_1.NamingUtils.toKebabCase(table.tableName)}.dto';`);
lines.push(`import { Update${entityName}Dto } from './dto/update-${naming_utils_1.NamingUtils.toKebabCase(table.tableName)}.dto';`);
if (this.config.crud.includeFiltering) {
lines.push(`import { Query${entityName}Dto } from './dto/query-${naming_utils_1.NamingUtils.toKebabCase(table.tableName)}.dto';`);
}
}
lines.push('');
// Controller decorators
if (this.config.crud.includeSwagger) {
lines.push(``);
}
if (this.config.crud.authGuards) {
lines.push('@UseGuards(JwtAuthGuard)');
}
lines.push(``);
lines.push(`export class ${entityName}Controller {`);
lines.push(` constructor(private readonly ${naming_utils_1.NamingUtils.toCamelCase(table.tableName)}Service: ${entityName}Service) {}`);
// CREATE endpoint
lines.push('');
lines.push(...this.buildCreateEndpoint(table));
// FIND ALL endpoint
lines.push('');
lines.push(...this.buildFindAllEndpoint(table));
// FIND ONE endpoint
lines.push('');
lines.push(...this.buildFindOneEndpoint(table));
// UPDATE endpoint
lines.push('');
lines.push(...this.buildUpdateEndpoint(table));
// DELETE endpoint
lines.push('');
lines.push(...this.buildDeleteEndpoint(table));
lines.push('}');
return lines.join('\n');
}
/**
* Build CREATE endpoint
*/
buildCreateEndpoint(table) {
const entityName = naming_utils_1.NamingUtils.toPascalCase(table.tableName);
const lines = [];
const dtoType = this.config.crud.useDTO ? `Create${entityName}Dto` : 'any';
if (this.config.crud.includeSwagger) {
lines.push(` `);
lines.push(` `);
lines.push(` `);
}
lines.push(` `);
lines.push(` create( create${entityName}Dto: ${dtoType}) {`);
lines.push(` return this.${naming_utils_1.NamingUtils.toCamelCase(table.tableName)}Service.create(create${entityName}Dto);`);
lines.push(` }`);
return lines;
}
/**
* Build FIND ALL endpoint
*/
buildFindAllEndpoint(table) {
const entityName = naming_utils_1.NamingUtils.toPascalCase(table.tableName);
const lines = [];
if (this.config.crud.includeSwagger) {
lines.push(` `);
lines.push(` `);
}
lines.push(` `);
if (this.config.crud.includeFiltering && this.config.crud.useDTO) {
lines.push(` findAll( queryDto: Query${entityName}Dto) {`);
lines.push(` return this.${naming_utils_1.NamingUtils.toCamelCase(table.tableName)}Service.findAll(queryDto);`);
}
else {
lines.push(` findAll() {`);
lines.push(` return this.${naming_utils_1.NamingUtils.toCamelCase(table.tableName)}Service.findAll();`);
}
lines.push(` }`);
return lines;
}
/**
* Build FIND ONE endpoint
*/
buildFindOneEndpoint(table) {
const entityName = naming_utils_1.NamingUtils.toPascalCase(table.tableName);
const lines = [];
if (this.config.crud.includeSwagger) {
lines.push(` `);
lines.push(` `);
lines.push(` `);
lines.push(` `);
}
lines.push(` `);
if (this.config.crud.includeValidation) {
lines.push(` findOne( id: number) {`);
}
else {
lines.push(` findOne( id: string) {`);
}
lines.push(` return this.${naming_utils_1.NamingUtils.toCamelCase(table.tableName)}Service.findOne(${this.config.crud.includeValidation ? 'id' : '+id'});`);
lines.push(` }`);
return lines;
}
/**
* Build UPDATE endpoint
*/
buildUpdateEndpoint(table) {
const entityName = naming_utils_1.NamingUtils.toPascalCase(table.tableName);
const lines = [];
const dtoType = this.config.crud.useDTO ? `Update${entityName}Dto` : 'any';
if (this.config.crud.includeSwagger) {
lines.push(` `);
lines.push(` `);
lines.push(` `);
lines.push(` `);
}
lines.push(` `);
if (this.config.crud.includeValidation) {
lines.push(` update( id: number, update${entityName}Dto: ${dtoType}) {`);
}
else {
lines.push(` update( id: string, update${entityName}Dto: ${dtoType}) {`);
}
lines.push(` return this.${naming_utils_1.NamingUtils.toCamelCase(table.tableName)}Service.update(${this.config.crud.includeValidation ? 'id' : '+id'}, update${entityName}Dto);`);
lines.push(` }`);
return lines;
}
/**
* Build DELETE endpoint
*/
buildDeleteEndpoint(table) {
const entityName = naming_utils_1.NamingUtils.toPascalCase(table.tableName);
const lines = [];
if (this.config.crud.includeSwagger) {
lines.push(` `);
lines.push(` `);
lines.push(` `);
lines.push(` `);
}
lines.push(` `);
if (this.config.crud.includeValidation) {
lines.push(` remove( id: number) {`);
}
else {
lines.push(` remove( id: string) {`);
}
lines.push(` return this.${naming_utils_1.NamingUtils.toCamelCase(table.tableName)}Service.remove(${this.config.crud.includeValidation ? 'id' : '+id'});`);
lines.push(` }`);
return lines;
}
/**
* Generate module
*/
async generateModule(table, moduleDir) {
const entityName = naming_utils_1.NamingUtils.toPascalCase(table.tableName);
const fileName = `${naming_utils_1.NamingUtils.toKebabCase(table.tableName)}.module.ts`;
const filePath = path.join(moduleDir, fileName);
const content = this.buildModuleContent(table);
await this.createFile(filePath, content);
return filePath;
}
/**
* Build module content
*/
buildModuleContent(table) {
const entityName = naming_utils_1.NamingUtils.toPascalCase(table.tableName);
const kebabName = naming_utils_1.NamingUtils.toKebabCase(table.tableName);
const entityPath = this.getEntityImportPath(table);
const lines = [];
// Imports
lines.push("import { Module } from '@nestjs/common';");
lines.push("import { TypeOrmModule } from '@nestjs/typeorm';");
lines.push(`import { ${entityName}Controller } from './${kebabName}.controller';`);
lines.push(`import { ${entityName}Service } from './${kebabName}.service';`);
lines.push(`import { ${entityName}Repository } from './${kebabName}.repository';`);
lines.push(`import { ${entityName} } from '${entityPath}';`);
lines.push('');
// Module decorator
lines.push('@Module({');
lines.push(' imports: [');
lines.push(` TypeOrmModule.forFeature([${entityName}]),`);
lines.push(' ],');
lines.push(` controllers: [${entityName}Controller],`);
lines.push(` providers: [${entityName}Service, ${entityName}Repository],`);
lines.push(` exports: [${entityName}Service, ${entityName}Repository],`);
lines.push('})');
lines.push(`export class ${entityName}Module {}`);
return lines.join('\n');
}
/**
* Generate tests
*/
async generateTests(table, moduleDir) {
const filePaths = [];
// Test generation implementation would go here
// For now, return empty array
return filePaths;
}
/**
* Update or generate app module with all CRUD modules
*/
async updateAppModule(modules) {
const appModulePath = path.join(this.config.paths.baseOutput, 'app.module.ts');
// Check if app.module.ts already exists
if (fs.existsSync(appModulePath)) {
// Update existing app module
return await this.updateExistingAppModule(appModulePath, modules);
}
else {
// Generate new app module
return await this.generateNewAppModule(modules);
}
}
/**
* Update existing app module
*/
async updateExistingAppModule(appModulePath, modules) {
console.log(`š Updating existing app module: ${appModulePath}`);
// Read existing app module
let content = fs.readFileSync(appModulePath, 'utf8');
// Add imports for new modules
let importSection = '';
modules.forEach(module => {
const importLine = `import { ${module.entityName}Module } from './${module.moduleName}/${naming_utils_1.NamingUtils.toKebabCase(module.tableName)}.module';`;
if (!content.includes(importLine)) {
importSection += importLine + '\n';
}
});
if (importSection) {
// Find where to insert imports (after existing imports, before @Module)
const moduleDecoratorIndex = content.indexOf('@Module(');
if (moduleDecoratorIndex > -1) {
content = content.slice(0, moduleDecoratorIndex) + importSection + '\n' + content.slice(moduleDecoratorIndex);
}
}
// Add modules to imports array if not already present
modules.forEach(module => {
const moduleImport = `${module.entityName}Module,`;
if (!content.includes(moduleImport)) {
// Find the imports array and add the module
const importsMatch = content.match(/imports:\s*\[([\s\S]*?)\]/);
if (importsMatch) {
const currentImports = importsMatch[1];
const newImports = currentImports.trim()
? currentImports + `\n ${moduleImport}`
: `\n ${moduleImport}\n `;
content = content.replace(importsMatch[0], `imports: [${newImports}]`);
}
}
});
await this.createFile(appModulePath, content);
return appModulePath;
}
/**
* Generate new app module
*/
async generateNewAppModule(modules) {
const filePath = path.join(this.config.paths.baseOutput, 'app.module.ts');
const lines = [];
// Imports
lines.push("import { Module } from '@nestjs/common';");
lines.push("import { TypeOrmModule } from '@nestjs/typeorm';");
modules.forEach(module => {
lines.push(`import { ${module.entityName}Module } from './${module.moduleName}/${naming_utils_1.NamingUtils.toKebabCase(module.tableName)}.module';`);
});
lines.push('');
// App Module
lines.push('@Module({');
lines.push(' imports: [');
lines.push(' TypeOrmModule.forRoot({');
lines.push(' // Database configuration should be provided by your application');
lines.push(' // This can be configured via environment variables or configuration service');
lines.push(' }),');
modules.forEach(module => {
lines.push(` ${module.entityName}Module,`);
});
lines.push(' ],');
lines.push('})');
lines.push('export class AppModule {}');
await this.createFile(filePath, lines.join('\n'));
return filePath;
}
/**
* Get TypeScript type from database type
*/
getTypeScriptType(dataType) {
const mapping = type_mapper_1.TypeMapper.mapType(dataType, this.config.database.type);
return mapping.tsType;
}
}
exports.CrudGenerator = CrudGenerator;
//# sourceMappingURL=crud-generator-new.js.map