quilon
Version:
Generate ERDs from your entity files automagically
208 lines (207 loc) • 7.8 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.TypeORMDriver = void 0;
const ts_morph_1 = require("ts-morph");
const types_1 = require("../types");
class TypeORMDriver {
constructor(filePath) {
this.filePath = filePath;
this.mappedDataTypes = {
string: "varchar",
number: "integer",
boolean: "boolean",
Date: "timestamp",
float: "float",
double: "double precision",
decimal: "numeric",
int: "integer",
integer: "integer",
smallint: "smallint",
bigint: "bigint",
text: "text",
json: "json",
jsonb: "jsonb",
uuid: "uuid",
char: "char",
varchar: "varchar",
bytea: "bytea",
real: "real",
date: "date",
time: "time",
timestamp: "timestamp",
timestamptz: "timestamptz",
interval: "interval",
array: "ARRAY",
geometry: "geometry",
geography: "geography",
enum: "enum",
};
}
/**
* Parses the entity from the provided file path.
*
* @returns {IEntityData} The parsed entity data including table name, columns, and relations.
* @throws {Error} If the entity class is not defined.
*/
parseEntity() {
const project = new ts_morph_1.Project();
const sourceFile = project.addSourceFileAtPath(this.filePath);
this.entityClass = sourceFile.getClass((cls) => {
return cls.getDecorator("Entity") !== undefined;
});
return {
name: this.extractTableName(),
columns: this.extractColumns(),
relations: this.extractRelations(),
};
}
/**
* Extracts the table name for the entity.
*
* @private
* @returns {string} The name of the table or a default placeholder if undefined.
* @throws {Error} If the entity class is not defined.
*/
extractTableName() {
if (!this.entityClass) {
throw new Error("entityClass is undefined");
}
return this.entityClass.getName() || "% ENTITY %";
}
/**
* Extracts column data from the entity's properties.
*
* @private
* @returns {IColumnData[]} An array of column data including names and types.
* @throws {Error} If the entity class is not defined.
*/
extractColumns() {
if (!this.entityClass) {
throw new Error("entityClass is undefined");
}
const columns = [];
let primaryKeyColumnType;
this.entityClass.getProperties().forEach((property) => {
const relationDecorator = this.getRelationDecorator(property);
const primaryKeyColumnDecorator = this.getPrimaryKeyColumnDecorator(property);
if (primaryKeyColumnDecorator) {
primaryKeyColumnType = property.getType().getText();
}
if (!relationDecorator) {
// Type can be specified with @Column({ type: "float" })
const decoratorType = this.getDecoratorType(property);
const columnName = `${property.getName()} ${primaryKeyColumnDecorator ? '"(PK)"' : ""}`;
const columnType = decoratorType || property.getType().getText();
// Typescript Type has to be converted into corresponding SQL type
const mappedType = this.mappedDataTypes[columnType] || "text";
columns.push({
name: columnName,
type: mappedType,
});
}
if (relationDecorator === types_1.TRelations.ManyToOne) {
const columnName = `${property.getName()}Id "(FK)"`;
// Use the type of the primary key because foreign keys will have the same type
const mappedType = this.mappedDataTypes[primaryKeyColumnType] || "number";
columns.push({
name: columnName,
type: mappedType,
});
}
});
return columns;
}
/**
* Extracts relation data from the entity's properties.
*
* @private
* @returns {IRelationData[]} An array of relation data including names, types, and relation types.
* @throws {Error} If the entity class is not defined.
*/
extractRelations() {
if (!this.entityClass) {
throw new Error("entityClass is undefined");
}
const relations = [];
this.entityClass.getProperties().forEach((property) => {
const relationDecorator = this.getRelationDecorator(property);
if (relationDecorator) {
const columnName = property.getName();
const columnType = property.getType().getText();
relations.push({
name: columnName,
type: columnType.replace(/\[\]/, ""),
relation: relationDecorator,
});
}
});
return relations;
}
/**
* Retrieves the column type specified in a decorator, if present.
*
* @private
* @param {PropertyDeclaration} property - The property to inspect for column type.
* @returns {string | undefined} The specified column type or undefined if not found.
*/
getDecoratorType(property) {
let decoratorType;
property.getDecorators().map((decorator) => {
const args = decorator.getArguments();
const type = args.find((arg) => arg.getText().includes("type"));
if (!type) {
return;
}
decoratorType = this.getDecoratorTypeValue(type.getText());
});
return decoratorType;
}
/**
* Extracts the actual column type from a decorator type string.
*
* @private
* @param {string} type - The decorator type string (e.g., '{ type: "float" }').
* @returns {string | undefined} The extracted type or undefined if not found.
*/
getDecoratorTypeValue(type) {
// For example: type --> '{ type: "float" }'
const regex = /type:\s*"(.*?)"/;
const match = type.match(regex);
let typeValue;
if (match) {
typeValue = match[1];
}
return typeValue;
}
/**
* Retrieves the relation type decorator (e.g., OneToMany) from a property.
*
* @private
* @param {PropertyDeclaration} property - The property to inspect for relation decorators.
* @returns {string | undefined} The relation type if found, or undefined otherwise.
*/
getRelationDecorator(property) {
const decorators = property.getDecorators().map((decorator) => decorator.getName());
return decorators.find((decorator) => {
if (Object.keys(types_1.TRelations).includes(decorator)) {
return decorator;
}
});
}
/**
* Retrieves the name of the primary key column decorator from a given property declaration.
*
* @private
* @param {PropertyDeclaration} property - The property declaration to inspect for decorators.
* @returns {string | undefined} The name of the primary key column decorator if found; otherwise, `undefined`.
*/
getPrimaryKeyColumnDecorator(property) {
const decorators = property.getDecorators().map((decorator) => decorator.getName());
return decorators.find((decorator) => {
if (decorator === "PrimaryGeneratedColumn") {
return decorator;
}
});
}
}
exports.TypeORMDriver = TypeORMDriver;
;