refactor utility methods to naming.utils.ts

This commit is contained in:
Roland Schneider 2025-11-20 08:06:05 +01:00
parent ed2388d7f6
commit 61338cc377
5 changed files with 76 additions and 104 deletions

View File

@ -8,6 +8,7 @@ import * as fs from 'fs';
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';
// Interface for structured field metadata passed to templates
interface FieldDefinition {
@ -42,7 +43,7 @@ export class AngularGeneratorService {
const fields: FieldDefinition[] = (columns || [])
.map(c => ({
name: c.name,
tsType: this.mapDbToTsType(c.type),
tsType: mapDbToTsType(c.type),
}));
names['modelProperties'] = fields.map(f => `${f.name}: ${f.tsType};`).join('\n ');
@ -126,15 +127,6 @@ private async generateTableComponent(tableName: string, columns: TableColumn[],
return `<div class="form-control">${label}\n ${input}</div>`;
}
private mapDbToTsType(dbType: string): string {
if (dbType.includes('int') || dbType.includes('serial')) return 'number';
if (['float', 'double', 'decimal', 'numeric', 'real'].includes(dbType)) return 'number';
if (dbType.includes('char') || dbType.includes('text') || dbType === 'uuid') return 'string';
if (dbType === 'boolean' || dbType === 'bool') return 'boolean';
if (dbType.includes('date') || dbType.includes('time')) return 'Date';
return 'any';
}
// ... (All other generate... and naming methods are unchanged)
private async generateModel(names: NamingConvention, featureDir: string) {
const modelsDir = path.join(featureDir, 'models');
@ -183,36 +175,16 @@ private async generateTableComponent(tableName: string, columns: TableColumn[],
fs.writeFileSync(path.join(compDir, `${names.singular}-form.component.html`), htmlContent);
}
private getNamingConvention(tableName: string): NamingConvention {
const singular = this.toSingular(tableName);
const pascal = this.toPascalCase(singular);
const singular = toSingular(tableName);
const pascal = toPascalCase(singular);
return {
singular: this.toKebabCase(singular),
plural: this.toKebabCase(tableName),
singular: toKebabCase(singular),
plural: toKebabCase(tableName),
pascal: pascal,
camel: this.toCamelCase(singular),
camel: toCamelCase(singular),
title: pascal,
kebab: this.toKebabCase(singular),
kebab: toKebabCase(singular),
};
}
private toSingular(name: string): string {
const kebab = this.toKebabCase(name);
if (kebab.endsWith('ies')) return kebab.slice(0, -3) + 'y';
return kebab.endsWith('s') ? kebab.slice(0, -1) : kebab;
}
private toPascalCase(text: string): string {
return this.toKebabCase(text)
.split('-')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join('');
}
private toCamelCase(text: string): string {
const pascal = this.toPascalCase(text);
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
}
private toKebabCase(text: string): string {
return text
.replace(/_/g, '-') // Replace underscores with hyphens
.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2') // Add hyphen before uppercase letters
.toLowerCase();
}
}

View File

@ -7,6 +7,7 @@ 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';
interface NamingConvention {
[key: string]: string;
@ -98,34 +99,21 @@ export class CrudGeneratorService {
}
private getNamingConvention(tableName: string, columns?: TableColumn[]): NamingConvention {
const singular = this.toSingular(tableName);
const pascal = this.toPascalCase(singular);
const singular = toSingular(tableName);
const pascal = toPascalCase(singular);
const searchableFields = (columns || [])
.filter(c => this.isStringBasedType(c.type))
.map(c => `'${c.name}'`)
.join(',\n ');
return {
singular: this.toKebabCase(singular),
plural: this.toKebabCase(tableName),
singular: toKebabCase(singular),
plural: toKebabCase(tableName),
pascal: pascal,
camel: this.toCamelCase(singular),
camel: toCamelCase(singular),
kebab: toKebabCase(singular),
searchableFields: searchableFields
};
}
private toSingular(name: string): string {
const kebab = this.toKebabCase(name);
if (kebab.endsWith('ies')) return kebab.slice(0, -3) + 'y';
return kebab.endsWith('s') ? kebab.slice(0, -1) : kebab;
}
private toPascalCase(text: string): string {
return this.toKebabCase(text).split('-').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join('');
}
private toCamelCase(text: string): string {
const pascal = this.toPascalCase(text);
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
}
private toKebabCase(text: string): string {
return text.replace(/_/g, '-').replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2').toLowerCase();
}
}

View File

@ -7,6 +7,7 @@ import { TemplateService } from './template.service'; // <-- 1. IMPORT TemplateS
import * as path from 'path';
import * as fs from 'fs';
import { promises as fsPromises } from 'fs';
import { mapDbToTsType, toPascalCase, toSingular } from '../utils/naming.utils';
@Injectable()
export class EntityGeneratorService {
@ -38,14 +39,14 @@ export class EntityGeneratorService {
const columns = table.columns;
console.log(`Found ${columns.length} columns in table "${tableName}".`);
const singularName = this.toSingular(tableName);
const className = this.toPascalCase(singularName);
const singularName = toSingular(tableName);
const className = toPascalCase(singularName);
const props = columns
.map((col) => {
const typeormDecorator = this.getDecorator(col);
const validationDecorators = this.getValidationDecorators(col);
const tsType = this.mapDbToTsType(col.type);
const tsType = mapDbToTsType(col.type);
const nullable = col.isNullable ? ' | null' : '';
const defaultValue = col.default ? ` = ${this.formatDefaultValue(col)}` : '';
const allDecorators = [typeormDecorator, validationDecorators].filter(Boolean).join('\n ');
@ -96,7 +97,7 @@ export class EntityGeneratorService {
decorators.push('@IsOptional()');
}
const tsType = this.mapDbToTsType(column.type);
const tsType = mapDbToTsType(column.type);
switch (tsType) {
case 'string':
decorators.push('@IsString()');
@ -160,38 +161,4 @@ export class EntityGeneratorService {
return column.default;
}
private mapDbToTsType(dbType: string): string {
if (dbType.includes('int') || dbType.includes('serial')) return 'number';
if (['float', 'double', 'decimal', 'numeric', 'real'].includes(dbType)) return 'number';
if (dbType.includes('char') || dbType.includes('text') || dbType === 'uuid') return 'string';
if (dbType === 'boolean' || dbType === 'bool') return 'boolean';
if (dbType.includes('date') || dbType.includes('time')) return 'Date';
return 'any';
}
private toSingular(name: string): string {
if (name.endsWith('ies')) {
return name.slice(0, -3) + 'y';
}
return name.endsWith('s') ? name.slice(0, -1) : name;
}
private toPascalCase(text: string): string {
return this.toKebabCase(text)
.split('-')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join('');
}
private toCamelCase(text: string): string {
const pascal = this.toPascalCase(text);
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
}
private toKebabCase(text: string): string {
return text
.replace(/_/g, '-') // Replace underscores with hyphens
.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2') // Add hyphen before uppercase letters
.toLowerCase();
}
}

View File

@ -5,6 +5,7 @@ import { TableColumn } from 'typeorm';
import * as path from 'path';
import * as fs from 'fs';
import { promises as fsPromises } from 'fs';
import { mapDbToTsType } from '../utils/naming.utils';
interface FieldDefinition { name: string; tsType: string; }
interface NamingConvention { [key: string]: string; /*...*/ }
@ -23,7 +24,7 @@ export class GenericTableGeneratorService {
const fields: FieldDefinition[] = columns.map(c => ({
name: c.name,
tsType: this.mapDbToTsType(c.type),
tsType: mapDbToTsType(c.type),
}));
names['columnDefinitions'] = this.buildColumnDefinitions(fields);
@ -75,12 +76,5 @@ export class GenericTableGeneratorService {
.join('\n');
}
private mapDbToTsType(dbType: string): string {
if (dbType.includes('int') || dbType.includes('serial')) return 'number';
if (['float', 'double', 'decimal', 'numeric', 'real'].includes(dbType)) return 'number';
if (dbType.includes('char') || dbType.includes('text') || dbType === 'uuid') return 'string';
if (dbType === 'boolean' || dbType === 'bool') return 'boolean';
if (dbType.includes('date') || dbType.includes('time')) return 'Date';
return 'any';
}
}

51
src/utils/naming.utils.ts Normal file
View File

@ -0,0 +1,51 @@
// dvbooking-cli/src/utils/naming.utils.ts
/**
* Converts any string to kebab-case.
* e.g., 'user_roles' -> 'user-roles', 'UserRoles' -> 'user-roles'
*/
export function toKebabCase(text: string): string {
return text
.replace(/_/g, '-') // Replace underscores with hyphens
.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2') // Add hyphen before uppercase letters
.toLowerCase();
}
/**
* Converts a kebab-cased string to its singular form.
*/
export function toSingular(name: string): string {
const kebab = toKebabCase(name);
if (kebab.endsWith('ies')) return kebab.slice(0, -3) + 'y';
return kebab.endsWith('s') ? kebab.slice(0, -1) : kebab;
}
/**
* Converts a kebab-cased string to PascalCase.
*/
export function toPascalCase(text: string): string {
return toKebabCase(text)
.split('-')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join('');
}
/**
* Converts a kebab-cased string to camelCase.
*/
export function toCamelCase(text: string): string {
const pascal = toPascalCase(text);
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
}
/**
* Maps database types to TypeScript types.
*/
export function mapDbToTsType(dbType: string): string {
if (dbType.includes('int') || dbType.includes('serial')) return 'number';
if (['float', 'double', 'decimal', 'numeric', 'real'].includes(dbType)) return 'number';
if (dbType.includes('char') || dbType.includes('text') || dbType === 'uuid') return 'string';
if (dbType === 'boolean' || dbType === 'bool') return 'boolean';
if (dbType.includes('date') || dbType.includes('time')) return 'Date';
return 'any';
}