nestjs-reverse-engineering
Version:
A powerful TypeScript/NestJS library for database reverse engineering, entity generation, and CRUD operations
289 lines • 13.1 kB
JavaScript
;
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ReverseEngineeringService = void 0;
/* eslint-disable prettier/prettier */
const common_1 = require("@nestjs/common");
const typeorm_1 = require("typeorm");
const introspector_factory_1 = require("./schema/introspector-factory");
const entity_builder_1 = require("./builders/entity-builder");
const path = require("path");
let ReverseEngineeringService = class ReverseEngineeringService {
constructor(dataSource) {
this.dataSource = dataSource;
}
/**
* Generate TypeScript entities from database schema
*/
async generateEntities(options = {}) {
const defaultOptions = {
outputPath: path.join(process.cwd(), 'generated/entities'),
generateInterfaces: false,
generateRepositories: false,
useDataTransferObjects: false,
includeComments: true,
namingConvention: 'camelCase',
includeRelations: true,
...options
};
console.log('Starting reverse engineering process...');
console.log(`Output path: ${defaultOptions.outputPath}`);
// Get database schema
const introspector = introspector_factory_1.SchemaIntrospectorFactory.create(this.dataSource);
const schema = await introspector.getDatabaseSchema();
console.log(`Found ${schema.tables.length} tables in ${schema.dialect} database`);
// Generate entities
const entityBuilder = new entity_builder_1.EntityBuilder(defaultOptions, schema.tables, schema.dialect);
for (const table of schema.tables) {
try {
await entityBuilder.generateEntity(table);
}
catch (error) {
console.error(`Failed to generate entity for table ${table.tableName}:`, error);
}
}
console.log('Reverse engineering process completed!');
}
/**
* Analyze database schema and return metadata
*/
async analyzeSchema() {
const introspector = introspector_factory_1.SchemaIntrospectorFactory.create(this.dataSource);
return await introspector.getDatabaseSchema();
}
/**
* Get basic table information
*/
async getTableList() {
const schema = await this.analyzeSchema();
return schema.tables.map(table => ({
tableName: table.tableName,
tableSchema: table.tableSchema,
columnCount: table.columns.length
}));
}
/**
* Generate SQL scripts for schema recreation
*/
async generateSQLScripts(options) {
const { SqlGenerator } = await Promise.resolve().then(() => require('./builders/sql-generator'));
const { DatabaseDialect } = await Promise.resolve().then(() => require('./types/database.types'));
const dialect = options?.dialect || introspector_factory_1.SchemaIntrospectorFactory.getDialectFromDataSource(this.dataSource);
const sqlGenerator = new SqlGenerator({
dialect: dialect === 'postgres' ? DatabaseDialect.POSTGRES : DatabaseDialect.MYSQL,
schemaName: options?.schemaName,
includeDropIfExists: options?.includeDropIfExists || false,
includeCreateIfNotExists: options?.includeCreateIfNotExists !== false,
outputPath: options?.outputPath || path.join(process.cwd(), 'generated/sql')
});
let result;
if (options?.entitiesPath) {
// Generate from entity files
console.log(`🔍 Generating SQL from entity files in ${options.entitiesPath}...`);
result = await sqlGenerator.generateFromEntityDirectory(options.entitiesPath);
}
else {
// Generate from database schema (default)
console.log('🔍 Generating SQL from database schema...');
const schema = await this.analyzeSchema();
result = sqlGenerator.generateCreateTableScripts(schema.tables);
}
// Save to file
const outputPath = await sqlGenerator.saveSqlScript(result);
return {
sql: result.sql,
tableCount: result.tableCount,
outputPath
};
}
/**
* Generate React/UI components
*/
async generateUIComponents() {
// TODO: Implement UI component generation
console.log('UI component generation not implemented yet');
}
/**
* Test database connection and compatibility
*/
async testConnection() {
try {
if (!this.dataSource.isInitialized) {
await this.dataSource.initialize();
}
const dialect = introspector_factory_1.SchemaIntrospectorFactory.getDialectFromDataSource(this.dataSource);
// Get database version
let version;
try {
switch (dialect) {
case 'postgres':
const pgResult = await this.dataSource.query('SELECT version()');
version = pgResult[0]?.version;
break;
case 'mysql':
const mysqlResult = await this.dataSource.query('SELECT VERSION() as version');
version = mysqlResult[0]?.version;
break;
// Add other dialects as needed
}
}
catch (versionError) {
console.warn('Could not retrieve database version:', versionError);
}
return {
connected: true,
dialect,
version
};
}
catch (error) {
return {
connected: false,
dialect: 'unknown',
error: error instanceof Error ? error.message : String(error)
};
}
}
/**
* Generate index.ts file for all entities
*/
async generateEntityIndex(entitiesPath) {
const { EntityIndexGenerator } = await Promise.resolve().then(() => require('./builders/entity-index-generator'));
const generator = new EntityIndexGenerator({
entitiesPath: entitiesPath || path.join(process.cwd(), 'generated/entities'),
outputPath: path.join(entitiesPath || path.join(process.cwd(), 'generated/entities'), 'index.ts'),
includeNamedExports: true,
includeEntitiesArray: true,
filePattern: '**/*.entity.ts'
});
await generator.generateIndex();
}
/**
* Scan entity files and return information
*/
async scanEntityFiles(entitiesPath) {
const { EntityIndexGenerator } = await Promise.resolve().then(() => require('./builders/entity-index-generator'));
const generator = new EntityIndexGenerator({
entitiesPath: entitiesPath || path.join(process.cwd(), 'generated/entities')
});
return await generator.scanEntityFiles();
}
/**
* Export table data as INSERT statements
*/
async exportTableData(options) {
const { DataExporter } = await Promise.resolve().then(() => require('./builders/data-exporter'));
const { DatabaseDialect } = await Promise.resolve().then(() => require('./types/database.types'));
const dialect = introspector_factory_1.SchemaIntrospectorFactory.getDialectFromDataSource(this.dataSource);
const dataExporter = new DataExporter(this.dataSource, {
dialect: dialect === 'postgres' ? DatabaseDialect.POSTGRES : DatabaseDialect.MYSQL,
batchSize: options?.batchSize || 1000,
outputPath: options?.outputPath || path.join(process.cwd(), 'generated/sql/data'),
prettyPrint: options?.prettyPrint !== false,
alignValues: options?.alignValues !== false,
nullHandling: options?.nullHandling || 'NULL',
includeHeaders: true,
includeTableComments: true,
tables: options?.tables,
excludeTables: options?.excludeTables,
whereConditions: options?.whereConditions,
dataMasking: {
enabled: options?.dataMasking?.enabled || false,
sensitiveFields: options?.dataMasking?.sensitiveFields || ['password', 'email', 'phone', 'mobile', 'ssn'],
maskingPatterns: {
email: { type: 'email', replacement: 'user{n}@example.com' },
password: { type: 'custom', replacement: '***MASKED***' },
phone: { type: 'phone', pattern: 'XXX-XXX-XXXX' },
mobile: { type: 'phone', pattern: 'XXX-XXX-XXXX' },
name: { type: 'name', replacement: 'User {n}' }
}
}
});
try {
const result = await dataExporter.exportAllTables();
return {
success: true,
tableCount: result.tableCount,
totalRows: result.totalRows,
fileCount: result.fileCount,
outputPaths: result.outputPaths,
statistics: result.statistics
};
}
catch (error) {
console.error('Data export failed:', error);
throw error;
}
}
/**
* Generate CRUD operations for all tables
*/
async generateCrudOperations(options) {
try {
const { CrudGenerator } = await Promise.resolve().then(() => require('./builders/crud-generator'));
const { DatabaseDialect } = await Promise.resolve().then(() => require('./types/database.types'));
const dialect = introspector_factory_1.SchemaIntrospectorFactory.getDialectFromDataSource(this.dataSource);
// Get database schema
const introspector = introspector_factory_1.SchemaIntrospectorFactory.create(this.dataSource);
const schema = await introspector.getDatabaseSchema();
// Filter tables if specified
let tables = schema.tables;
if (options?.tables) {
tables = tables.filter(table => options.tables.includes(table.tableName));
}
if (options?.excludeTables) {
tables = tables.filter(table => !options.excludeTables.includes(table.tableName));
}
const crudGenerator = new CrudGenerator({
outputPath: options?.outputPath || path.join(process.cwd(), 'generated/crud'),
dialect: dialect === 'postgres' ? DatabaseDialect.POSTGRES : DatabaseDialect.MYSQL,
framework: options?.framework || 'nestjs',
includeValidation: options?.includeValidation !== false,
includeSwagger: options?.includeSwagger !== false,
includePagination: options?.includePagination !== false,
includeFiltering: options?.includeFiltering !== false,
includeSorting: options?.includeSorting !== false,
includeRelations: options?.includeRelations !== false,
generateTests: options?.generateTests || false,
authGuards: options?.authGuards || false,
useTypeORM: options?.useTypeORM !== false,
useDTO: options?.useDTO !== false,
});
const result = await crudGenerator.generateCrudForTables(tables);
return {
success: true,
tablesProcessed: result.tablesProcessed,
filesGenerated: result.filesGenerated,
outputPaths: result.outputPaths,
modules: result.modules
};
}
catch (error) {
console.error('CRUD generation failed:', error);
throw error;
}
}
/**
* Generate CRUD operations for specific tables
*/
async generateCrudForTables(tableNames, options) {
return this.generateCrudOperations({
...options,
tables: tableNames
});
}
};
exports.ReverseEngineeringService = ReverseEngineeringService;
exports.ReverseEngineeringService = ReverseEngineeringService = __decorate([
(0, common_1.Injectable)(),
__metadata("design:paramtypes", [typeorm_1.DataSource])
], ReverseEngineeringService);
//# sourceMappingURL=reverse-engineering.service.js.map