generate server side

This commit is contained in:
Roland Schneider
2025-11-18 18:32:52 +01:00
parent e352f161bc
commit c5addf58d3
6 changed files with 237 additions and 7 deletions

View File

@@ -5,9 +5,10 @@ import { GenerateCommand } from './commands/generate.command';
import { ConfigService } from './services/config.service';
import { EntityGeneratorService } from './services/entity-generator.service';
import { CrudGeneratorService } from './services/crud-generator.service';
import { ModuleUpdaterService } from './services/module-updater.service';
@Module({
imports: [],
providers: [AppService,GenerateCommand,ConfigService,EntityGeneratorService,CrudGeneratorService],
providers: [AppService,GenerateCommand,ConfigService,EntityGeneratorService,CrudGeneratorService,ModuleUpdaterService],
})
export class AppModule {}

View File

@@ -4,6 +4,7 @@ import { ConfigService } from './config.service';
import * as path from 'path';
import * as fs from 'fs';
import { promises as fsPromises } from 'fs';
import { ModuleUpdaterService } from './module-updater.service';
interface NamingConvention {
singular: string;
@@ -14,7 +15,7 @@ interface NamingConvention {
@Injectable()
export class CrudGeneratorService {
constructor(private readonly configService: ConfigService) {
constructor(private readonly configService: ConfigService, private readonly moduleUpdaterService: ModuleUpdaterService,) {
}
public async generate(tableName: string): Promise<void> {
@@ -31,7 +32,15 @@ export class CrudGeneratorService {
await this.generateDtoFiles(names, moduleDir);
console.log(`✅ CRUD module for "${tableName}" created successfully in: ${moduleDir}`);
console.warn(`\n🔔 Action Required: Remember to import ${names.pascal}sModule into your main app.module.ts!`);
// --- 3. CALL THE UPDATER ---
const moduleFileName = `${names.plural}.module.ts`;
const fullModulePath = path.join(moduleDir, moduleFileName);
await this.moduleUpdaterService.addImportToAppModule(`${names.pascal}sModule`, fullModulePath);
// --- END OF CALL ---
// We can now remove the old warning!
console.log('\n✨ AppModule has been updated automatically! ✨');
} catch (error) {
console.error(`❌ An error occurred during CRUD generation:`, error.message);

View File

@@ -5,10 +5,13 @@ import { DataSource, DataSourceOptions, TableColumn } from 'typeorm';
import * as path from 'path';
import * as fs from 'fs';
import { promises as fsPromises } from 'fs';
import { ModuleUpdaterService } from './module-updater.service';
@Injectable()
export class EntityGeneratorService {
constructor(private readonly configService: ConfigService) {}
constructor(private readonly configService: ConfigService,
private readonly moduleUpdaterService: ModuleUpdaterService) {}
public async generate(tableName: string): Promise<void> {
console.log(`Generating entity for table: ${tableName}...`);
@@ -35,6 +38,12 @@ export class EntityGeneratorService {
const entityContent = this.createEntityTemplate(tableName, columns);
const singularName = this.toSingular(tableName);
// --- THIS IS THE FIX ---
// Define className here so it can be used below.
const className = this.toPascalCase(singularName);
// --- END OF FIX ---
const entityFileName = `${singularName}.entity.ts`;
const serverRoot = path.resolve(process.cwd(), config.server.path);
const entitiesDir = path.join(serverRoot, config.server.entitiesPath);
@@ -44,6 +53,9 @@ export class EntityGeneratorService {
fs.writeFileSync(outputPath, entityContent);
console.log(`✅ Entity created successfully at: ${outputPath}`);
// Now the call will work correctly
await this.moduleUpdaterService.addEntityToTypeOrm(className, outputPath);
} catch (error) {
console.error('❌ An error occurred:', error.message);
} finally {

View File

@@ -0,0 +1,115 @@
// src/services/module-updater.service.ts
import { Injectable } from '@nestjs/common';
import { ConfigService } from './config.service';
import { Project, PropertyAssignment, SyntaxKind, ObjectLiteralExpression, ArrayLiteralExpression, ArrowFunction } from 'ts-morph';
import * as path from 'path';
@Injectable()
export class ModuleUpdaterService {
constructor(private readonly configService: ConfigService) {}
public async addImportToAppModule(moduleNameToAdd: string, modulePath: string): Promise<void> {
// ... (this method remains unchanged)
const config = this.configService.get();
const serverRoot = path.resolve(process.cwd(), config.server.path);
const appModulePath = path.join(serverRoot, 'src', 'app.module.ts');
console.log(`Attempting to update ${appModulePath} for module import...`);
const project = new Project();
const sourceFile = project.addSourceFileAtPath(appModulePath);
const relativeModulePath = path.relative(path.dirname(appModulePath), modulePath).replace(/\\/g, '/').replace('.ts', '');
const existingImport = sourceFile.getImportDeclaration(d => d.getModuleSpecifierValue() === `./${relativeModulePath}`);
if (!existingImport) {
sourceFile.addImportDeclaration({
namedImports: [moduleNameToAdd],
moduleSpecifier: `./${relativeModulePath}`,
});
console.log(`Added import for ${moduleNameToAdd}.`);
} else {
console.log(`Import for ${moduleNameToAdd} already exists.`);
}
const appModuleClass = sourceFile.getClassOrThrow('AppModule');
const moduleDecorator = appModuleClass.getDecoratorOrThrow('Module');
const decoratorArg = moduleDecorator.getArguments()[0] as ObjectLiteralExpression;
const importsProperty = decoratorArg.getProperty('imports') as PropertyAssignment;
if (!importsProperty) {
throw new Error('Could not find "imports" array in AppModule decorator.');
}
const importsArray = importsProperty.getInitializerIfKindOrThrow(SyntaxKind.ArrayLiteralExpression);
const moduleAlreadyExists = importsArray.getElements().some(elem => elem.getText() === moduleNameToAdd);
if (!moduleAlreadyExists) {
importsArray.addElement(moduleNameToAdd);
console.log(`Added ${moduleNameToAdd} to the AppModule imports array.`);
} else {
console.log(`${moduleNameToAdd} is already present in the AppModule imports array.`);
}
await sourceFile.save();
console.log('AppModule saved successfully.');
}
// --- NEW METHOD ---
public async addEntityToTypeOrm(entityNameToAdd: string, entityPath: string): Promise<void> {
const config = this.configService.get();
const serverRoot = path.resolve(process.cwd(), config.server.path);
const appModulePath = path.join(serverRoot, 'src', 'app.module.ts');
console.log(`Attempting to update ${appModulePath} for entity registration...`);
const project = new Project();
const sourceFile = project.addSourceFileAtPath(appModulePath);
// 1. Add the import for the entity
const relativeEntityPath = path.relative(path.dirname(appModulePath), entityPath).replace(/\\/g, '/').replace('.ts', '');
const existingImport = sourceFile.getImportDeclaration(d => d.getModuleSpecifierValue() === `./${relativeEntityPath}`);
if (!existingImport) {
sourceFile.addImportDeclaration({
namedImports: [entityNameToAdd],
moduleSpecifier: `./${relativeEntityPath}`,
});
console.log(`Added import for entity ${entityNameToAdd}.`);
} else {
console.log(`Import for entity ${entityNameToAdd} already exists.`);
}
// 2. Find the 'moduleTypeOrm' variable declaration
const variableDeclaration = sourceFile.getVariableDeclarationOrThrow('moduleTypeOrm');
// 3. Navigate through the AST to find the 'entities' array
const useFactory = variableDeclaration
.getInitializerIfKindOrThrow(SyntaxKind.CallExpression)
.getArguments()[0]
.asKindOrThrow(SyntaxKind.ObjectLiteralExpression)
.getPropertyOrThrow('useFactory')
.asKindOrThrow(SyntaxKind.PropertyAssignment);
const factoryFunction = useFactory.getInitializerIfKindOrThrow(SyntaxKind.ArrowFunction);
const returnStatement = factoryFunction.getFirstDescendantByKindOrThrow(SyntaxKind.ReturnStatement);
const returnedObject = returnStatement.getExpressionIfKindOrThrow(SyntaxKind.ObjectLiteralExpression);
const entitiesProperty = returnedObject.getProperty('entities')!.asKindOrThrow(SyntaxKind.PropertyAssignment);
const entitiesArray = entitiesProperty.getInitializerIfKindOrThrow(SyntaxKind.ArrayLiteralExpression);
// 4. Check if the entity is already in the array and add it if not
const entityAlreadyExists = entitiesArray.getElements().some(elem => elem.getText() === entityNameToAdd);
if (!entityAlreadyExists) {
entitiesArray.addElement(entityNameToAdd);
console.log(`Added ${entityNameToAdd} to the TypeORM entities array.`);
} else {
console.log(`${entityNameToAdd} is already in the TypeORM entities array.`);
}
// 5. Save the file
await sourceFile.save();
console.log('AppModule saved successfully after entity registration.');
}
}