From 7dc9126ab36b847e9cf695ce0c4b329dce791424 Mon Sep 17 00:00:00 2001 From: Milton Howe Date: Tue, 12 Sep 2017 15:19:55 -0400 Subject: [PATCH] add option to generate entities cased for Angular style guide (Pascal Entities, camel propertis, kebab files) --- README.md | 1 + package.json | 1 + src/Engine.ts | 24 ++++++++++++-- src/entity.mst | 48 +++++++++++++++------------- src/index.ts | 7 +++- src/models/EntityInfo.ts | 24 ++------------ test/integration/integration.test.ts | 12 ++++--- 7 files changed, 64 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index 2e85bf821e..d8862a56bc 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ Options: -e, --engine Database engine. [choices: "mssql", "postgres", "mysql", "mariadb"] [default: "mssql"] -o, --output Where to place generated models. + -c, --case Convert snake_case tables names to PascalCase entities and snake_case columns to camelCase properties ``` ### Examples diff --git a/package.json b/package.json index 1c01f17a1a..5755d9a353 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "dependencies": { "@types/mysql": "0.0.34", "@types/pg": "^6.1.41", + "change-case": "^3.0.1", "handlebars": "^4.0.10", "mssql": "^3.3.0", "mysql": "^2.14.1", diff --git a/src/Engine.ts b/src/Engine.ts index a6135118cf..f473945ad7 100644 --- a/src/Engine.ts +++ b/src/Engine.ts @@ -3,6 +3,7 @@ import { DatabaseModel } from './models/DatabaseModel' import * as Handlebars from 'handlebars' import fs = require('fs'); import path = require('path') +import changeCase = require("change-case"); /** * Engine */ @@ -24,6 +25,10 @@ export class Engine { } private createModelFromMetadata(databaseModel: DatabaseModel) { + Handlebars.registerHelper("curly", (open) => {return open ? "{" : "}";}); + Handlebars.registerHelper("toEntityName", str => {return this.Options.convertCase ? changeCase.pascalCase(str) : str;}); + Handlebars.registerHelper("toFileName", str => {return this.Options.convertCase ? changeCase.paramCase(str) : str;}); + Handlebars.registerHelper("toPropertyName", str => {return this.Options.convertCase ? changeCase.camelCase(str) : str;}); let templatePath = path.resolve(__dirname, '../../src/entity.mst') let template = fs.readFileSync(templatePath, 'UTF-8'); let resultPath = this.Options.resultsPath @@ -39,7 +44,19 @@ export class Engine { fs.mkdirSync(entitesPath); let compliedTemplate = Handlebars.compile(template,{noEscape:true}) databaseModel.entities.forEach(element => { - let resultFilePath = path.resolve(entitesPath, element.EntityName + '.ts'); + element.Imports = []; + element.Columns.forEach((column) => { + column.relations.forEach( + (relation) => { + if (element.EntityName !== relation.relatedTable) + {element.Imports.push(relation.relatedTable);} + } + ); + }); + element.Imports.filter(function (elem, index, self) { + return index === self.indexOf(elem); + }); + let resultFilePath = path.resolve(entitesPath, (this.Options.convertCase ? changeCase.paramCase(element.EntityName) : element.EntityName) + '.ts'); let rendered =compliedTemplate(element) fs.writeFileSync(resultFilePath, rendered, { encoding: 'UTF-8', flag: 'w' }) }); @@ -81,5 +98,6 @@ export interface EngineOptions { user: string, password: string, resultsPath: string, - databaseType: string -} \ No newline at end of file + databaseType: string, + convertCase: boolean +} diff --git a/src/entity.mst b/src/entity.mst index eef4c5505d..43ce01ddb4 100644 --- a/src/entity.mst +++ b/src/entity.mst @@ -1,28 +1,30 @@ import {Index,Entity, PrimaryColumn, Column, OneToOne, OneToMany, ManyToOne, JoinColumn} from "typeorm"; -{{relationImports}} +{{#each Imports}} +import {{curly true}}{{toEntityName this}}{{curly false}} from "./{{toFileName this}}"; +{{/each}} -@Entity() +@Entity("{{toEntityName EntityName}}") {{#Indexes}}{{^isPrimaryKey}}@Index("{{name}}",[{{#columns}}"{{name}}",{{/columns}}]{{#isUnique}},{unique:true}{{/isUnique}}) -{{/isPrimaryKey}}{{/Indexes}}export class {{EntityName}} { +{{/isPrimaryKey}}{{/Indexes}}export class {{toEntityName EntityName}} { +{{#Columns}} - {{#Columns}} +{{~^relations}} @Column("{{sql_type}}", {{curly true}}{{#is_generated}}generated: true,{{/is_generated}} + name: "{{name}}",{{#is_nullable}} + nullable: true,{{/is_nullable}}{{^is_nullable}} + nullable: false,{{/is_nullable}}{{#char_max_lenght}} + length: {{.}},{{/char_max_lenght}}{{#default}} + default: "{{.}}",{{/default}}{{#numericPrecision}} + precision:{{.}},{{/numericPrecision}}{{#numericScale}} + scale: {{.}},{{/numericScale}}{{#isPrimary}} + primary: {{isPrimary}},{{/isPrimary}} + }) + {{toPropertyName name}}: {{ts_type}}; +{{/relations~}} - {{^relations}} @Column("{{sql_type}}",{ {{#is_generated}} - generated:true,{{/is_generated}}{{#is_nullable}} - nullable:true,{{/is_nullable}}{{^is_nullable}} - nullable:false,{{/is_nullable}}{{#char_max_lenght}} - length:{{.}},{{/char_max_lenght}}{{#default}} - default:"{{.}}",{{/default}}{{#numericPrecision}} - precision:{{.}},{{/numericPrecision}}{{#numericScale}} - scale:{{.}},{{/numericScale}}{{#isPrimary}} - primary:{{isPrimary}},{{/isPrimary}} - }) - {{name}}:{{ts_type}}; - {{/relations}}{{#relations}} - @{{relationType}}(type=>{{relatedTable}}, {{../name}}=>{{../name}}.{{#if isOwner}}{{ownerColumn}}{{else}}{{relatedColumn}}{{/if}}){{#isOwner}} - @JoinColumn(){{/isOwner}} - {{#if isOneToMany}}{{../name}}:{{relatedTable}}[]; - {{else}}{{../name}}:{{relatedTable}}; - {{/if}}{{/relations}} - {{/Columns}} -} \ No newline at end of file +{{~#relations}} @{{relationType}}(type => {{toEntityName relatedTable}}, {{toPropertyName ../name}} => {{toPropertyName ../name}}.{{#if isOwner}}{{toPropertyName ownerColumn}}{{else}}{{toPropertyName relatedColumn}}{{/if}}){{#isOwner}} + @JoinColumn(){{/isOwner}} + {{#if isOneToMany}}{{toPropertyName ../name}}: {{toEntityName relatedTable}}[];{{else}}{{toPropertyName ../name}}: {{toEntityName relatedTable}};{{/if}} +{{/relations~}} +{{"\n"}} +{{/Columns}} +} diff --git a/src/index.ts b/src/index.ts index 1e18b40070..cc602e445c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -46,6 +46,10 @@ var argv = Yargs describe: 'Where to place generated models.', default: path.resolve(process.cwd(), 'output') }) + .option('c', { + alias: 'case', + describe: 'Convert snake_case tables names to PascalCase entities and snake_case columns to camelCase properties' + }) .argv; @@ -82,7 +86,8 @@ let engine = new Engine( user: argv.u, password: argv.x, databaseType: argv.e, - resultsPath: argv.o + resultsPath: argv.o, + convertCase: !!argv.c }); console.log(`[${new Date().toLocaleTimeString()}] Starting creation of model classes.`); diff --git a/src/models/EntityInfo.ts b/src/models/EntityInfo.ts index 68c4a3a6d7..31d34d802a 100644 --- a/src/models/EntityInfo.ts +++ b/src/models/EntityInfo.ts @@ -5,26 +5,6 @@ import { ColumnInfo } from './ColumnInfo' export class EntityInfo { EntityName: string; Columns: ColumnInfo[]; + Imports: string[]; Indexes: IndexInfo[]; - - relationImports(): any { - var returnString = ""; - var imports: string[] = []; - this.Columns.forEach((column) => { - column.relations.forEach( - (relation) => { - if (this.EntityName!=relation.relatedTable) - imports.push(relation.relatedTable); - } - ) - }); - imports.filter(function (elem, index, self) { - return index == self.indexOf(elem); - }).forEach((imp)=>{ - returnString+=`import {${imp}} from './${imp}'\n` - }) - - return returnString; - } - -} \ No newline at end of file +} diff --git a/test/integration/integration.test.ts b/test/integration/integration.test.ts index ce5cb09233..9db9050c0d 100644 --- a/test/integration/integration.test.ts +++ b/test/integration/integration.test.ts @@ -141,7 +141,8 @@ async function createMSSQLModels(filesOrgPath: string, resultsPath: string): Pro user: process.env.MSSQL_Username, password: process.env.MSSQL_Password, databaseType: 'mssql', - resultsPath: resultsPath + resultsPath: resultsPath, + convertCase: true }); @@ -184,7 +185,8 @@ async function createPostgresModels(filesOrgPath: string, resultsPath: string): user: process.env.POSTGRES_Username, password: process.env.POSTGRES_Password, databaseType: 'postgres', - resultsPath: resultsPath + resultsPath: resultsPath, + convertCase: true }); @@ -228,7 +230,8 @@ async function createMysqlModels(filesOrgPath: string, resultsPath: string): Pro user: process.env.MYSQL_Username, password: process.env.MYSQL_Password, databaseType: 'mysql', - resultsPath: resultsPath + resultsPath: resultsPath, + convertCase: true }); @@ -271,7 +274,8 @@ async function createMariaDBModels(filesOrgPath: string, resultsPath: string): P user: process.env.MARIADB_Username, password: process.env.MARIADB_Password, databaseType: 'mariadb', - resultsPath: resultsPath + resultsPath: resultsPath, + convertCase: true });