add force flag to overwrite existing files
This commit is contained in:
parent
a75f286e6b
commit
90cb73379e
@ -8,6 +8,7 @@ import { ModuleUpdaterService } from './services/module-updater.service';
|
||||
import { AngularGeneratorService } from './services/angular-generator.service';
|
||||
import { TemplateService } from './services/template.service';
|
||||
import { GenericTableGeneratorService } from './services/generic-table-generator.service';
|
||||
import { FileSystemService } from './services/file-system.service';
|
||||
|
||||
@Module({
|
||||
imports: [],
|
||||
@ -19,7 +20,8 @@ import { GenericTableGeneratorService } from './services/generic-table-generator
|
||||
ModuleUpdaterService,
|
||||
AngularGeneratorService,
|
||||
TemplateService,
|
||||
GenericTableGeneratorService
|
||||
GenericTableGeneratorService,
|
||||
FileSystemService
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
@ -1,9 +1,14 @@
|
||||
// src/commands/generate.command.ts
|
||||
import { Command, CommandRunner } from 'nest-commander';
|
||||
import { Command, CommandRunner, Option } from 'nest-commander';
|
||||
import { EntityGeneratorService } from '../services/entity-generator.service';
|
||||
import { CrudGeneratorService } from '../services/crud-generator.service';
|
||||
import { AngularGeneratorService } from '../services/angular-generator.service';
|
||||
|
||||
interface GenerateCommandOptions {
|
||||
force: boolean;
|
||||
}
|
||||
|
||||
|
||||
@Command({
|
||||
name: 'generate',
|
||||
description: 'Generate code for the dvbooking project',
|
||||
@ -19,8 +24,9 @@ export class GenerateCommand extends CommandRunner {
|
||||
super();
|
||||
}
|
||||
|
||||
async run(passedParams: string[]): Promise<void> {
|
||||
async run(passedParams: string[], options?: GenerateCommandOptions): Promise<void> {
|
||||
const [type, name] = passedParams;
|
||||
const force = options?.force || false; // Default force to false
|
||||
|
||||
if (!type || !name) {
|
||||
console.error('Error: Missing required arguments: <type> and <name>.');
|
||||
@ -31,20 +37,21 @@ export class GenerateCommand extends CommandRunner {
|
||||
|
||||
switch (type) {
|
||||
case 'entity':
|
||||
await this.entityGeneratorService.generate(name);
|
||||
await this.entityGeneratorService.generate(name, force);
|
||||
break;
|
||||
|
||||
case 'crud':
|
||||
await this.crudGeneratorService.generate(name);
|
||||
//await this.crudGeneratorService.generate(name,force);
|
||||
break;
|
||||
|
||||
case 'angular': // <-- 3. Add new case
|
||||
await this.angularGeneratorService.generate(name);
|
||||
case 'angular':
|
||||
// todo: implement
|
||||
// await this.angularGeneratorService.generate(name,force);
|
||||
break;
|
||||
|
||||
case 'all':
|
||||
console.log('--- Generating Entity ---');
|
||||
const columns = await this.entityGeneratorService.generate(name);
|
||||
const columns = await this.entityGeneratorService.generate(name, force);
|
||||
|
||||
if (!columns) {
|
||||
console.error('❌ Entity generation failed. Aborting subsequent steps.');
|
||||
@ -52,10 +59,10 @@ export class GenerateCommand extends CommandRunner {
|
||||
}
|
||||
|
||||
console.log('\n--- Generating CRUD Module ---');
|
||||
await this.crudGeneratorService.generate(name, columns);
|
||||
await this.crudGeneratorService.generate(name, columns, force);
|
||||
console.log('\n✨ All backend files generated successfully! ✨');
|
||||
console.log('\n--- Generating Angular Files ---');
|
||||
await this.angularGeneratorService.generate(name,columns);
|
||||
await this.angularGeneratorService.generate(name,columns, force);
|
||||
console.log('\n✨ All files generated successfully! ✨');
|
||||
break;
|
||||
|
||||
@ -64,4 +71,14 @@ export class GenerateCommand extends CommandRunner {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// NEW: Add the --force option
|
||||
@Option({
|
||||
flags: '-f, --force',
|
||||
description: 'Overwrite existing files.',
|
||||
defaultValue: false,
|
||||
})
|
||||
parseForce(): boolean {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -9,6 +9,7 @@ import { promises as fsPromises } from 'fs';
|
||||
import { TableColumn } from 'typeorm';
|
||||
import { GenericTableGeneratorService } from './generic-table-generator.service';
|
||||
import { mapDbToTsType, toCamelCase, toKebabCase, toPascalCase, toSingular } from '../utils/naming.utils';
|
||||
import { FileSystemService } from './file-system.service';
|
||||
|
||||
// Interface for structured field metadata passed to templates
|
||||
interface FieldDefinition {
|
||||
@ -34,9 +35,10 @@ export class AngularGeneratorService {
|
||||
private readonly templateService: TemplateService,
|
||||
private readonly moduleUpdaterService: ModuleUpdaterService,
|
||||
private readonly genericTableGeneratorService: GenericTableGeneratorService,
|
||||
private readonly fileSystemService: FileSystemService,
|
||||
) {}
|
||||
|
||||
public async generate(tableName: string, columns?: TableColumn[]): Promise<void> {
|
||||
public async generate(tableName: string, columns: TableColumn[], force: boolean): Promise<void> {
|
||||
console.log(`Generating Angular module for table: ${tableName}...`);
|
||||
const names = this.getNamingConvention(tableName);
|
||||
|
||||
@ -69,40 +71,40 @@ export class AngularGeneratorService {
|
||||
const featureDir = path.join(adminRoot, 'src', 'app', 'features', names.plural);
|
||||
|
||||
try {
|
||||
await this.generateModel(names, featureDir);
|
||||
await this.generateService(names, featureDir);
|
||||
await this.generateFilterComponent(names, featureDir);
|
||||
await this.generateListComponent(names, featureDir);
|
||||
|
||||
// 3. New Generic Table View
|
||||
const tableCompPath = await this.generateTableComponent(tableName, columns || [], names, featureDir);
|
||||
|
||||
// 4. Details & Form Views
|
||||
await this.generateDetailsComponent(names, featureDir);
|
||||
await this.generateFormComponent(names, featureDir);
|
||||
await this.generateModel(names, featureDir, force);
|
||||
await this.generateService(names, featureDir, force);
|
||||
await this.generateFilterComponent(names, featureDir, force);
|
||||
await this.generateListComponent(names, featureDir, force);
|
||||
const tableCompPath = await this.generateTableComponent(tableName, columns || [], names, featureDir,force);
|
||||
await this.generateDetailsComponent(names, featureDir, force);
|
||||
await this.generateFormComponent(names, featureDir, force);
|
||||
|
||||
const listCompPath = path.join(featureDir, 'components', `${names.singular}-list`, `${names.singular}-list.component.ts`);
|
||||
const detailsCompPath = path.join(featureDir, 'components', `${names.singular}-details`, `${names.singular}-details.component.ts`);
|
||||
const formCompPath = path.join(featureDir, 'components', `${names.singular}-form`, `${names.singular}-form.component.ts`);
|
||||
|
||||
await this.moduleUpdaterService.addRouteToAngularApp(`${names.pascal}FormComponent`, formCompPath, `${names.plural}/new`);
|
||||
await this.moduleUpdaterService.addRouteToAngularApp(`${names.pascal}FormComponent`, formCompPath, `${names.plural}/:id/edit`);
|
||||
await this.moduleUpdaterService.addRouteToAngularApp(`${names.pascal}DetailsComponent`, detailsCompPath, `${names.plural}/:id`);
|
||||
// Define the auth object. The role is the kebab-cased singular name (e.g., 'event-type')
|
||||
const auth = { role: 'admin' };
|
||||
|
||||
await this.moduleUpdaterService.addRouteToAngularApp(`${names.pascal}TableComponent`, tableCompPath, `${names.plural}/table`);
|
||||
await this.moduleUpdaterService.addRouteToAngularApp(`${names.pascal}ListComponent`, listCompPath, names.plural);
|
||||
// Pass the auth object to each route registration call
|
||||
await this.moduleUpdaterService.addRouteToAngularApp(`${names.pascal}FormComponent`, formCompPath, `${names.plural}/new`, auth);
|
||||
await this.moduleUpdaterService.addRouteToAngularApp(`${names.pascal}FormComponent`, formCompPath, `${names.plural}/:id/edit`, auth);
|
||||
await this.moduleUpdaterService.addRouteToAngularApp(`${names.pascal}DetailsComponent`, detailsCompPath, `${names.plural}/:id`, auth);
|
||||
await this.moduleUpdaterService.addRouteToAngularApp(`${names.pascal}TableComponent`, tableCompPath, `${names.plural}/table`, auth);
|
||||
await this.moduleUpdaterService.addRouteToAngularApp(`${names.pascal}ListComponent`, listCompPath, names.plural, auth);
|
||||
// --- END OF FIX ---
|
||||
|
||||
console.log(`✅ Angular files for "${tableName}" created successfully in: ${featureDir}`);
|
||||
console.log('\n✨ app.routes.ts has been updated with full CRUD routes (List + Table)! ✨');
|
||||
console.log('\n✨ app.routes.ts has been updated with full, secured CRUD routes! ✨');
|
||||
|
||||
} catch (error) {
|
||||
console.error(`❌ An error occurred during Angular generation:`, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
private async generateTableComponent(tableName: string, columns: TableColumn[], names: NamingConvention, featureDir: string): Promise<string> {
|
||||
private async generateTableComponent(tableName: string, columns: TableColumn[], names: NamingConvention, featureDir: string, force: boolean): Promise<string> {
|
||||
// This method now correctly calls the renamed service
|
||||
return this.genericTableGeneratorService.generate(tableName, columns, names, featureDir);
|
||||
return this.genericTableGeneratorService.generate(tableName, columns, names, featureDir, force);
|
||||
}
|
||||
|
||||
private getFormFieldHtml(field: FieldDefinition, isFilter: boolean = false): string {
|
||||
@ -128,51 +130,51 @@ private async generateTableComponent(tableName: string, columns: TableColumn[],
|
||||
}
|
||||
|
||||
// ... (All other generate... and naming methods are unchanged)
|
||||
private async generateModel(names: NamingConvention, featureDir: string) {
|
||||
private async generateModel(names: NamingConvention, featureDir: string, force: boolean) {
|
||||
const modelsDir = path.join(featureDir, 'models');
|
||||
const content = this.templateService.render('angular/model.ts.tpl', names);
|
||||
const filePath = path.join(modelsDir, `${names.singular}.model.ts`);
|
||||
await fsPromises.mkdir(modelsDir, { recursive: true });
|
||||
fs.writeFileSync(filePath, content);
|
||||
await this.fileSystemService.writeFile(filePath, content, force);
|
||||
}
|
||||
private async generateService(names: NamingConvention, featureDir: string) {
|
||||
private async generateService(names: NamingConvention, featureDir: string, force: boolean) {
|
||||
const servicesDir = path.join(featureDir, 'services');
|
||||
const content = this.templateService.render('angular/service.ts.tpl', names);
|
||||
const filePath = path.join(servicesDir, `${names.singular}.service.ts`);
|
||||
await fsPromises.mkdir(servicesDir, { recursive: true });
|
||||
fs.writeFileSync(filePath, content);
|
||||
await this.fileSystemService.writeFile(filePath, content, force);
|
||||
}
|
||||
private async generateFilterComponent(names: NamingConvention, featureDir: string) {
|
||||
private async generateFilterComponent(names: NamingConvention, featureDir: string, force: boolean) {
|
||||
const compDir = path.join(featureDir, 'components', `${names.singular}-filter`);
|
||||
const tsContent = this.templateService.render('angular/filter.component.ts.tpl', names);
|
||||
const htmlContent = this.templateService.render('angular/filter.component.html.tpl', names);
|
||||
await fsPromises.mkdir(compDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(compDir, `${names.singular}-filter.component.ts`), tsContent);
|
||||
fs.writeFileSync(path.join(compDir, `${names.singular}-filter.component.html`), htmlContent);
|
||||
await this.fileSystemService.writeFile(path.join(compDir, `${names.singular}-filter.component.ts`), tsContent, force);
|
||||
await this.fileSystemService.writeFile(path.join(compDir, `${names.singular}-filter.component.html`), htmlContent, force);
|
||||
}
|
||||
private async generateListComponent(names: NamingConvention, featureDir: string) {
|
||||
private async generateListComponent(names: NamingConvention, featureDir: string, force: boolean) {
|
||||
const compDir = path.join(featureDir, 'components', `${names.singular}-list`);
|
||||
const tsContent = this.templateService.render('angular/list.component.ts.tpl', names);
|
||||
const htmlContent = this.templateService.render('angular/list.component.html.tpl', names);
|
||||
await fsPromises.mkdir(compDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(compDir, `${names.singular}-list.component.ts`), tsContent);
|
||||
fs.writeFileSync(path.join(compDir, `${names.singular}-list.component.html`), htmlContent);
|
||||
await this.fileSystemService.writeFile(path.join(compDir, `${names.singular}-list.component.ts`), tsContent, force);
|
||||
await this.fileSystemService.writeFile(path.join(compDir, `${names.singular}-list.component.html`), htmlContent, force);
|
||||
}
|
||||
private async generateDetailsComponent(names: NamingConvention, featureDir: string) {
|
||||
private async generateDetailsComponent(names: NamingConvention, featureDir: string, force: boolean) {
|
||||
const compDir = path.join(featureDir, 'components', `${names.singular}-details`);
|
||||
const tsContent = this.templateService.render('angular/details.component.ts.tpl', names);
|
||||
const htmlContent = this.templateService.render('angular/details.component.html.tpl', names);
|
||||
await fsPromises.mkdir(compDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(compDir, `${names.singular}-details.component.ts`), tsContent);
|
||||
fs.writeFileSync(path.join(compDir, `${names.singular}-details.component.html`), htmlContent);
|
||||
await this.fileSystemService.writeFile(path.join(compDir, `${names.singular}-details.component.ts`), tsContent, force);
|
||||
await this.fileSystemService.writeFile(path.join(compDir, `${names.singular}-details.component.html`), htmlContent, force);
|
||||
}
|
||||
private async generateFormComponent(names: NamingConvention, featureDir: string) {
|
||||
private async generateFormComponent(names: NamingConvention, featureDir: string, force: boolean) {
|
||||
const compDir = path.join(featureDir, 'components', `${names.singular}-form`);
|
||||
const tsContent = this.templateService.render('angular/form.component.ts.tpl', names);
|
||||
const htmlContent = this.templateService.render('angular/form.component.html.tpl', names);
|
||||
await fsPromises.mkdir(compDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(compDir, `${names.singular}-form.component.ts`), tsContent);
|
||||
fs.writeFileSync(path.join(compDir, `${names.singular}-form.component.html`), htmlContent);
|
||||
await this.fileSystemService.writeFile(path.join(compDir, `${names.singular}-form.component.ts`), tsContent, force);
|
||||
await this.fileSystemService.writeFile(path.join(compDir, `${names.singular}-form.component.html`), htmlContent, force);
|
||||
}
|
||||
private getNamingConvention(tableName: string): NamingConvention {
|
||||
const singular = toSingular(tableName);
|
||||
|
||||
@ -5,9 +5,9 @@ import { ModuleUpdaterService } from './module-updater.service';
|
||||
import { TemplateService } from './template.service';
|
||||
import { TableColumn } from 'typeorm';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import { promises as fsPromises } from 'fs';
|
||||
import { toCamelCase, toKebabCase, toPascalCase, toSingular } from '../utils/naming.utils';
|
||||
import { FileSystemService } from './file-system.service';
|
||||
|
||||
interface NamingConvention {
|
||||
[key: string]: string;
|
||||
@ -24,9 +24,10 @@ export class CrudGeneratorService {
|
||||
private readonly configService: ConfigService,
|
||||
private readonly moduleUpdaterService: ModuleUpdaterService,
|
||||
private readonly templateService: TemplateService,
|
||||
private readonly fileSystemService: FileSystemService,
|
||||
) {}
|
||||
|
||||
public async generate(tableName: string, columns?: TableColumn[]): Promise<void> {
|
||||
public async generate(tableName: string, columns: TableColumn[] , force: boolean): Promise<void> {
|
||||
console.log(`Generating CRUD module for table: ${tableName}...`);
|
||||
const names = this.getNamingConvention(tableName, columns);
|
||||
const config = this.configService.get();
|
||||
@ -41,10 +42,10 @@ export class CrudGeneratorService {
|
||||
names['entityPathForModule'] = path.relative(moduleDir, entityFullPath).replace(/\\/g, '/').replace('.ts', '');
|
||||
names['entityPathForDtos'] = path.relative(path.join(moduleDir, 'dto'), entityFullPath).replace(/\\/g, '/').replace('.ts', '');
|
||||
|
||||
await this.generateModuleFile(names, moduleDir);
|
||||
await this.generateControllerFile(names, moduleDir);
|
||||
await this.generateServiceFile(names, moduleDir);
|
||||
await this.generateDtoFiles(names, moduleDir);
|
||||
await this.generateModuleFile(names, moduleDir, force);
|
||||
await this.generateControllerFile(names, moduleDir, force);
|
||||
await this.generateServiceFile(names, moduleDir, force);
|
||||
await this.generateDtoFiles(names, moduleDir, force);
|
||||
|
||||
const moduleFileName = `${names.plural}.module.ts`;
|
||||
const fullModulePath = path.join(moduleDir, moduleFileName);
|
||||
@ -57,39 +58,39 @@ export class CrudGeneratorService {
|
||||
}
|
||||
}
|
||||
|
||||
private async generateModuleFile(names: NamingConvention, moduleDir: string) {
|
||||
private async generateModuleFile(names: NamingConvention, moduleDir: string, force: boolean) {
|
||||
// Use the pre-calculated path
|
||||
const content = this.templateService.render('nestjs/module.ts.tpl', { ...names, entityPath: names['entityPathForModule'] });
|
||||
const filePath = path.join(moduleDir, `${names.plural}.module.ts`);
|
||||
await fsPromises.mkdir(moduleDir, { recursive: true });
|
||||
fs.writeFileSync(filePath, content);
|
||||
await this.fileSystemService.writeFile(filePath, content, force);
|
||||
}
|
||||
|
||||
private async generateControllerFile(names: NamingConvention, moduleDir: string) {
|
||||
private async generateControllerFile(names: NamingConvention, moduleDir: string, force: boolean) {
|
||||
const content = this.templateService.render('nestjs/controller.ts.tpl', names);
|
||||
const filePath = path.join(moduleDir, `${names.plural}.controller.ts`);
|
||||
await fsPromises.mkdir(moduleDir, { recursive: true });
|
||||
fs.writeFileSync(filePath, content);
|
||||
await this.fileSystemService.writeFile(filePath, content, force);
|
||||
}
|
||||
|
||||
private async generateServiceFile(names: NamingConvention, moduleDir: string) {
|
||||
private async generateServiceFile(names: NamingConvention, moduleDir: string, force: boolean) {
|
||||
// Use the pre-calculated path
|
||||
const content = this.templateService.render('nestjs/service.ts.tpl', { ...names, entityPath: names['entityPathForModule'] });
|
||||
const filePath = path.join(moduleDir, `${names.plural}.service.ts`);
|
||||
await fsPromises.mkdir(moduleDir, { recursive: true });
|
||||
fs.writeFileSync(filePath, content);
|
||||
await this.fileSystemService.writeFile(filePath, content, force);
|
||||
}
|
||||
|
||||
private async generateDtoFiles(names: NamingConvention, moduleDir: string) {
|
||||
private async generateDtoFiles(names: NamingConvention, moduleDir: string, force: boolean) {
|
||||
const dtoDir = path.join(moduleDir, 'dto');
|
||||
await fsPromises.mkdir(dtoDir, { recursive: true });
|
||||
|
||||
// Use the pre-calculated path
|
||||
const dtoNames = { ...names, entityPath: names['entityPathForDtos'] };
|
||||
|
||||
fs.writeFileSync(path.join(dtoDir, `create-${names.singular}.dto.ts`), this.templateService.render('nestjs/create-dto.ts.tpl', dtoNames));
|
||||
fs.writeFileSync(path.join(dtoDir, `update-${names.singular}.dto.ts`), this.templateService.render('nestjs/update-dto.ts.tpl', dtoNames));
|
||||
fs.writeFileSync(path.join(dtoDir, `query-${names.singular}.dto.ts`), this.templateService.render('nestjs/query-dto.ts.tpl', dtoNames));
|
||||
await this.fileSystemService.writeFile(path.join(dtoDir, `create-${names.singular}.dto.ts`), this.templateService.render('nestjs/create-dto.ts.tpl', dtoNames), force);
|
||||
await this.fileSystemService.writeFile(path.join(dtoDir, `update-${names.singular}.dto.ts`), this.templateService.render('nestjs/update-dto.ts.tpl', dtoNames), force);
|
||||
await this.fileSystemService.writeFile(path.join(dtoDir, `query-${names.singular}.dto.ts`), this.templateService.render('nestjs/query-dto.ts.tpl', dtoNames), force);
|
||||
}
|
||||
|
||||
// --- NAMING HELPERS ---
|
||||
|
||||
@ -8,16 +8,18 @@ import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import { promises as fsPromises } from 'fs';
|
||||
import { mapDbToTsType, toPascalCase, toSingular } from '../utils/naming.utils';
|
||||
import { FileSystemService } from './file-system.service';
|
||||
|
||||
@Injectable()
|
||||
export class EntityGeneratorService {
|
||||
constructor(
|
||||
private readonly configService: ConfigService,
|
||||
private readonly moduleUpdaterService: ModuleUpdaterService,
|
||||
private readonly templateService: TemplateService, // <-- 2. INJECT TemplateService
|
||||
private readonly templateService: TemplateService,
|
||||
private readonly fileSystemService: FileSystemService,
|
||||
) {}
|
||||
|
||||
public async generate(tableName: string): Promise<TableColumn[] | null> {
|
||||
public async generate(tableName: string, force: boolean): Promise<TableColumn[] | null> {
|
||||
console.log(`Generating entity for table: ${tableName}...`);
|
||||
|
||||
const config = this.configService.get();
|
||||
@ -68,7 +70,7 @@ export class EntityGeneratorService {
|
||||
const outputPath = path.join(entitiesDir, entityFileName);
|
||||
|
||||
await fsPromises.mkdir(entitiesDir, { recursive: true });
|
||||
fs.writeFileSync(outputPath, entityContent);
|
||||
await this.fileSystemService.writeFile(outputPath, entityContent, force);
|
||||
console.log(`✅ Entity created successfully at: ${outputPath}`);
|
||||
|
||||
await this.moduleUpdaterService.addEntityToTypeOrm(className, outputPath);
|
||||
|
||||
41
src/services/file-system.service.ts
Normal file
41
src/services/file-system.service.ts
Normal file
@ -0,0 +1,41 @@
|
||||
// src/services/file-system.service.ts
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import * as fs from 'fs';
|
||||
import { promises as fsPromises } from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
@Injectable()
|
||||
export class FileSystemService {
|
||||
|
||||
/**
|
||||
* Writes content to a file, but only if the file does not already exist,
|
||||
* or if the 'force' flag is set to true.
|
||||
*
|
||||
* @param filePath The full path to the file.
|
||||
* @param content The content to write.
|
||||
* @param force If true, overwrites the file even if it exists.
|
||||
*/
|
||||
public async writeFile(filePath: string, content: string, force: boolean): Promise<void> {
|
||||
if (!force && fs.existsSync(filePath)) {
|
||||
console.warn(`⚠️ SKIPPED: File already exists at ${filePath}. Use --force to overwrite.`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Ensure the directory exists before writing the file
|
||||
const dir = path.dirname(filePath);
|
||||
await fsPromises.mkdir(dir, { recursive: true });
|
||||
|
||||
fs.writeFileSync(filePath, content);
|
||||
|
||||
if (force && fs.existsSync(filePath)) {
|
||||
console.log(`✅ OVERWRITTEN: ${filePath}`);
|
||||
} else {
|
||||
console.log(`✅ CREATED: ${filePath}`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error(`❌ FAILED to write file at ${filePath}:`, error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6,19 +6,22 @@ import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import { promises as fsPromises } from 'fs';
|
||||
import { mapDbToTsType } from '../utils/naming.utils';
|
||||
import { FileSystemService } from './file-system.service';
|
||||
|
||||
interface FieldDefinition { name: string; tsType: string; }
|
||||
interface NamingConvention { [key: string]: string; /*...*/ }
|
||||
|
||||
@Injectable()
|
||||
export class GenericTableGeneratorService {
|
||||
constructor(private readonly templateService: TemplateService) {}
|
||||
constructor(private readonly templateService: TemplateService,
|
||||
private readonly fileSystemService: FileSystemService,) {}
|
||||
|
||||
public async generate(
|
||||
tableName: string,
|
||||
columns: TableColumn[],
|
||||
names: NamingConvention,
|
||||
featureDir: string,
|
||||
force: boolean
|
||||
): Promise<string> {
|
||||
console.log(`Generating Generic Table View for ${tableName}...`);
|
||||
|
||||
@ -34,21 +37,24 @@ export class GenericTableGeneratorService {
|
||||
await fsPromises.mkdir(compDir, { recursive: true });
|
||||
|
||||
// Generate Data Provider
|
||||
fs.writeFileSync(
|
||||
await this.fileSystemService.writeFile(
|
||||
path.join(compDir, `${names.singular}-data-provider.service.ts`),
|
||||
this.templateService.render('angular-generic/data-provider.service.ts.tpl', names),
|
||||
force
|
||||
);
|
||||
|
||||
// Generate Table Component TS
|
||||
fs.writeFileSync(
|
||||
await this.fileSystemService.writeFile(
|
||||
path.join(compDir, `${names.singular}-table.component.ts`),
|
||||
this.templateService.render('angular-generic/table.component.ts.tpl', names),
|
||||
force
|
||||
);
|
||||
|
||||
// Generate Table Component HTML
|
||||
fs.writeFileSync(
|
||||
await this.fileSystemService.writeFile(
|
||||
path.join(compDir, `${names.singular}-table.component.html`),
|
||||
this.templateService.render('angular-generic/table.component.html.tpl', names),
|
||||
force
|
||||
);
|
||||
|
||||
return path.join(compDir, `${names.singular}-table.component.ts`);
|
||||
|
||||
@ -115,6 +115,7 @@ export class ModuleUpdaterService {
|
||||
componentNameToAdd: string,
|
||||
componentPath: string,
|
||||
routeName: string,
|
||||
auth?: { role: string } // NEW: Optional auth parameter
|
||||
): Promise<void> {
|
||||
const config = this.configService.get();
|
||||
const adminRoot = path.resolve(process.cwd(), config.admin.path);
|
||||
@ -126,37 +127,48 @@ export class ModuleUpdaterService {
|
||||
const project = new Project();
|
||||
const sourceFile = project.addSourceFileAtPath(appRoutesPath);
|
||||
|
||||
const relativeComponentPath = path
|
||||
.relative(path.dirname(appRoutesPath), componentPath)
|
||||
.replace(/\\/g, '/')
|
||||
.replace('.ts', '');
|
||||
|
||||
const existingImport = sourceFile.getImportDeclaration(
|
||||
(d) => d.getModuleSpecifierValue() === `./${relativeComponentPath}`,
|
||||
);
|
||||
|
||||
if (!existingImport) {
|
||||
sourceFile.addImportDeclaration({
|
||||
namedImports: [componentNameToAdd],
|
||||
moduleSpecifier: `./${relativeComponentPath}`,
|
||||
});
|
||||
// 1. Add Component Import (unchanged)
|
||||
const relativeComponentPath = path.relative(path.dirname(appRoutesPath), componentPath).replace(/\\/g, '/').replace('.ts', '');
|
||||
const existingCompImport = sourceFile.getImportDeclaration(d => d.getModuleSpecifierValue() === `./${relativeComponentPath}`);
|
||||
if (!existingCompImport) {
|
||||
sourceFile.addImportDeclaration({ namedImports: [componentNameToAdd], moduleSpecifier: `./${relativeComponentPath}` });
|
||||
console.log(`Added import for component ${componentNameToAdd}.`);
|
||||
} else {
|
||||
console.log(`Import for component ${componentNameToAdd} already exists.`);
|
||||
}
|
||||
|
||||
// 2. Add AuthGuard Import if needed
|
||||
if (auth) {
|
||||
// NOTE: This assumes a specific path for your AuthGuard. Adjust if necessary.
|
||||
const authGuardPath = './auth/auth.guard';
|
||||
const existingAuthImport = sourceFile.getImportDeclaration(d => d.getModuleSpecifierValue() === authGuardPath);
|
||||
if (!existingAuthImport) {
|
||||
sourceFile.addImportDeclaration({ namedImports: ['AuthGuard'], moduleSpecifier: authGuardPath });
|
||||
console.log('Added import for AuthGuard.');
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Find routes array (unchanged)
|
||||
const routesDeclaration = sourceFile.getVariableDeclarationOrThrow('routes');
|
||||
const routesArray = routesDeclaration.getInitializerIfKindOrThrow(
|
||||
SyntaxKind.ArrayLiteralExpression,
|
||||
);
|
||||
const routesArray = routesDeclaration.getInitializerIfKindOrThrow(SyntaxKind.ArrayLiteralExpression);
|
||||
|
||||
const routeAlreadyExists = routesArray
|
||||
.getElements()
|
||||
.some((elem) => elem.getText().includes(`path: '${routeName}'`));
|
||||
// 4. Build the Route Object String
|
||||
let routeObjectString = `{ path: '${routeName}', component: ${componentNameToAdd} }`;
|
||||
if (auth) {
|
||||
// If auth is requested, build a multi-line string for the route object
|
||||
routeObjectString = `{
|
||||
path: '${routeName}',
|
||||
component: ${componentNameToAdd},
|
||||
canActivate: [AuthGuard],
|
||||
data: {
|
||||
roles: ['${auth.role}'],
|
||||
},
|
||||
}`;
|
||||
}
|
||||
|
||||
// 5. Check existence and insert the route
|
||||
const routeAlreadyExists = routesArray.getElements().some((elem) => elem.getText().includes(`path: '${routeName}'`));
|
||||
if (!routeAlreadyExists) {
|
||||
routesArray.insertElement(0, `{ path: '${routeName}', component: ${componentNameToAdd} }`);
|
||||
console.log(`Added route for path '${routeName}' to the beginning of the routes array.`);
|
||||
routesArray.insertElement(0, routeObjectString);
|
||||
console.log(`Added route for path '${routeName}' with auth config.`);
|
||||
} else {
|
||||
console.log(`Route for path '${routeName}' already exists.`);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user