Option to disable column name pluralization (#142)

This commit is contained in:
Kononnable 2019-12-20 19:24:15 +01:00
parent 7679ef2b77
commit ff637721ed
6 changed files with 113 additions and 29 deletions

View File

@ -5,6 +5,7 @@ import path = require("path");
// eslint-disable-next-line @typescript-eslint/interface-name-prefix
export default interface IGenerationOptions {
resultsPath: string;
pluralizeNames: boolean;
noConfigs: boolean;
convertCaseFile: "pascal" | "param" | "camel" | "none";
convertCaseEntity: "pascal" | "camel" | "none";
@ -21,6 +22,7 @@ export default interface IGenerationOptions {
export function getDefaultGenerationOptions(): IGenerationOptions {
const generationOptions: IGenerationOptions = {
resultsPath: path.resolve(process.cwd(), "output"),
pluralizeNames: true,
noConfigs: false,
convertCaseFile: "pascal",
convertCaseEntity: "pascal",

View File

@ -9,6 +9,7 @@ import { RelationId } from "./models/RelationId";
import { Column } from "./models/Column";
type NamingStrategy = {
enablePluralization: (value: boolean) => void;
relationIdName: (
relationId: RelationId,
relation: Relation,
@ -25,6 +26,7 @@ export default function modelCustomizationPhase(
defaultValues: DataTypeDefaults
): Entity[] {
const namingStrategy: NamingStrategy = {
enablePluralization: NamingStrategy.enablePluralization,
columnName: NamingStrategy.columnName,
entityName: NamingStrategy.entityName,
relationIdName: NamingStrategy.relationIdName,
@ -78,7 +80,18 @@ export default function modelCustomizationPhase(
`[${new Date().toLocaleTimeString()}] Using standard naming strategy for relation field names.`
);
}
if (req.enablePluralization) {
console.log(
`[${new Date().toLocaleTimeString()}] Using custom pluralization method for OneToMany, ManyToMany relation field names.`
);
namingStrategy.enablePluralization = req.enablePluralization;
} else {
console.log(
`[${new Date().toLocaleTimeString()}] Using custom pluralization method for OneToMany, ManyToMany relation field names.`
);
}
}
namingStrategy.enablePluralization(generationOptions.pluralizeNames);
let retVal = removeIndicesGeneratedByTypeorm(dbModel);
retVal = removeColumnsInRelation(dbModel);
retVal = applyNamingStrategy(namingStrategy, dbModel);

View File

@ -3,6 +3,12 @@ import * as changeCase from "change-case";
import { Relation } from "./models/Relation";
import { RelationId } from "./models/RelationId";
let pluralize: boolean;
export function enablePluralization(value: boolean) {
pluralize = value;
}
export function relationIdName(
relationId: RelationId,
relation: Relation
@ -22,7 +28,7 @@ export function relationIdName(
if (!Number.isNaN(parseInt(newColumnName[newColumnName.length - 1], 10))) {
newColumnName = newColumnName.substring(0, newColumnName.length - 1);
}
if (isRelationToMany) {
if (isRelationToMany && pluralize) {
newColumnName = plural(newColumnName);
}
@ -54,7 +60,7 @@ export function relationName(relation: Relation): string {
if (!Number.isNaN(parseInt(newColumnName[newColumnName.length - 1], 10))) {
newColumnName = newColumnName.substring(0, newColumnName.length - 1);
}
if (isRelationToMany) {
if (isRelationToMany && pluralize) {
newColumnName = plural(newColumnName);
}
return newColumnName;

View File

@ -239,6 +239,12 @@ function checkYargsParameters(options: options): options {
default: options.generationOptions.generateConstructor,
describe: "Generate constructor allowing partial initialization"
},
disablePluralization: {
boolean: true,
default: !options.generationOptions.pluralizeNames,
describe:
"Disable pluralization of OneToMany, ManyToMany relation names."
},
strictMode: {
choices: ["none", "?", "!"],
default: options.generationOptions.strictMode,
@ -272,6 +278,7 @@ function checkYargsParameters(options: options): options {
options.generationOptions.relationIds = argv.relationIds;
options.generationOptions.skipSchema = argv.skipSchema;
options.generationOptions.resultsPath = argv.o;
options.generationOptions.pluralizeNames = !argv.disablePluralization;
options.generationOptions.strictMode = argv.strictMode as IGenerationOptions["strictMode"];
return options;
@ -399,6 +406,7 @@ async function useInquirer(options: options): Promise<options> {
}
]);
if (customizeGeneration) {
const defaultGenerationOptions = getDefaultGenerationOptions();
const customizations: string[] = (
await inquirer.prompt([
{
@ -446,7 +454,20 @@ async function useInquirer(options: options): Promise<options> {
{
name: "Use specific naming convention",
value: "namingConvention",
checked: options.generationOptions.lazy
checked:
options.generationOptions.convertCaseEntity !==
defaultGenerationOptions.convertCaseEntity ||
options.generationOptions
.convertCaseProperty !==
defaultGenerationOptions.convertCaseProperty ||
options.generationOptions.convertCaseFile !==
defaultGenerationOptions.convertCaseFile
},
{
name:
"Pluralize OneToMany, ManyToMany relation names.",
value: "pluralize",
checked: options.generationOptions.pluralizeNames
}
],
message: "Available customizations",
@ -484,6 +505,9 @@ async function useInquirer(options: options): Promise<options> {
options.generationOptions.noConfigs = !customizations.includes(
"config"
);
options.generationOptions.pluralizeNames = customizations.includes(
"pluralize"
);
options.generationOptions.lazy = customizations.includes("lazy");
options.generationOptions.activeRecord = customizations.includes(
"activeRecord"

View File

@ -47,7 +47,7 @@ describe("Model customization phase", async () => {
fieldName: "Post",
relatedField: "authorId",
relatedTable: "Post",
relationType: "OneToOne"
relationType: "OneToMany"
}
],
relationIds: [],
@ -101,7 +101,7 @@ describe("Model customization phase", async () => {
{ name: "authorId", referencedColumnName: "id" }
],
relatedTable: "PostAuthor",
relationType: "OneToOne"
relationType: "ManyToOne"
}
],
relationIds: [],
@ -252,7 +252,7 @@ describe("Model customization phase", async () => {
.readFileSync(path.resolve(filesGenPath, "PostAuthor.ts"))
.toString();
expect(postContent).to.contain("Title: string;");
expect(postAuthorContent).to.contain("Post: Post;");
expect(postAuthorContent).to.contain("Posts: Post[];");
compileGeneratedModel(generationOptions.resultsPath, [""]);
});
@ -280,7 +280,7 @@ describe("Model customization phase", async () => {
.readFileSync(path.resolve(filesGenPath, "PostAuthor.ts"))
.toString();
expect(postContent).to.contain("title: string;");
expect(postAuthorContent).to.contain("post: Post;");
expect(postAuthorContent).to.contain("posts: Post[];");
compileGeneratedModel(generationOptions.resultsPath, [""]);
});
@ -310,7 +310,7 @@ describe("Model customization phase", async () => {
.readFileSync(path.resolve(filesGenPath, "PostAuthor.ts"))
.toString();
expect(postContent).to.have.string(" public title: string");
expect(postAuthorContent).to.have.string(" public post: Post;");
expect(postAuthorContent).to.have.string(" public posts: Post[];");
compileGeneratedModel(generationOptions.resultsPath, [""]);
});
@ -338,7 +338,7 @@ describe("Model customization phase", async () => {
.readFileSync(path.resolve(filesGenPath, "PostAuthor.ts"))
.toString();
expect(postContent).to.have.string(" title: string");
expect(postAuthorContent).to.have.string(" post: Post;");
expect(postAuthorContent).to.have.string(" posts: Post[];");
compileGeneratedModel(generationOptions.resultsPath, [""]);
});
@ -349,6 +349,7 @@ describe("Model customization phase", async () => {
clearGenerationDir();
generationOptions.lazy = true;
generationOptions.pluralizeNames = false;
const customizedModel = modelCustomizationPhase(
data,
generationOptions,
@ -369,7 +370,7 @@ describe("Model customization phase", async () => {
expect(postContent).to.have.string("lazy: true");
expect(postContent).to.have.string("Promise<PostAuthor>;");
expect(postAuthorContent).to.have.string("lazy: true");
expect(postAuthorContent).to.have.string("Promise<Post>");
expect(postAuthorContent).to.have.string("Promise<Post[]>");
compileGeneratedModel(generationOptions.resultsPath, [""]);
});
@ -530,7 +531,7 @@ describe("Model customization phase", async () => {
expect(postContent).to.have.string(`author!: PostAuthor;`);
expect(postAuthorContent).to.have.string(`id!: number;`);
expect(postAuthorContent).to.have.string(`name!: string;`);
expect(postAuthorContent).to.have.string(`post!: Post;`);
expect(postAuthorContent).to.have.string(`posts!: Post[];`);
compileGeneratedModel(generationOptions.resultsPath, [""]);
});
@ -563,7 +564,7 @@ describe("Model customization phase", async () => {
expect(postContent).to.have.string(`author?: PostAuthor;`);
expect(postAuthorContent).to.have.string(`id?: number;`);
expect(postAuthorContent).to.have.string(`name?: string;`);
expect(postAuthorContent).to.have.string(`post?: Post;`);
expect(postAuthorContent).to.have.string(`posts?: Post[];`);
compileGeneratedModel(generationOptions.resultsPath, [""]);
});
@ -612,4 +613,54 @@ describe("Model customization phase", async () => {
compileGeneratedModel(generationOptions.resultsPath, [""]);
});
describe("pluralization", () => {
it("enabled", async () => {
const data = generateSampleData();
const generationOptions = generateGenerationOptions();
generationOptions.pluralizeNames = true;
clearGenerationDir();
const customizedModel = modelCustomizationPhase(
data,
generationOptions,
{}
);
modelGenerationPhase(
getDefaultConnectionOptions(),
generationOptions,
customizedModel
);
const filesGenPath = path.resolve(resultsPath, "entities");
const postAuthorContent = fs
.readFileSync(path.resolve(filesGenPath, "PostAuthor.ts"))
.toString();
expect(postAuthorContent).to.contain("posts: Post[];");
compileGeneratedModel(generationOptions.resultsPath, [""]);
})
it("disabled", async () => {
const data = generateSampleData();
const generationOptions = generateGenerationOptions();
generationOptions.pluralizeNames = false;
clearGenerationDir();
const customizedModel = modelCustomizationPhase(
data,
generationOptions,
{}
);
modelGenerationPhase(
getDefaultConnectionOptions(),
generationOptions,
customizedModel
);
const filesGenPath = path.resolve(resultsPath, "entities");
const postAuthorContent = fs
.readFileSync(path.resolve(filesGenPath, "PostAuthor.ts"))
.toString();
expect(postAuthorContent).to.contain("post: Post[];");
compileGeneratedModel(generationOptions.resultsPath, [""]);
})
})
});

View File

@ -1,7 +1,7 @@
import { ConnectionOptions, createConnection } from "typeorm";
import * as ts from "typescript";
import * as yn from "yn";
import IGenerationOptions from "../../src/IGenerationOptions";
import IGenerationOptions, { getDefaultGenerationOptions } from "../../src/IGenerationOptions";
import IConnectionOptions from "../../src/IConnectionOptions";
import MssqlDriver from "../../src/drivers/MssqlDriver";
import MariaDbDriver from "../../src/drivers/MariaDbDriver";
@ -12,21 +12,9 @@ import MysqlDriver from "../../src/drivers/MysqlDriver";
import path = require("path");
export function getGenerationOptions(resultsPath: string): IGenerationOptions {
return {
resultsPath,
noConfigs: false,
convertCaseEntity: "pascal",
convertCaseFile: "pascal",
convertCaseProperty: "camel",
propertyVisibility: "none",
lazy: false,
generateConstructor: false,
customNamingStrategyPath: "",
relationIds: false,
skipSchema: false,
activeRecord: false,
strictMode: "none"
};
const retVal = getDefaultGenerationOptions();
retVal.resultsPath = resultsPath;
return retVal;
}
export async function createMSSQLModels(
@ -316,7 +304,7 @@ export function compileTsFiles(
);
console.log(
`${diagnostic.file!.fileName} (${lineAndCharacter.line +
1},${lineAndCharacter.character + 1}): ${message}`
1},${lineAndCharacter.character + 1}): ${message}`
);
compiledWithoutErrors = false;
});