code cleanup, dependencies upgrade

This commit is contained in:
Kononnable 2019-10-13 11:06:11 +02:00
parent e478562c6e
commit bd9750bbb4
14 changed files with 535 additions and 543 deletions

72
package-lock.json generated
View File

@ -407,60 +407,42 @@
}
},
"@types/yargs": {
"version": "12.0.1",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-12.0.1.tgz",
"integrity": "sha512-UVjo2oH79aRNcsDlFlnQ/iJ67Jd7j6uSg7jUJP/RZ/nUjAh5ElmnwlD5K/6eGgETJUgCHkiWn91B8JjXQ6ubAw==",
"dev": true
},
"@types/yn": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@types/yn/-/yn-3.1.0.tgz",
"integrity": "sha512-Qs2tU/syFYlALjR3EoT+NcvpMwAd6voSiDxW+c8bhAN1WbzQUnRfWTmttORf4R1WqDUT+dvHKj+llupSxs0O/w==",
"version": "13.0.3",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz",
"integrity": "sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==",
"dev": true,
"requires": {
"yn": "*"
"@types/yargs-parser": "*"
}
},
"@types/yargs-parser": {
"version": "13.1.0",
"resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-13.1.0.tgz",
"integrity": "sha512-gCubfBUZ6KxzoibJ+SCUc/57Ms1jz5NjHe4+dI2krNmU5zCPAphyLJYyTOg06ueIyfj+SaCUqmzun7ImlxDcKg==",
"dev": true
},
"@typescript-eslint/eslint-plugin": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.13.0.tgz",
"integrity": "sha512-WQHCozMnuNADiqMtsNzp96FNox5sOVpU8Xt4meaT4em8lOG1SrOv92/mUbEHQVh90sldKSfcOc/I0FOb/14G1g==",
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.3.3.tgz",
"integrity": "sha512-12cCbwu5PbQudkq2xCIS/QhB7hCMrsNPXK+vJtqy/zFqtzVkPRGy12O5Yy0gUK086f3VHV/P4a4R4CjMW853pA==",
"dev": true,
"requires": {
"@typescript-eslint/experimental-utils": "1.13.0",
"eslint-utils": "^1.3.1",
"@typescript-eslint/experimental-utils": "2.3.3",
"eslint-utils": "^1.4.2",
"functional-red-black-tree": "^1.0.1",
"regexpp": "^2.0.1",
"tsutils": "^3.7.0"
"tsutils": "^3.17.1"
}
},
"@typescript-eslint/experimental-utils": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-1.13.0.tgz",
"integrity": "sha512-zmpS6SyqG4ZF64ffaJ6uah6tWWWgZ8m+c54XXgwFtUv0jNz8aJAVx8chMCvnk7yl6xwn8d+d96+tWp7fXzTuDg==",
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.3.3.tgz",
"integrity": "sha512-MQ4jKPMTU1ty4TigJCRKFPye2qyQdH8jzIIkceaHgecKFmkNS1hXPqKiZ+mOehkz6+HcN5Nuvwm+frmWZR9tdg==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.3",
"@typescript-eslint/typescript-estree": "1.13.0",
"eslint-scope": "^4.0.0"
},
"dependencies": {
"@typescript-eslint/typescript-estree": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-1.13.0.tgz",
"integrity": "sha512-b5rCmd2e6DCC6tCTN9GSUAuxdYwCM/k/2wdjHGrIRGPSJotWMCe/dGpi66u42bhuh8q3QBzqM4TMA1GUUCJvdw==",
"dev": true,
"requires": {
"lodash.unescape": "4.0.1",
"semver": "5.5.0"
}
},
"semver": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
"integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
"dev": true
}
"@typescript-eslint/typescript-estree": "2.3.3",
"eslint-scope": "^5.0.0"
}
},
"@typescript-eslint/parser": {
@ -1973,9 +1955,9 @@
}
},
"eslint-scope": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz",
"integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==",
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz",
"integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==",
"dev": true,
"requires": {
"esrecurse": "^4.1.0",
@ -5850,9 +5832,9 @@
}
},
"yn": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz",
"integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo="
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q=="
}
}
}

View File

@ -38,7 +38,7 @@
"typeorm": "^0.2.19",
"typescript": "^3.6.4",
"yargs": "^14.2.0",
"yn": "^2.0.0"
"yn": "^3.1.1"
},
"devDependencies": {
"@types/chai": "^4.2.3",
@ -57,9 +57,8 @@
"@types/prettier": "^1.18.3",
"@types/sinon": "^7.5.0",
"@types/sqlite3": "^3.1.5",
"@types/yargs": "^12.0.1",
"@types/yn": "^3.1.0",
"@typescript-eslint/eslint-plugin": "^1.13.0",
"@types/yargs": "^13.0.3",
"@typescript-eslint/eslint-plugin": "^2.3.3",
"@typescript-eslint/parser": "^2.3.3",
"@typescript-eslint/typescript-estree": "^2.3.3",
"chai": "^4.2.0",

View File

@ -1,9 +1,3 @@
import * as Handlebars from "handlebars";
import * as Prettier from "prettier";
import { DataTypeDefaults } from "typeorm/driver/types/DataTypeDefaults";
import * as changeCase from "change-case";
import * as fs from "fs";
import * as path from "path";
import * as TomgUtils from "./Utils";
import AbstractDriver from "./drivers/AbstractDriver";
import MssqlDriver from "./drivers/MssqlDriver";
@ -14,10 +8,8 @@ import PostgresDriver from "./drivers/PostgresDriver";
import MysqlDriver from "./drivers/MysqlDriver";
import OracleDriver from "./drivers/OracleDriver";
import SqliteDriver from "./drivers/SqliteDriver";
import NamingStrategy from "./NamingStrategy";
import AbstractNamingStrategy from "./AbstractNamingStrategy";
import { Entity } from "./models/Entity";
import { Relation } from "./models/Relation";
import modelCustomizationPhase from "./ModelCustomization";
import modelGenerationPhase from "./ModelGeneration";
export function createDriver(driverName: string): AbstractDriver {
switch (driverName) {
@ -70,382 +62,3 @@ export async function dataCollectionPhase(
) {
return driver.GetDataFromServer(connectionOptions, generationOptions);
}
export function modelCustomizationPhase(
dbModel: Entity[],
generationOptions: IGenerationOptions,
defaultValues: DataTypeDefaults
) {
let namingStrategy: AbstractNamingStrategy;
if (
generationOptions.customNamingStrategyPath &&
generationOptions.customNamingStrategyPath !== ""
) {
// eslint-disable-next-line global-require, import/no-dynamic-require, @typescript-eslint/no-var-requires
const req = require(generationOptions.customNamingStrategyPath);
namingStrategy = new req.NamingStrategy();
} else {
namingStrategy = new NamingStrategy();
}
let retVal = applyNamingStrategy(namingStrategy, dbModel);
retVal = addImportsAndGenerationOptions(retVal, generationOptions);
retVal = removeColumnDefaultProperties(retVal, defaultValues);
return retVal;
}
function removeColumnDefaultProperties(
dbModel: Entity[],
defaultValues: DataTypeDefaults
) {
if (!defaultValues) {
return dbModel;
}
dbModel.forEach(entity => {
entity.columns.forEach(column => {
const defVal = defaultValues[column.tscType];
if (defVal) {
if (
column.options.length &&
defVal.length &&
column.options.length === defVal.length
) {
column.options.length = undefined;
}
if (
column.options.precision &&
defVal.precision &&
column.options.precision === defVal.precision &&
column.options.scale &&
defVal.scale &&
column.options.scale === defVal.scale
) {
column.options.precision = undefined;
column.options.scale = undefined;
}
if (
column.options.width &&
defVal.width &&
column.options.width === defVal.width
) {
column.options.width = undefined;
}
}
});
});
return dbModel;
}
function addImportsAndGenerationOptions(
dbModel: Entity[],
generationOptions: IGenerationOptions
) {
dbModel.forEach(entity => {
entity.relations.forEach(relation => {
if (generationOptions.lazy) {
if (!relation.relationOptions) {
relation.relationOptions = {};
}
relation.relationOptions.lazy = true;
}
});
if (generationOptions.skipSchema) {
entity.schema = undefined;
entity.database = undefined;
}
});
return dbModel;
}
export function modelGenerationPhase(
connectionOptions: IConnectionOptions,
generationOptions: IGenerationOptions,
databaseModel: Entity[]
) {
createHandlebarsHelpers(generationOptions);
const templatePath = path.resolve(__dirname, "templates", "entity.mst");
const template = fs.readFileSync(templatePath, "UTF-8");
const resultPath = generationOptions.resultsPath;
if (!fs.existsSync(resultPath)) {
fs.mkdirSync(resultPath);
}
let entitiesPath = resultPath;
if (!generationOptions.noConfigs) {
createTsConfigFile(resultPath);
createTypeOrmConfig(resultPath, connectionOptions);
entitiesPath = path.resolve(resultPath, "./entities");
if (!fs.existsSync(entitiesPath)) {
fs.mkdirSync(entitiesPath);
}
}
const compliedTemplate = Handlebars.compile(template, {
noEscape: true
});
databaseModel.forEach(element => {
let casedFileName = "";
switch (generationOptions.convertCaseFile) {
case "camel":
casedFileName = changeCase.camelCase(element.tscName);
break;
case "param":
casedFileName = changeCase.paramCase(element.tscName);
break;
case "pascal":
casedFileName = changeCase.pascalCase(element.tscName);
break;
case "none":
casedFileName = element.tscName;
break;
default:
throw new Error("Unknown case style");
}
const resultFilePath = path.resolve(
entitiesPath,
`${casedFileName}.ts`
);
const rendered = compliedTemplate(element);
const formatted = Prettier.format(rendered, { parser: "typescript" });
fs.writeFileSync(resultFilePath, formatted, {
encoding: "UTF-8",
flag: "w"
});
});
}
function createHandlebarsHelpers(generationOptions: IGenerationOptions) {
Handlebars.registerHelper("json", context => {
const json = JSON.stringify(context);
const withoutQuotes = json.replace(/"([^(")"]+)":/g, "$1:");
return withoutQuotes.slice(1, withoutQuotes.length - 1);
});
Handlebars.registerHelper("toEntityName", str => {
let retStr = "";
switch (generationOptions.convertCaseEntity) {
case "camel":
retStr = changeCase.camelCase(str);
break;
case "pascal":
retStr = changeCase.pascalCase(str);
break;
case "none":
retStr = str;
break;
default:
throw new Error("Unknown case style");
}
return retStr;
});
Handlebars.registerHelper("toFileName", str => {
let retStr = "";
switch (generationOptions.convertCaseFile) {
case "camel":
retStr = changeCase.camelCase(str);
break;
case "param":
retStr = changeCase.paramCase(str);
break;
case "pascal":
retStr = changeCase.pascalCase(str);
break;
case "none":
retStr = str;
break;
default:
throw new Error("Unknown case style");
}
return retStr;
});
Handlebars.registerHelper("printPropertyVisibility", () =>
// TODO:
generationOptions.propertyVisibility !== "none"
? `${generationOptions.propertyVisibility} `
: ""
);
Handlebars.registerHelper("toPropertyName", str => {
let retStr = "";
switch (generationOptions.convertCaseProperty) {
case "camel":
retStr = changeCase.camelCase(str);
break;
case "pascal":
retStr = changeCase.pascalCase(str);
break;
case "none":
retStr = str;
break;
default:
throw new Error("Unknown case style");
}
return retStr;
});
Handlebars.registerHelper(
"toRelation",
(entityType: string, relationType: Relation["relationType"]) => {
let retVal = entityType;
if (relationType === "ManyToMany" || relationType === "OneToMany") {
retVal = `${retVal}[]`;
}
if (generationOptions.lazy) {
retVal = `Promise<${retVal}>`;
}
return retVal;
}
);
Handlebars.registerHelper("strictMode", () =>
// TODO:
generationOptions.strictMode ? generationOptions.strictMode : ""
);
Handlebars.registerHelper({
and: (v1, v2) => v1 && v2,
eq: (v1, v2) => v1 === v2,
gt: (v1, v2) => v1 > v2,
gte: (v1, v2) => v1 >= v2,
lt: (v1, v2) => v1 < v2,
lte: (v1, v2) => v1 <= v2,
ne: (v1, v2) => v1 !== v2,
or: (v1, v2) => v1 || v2
});
}
function createTsConfigFile(outputPath: string) {
const templatePath = path.resolve(__dirname, "templates", "tsconfig.mst");
const template = fs.readFileSync(templatePath, "UTF-8");
const compliedTemplate = Handlebars.compile(template, {
noEscape: true
});
const rendered = compliedTemplate({});
const formatted = Prettier.format(rendered, { parser: "json" });
const resultFilePath = path.resolve(outputPath, "tsconfig.json");
fs.writeFileSync(resultFilePath, formatted, {
encoding: "UTF-8",
flag: "w"
});
}
function createTypeOrmConfig(
outputPath: string,
connectionOptions: IConnectionOptions
) {
const templatePath = path.resolve(__dirname, "templates", "ormconfig.mst");
const template = fs.readFileSync(templatePath, "UTF-8");
const compliedTemplate = Handlebars.compile(template, {
noEscape: true
});
const rendered = compliedTemplate(connectionOptions);
const formatted = Prettier.format(rendered, { parser: "json" });
const resultFilePath = path.resolve(outputPath, "ormconfig.json");
fs.writeFileSync(resultFilePath, formatted, {
encoding: "UTF-8",
flag: "w"
});
}
function applyNamingStrategy(
namingStrategy: AbstractNamingStrategy,
dbModel: Entity[]
) {
let retVal = changeRelationNames(dbModel);
retVal = changeRelationIdNames(retVal);
retVal = changeEntityNames(retVal);
retVal = changeColumnNames(retVal);
return retVal;
function changeRelationIdNames(model: Entity[]) {
model.forEach(entity => {
entity.relationIds.forEach(relationId => {
const oldName = relationId.fieldName;
const relation = entity.relations.find(
v => v.fieldName === relationId.relationField
)!;
let newName = namingStrategy.relationIdName(
relationId,
relation,
entity
);
newName = TomgUtils.findNameForNewField(
newName,
entity,
oldName
);
entity.indices.forEach(index => {
index.columns = index.columns.map(column2 =>
column2 === oldName ? newName : column2
);
});
relationId.fieldName = newName;
});
});
return dbModel;
}
function changeRelationNames(model: Entity[]) {
model.forEach(entity => {
entity.relations.forEach(relation => {
const oldName = relation.fieldName;
let newName = namingStrategy.relationName(relation, entity);
newName = TomgUtils.findNameForNewField(
newName,
entity,
oldName
);
const relatedEntity = model.find(
v => v.tscName === relation.relatedTable
)!;
const relation2 = relatedEntity.relations.find(
v => v.fieldName === relation.relatedField
)!;
entity.relationIds
.filter(v => v.relationField === oldName)
.forEach(v => {
v.relationField = newName;
});
relation.fieldName = newName;
relation2.relatedField = newName;
if (relation.relationOptions) {
entity.indices.forEach(ind => {
ind.columns.map(column2 =>
column2 === oldName ? newName : column2
);
});
}
});
});
return dbModel;
}
function changeColumnNames(model: Entity[]) {
model.forEach(entity => {
entity.columns.forEach(column => {
const oldName = column.tscName;
let newName = namingStrategy.columnName(column.tscName, column);
newName = TomgUtils.findNameForNewField(
newName,
entity,
oldName
);
entity.indices.forEach(index => {
index.columns = index.columns.map(column2 =>
column2 === oldName ? newName : column2
);
});
column.tscName = newName;
});
});
return model;
}
function changeEntityNames(entities: Entity[]) {
entities.forEach(entity => {
const newName = namingStrategy.entityName(entity.tscName, entity);
entities.forEach(entity2 => {
entity2.relations.forEach(relation => {
if (relation.relatedTable === entity.tscName) {
relation.relatedTable = newName;
}
});
});
entity.tscName = newName;
});
return entities;
}
}

View File

@ -1,19 +1,19 @@
export default class IConnectionOptions {
public host: string = "";
public host = "";
public port: number = 0;
public port = 0;
public databaseName: string = "";
public databaseName = "";
public user: string = "";
public user = "";
public password: string = "";
public password = "";
public databaseType: string = "";
public databaseType = "";
public schemaName: string = "";
public schemaName = "";
public ssl: boolean = false;
public ssl = false;
public timeout?: number;
}

View File

@ -1,7 +1,7 @@
export default class IGenerationOptions {
public resultsPath: string = "";
public resultsPath = "";
public noConfigs: boolean = false;
public noConfigs = false;
public convertCaseFile: "pascal" | "param" | "camel" | "none" = "none";
@ -12,17 +12,17 @@ export default class IGenerationOptions {
public propertyVisibility: "public" | "protected" | "private" | "none" =
"none";
public lazy: boolean = false;
public lazy = false;
public activeRecord: boolean = false;
public activeRecord = false;
public generateConstructor: boolean = false;
public generateConstructor = false;
public customNamingStrategyPath: string = "";
public customNamingStrategyPath = "";
public relationIds: boolean = false;
public relationIds = false;
public strictMode: false | "?" | "!" = false;
public skipSchema: boolean = false;
public skipSchema = false;
}

204
src/ModelCustomization.ts Normal file
View File

@ -0,0 +1,204 @@
import { DataTypeDefaults } from "typeorm/driver/types/DataTypeDefaults";
import { Entity } from "./models/Entity";
import IGenerationOptions from "./IGenerationOptions";
import AbstractNamingStrategy from "./AbstractNamingStrategy";
import NamingStrategy from "./NamingStrategy";
import * as TomgUtils from "./Utils";
export default function modelCustomizationPhase(
dbModel: Entity[],
generationOptions: IGenerationOptions,
defaultValues: DataTypeDefaults
) {
let namingStrategy: AbstractNamingStrategy;
if (
generationOptions.customNamingStrategyPath &&
generationOptions.customNamingStrategyPath !== ""
) {
// eslint-disable-next-line global-require, import/no-dynamic-require, @typescript-eslint/no-var-requires
const req = require(generationOptions.customNamingStrategyPath);
namingStrategy = new req.NamingStrategy();
} else {
namingStrategy = new NamingStrategy();
}
let retVal = applyNamingStrategy(namingStrategy, dbModel);
retVal = addImportsAndGenerationOptions(retVal, generationOptions);
retVal = removeColumnDefaultProperties(retVal, defaultValues);
return retVal;
}
function removeColumnDefaultProperties(
dbModel: Entity[],
defaultValues: DataTypeDefaults
) {
if (!defaultValues) {
return dbModel;
}
dbModel.forEach(entity => {
entity.columns.forEach(column => {
const defVal = defaultValues[column.tscType];
if (defVal) {
if (
column.options.length &&
defVal.length &&
column.options.length === defVal.length
) {
column.options.length = undefined;
}
if (
column.options.precision &&
defVal.precision &&
column.options.precision === defVal.precision &&
column.options.scale &&
defVal.scale &&
column.options.scale === defVal.scale
) {
column.options.precision = undefined;
column.options.scale = undefined;
}
if (
column.options.width &&
defVal.width &&
column.options.width === defVal.width
) {
column.options.width = undefined;
}
}
});
});
return dbModel;
}
function addImportsAndGenerationOptions(
dbModel: Entity[],
generationOptions: IGenerationOptions
) {
dbModel.forEach(entity => {
entity.relations.forEach(relation => {
if (generationOptions.lazy) {
if (!relation.relationOptions) {
relation.relationOptions = {};
}
relation.relationOptions.lazy = true;
}
});
if (generationOptions.skipSchema) {
entity.schema = undefined;
entity.database = undefined;
}
});
return dbModel;
}
function applyNamingStrategy(
namingStrategy: AbstractNamingStrategy,
dbModel: Entity[]
) {
let retVal = changeRelationNames(dbModel);
retVal = changeRelationIdNames(retVal);
retVal = changeEntityNames(retVal);
retVal = changeColumnNames(retVal);
return retVal;
function changeRelationIdNames(model: Entity[]) {
model.forEach(entity => {
entity.relationIds.forEach(relationId => {
const oldName = relationId.fieldName;
const relation = entity.relations.find(
v => v.fieldName === relationId.relationField
)!;
let newName = namingStrategy.relationIdName(
relationId,
relation,
entity
);
newName = TomgUtils.findNameForNewField(
newName,
entity,
oldName
);
entity.indices.forEach(index => {
index.columns = index.columns.map(column2 =>
column2 === oldName ? newName : column2
);
});
relationId.fieldName = newName;
});
});
return dbModel;
}
function changeRelationNames(model: Entity[]) {
model.forEach(entity => {
entity.relations.forEach(relation => {
const oldName = relation.fieldName;
let newName = namingStrategy.relationName(relation, entity);
newName = TomgUtils.findNameForNewField(
newName,
entity,
oldName
);
const relatedEntity = model.find(
v => v.tscName === relation.relatedTable
)!;
const relation2 = relatedEntity.relations.find(
v => v.fieldName === relation.relatedField
)!;
entity.relationIds
.filter(v => v.relationField === oldName)
.forEach(v => {
v.relationField = newName;
});
relation.fieldName = newName;
relation2.relatedField = newName;
if (relation.relationOptions) {
entity.indices.forEach(ind => {
ind.columns.map(column2 =>
column2 === oldName ? newName : column2
);
});
}
});
});
return dbModel;
}
function changeColumnNames(model: Entity[]) {
model.forEach(entity => {
entity.columns.forEach(column => {
const oldName = column.tscName;
let newName = namingStrategy.columnName(column.tscName, column);
newName = TomgUtils.findNameForNewField(
newName,
entity,
oldName
);
entity.indices.forEach(index => {
index.columns = index.columns.map(column2 =>
column2 === oldName ? newName : column2
);
});
column.tscName = newName;
});
});
return model;
}
function changeEntityNames(entities: Entity[]) {
entities.forEach(entity => {
const newName = namingStrategy.entityName(entity.tscName, entity);
entities.forEach(entity2 => {
entity2.relations.forEach(relation => {
if (relation.relatedTable === entity.tscName) {
relation.relatedTable = newName;
}
});
});
entity.tscName = newName;
});
return entities;
}
}

191
src/ModelGeneration.ts Normal file
View File

@ -0,0 +1,191 @@
import * as Handlebars from "handlebars";
import * as Prettier from "prettier";
import * as changeCase from "change-case";
import * as fs from "fs";
import * as path from "path";
import IConnectionOptions from "./IConnectionOptions";
import IGenerationOptions from "./IGenerationOptions";
import { Entity } from "./models/Entity";
import { Relation } from "./models/Relation";
export default function modelGenerationPhase(
connectionOptions: IConnectionOptions,
generationOptions: IGenerationOptions,
databaseModel: Entity[]
) {
createHandlebarsHelpers(generationOptions);
const templatePath = path.resolve(__dirname, "templates", "entity.mst");
const template = fs.readFileSync(templatePath, "UTF-8");
const resultPath = generationOptions.resultsPath;
if (!fs.existsSync(resultPath)) {
fs.mkdirSync(resultPath);
}
let entitiesPath = resultPath;
if (!generationOptions.noConfigs) {
createTsConfigFile(resultPath);
createTypeOrmConfig(resultPath, connectionOptions);
entitiesPath = path.resolve(resultPath, "./entities");
if (!fs.existsSync(entitiesPath)) {
fs.mkdirSync(entitiesPath);
}
}
const compliedTemplate = Handlebars.compile(template, {
noEscape: true
});
databaseModel.forEach(element => {
let casedFileName = "";
switch (generationOptions.convertCaseFile) {
case "camel":
casedFileName = changeCase.camelCase(element.tscName);
break;
case "param":
casedFileName = changeCase.paramCase(element.tscName);
break;
case "pascal":
casedFileName = changeCase.pascalCase(element.tscName);
break;
case "none":
casedFileName = element.tscName;
break;
default:
throw new Error("Unknown case style");
}
const resultFilePath = path.resolve(
entitiesPath,
`${casedFileName}.ts`
);
const rendered = compliedTemplate(element);
const formatted = Prettier.format(rendered, { parser: "typescript" });
fs.writeFileSync(resultFilePath, formatted, {
encoding: "UTF-8",
flag: "w"
});
});
}
function createHandlebarsHelpers(generationOptions: IGenerationOptions) {
Handlebars.registerHelper("json", context => {
const json = JSON.stringify(context);
const withoutQuotes = json.replace(/"([^(")"]+)":/g, "$1:");
return withoutQuotes.slice(1, withoutQuotes.length - 1);
});
Handlebars.registerHelper("toEntityName", str => {
let retStr = "";
switch (generationOptions.convertCaseEntity) {
case "camel":
retStr = changeCase.camelCase(str);
break;
case "pascal":
retStr = changeCase.pascalCase(str);
break;
case "none":
retStr = str;
break;
default:
throw new Error("Unknown case style");
}
return retStr;
});
Handlebars.registerHelper("toFileName", str => {
let retStr = "";
switch (generationOptions.convertCaseFile) {
case "camel":
retStr = changeCase.camelCase(str);
break;
case "param":
retStr = changeCase.paramCase(str);
break;
case "pascal":
retStr = changeCase.pascalCase(str);
break;
case "none":
retStr = str;
break;
default:
throw new Error("Unknown case style");
}
return retStr;
});
Handlebars.registerHelper("printPropertyVisibility", () =>
// TODO:
generationOptions.propertyVisibility !== "none"
? `${generationOptions.propertyVisibility} `
: ""
);
Handlebars.registerHelper("toPropertyName", str => {
let retStr = "";
switch (generationOptions.convertCaseProperty) {
case "camel":
retStr = changeCase.camelCase(str);
break;
case "pascal":
retStr = changeCase.pascalCase(str);
break;
case "none":
retStr = str;
break;
default:
throw new Error("Unknown case style");
}
return retStr;
});
Handlebars.registerHelper(
"toRelation",
(entityType: string, relationType: Relation["relationType"]) => {
let retVal = entityType;
if (relationType === "ManyToMany" || relationType === "OneToMany") {
retVal = `${retVal}[]`;
}
if (generationOptions.lazy) {
retVal = `Promise<${retVal}>`;
}
return retVal;
}
);
Handlebars.registerHelper("strictMode", () =>
// TODO:
generationOptions.strictMode ? generationOptions.strictMode : ""
);
Handlebars.registerHelper({
and: (v1, v2) => v1 && v2,
eq: (v1, v2) => v1 === v2,
gt: (v1, v2) => v1 > v2,
gte: (v1, v2) => v1 >= v2,
lt: (v1, v2) => v1 < v2,
lte: (v1, v2) => v1 <= v2,
ne: (v1, v2) => v1 !== v2,
or: (v1, v2) => v1 || v2
});
}
function createTsConfigFile(outputPath: string) {
const templatePath = path.resolve(__dirname, "templates", "tsconfig.mst");
const template = fs.readFileSync(templatePath, "UTF-8");
const compliedTemplate = Handlebars.compile(template, {
noEscape: true
});
const rendered = compliedTemplate({});
const formatted = Prettier.format(rendered, { parser: "json" });
const resultFilePath = path.resolve(outputPath, "tsconfig.json");
fs.writeFileSync(resultFilePath, formatted, {
encoding: "UTF-8",
flag: "w"
});
}
function createTypeOrmConfig(
outputPath: string,
connectionOptions: IConnectionOptions
) {
const templatePath = path.resolve(__dirname, "templates", "ormconfig.mst");
const template = fs.readFileSync(templatePath, "UTF-8");
const compliedTemplate = Handlebars.compile(template, {
noEscape: true
});
const rendered = compliedTemplate(connectionOptions);
const formatted = Prettier.format(rendered, { parser: "json" });
const resultFilePath = path.resolve(outputPath, "ormconfig.json");
fs.writeFileSync(resultFilePath, formatted, {
encoding: "UTF-8",
flag: "w"
});
}

View File

@ -3,7 +3,7 @@ import { Entity } from "./models/Entity";
export function LogError(
errText: string,
isABug: boolean = true,
isABug = true,
passedError?: string | ErrorConstructor
) {
let errObject = passedError;

View File

@ -350,11 +350,10 @@ export default class OracleDriver extends AbstractDriver {
user: connectionOptions.user
};
}
const that = this;
const promise = new Promise<boolean>((resolve, reject) => {
this.Oracle.getConnection(config, (err, connection) => {
if (!err) {
that.Connection = connection;
this.Connection = connection;
resolve(true);
} else {
TomgUtils.LogError(

View File

@ -51,33 +51,38 @@ async function CliLogic() {
function GetUtilParametersByArgs() {
const { argv } = Yargs.usage(
"Usage: typeorm-model-generator -h <host> -d <database> -p [port] -u <user> -x [password] -e [engine]\nYou can also run program without specyfiying any parameters."
)
.option("h", {
"Usage: typeorm-model-generator -h <host> -d <database> -p [port] -u <user> -x [password] -e [engine]\nYou can also run program without specifying any parameters."
).options({
h: {
alias: "host",
string: true,
default: "127.0.0.1",
describe: "IP address/Hostname for database server"
})
.option("d", {
},
d: {
alias: "database",
string: true,
demand: true,
describe:
"Database name(or path for sqlite). You can pass multiple values separated by comma."
})
.option("u", {
},
u: {
alias: "user",
string: true,
describe: "Username for database server"
})
.option("x", {
},
x: {
alias: "pass",
string: true,
default: "",
describe: "Password for database server"
})
.option("p", {
},
p: {
number: true,
alias: "port",
describe: "Port number for database server"
})
.option("e", {
},
e: {
alias: "engine",
choices: [
"mssql",
@ -89,94 +94,95 @@ function GetUtilParametersByArgs() {
],
default: "mssql",
describe: "Database engine"
})
.option("o", {
},
o: {
alias: "output",
default: path.resolve(process.cwd(), "output"),
describe: "Where to place generated models"
})
.option("s", {
},
s: {
alias: "schema",
string: true,
describe:
"Schema name to create model from. Only for mssql and postgres. You can pass multiple values separated by comma eg. -s scheme1,scheme2,scheme3"
})
.option("ssl", {
},
ssl: {
boolean: true,
default: false
})
.option("noConfig", {
},
noConfig: {
boolean: true,
default: false,
describe: `Doesn't create tsconfig.json and ormconfig.json`
})
.option("cf", {
},
cf: {
alias: "case-file",
choices: ["pascal", "param", "camel", "none"],
default: "pascal",
describe: "Convert file names to specified case"
})
.option("ce", {
},
ce: {
alias: "case-entity",
choices: ["pascal", "camel", "none"],
default: "pascal",
describe: "Convert class names to specified case"
})
.option("cp", {
},
cp: {
alias: "case-property",
choices: ["pascal", "camel", "none"],
default: "camel",
describe: "Convert property names to specified case"
})
.option("pv", {
},
pv: {
alias: "property-visibility",
choices: ["public", "protected", "private", "none"],
default: "none",
describe:
"Defines which visibility should have the generated property"
})
.option("lazy", {
},
lazy: {
boolean: true,
default: false,
describe: "Generate lazy relations"
})
.option("a", {
},
a: {
alias: "active-record",
boolean: true,
default: false,
describe: "Use ActiveRecord syntax for generated models"
})
.option("namingStrategy", {
describe: "Use custom naming strategy"
})
.option("relationIds", {
},
namingStrategy: {
describe: "Use custom naming strategy",
string: true
},
relationIds: {
boolean: true,
default: false,
describe: "Generate RelationId fields"
})
.option("skipSchema", {
},
skipSchema: {
boolean: true,
default: false,
describe: "Omits schema identifier in generated entities"
})
.option("generateConstructor", {
},
generateConstructor: {
boolean: true,
default: false,
describe: "Generate constructor allowing partial initialization"
})
.option("strictMode", {
},
strictMode: {
choices: ["none", "?", "!"],
default: "none",
describe: "Mark fields as optional(?) or non-null(!)"
})
.option("timeout", {
},
timeout: {
describe: "SQL Query timeout(ms)",
number: true
});
}
});
const driver = createDriver(argv.e);
const { standardPort } = driver;
const { standardSchema } = driver;
const standardUser = driver.standardPort;
const { standardPort, standardSchema, standardUser } = driver;
let namingStrategyPath: string;
if (argv.namingStrategy && argv.namingStrategy !== "") {
namingStrategyPath = argv.namingStrategy;
@ -184,11 +190,11 @@ function GetUtilParametersByArgs() {
namingStrategyPath = "";
}
const connectionOptions: IConnectionOptions = new IConnectionOptions();
connectionOptions.databaseName = argv.d ? argv.d.toString() : null;
connectionOptions.databaseName = argv.d;
connectionOptions.databaseType = argv.e;
connectionOptions.host = argv.h;
connectionOptions.password = argv.x ? argv.x.toString() : null;
connectionOptions.port = parseInt(argv.p, 10) || standardPort;
connectionOptions.password = argv.x;
connectionOptions.port = argv.p || standardPort;
connectionOptions.schemaName = argv.s ? argv.s.toString() : standardSchema;
connectionOptions.ssl = argv.ssl;
connectionOptions.timeout = argv.timeout;
@ -196,18 +202,20 @@ function GetUtilParametersByArgs() {
const generationOptions: IGenerationOptions = new IGenerationOptions();
generationOptions.activeRecord = argv.a;
generationOptions.generateConstructor = argv.generateConstructor;
generationOptions.convertCaseEntity = argv.ce;
generationOptions.convertCaseFile = argv.cf;
generationOptions.convertCaseProperty = argv.cp;
generationOptions.convertCaseEntity = argv.ce as IGenerationOptions["convertCaseEntity"];
generationOptions.convertCaseFile = argv.cf as IGenerationOptions["convertCaseFile"];
generationOptions.convertCaseProperty = argv.cp as IGenerationOptions["convertCaseProperty"];
generationOptions.lazy = argv.lazy;
generationOptions.customNamingStrategyPath = namingStrategyPath;
generationOptions.noConfigs = argv.noConfig;
generationOptions.propertyVisibility = argv.pv;
generationOptions.propertyVisibility = argv.pv as IGenerationOptions["propertyVisibility"];
generationOptions.relationIds = argv.relationIds;
generationOptions.skipSchema = argv.skipSchema;
generationOptions.resultsPath = argv.o ? argv.o.toString() : null;
generationOptions.resultsPath = argv.o;
generationOptions.strictMode =
argv.strictMode === "none" ? false : argv.strictMode;
argv.strictMode === "none"
? false
: (argv.strictMode as IGenerationOptions["strictMode"]);
return { driver, connectionOptions, generationOptions };
}

View File

@ -1,32 +1,28 @@
import "reflect-metadata";
import { expect } from "chai";
import * as chai from "chai";
import * as ts from "typescript";
import * as fs from "fs-extra";
import * as path from "path";
import * as chaiSubset from "chai-subset";
import yn from "yn";
import EntityFileToJson from "../utils/EntityFileToJson";
import {
createDriver,
dataCollectionPhase,
modelCustomizationPhase,
modelGenerationPhase
} from "../../src/Engine";
import { createDriver, dataCollectionPhase } from "../../src/Engine";
import * as GTU from "../utils/GeneralTestUtils";
import { Entity } from "../../src/models/Entity";
import IConnectionOptions from "../../src/IConnectionOptions";
import fs = require("fs-extra");
import path = require("path");
import chaiSubset = require("chai-subset");
import chai = require("chai");
import yn = require("yn");
import modelCustomizationPhase from "../../src/ModelCustomization";
import modelGenerationPhase from "../../src/ModelGeneration";
require("dotenv").config();
chai.use(chaiSubset);
const { expect } = chai;
it("Column default values", async () => {
const testPartialPath = "test/integration/defaultValues";
await runTestsFromPath(testPartialPath, true);
}).timeout();
it("Platform specyfic types", async () => {
it("Platform specific types", async () => {
const testPartialPath = "test/integration/entityTypes";
await runTestsFromPath(testPartialPath, true);
});
@ -70,7 +66,7 @@ function runTestForMultipleDrivers(
testPartialPath: string
) {
it(testName, async () => {
const driversToRun = selectDriversForSpecyficTest();
const driversToRun = selectDriversForSpecificTest();
const modelGenerationPromises = driversToRun.map(async dbDriver => {
const {
generationOptions,
@ -120,7 +116,7 @@ function runTestForMultipleDrivers(
compileGeneratedModel(path.resolve(process.cwd(), `output`), dbDrivers);
});
function selectDriversForSpecyficTest() {
function selectDriversForSpecificTest() {
switch (testName) {
case "39":
return dbDrivers.filter(
@ -187,7 +183,7 @@ function compareGeneratedFiles(filesOrgPathTS: string, filesGenPath: string) {
const filesGen = fs
.readdirSync(filesGenPath)
.filter(val => val.toString().endsWith(".ts"));
expect(filesOrg, "Errors detected in model comparision").to.be.deep.equal(
expect(filesOrg, "Errors detected in model comparison").to.be.deep.equal(
filesGen
);
filesOrg.forEach(file => {
@ -275,7 +271,7 @@ async function prepareTestRuns(
password: String(process.env.MYSQL_Password),
databaseType: "mysql",
schemaName: "ignored",
ssl: yn(process.env.MYSQL_SSL)
ssl: yn(process.env.MYSQL_SSL, { default: false })
};
break;
case "mariadb":
@ -287,7 +283,7 @@ async function prepareTestRuns(
password: String(process.env.MARIADB_Password),
databaseType: "mariadb",
schemaName: "ignored",
ssl: yn(process.env.MARIADB_SSL)
ssl: yn(process.env.MARIADB_SSL, { default: false })
};
break;

View File

@ -17,14 +17,14 @@ class EntityColumn {
public relationType: "OneToOne" | "OneToMany" | "ManyToOne" | "ManyToMany";
public isOwnerOfRelation: boolean = false;
public isOwnerOfRelation = false;
}
class EntityIndex {
public indexName: string;
public columnNames: string[] = [];
public isUnique: boolean = false;
public isUnique = false;
}
export default class EntityFileToJson {

View File

@ -41,7 +41,7 @@ export async function createMSSQLModels(
password: String(process.env.MSSQL_Password),
databaseType: "mssql",
schemaName: "dbo,sch1,sch2",
ssl: yn(process.env.MSSQL_SSL)
ssl: yn(process.env.MSSQL_SSL, { default: false })
};
await driver.ConnectToServer(connectionOptions);
connectionOptions.databaseName = String(process.env.MSSQL_Database);
@ -92,7 +92,7 @@ export async function createPostgresModels(
password: String(process.env.POSTGRES_Password),
databaseType: "postgres",
schemaName: "public,sch1,sch2",
ssl: yn(process.env.POSTGRES_SSL)
ssl: yn(process.env.POSTGRES_SSL, { default: false })
};
await driver.ConnectToServer(connectionOptions);
connectionOptions.databaseName = String(process.env.POSTGRES_Database);
@ -176,7 +176,7 @@ export async function createMysqlModels(
password: String(process.env.MYSQL_Password),
databaseType: "mysql",
schemaName: "ignored",
ssl: yn(process.env.MYSQL_SSL)
ssl: yn(process.env.MYSQL_SSL, { default: false })
};
await driver.ConnectToServer(connectionOptions);
@ -218,7 +218,7 @@ export async function createMariaDBModels(
password: String(process.env.MARIADB_Password),
databaseType: "mariadb",
schemaName: "ignored",
ssl: yn(process.env.MARIADB_SSL)
ssl: yn(process.env.MARIADB_SSL, { default: false })
};
await driver.ConnectToServer(connectionOptions);
@ -262,7 +262,7 @@ export async function createOracleDBModels(
password: String(process.env.ORACLE_PasswordSys),
databaseType: "oracle",
schemaName: String(process.env.ORACLE_Username),
ssl: yn(process.env.ORACLE_SSL)
ssl: yn(process.env.ORACLE_SSL, { default: false })
};
await driver.ConnectToServer(connectionOptions);
connectionOptions.user = String(process.env.ORACLE_Username);

View File

@ -1,7 +1,7 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es6",
"target": "es2017",
"noImplicitAny": false,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,