add force flag to overwrite existing files

This commit is contained in:
Roland Schneider 2025-11-20 12:13:54 +01:00
parent a75f286e6b
commit 90cb73379e
8 changed files with 176 additions and 93 deletions

View File

@ -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 {}

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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 ---

View File

@ -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);

View 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);
}
}
}

View File

@ -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`);

View File

@ -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.`);
}