erdia
Version:
CLI to generate mermaid.js ER diagram using TypeORM entity
1,410 lines (1,322 loc) • 90.5 kB
JavaScript
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
var __accessCheck = (obj, member, msg) => {
if (!member.has(obj))
throw TypeError("Cannot " + msg);
};
var __privateGet = (obj, member, getter) => {
__accessCheck(obj, member, "read from private field");
return getter ? getter.call(obj) : member.get(obj);
};
var __privateAdd = (obj, member, value) => {
if (member.has(obj))
throw TypeError("Cannot add the same private member more than once");
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
};
var __privateSet = (obj, member, value, setter) => {
__accessCheck(obj, member, "write to private field");
setter ? setter.call(obj, value) : member.set(obj, value);
return value;
};
// src/common/getColumnHash.ts
function getColumnHash(column) {
const baseHash = [column.entity, column.dbName].join(":");
const base64 = Buffer.from(baseHash).toString("base64");
return base64;
}
// src/common/getDatabaseName.ts
import { TextDecoder } from "util";
function getDatabaseName(options) {
const name = options.database;
if (typeof name === "string") {
return name;
}
if (name instanceof Uint8Array) {
return new TextDecoder().decode(name);
}
return "default";
}
// src/common/getEntityHash.ts
function getEntityHash(entity) {
const baseHash = [entity.entity, entity.dbName].join(":");
const base64 = Buffer.from(baseHash).toString("base64");
return base64;
}
// src/common/getFileVersion.ts
import { parse } from "jsonc-parser";
import semver from "semver";
function getFileVersion(buf) {
const rawJson = buf.toString();
if (semver.valid(rawJson)) {
return rawJson;
}
const parsed = parse(rawJson);
const { version } = parsed;
if (version == null || version === "") {
throw new Error(`invalid version file: ${rawJson}`);
}
return version;
}
// src/common/getIndexHash.ts
function getIndexHash(column) {
const baseHash = [column.entity, column.dbName].join(":");
const base64 = Buffer.from(baseHash).toString("base64");
return base64;
}
// src/common/getPackageName.ts
function getPackageName(json) {
const { name } = json;
if (typeof name === "string" && name !== "") {
return name;
}
throw new Error("Cannot get project name from package.json");
}
// src/configs/const-enum/CE_PROJECT_NAME_FROM.ts
var CE_PROJECT_NAME_FROM = {
DATABASE: "db",
APPLICATION: "app"
};
// src/common/getProjectName.ts
async function getProjectName(dataSource, json, option) {
if (option.projectName === CE_PROJECT_NAME_FROM.DATABASE) {
if (dataSource.options.database != null) {
const databaseName = getDatabaseName(dataSource.options);
return databaseName;
}
const name2 = getPackageName(json);
return name2;
}
const name = getPackageName(json);
return name;
}
// src/configs/const-enum/CE_DEFAULT_VALUE.ts
var CE_DEFAULT_VALUE = {
CONFIG_FILE_NAME: ".erdiarc",
TSCONFIG_FILE_NAME: "tsconfig.json",
HTML_INDEX_FILENAME: "index.html",
HTML_MERMAID_FILENAME: "mermaid.html",
MARKDOWN_FILENAME: "erdia.md",
DATABASE_FILENAME: "erdiadb.json",
VERSION_FILENAME: ".erdiaverrc",
TEMPLATES_PATH: "templates",
DATA_SOURCE_FILE_FUZZY_SCORE_LIMIT: 50,
OUTPUT_DIRECTORY_FUZZY_SCORE_LIMIT: 50
};
// src/configs/modules/getCwd.ts
function getCwd(env) {
if ((env.USE_INIT_CWD ?? "false") === "false") {
return process.cwd();
}
if (env.INIT_CWD != null) {
return env.INIT_CWD;
}
return process.cwd();
}
// src/modules/files/getFindFile.ts
import findUp from "find-up";
async function getFindFile(filename, option) {
const finded = await findUp(filename, option);
return finded;
}
// src/modules/files/betterMkdir.ts
import { isFalse } from "my-easy-fp";
import { exists, getDirname } from "my-node-fp";
import fs from "fs";
import pathe from "pathe";
async function betterMkdir(filePath) {
const isFilePathExist = await exists(filePath);
if (isFalse(isFilePathExist)) {
const extname = pathe.extname(filePath);
const hasExtname = extname !== "" && extname.length > 0;
if (hasExtname) {
const dirPath = await getDirname(filePath);
await fs.promises.mkdir(dirPath, { recursive: true });
} else {
await fs.promises.mkdir(filePath, { recursive: true });
}
}
}
// src/modules/files/getOutputDirPath.ts
import { isFalse as isFalse2 } from "my-easy-fp";
import { exists as exists2, getDirname as getDirname2, isDirectory } from "my-node-fp";
import pathe2 from "pathe";
async function getOutputDirPath(option, cwd) {
const outputDirPath = option.output ?? cwd;
const resolvedOutputDirPath = pathe2.resolve(outputDirPath);
if (isFalse2(await exists2(resolvedOutputDirPath))) {
await betterMkdir(pathe2.join(resolvedOutputDirPath));
return resolvedOutputDirPath;
}
if (isFalse2(await isDirectory(outputDirPath))) {
return pathe2.resolve(await getDirname2(outputDirPath));
}
return pathe2.resolve(outputDirPath);
}
// src/common/getVersion.ts
import dayjs from "dayjs";
import fs2 from "fs";
import pathe3 from "pathe";
async function getVersionFilename(option, versionFilename) {
if (option.versionPath != null) {
const filename2 = await getFindFile(
pathe3.join(await getOutputDirPath({ output: option.versionPath }, getCwd(process.env)), versionFilename),
{ cwd: getCwd(process.env) }
);
return filename2;
}
const filename = await getFindFile(versionFilename, { cwd: getCwd(process.env) });
return filename;
}
async function getVersion(json, option) {
if (option.versionFrom === "package.json") {
const { version } = json;
if (!(typeof version === "string") || version == null) {
throw new Error(`Cannot found version field in package.json`);
}
return { version };
}
if (option.versionFrom === "file") {
const getVersionFile = async () => {
const filename = await getVersionFilename(option, CE_DEFAULT_VALUE.VERSION_FILENAME);
if (filename != null) {
return filename;
}
const fromConfig = await getVersionFilename(option, CE_DEFAULT_VALUE.CONFIG_FILE_NAME);
return fromConfig;
};
const versionFilename = await getVersionFile();
if (versionFilename == null) {
throw new Error(`Cannot found version file: ${CE_DEFAULT_VALUE.VERSION_FILENAME}`);
}
const versionBuf = await fs2.promises.readFile(versionFilename);
const version = getFileVersion(versionBuf);
return { version: version.trim() };
}
return { version: `${dayjs().valueOf()}` };
}
// src/modules/containers/container.ts
import { createContainer } from "awilix";
var container = createContainer();
// src/modules/containers/keys/SymbolDataSource.ts
var SymbolDataSource = Symbol("data-source");
// src/common/getMetadata.ts
import dayjs2 from "dayjs";
import filenamify from "filenamify";
import readPkg from "read-pkg";
async function getMetadata(option) {
const dataSource = container.resolve(SymbolDataSource);
const json = await readPkg({ normalize: false });
const rawName = await getProjectName(dataSource, json, option);
const name = filenamify(rawName, { replacement: "_" });
const { version } = await getVersion(json, option);
return {
name,
title: option.title,
version,
createdAt: dayjs2().format(),
updatedAt: dayjs2().format()
};
}
// src/common/getPlainRelationType.ts
function getPlainRelationType(relationType) {
if (relationType === "one-to-one") {
return "one-to-one";
}
if (relationType === "many-to-many") {
return "many-to-many";
}
return "one-to-many";
}
// src/common/getRelationHash.ts
function getRelationHash(relation) {
const entities = [relation.entity, relation.inverseEntityName].sort((l, r) => l.localeCompare(r));
const plainRelationType = getPlainRelationType(relation.relationType);
const baseHash = [...entities, plainRelationType].join(":");
const base64 = Buffer.from(baseHash).toString("base64");
return base64;
}
// src/creators/applyPretter.ts
import consola from "consola";
import { isError } from "my-easy-fp";
async function applyPrettier(document, format, configPath) {
try {
const prettier = (await import("prettier")).default;
const prettierConfig = await prettier.resolveConfig(configPath ?? ".");
const formatted = await prettier.format(document, {
...prettierConfig ?? {},
parser: format === "md" ? "markdown" : format
});
return formatted;
} catch (caught) {
const err = isError(caught, new Error("unknown error raised from prettier appling function"));
consola.error(err.message);
consola.error(err.stack);
return document;
}
}
// src/configs/const-enum/CE_OUTPUT_COMPONENT.ts
var CE_OUTPUT_COMPONENT = {
TABLE: "table",
ER: "er"
};
// src/modules/containers/keys/SymbolTemplateRenderer.ts
var SymbolTemplateRenderer = Symbol("template-renderer");
// src/templates/cosnt-enum/CE_TEMPLATE_NAME.ts
var CE_TEMPLATE_NAME = {
HTML_DOCUMENT_TOC: "html-document-toc",
HTML_DOCUMENT: "html-document",
HTML_MERMAID: "html-mermaid",
HTML_MERMAID_TOC: "html-mermaid-toc",
HTML_MERMAID_DIAGRAM: "html-mermaid-diagram",
HTML_STYLE: "html-style",
HTML_TABLE: "html-table",
IMAGE_DOCUMENT: "image-document",
IMAGE_MERMAID_DIAGRAM: "image-mermaid-diagram",
IMAGE_STYLE: "image-style",
MARKDOWN_DOCUMENT: "markdown-document",
MARKDOWN_MERMAID_DIAGRAM: "markdown-mermaid-diagram",
MARKDOWN_TABLE: "markdown-table",
MARKDOWN_TOC: "markdown-toc",
PDF_DOCUMENT_TOC: "pdf-document-toc",
PDF_DOCUMENT: "pdf-document",
PDF_MERMAID_DIAGRAM: "pdf-mermaid-diagram",
PDF_STYLE: "pdf-style",
PDF_TABLE: "pdf-table",
CONFIG_JSON: "config-json"
};
// src/creators/createHtml.ts
import consola2 from "consola";
import pathe4 from "pathe";
async function getTables(option, renderData, outputDir) {
if (!option.components.includes(CE_OUTPUT_COMPONENT.TABLE)) {
return [];
}
const renderer = container.resolve(SymbolTemplateRenderer);
const rawTables = await renderer.evaluate(CE_TEMPLATE_NAME.HTML_DOCUMENT, renderData);
const prettiedTables = await applyPrettier(rawTables, "html", option.prettierConfig);
const tablesFileName = pathe4.join(outputDir, CE_DEFAULT_VALUE.HTML_INDEX_FILENAME);
return [
{
dirname: pathe4.resolve(outputDir),
filename: pathe4.resolve(tablesFileName),
content: prettiedTables
}
];
}
async function getDiagram(option, renderData, outputDir) {
if (!option.components.includes(CE_OUTPUT_COMPONENT.ER)) {
return [];
}
const renderer = container.resolve(SymbolTemplateRenderer);
const rawDiagram = await renderer.evaluate(CE_TEMPLATE_NAME.HTML_MERMAID, renderData);
const prettiedDiagram = await applyPrettier(rawDiagram, "html", option.prettierConfig);
const diagramFileName = option.components.includes(CE_OUTPUT_COMPONENT.TABLE) ? pathe4.join(outputDir, CE_DEFAULT_VALUE.HTML_MERMAID_FILENAME) : pathe4.join(outputDir, CE_DEFAULT_VALUE.HTML_INDEX_FILENAME);
return [
{
dirname: pathe4.resolve(outputDir),
filename: pathe4.resolve(diagramFileName),
content: prettiedDiagram
}
];
}
async function createHtml(option, renderData) {
const outputDir = await getOutputDirPath(option, getCwd(process.env));
consola2.info(`export component: ${option.components.join(", ")}`);
const documents = (await Promise.all(
option.components.map(async (component) => {
if (component === CE_OUTPUT_COMPONENT.TABLE) {
return getTables(option, renderData, outputDir);
}
if (component === CE_OUTPUT_COMPONENT.ER) {
return getDiagram(option, renderData, outputDir);
}
return [];
})
)).flat();
return documents;
}
// src/creators/createImageHtml.ts
import { getDirname as getDirname3 } from "my-node-fp";
import { randomUUID } from "crypto";
import pathe5 from "pathe";
async function createImageHtml(option, renderData) {
const renderer = container.resolve(SymbolTemplateRenderer);
const rawHtml = await renderer.evaluate(CE_TEMPLATE_NAME.IMAGE_DOCUMENT, {
...renderData,
option: { ...renderData.option, width: "200vw" }
});
const prettiedHtml = await applyPrettier(rawHtml, "html", option.prettierConfig);
const outputDirPath = option.output != null ? pathe5.resolve(option.output) : process.cwd();
await betterMkdir(outputDirPath);
const tempFileName = pathe5.join(outputDirPath, `${randomUUID()}.html`);
return {
dirname: await getDirname3(outputDirPath),
content: prettiedHtml,
filename: pathe5.resolve(tempFileName)
};
}
// src/creators/createMarkdown.ts
import pathe6 from "pathe";
async function createMarkdown(option, renderData) {
const renderer = container.resolve(SymbolTemplateRenderer);
const rawMarkdown = await renderer.evaluate(CE_TEMPLATE_NAME.MARKDOWN_DOCUMENT, renderData);
const prettiedMarkdown = await applyPrettier(rawMarkdown, "md", option.prettierConfig);
const markdownFileName = `${renderData.metadata.name}.md`;
const outputDir = await getOutputDirPath(option, getCwd(process.env));
return {
filename: pathe6.resolve(pathe6.join(outputDir, markdownFileName)),
dirname: pathe6.resolve(outputDir),
content: prettiedMarkdown
};
}
// src/creators/createPdfHtml.ts
import { getDirname as getDirname4 } from "my-node-fp";
import { randomUUID as randomUUID2 } from "crypto";
import pathe7 from "pathe";
async function createPdfHtml(option, renderData) {
const renderer = container.resolve(SymbolTemplateRenderer);
const rawHtml = await renderer.evaluate(CE_TEMPLATE_NAME.PDF_DOCUMENT, renderData);
const prettiedHtml = await applyPrettier(rawHtml, "html", option.prettierConfig);
const outputDirPath = option.output != null ? pathe7.resolve(option.output) : process.cwd();
await betterMkdir(outputDirPath);
const tempFileName = pathe7.join(outputDirPath, `${randomUUID2()}.html`);
return {
dirname: await getDirname4(outputDirPath),
content: prettiedHtml,
filename: pathe7.resolve(tempFileName)
};
}
// src/configs/const-enum/CE_OUTPUT_FORMAT.ts
var CE_OUTPUT_FORMAT = {
HTML: "html",
MARKDOWN: "md",
PDF: "pdf",
IMAGE: "image"
};
// src/databases/const-enum/CE_RECORD_KIND.ts
var CE_RECORD_KIND = {
COLUMN: "column",
ENTITY: "entity",
RELATION: "relation",
INDEX: "index"
};
// src/modules/getSlashEndRoutePath.ts
function getSlashEndRoutePath(basePath) {
if (basePath.endsWith("/")) {
return basePath;
}
return `${basePath}/`;
}
// src/creators/getRenderData.ts
import alasql from "alasql";
import { compareVersions } from "compare-versions";
async function getRenderData(records, metadata, option) {
const versionRows = await alasql.promise("SELECT DISTINCT version FROM ?", [records]);
const unSortedVersions = versionRows.map((version) => version.version);
const versions = option.versionFrom === "timestamp" ? unSortedVersions.sort((l, r) => r.localeCompare(l)) : unSortedVersions.sort((l, r) => compareVersions(r, l));
const renderDatas = await Promise.all(
versions.map(async (version) => {
const entities = await alasql.promise(`SELECT * FROM ? WHERE [$kind] = ? AND version = ?`, [
records,
"entity",
version
]);
const renderData = await Promise.all(
entities.map(async (entity) => {
const [columns, relations, indices] = await Promise.all([
await alasql.promise("SELECT * FROM ? WHERE [$kind] = ? AND entity = ? AND version = ?", [
records,
CE_RECORD_KIND.COLUMN,
entity.entity,
version
]),
await alasql.promise("SELECT * FROM ? WHERE [$kind] = ? AND entity = ? AND version = ?", [
records,
CE_RECORD_KIND.RELATION,
entity.entity,
version
]),
await alasql.promise("SELECT * FROM ? WHERE [$kind] = ? AND entity = ? AND version = ?", [
records,
CE_RECORD_KIND.INDEX,
entity.entity,
version
])
]);
return { ...entity, columns, relations, indices };
})
);
return { version, entities: renderData, latest: version === metadata.version };
})
);
if (option.format === CE_OUTPUT_FORMAT.HTML) {
return {
versions: renderDatas,
option: {
...option,
routeBasePath: option.routeBasePath != null ? getSlashEndRoutePath(option.routeBasePath) : void 0
},
metadata
};
}
return { versions: renderDatas, option, metadata };
}
// src/modules/getPuppeteerConfig.ts
import fs3 from "fs";
import { parse as parse2 } from "jsonc-parser";
import { exists as exists3 } from "my-node-fp";
async function getPuppeteerConfig(confgFilePath) {
try {
if (confgFilePath == null) {
return {};
}
if (await exists3(confgFilePath)) {
const buf = await fs3.promises.readFile(confgFilePath);
const option = parse2(buf.toString());
return option;
}
return {};
} catch {
return {};
}
}
// src/creators/writeToImage.ts
import consola3 from "consola";
import del from "del";
import { isError as isError2 } from "my-easy-fp";
import fs4 from "fs";
import pathe8 from "pathe";
import * as puppeteer from "puppeteer";
async function writeToImage(document, option, renderData) {
let localBrowser;
let localPage;
try {
const puppeteerConfig = await getPuppeteerConfig(option.prettierConfig);
const browser = await puppeteer.launch({ ...puppeteerConfig, headless: true });
const page = await browser.newPage();
const puppeteerGotoOption = {
waitUntil: "domcontentloaded",
timeout: 6e4
};
localBrowser = browser;
localPage = page;
await betterMkdir(document.filename);
await fs4.promises.writeFile(document.filename, document.content);
await page.setViewport({ width: option.viewportWidth ?? 1280, height: option.viewportHeight ?? 720 * 2 });
await page.goto(`file://${document.filename}`, puppeteerGotoOption);
consola3.debug(`file write start: ${document.filename}`);
await page.$eval(
"body",
(body, backgroundColor) => {
body.style.background = backgroundColor;
},
option.backgroundColor ?? "white"
);
if (option.imageFormat === "svg") {
const svg = await page.$eval("#mermaid-diagram-container", (container2) => container2.innerHTML);
if (svg == null) {
await del(document.filename);
throw new Error("invalid image html document template");
}
await fs4.promises.writeFile(pathe8.join(document.dirname, `${renderData.metadata.name}.svg`), svg);
consola3.debug("file write end");
await del(document.filename);
consola3.info(`Component ER diagram successfully write on ${renderData.metadata.name}.svg`);
return [pathe8.join(document.dirname, `${renderData.metadata.name}.svg`)];
}
const clip = await page.$eval("svg", (htmlSvgElement) => {
const react = htmlSvgElement.getBoundingClientRect();
return { x: react.left, y: react.top, width: react.width, height: react.height };
});
await page.screenshot({
path: pathe8.join(document.dirname, `${renderData.metadata.name}.png`),
clip,
omitBackground: false
});
consola3.debug("file write end");
await del(document.filename);
consola3.info(`Component ER diagram successfully write on ${renderData.metadata.name}.png`);
return [pathe8.join(document.dirname, `${renderData.metadata.name}.png`)];
} catch (caught) {
const err = isError2(caught, new Error("unknown error raised from writeToImage"));
consola3.error(err.message);
consola3.error(err.stack);
return false;
} finally {
consola3.debug("Start page, brower close");
if (localPage !== void 0 && localPage !== null) {
await localPage.close();
}
if (localBrowser !== void 0 && localBrowser !== null) {
await localBrowser.close();
}
}
}
// src/creators/writeToPdf.ts
import consola4 from "consola";
import del2 from "del";
import fs5 from "fs";
import { isError as isError3 } from "my-easy-fp";
import pathe9 from "pathe";
import * as puppeteer2 from "puppeteer";
async function writeToPdf(document, option, renderData) {
let localBrowser;
let localPage;
try {
const puppeteerConfig = await getPuppeteerConfig(option.puppeteerConfig);
const browser = await puppeteer2.launch({ ...puppeteerConfig, headless: true });
const page = await browser.newPage();
const puppeteerGotoOption = {
waitUntil: "domcontentloaded",
timeout: 6e4
};
localBrowser = browser;
localPage = page;
consola4.info("filename: ", document.filename);
await page.setViewport({ width: option.viewportWidth ?? 1280, height: option.viewportHeight ?? 720 * 2 });
await fs5.promises.writeFile(document.filename, document.content);
await page.goto(`file://${document.filename}`, puppeteerGotoOption);
await page.pdf({
path: pathe9.join(document.dirname, `${renderData.metadata.name}.pdf`),
printBackground: option.backgroundColor !== "transparent"
});
await del2(document.filename);
return [pathe9.join(document.dirname, `${renderData.metadata.name}.pdf`)];
} catch (caught) {
const err = isError3(caught, new Error("unknown error raised from writeToPdf"));
consola4.error(err.message);
consola4.error(err.stack);
return [];
} finally {
if (localPage !== void 0 && localPage !== null) {
consola4.debug("Session Closed");
await localPage.close();
}
if (localBrowser !== void 0 && localBrowser !== null) {
await localBrowser.close();
}
}
}
// src/databases/const-enum/CE_CHANGE_KIND.ts
var CE_CHANGE_KIND = {
CHANGE: "change",
ADD: "add",
DELETE: "delete",
NONE: "none"
};
// src/databases/compareDatabase.ts
import { detailedDiff } from "deep-object-diff";
import { settify } from "my-easy-fp";
function compareDatabase(metadata, next, prev) {
if (prev.length <= 0) {
return next.map((record) => ({ ...record, change: CE_CHANGE_KIND.NONE }));
}
const nextMap = next.reduce((aggregation, record) => {
switch (record.$kind) {
case CE_RECORD_KIND.ENTITY:
return { ...aggregation, [getEntityHash(record)]: record };
case CE_RECORD_KIND.COLUMN:
return { ...aggregation, [getColumnHash(record)]: record };
case CE_RECORD_KIND.RELATION:
return { ...aggregation, [getRelationHash(record)]: record };
case CE_RECORD_KIND.INDEX:
return { ...aggregation, [getIndexHash(record)]: record };
default:
return aggregation;
}
}, {});
const prevMap = prev.reduce((aggregation, record) => {
switch (record.$kind) {
case CE_RECORD_KIND.ENTITY:
return { ...aggregation, [getEntityHash(record)]: record };
case CE_RECORD_KIND.COLUMN:
return { ...aggregation, [getColumnHash(record)]: record };
case CE_RECORD_KIND.RELATION:
return { ...aggregation, [getRelationHash(record)]: record };
case CE_RECORD_KIND.INDEX:
return { ...aggregation, [getIndexHash(record)]: record };
default:
return aggregation;
}
}, {});
const compared = settify([...Object.keys(nextMap), ...Object.keys(prevMap)]).map((key) => {
const fromNext = nextMap[key];
const fromPrev = prevMap[key];
if (fromNext != null && fromPrev == null) {
return { ...fromNext, change: CE_CHANGE_KIND.ADD };
}
if (fromNext == null && fromPrev != null) {
return { ...fromPrev, change: CE_CHANGE_KIND.DELETE, version: metadata.version };
}
const forCompareNext = {
...fromNext,
title: fromNext.title ?? "",
change: CE_CHANGE_KIND.NONE,
createdAt: "",
updatedAt: "",
version: ""
};
const forComparePrev = {
...fromPrev,
title: fromPrev.title ?? "",
change: CE_CHANGE_KIND.NONE,
createdAt: "",
updatedAt: "",
version: ""
};
const diffed = detailedDiff(forCompareNext, forComparePrev);
if (Object.keys(diffed.added).length <= 0 && Object.keys(diffed.updated).length <= 0 && Object.keys(diffed.deleted).length <= 0) {
return { ...fromNext, change: CE_CHANGE_KIND.NONE };
}
return { ...fromNext, change: CE_CHANGE_KIND.CHANGE, prev: fromPrev };
});
return compared;
}
// src/databases/flushDatabase.ts
import fs6 from "fs";
import pathe10 from "pathe";
async function flushDatabase(option, records) {
const dirname = await getOutputDirPath({ output: option.databasePath }, process.cwd());
const filename = pathe10.join(dirname, CE_DEFAULT_VALUE.DATABASE_FILENAME);
if (filename == null) {
throw new Error(`invalid database name: undefined`);
}
await fs6.promises.writeFile(
pathe10.join(dirname, CE_DEFAULT_VALUE.DATABASE_FILENAME),
JSON.stringify(records, void 0, 2)
);
return records;
}
// src/databases/openDatabase.ts
import { parse as parse3 } from "jsonc-parser";
import { isFalse as isFalse3 } from "my-easy-fp";
import { exists as exists4 } from "my-node-fp";
import fs7 from "fs";
import pathe11 from "pathe";
async function openDatabase(option) {
const dirname = await getOutputDirPath({ output: option.databasePath }, process.cwd());
const filename = pathe11.join(dirname, CE_DEFAULT_VALUE.DATABASE_FILENAME);
if (filename == null) {
return [];
}
if (isFalse3(await exists4(filename))) {
return [];
}
const db = parse3((await fs7.promises.readFile(filename)).toString());
return db;
}
// src/databases/processDatabase.ts
import alasql2 from "alasql";
import { compareVersions as compareVersions2 } from "compare-versions";
import { atOrThrow } from "my-easy-fp";
async function processDatabase(metadata, db, option) {
if (db.length <= 0) {
return { next: [], deleted: [], prev: [] };
}
const currentVersion = metadata.version;
const versions = await alasql2.promise("SELECT DISTINCT version FROM ?", [db]);
const sortedVersions = option.versionFrom === "timestamp" ? versions.sort((l, r) => r.version.localeCompare(l.version)) : versions.sort((l, r) => compareVersions2(r.version, l.version));
const firstVersionFromDb = atOrThrow(sortedVersions, 0).version;
if (currentVersion !== firstVersionFromDb) {
const latestRecords2 = await alasql2.promise("SELECT * FROM ? WHERE version = ?", [
db,
firstVersionFromDb
]);
return { next: db, deleted: [], prev: latestRecords2 };
}
if (sortedVersions.length <= 1) {
return { next: [], deleted: [], prev: [] };
}
const secondVersionFromDb = atOrThrow(sortedVersions, 1).version;
const partialRecords = await alasql2.promise("SELECT * FROM ? WHERE version != ?", [
db,
currentVersion
]);
const oldRecords = await alasql2.promise("SELECT * FROM ? WHERE version = ?", [
db,
currentVersion
]);
const latestRecords = await alasql2.promise("SELECT * FROM ? WHERE version = ?", [
db,
secondVersionFromDb
]);
return { next: partialRecords, deleted: oldRecords, prev: latestRecords };
}
// src/templates/TemplateRenderer.ts
import consola5 from "consola";
import { Eta } from "eta";
import { isError as isError4, orThrow } from "my-easy-fp";
var _eta, _templates, _defaultTemplates;
var TemplateRenderer = class {
constructor(templates, defaultTemplates) {
__privateAdd(this, _eta, void 0);
__privateAdd(this, _templates, void 0);
__privateAdd(this, _defaultTemplates, void 0);
__privateSet(this, _templates, templates);
__privateSet(this, _defaultTemplates, defaultTemplates);
__privateSet(this, _eta, new Eta({ views: "erdia", autoEscape: false }));
__privateGet(this, _eta).resolvePath = (templatePath) => templatePath;
__privateGet(this, _eta).readFile = (templatePath) => {
const template = __privateGet(this, _templates).get(templatePath) ?? __privateGet(this, _defaultTemplates).get(templatePath);
return orThrow(template, new Error(`cannot found template: ${templatePath}`));
};
}
async evaluate(name, data) {
try {
const rendered = __privateGet(this, _eta).render(name, data);
return rendered;
} catch (caught) {
const err = isError4(caught, new Error(`raise error from evaluateTemplate: ${name}`));
consola5.error(`template: ${name}`, data);
consola5.error(err);
throw err;
}
}
};
_eta = new WeakMap();
_templates = new WeakMap();
_defaultTemplates = new WeakMap();
// src/typeorm/loadDataSource.ts
import { isError as isError5 } from "my-easy-fp";
import { InstanceChecker } from "typeorm";
import { importOrRequireFile } from "typeorm/util/ImportUtils";
async function loadDataSource(dataSourceFilePath) {
let dataSourceFileExports;
try {
[dataSourceFileExports] = await importOrRequireFile(dataSourceFilePath);
} catch (caught) {
const err = isError5(caught, new Error(`Unable to open file: "${dataSourceFilePath}".`));
throw new Error(`Unable to open file: "${dataSourceFilePath}". ${err.message}`);
}
if (!dataSourceFileExports || typeof dataSourceFileExports !== "object") {
throw new Error(`Given data source file must contain export of a DataSource instance`);
}
if (InstanceChecker.isDataSource(dataSourceFileExports)) {
return dataSourceFileExports;
}
const dataSourceExports = [];
for (const fileExportKey in dataSourceFileExports) {
const fileExport = dataSourceFileExports[fileExportKey];
const awaitedFileExport = await fileExport;
if (InstanceChecker.isDataSource(awaitedFileExport)) {
dataSourceExports.push(awaitedFileExport);
}
}
if (dataSourceExports.length === 0) {
throw new Error(`Given data source file must contain export of a DataSource instance`);
}
if (dataSourceExports.length > 1) {
throw new Error(`Given data source file must contain only one export of DataSource instance`);
}
return dataSourceExports[0];
}
// src/typeorm/getDataSource.ts
import { isFalse as isFalse4 } from "my-easy-fp";
import { exists as exists5 } from "my-node-fp";
import pathe12 from "pathe";
async function getDataSource(options) {
const dataSourcePath = pathe12.resolve(options.dataSourcePath);
if (isFalse4(await exists5(dataSourcePath))) {
throw new Error(`Cannot found dataSource: ${dataSourcePath}`);
}
const dataSource = await loadDataSource(dataSourcePath);
if (dataSource == null) {
throw new Error(`Cannot found dataSource in ${options.dataSourcePath}`);
}
return dataSource;
}
// src/cli/builders/buildOptionBuilder.ts
function buildOptionBuilder(args) {
args.option("route-base-path", {
describe: "define the route base path. The route base path is used as the base path for navbar anchor when generating HTML documents",
type: "string",
default: void 0
}).option("title", {
describe: "define what will be written in the HTML document title tag",
type: "string",
default: void 0
}).option("prettier-config", {
describe: "define the path to the prettier configuration file",
type: "string",
default: void 0
}).option("puppeteer-config", {
describe: "define the path to the puppeteer configuration file",
type: "string"
}).option("width", {
describe: "define the ER diagram width. The width is defined by the HTML document css attribute width",
type: "string",
default: "100%"
}).option("viewport-width", {
describe: "define the viewport width to puppeteer. The width is defined by the HTML document css attribute width",
type: "number",
default: 1280
}).option("viewport-height", {
describe: "define the viewport height to puppeteer. The width is defined by the HTML document css attribute height",
type: "number",
default: 720 * 2
}).option("image-format", {
describe: "define the format to image file",
type: "string",
choices: ["svg", "png"],
default: "svg"
}).option("background-color", {
describe: "define the background color to html documents. eg. transparent, red, '#F0F0F0'",
type: "string",
default: "white"
});
return args;
}
// src/cli/builders/outputOptionBuilder.ts
function outputOptionBuilder(args) {
args.option("output", {
alias: "o",
describe: "define the directory to output file",
type: "string"
});
return args;
}
// src/cli/builders/commonOptionBuilder.ts
function commonOptionBuilder(args) {
outputOptionBuilder(args).option("config", {
alias: "c",
describe: "define the path to to configuration file: .erdiarc",
type: "string"
}).option("data-source-path", {
alias: "d",
describe: "define the path to TypeORM data source file",
type: "string"
}).option("show-logo", {
describe: "define the logo display on cli interface",
type: "boolean",
default: false
}).demandOption("data-source-path");
return args;
}
// src/configs/const-enum/CE_ENTITY_VERSION_FROM.ts
var CE_ENTITY_VERSION_FROM = {
TIMESTAMP: "timestamp",
PACKAGE_JSON: "package.json",
FILE: "file"
};
// src/configs/const-enum/CE_MERMAID_THEME.ts
var CE_MERMAID_THEME = {
DEFAULT: "default",
FOREST: "forest",
DARK: "dark",
NEUTRAL: "neutral",
NULL: "null"
};
// src/cli/builders/documentOptionBuilder.ts
function documentOptionBuilder(args) {
args.option("components", {
alias: "t",
describe: "define the output component to builded documents",
choices: [CE_OUTPUT_COMPONENT.TABLE, CE_OUTPUT_COMPONENT.ER],
type: "array",
default: [CE_OUTPUT_COMPONENT.TABLE, CE_OUTPUT_COMPONENT.ER]
}).option("project-name", {
describe: "define whether project name will come from the `package.json` name field or database name",
type: "string",
choices: [CE_PROJECT_NAME_FROM.APPLICATION, CE_PROJECT_NAME_FROM.DATABASE],
default: CE_PROJECT_NAME_FROM.APPLICATION
}).option("database-path", {
describe: "define the directory to store `erdiadb.json`",
type: "string",
default: void 0
}).option("template-path", {
describe: "define the directory to ETA templates",
type: "string"
}).option("skip-image-in-html", {
describe: "enabling the this option will skip attaching the ER diagram image file to the html document",
type: "boolean",
default: false
}).option("format", {
describe: "define the output format to builded documents",
choices: [CE_OUTPUT_FORMAT.HTML, CE_OUTPUT_FORMAT.MARKDOWN, CE_OUTPUT_FORMAT.PDF, CE_OUTPUT_FORMAT.IMAGE],
type: "string",
default: CE_OUTPUT_FORMAT.HTML
}).option("version-from", {
describe: "define whether document version will come from the `package.json` version field or specific file, timestamp",
choices: [CE_ENTITY_VERSION_FROM.PACKAGE_JSON, CE_ENTITY_VERSION_FROM.FILE, CE_ENTITY_VERSION_FROM.TIMESTAMP],
type: "string",
default: CE_ENTITY_VERSION_FROM.PACKAGE_JSON
}).option("version-path", {
describe: "If the versionFrom option set `file`, read the file from this path",
type: "string",
default: void 0
}).option("theme", {
describe: "define the mermaid.js plugin theme. see https://mermaid-js.github.io/mermaid/#/Setup?id=theme",
choices: [
CE_MERMAID_THEME.DEFAULT,
CE_MERMAID_THEME.DARK,
CE_MERMAID_THEME.FOREST,
CE_MERMAID_THEME.DARK,
CE_MERMAID_THEME.NEUTRAL,
CE_MERMAID_THEME.NULL
],
default: CE_MERMAID_THEME.DARK,
type: "string"
});
return args;
}
// src/modules/containers/keys/SymbolDefaultTemplate.ts
var SymbolDefaultTemplate = Symbol("default-template");
// src/modules/containers/keys/SymbolLogger.ts
var SymbolLogger = Symbol("symbol-logger");
// src/modules/containers/keys/SymbolTemplate.ts
var SymbolTemplate = Symbol("template");
// src/modules/loggers/Logger.ts
import consola6 from "consola";
var _enable;
var Logger = class {
constructor(enable) {
__privateAdd(this, _enable, void 0);
__publicField(this, "info", this.logging.bind(this, "info"));
__publicField(this, "warn", this.logging.bind(this, "warn"));
__publicField(this, "silent", this.logging.bind(this, "silent"));
__publicField(this, "error", this.logging.bind(this, "error"));
__publicField(this, "success", this.logging.bind(this, "success"));
__publicField(this, "fail", this.logging.bind(this, "fail"));
__publicField(this, "fatal", this.logging.bind(this, "fatal"));
__publicField(this, "debug", this.logging.bind(this, "debug"));
__publicField(this, "trace", this.logging.bind(this, "trace"));
__publicField(this, "verbose", this.logging.bind(this, "verbose"));
__publicField(this, "ready", this.logging.bind(this, "ready"));
__publicField(this, "box", this.logging.bind(this, "box"));
__publicField(this, "log", this.logging.bind(this, "log"));
__privateSet(this, _enable, enable ?? false);
}
get enable() {
return __privateGet(this, _enable);
}
set enable(value) {
__privateSet(this, _enable, value);
}
get level() {
return consola6.level;
}
set level(level) {
consola6.level = level;
}
logging(level, message, ...args) {
if (__privateGet(this, _enable)) {
consola6[level](message, ...args);
}
}
};
_enable = new WeakMap();
// src/modules/loggers/createLogger.ts
import { asValue } from "awilix";
function createLogger(enable) {
if (!container.hasRegistration(SymbolLogger)) {
const logger = new Logger(enable ?? false);
container.register(SymbolLogger, asValue(logger));
}
}
// src/templates/modules/configTemplate.ts
var configTemplate = `
{
// directory for output files
"output": "<%= it.config.output %>",
// typeorm dataSourcePath
"data-source-path": "<%= it.config.dataSourceFile %>",
// type of generated document
"components": <%~ JSON.stringify(it.config.components) %>,
// kind of document name
// - db: database name from TypeORM
// - app: application name from package.json
"project-name": "<%= it.config.projectName %>",
// custom template file path. erdia are using [ETA](https://eta.js.org/) template engine
<% if (it.config.templatePath != null) { %>
"template-path": "<%= it.config.templatePath %>",
<% } else { %>
// "template-path": "",
<% } %>
// erdia entity database file path
<% if (it.config.databasePath != null) { %>
"database-path": "<%= it.config.databasePath %>",
<% } else { %>
// "database-path": "",
<% } %>
// erdia entity database file path
<% if (it.config.routeBasePath != null) { %>
"route-base-path": "<%= it.config.routeBasePath %>",
<% } else { %>
// "route-base-path": "",
<% } %>
// document version using package.json version or timestamp
"version-from": "<%= it.config.versionFrom %>",
// If the versionFrom option set file, read the file from this path
<% if (it.config.versionPath != null) { %>
"version-path": "<%= it.config.versionPath %>",
<% } else { %>
// "version-path": "",
<% } %>
// output format of generated documents
// - html
// - md
// - pdf
// - image
"format": "<%= it.config.format %>",
// skip image file attachment in html document
"skipImageInHtml": false,
// mermaid.js plugin theme configuration
// @url https://mermaid-js.github.io/mermaid/#/Setup?id=theme
"theme": "<%= it.config.theme %>",
// prettier config path
// "prettier-config": "set your .prettierrc file path",
// title tag content that inside of html document
// "title": "set title tag content in html document",
// ER diagram width, it will be set width css attribute
// @format pdf, image
<% if (it.config.format === 'pdf' || it.config.format === 'image') { -%>
"width": "100%",
<% } else { -%>
// "width": "100%",
<% } -%>
// puppeteer viewport width
// @format pdf, image
<% if (it.config.format === 'pdf' || it.config.format === 'image') { -%>
"viewport-width": 1280,
<% } else { -%>
// "viewport-width": 1280,
<% } -%>
// puppeteer viewport height
// @format pdf, image
<% if (it.config.format === 'pdf' || it.config.format === 'image') { -%>
"viewport-height": 1440,
<% } else { -%>
// "viewport-height": 1440,
<% } -%>
// puppeteer config file path
// @format pdf, image
// "puppeteer-config-path": "set your puppeteer configuration file path",
// Background color. Example: transparent, red, '#F0F0F0'. Optional. Default: white
// @format pdf, image
// "background-color": "#FFFFFF",
// ER diagram export image file format
// @format image
<% if (it.config.format === 'image') { %>
"image-format": "<%= it.config.imageFormat %>",
<% } else { %>
// "image-format": "svg",
<% } %>
}
`;
// src/templates/modules/getTemplateModulePath.ts
import { exists as exists6 } from "my-node-fp";
import pathe13 from "pathe";
async function getTemplateModulePath(templatePathParam) {
const currentFilePath = pathe13.resolve(__dirname);
if (templatePathParam != null) {
const currentWithTemplatePath = pathe13.resolve(pathe13.join(currentFilePath, templatePathParam));
if (await exists6(currentWithTemplatePath)) {
return currentWithTemplatePath;
}
}
const packageRootTemplatePath = pathe13.resolve(
pathe13.join(currentFilePath, "..", "..", "..", CE_DEFAULT_VALUE.TEMPLATES_PATH)
);
if (await exists6(packageRootTemplatePath)) {
return packageRootTemplatePath;
}
const distTemplatePath = pathe13.resolve(pathe13.join(currentFilePath, "..", "..", CE_DEFAULT_VALUE.TEMPLATES_PATH));
if (await exists6(distTemplatePath)) {
return distTemplatePath;
}
throw new Error("cannot found template directory!");
}
// src/templates/modules/getTemplatePath.ts
import { exists as exists7 } from "my-node-fp";
import pathe14 from "pathe";
async function getTemplatePath(templatePathParam) {
if (templatePathParam != null && await exists7(pathe14.resolve(templatePathParam))) {
return pathe14.resolve(templatePathParam);
}
return getTemplateModulePath(templatePathParam);
}
// src/modules/files/getGlobFiles.ts
import pathe15 from "pathe";
function getGlobFiles(glob) {
const filePathSet = /* @__PURE__ */ new Set();
for (const filePath of glob) {
filePathSet.add(typeof filePath === "string" ? filePath : pathe15.join(filePath.path, filePath.name));
}
return Array.from(filePathSet);
}
// src/modules/scopes/defaultExclude.ts
var defaultExclude = ["node_modules/**", "flow-typed/**", "coverage/**", ".git/**"];
// src/templates/modules/getTemplate.ts
import { isTrue } from "my-easy-fp";
import { basenames, exists as exists8, getDirname as getDirname5 } from "my-node-fp";
import fs8 from "fs";
import pathe16 from "pathe";
async function getTemplate(dirPath, filePath) {
if (isTrue(await exists8(filePath))) {
const buf = await fs8.promises.readFile(filePath);
const relative = pathe16.relative(dirPath, filePath).replace(`.${pathe16.sep}`, "");
const dirname = await getDirname5(relative);
const basename = basenames(relative, [".eta", ".ejs"]);
return { key: pathe16.join(dirname, basename), content: buf.toString() };
}
return void 0;
}
// src/templates/modules/getTemplates.ts
import { Glob } from "glob";
import pathe17 from "pathe";
async function getTemplates(templatePath, globOptions) {
const resolvedTemplatePath = pathe17.resolve(templatePath);
const globs = new Glob(pathe17.join(resolvedTemplatePath, `**`, "*.eta"), {
...globOptions,
absolute: true,
ignore: defaultExclude,
cwd: resolvedTemplatePath,
windowsPathsNoEscape: true
});
const templateFilePaths = getGlobFiles(globs).map((filePath) => [filePath, true]).map(([filePath, _flag]) => filePath);
const loadedTemplateFiles = (await Promise.all(templateFilePaths.map((templateFilePath) => getTemplate(resolvedTemplatePath, templateFilePath)))).filter((template) => template != null);
return loadedTemplateFiles;
}
// src/templates/modules/loadTemplates.ts
import pathe18 from "pathe";
async function loadTemplates(option) {
const defaultTemplatePath = await getTemplatePath(CE_DEFAULT_VALUE.TEMPLATES_PATH);
const [defaultHtml, defaultMarkdown, defaultImage, defaultPdf] = await Promise.all([
getTemplates(pathe18.join(defaultTemplatePath, "html"), {}),
getTemplates(pathe18.join(defaultTemplatePath, "markdown"), {}),
getTemplates(pathe18.join(defaultTemplatePath, "image"), {}),
getTemplates(pathe18.join(defaultTemplatePath, "pdf"), {})
]);
const defaultTemplateMap = new Map([
["config-json", configTemplate.trim()],
...defaultHtml.map((template) => [`html-${template.key}`, template.content]),
...defaultMarkdown.map((template) => [`markdown-${template.key}`, template.content]),
...defaultImage.map((template) => [`image-${template.key}`, template.content]),
...defaultPdf.map((template) => [`pdf-${template.key}`, template.content])
]);
if (option?.templatePath == null) {
return {
default: defaultTemplateMap,
template: defaultTemplateMap
};
}
const templatePath = await getTemplatePath(option.templatePath);
const [templateHtml, templateMarkdown, templateImage, templatePdf] = await Promise.all([
getTemplates(pathe18.join(templatePath, "html"), {}),
getTemplates(pathe18.join(templatePath, "markdown"), {}),
getTemplates(pathe18.join(templatePath, "image"), {}),
getTemplates(pathe18.join(templatePath, "pdf"), {})
]);
const templateMap = new Map([
["config-json", configTemplate.trim()],
...templateHtml.map((template) => [`html-${template.key}`, template.content]),
...templateMarkdown.map((template) => [`markdown-${template.key}`, template.content]),
...templateImage.map((template) => [`image-${template.key}`, template.content]),
...templatePdf.map((template) => [`pdf-${template.key}`, template.content])
]);
return {
default: defaultTemplateMap,
template: templateMap
};
}
// src/configs/const-enum/CE_COLUMN_ATTRIBUTE.ts
var CE_COLUMN_ATTRIBUTE = {
PK: "PK",
FK: "FK",
UK: "UK"
};
// src/creators/columns/getColumnWeight.ts
import { bignumber } from "mathjs";
import { populate } from "my-easy-fp";
function getColumnWeight(column) {
const weight = bignumber(0);
const type = bignumber(
populate(column.columnType.length).reduce((sum, index) => sum + column.columnType.charCodeAt(index), 0)
).mul(1e3);
return weight.add(column.attributeKey.indexOf(CE_COLUMN_ATTRIBUTE.PK) >= 0 ? 2e7 : 0).add(column.attributeKey.indexOf(CE_COLUMN_ATTRIBUTE.FK) >= 0 ? 1e7 : 0).add(type).add(bignumber(122).sub(bignumber(column.name.toLowerCase().charCodeAt(0)))).add(
bignumber(122).sub(bignumber(column.name.toLowerCase().charCodeAt(1))).div(100)
);
}
// src/typeorm/columns/getColumnAttributeKey.ts
import alasql3 from "alasql";
import { atOrUndefined } from "my-easy-fp";
function getColumnAttributeKey(columnMetadata, dbName, tableDBName, indexRecords) {
const indices = alasql3("SELECT * FROM ? WHERE ? = ANY (columnNames) and tableDBName = ?", [
indexRecords,
dbName,
tableDBName
]);
const index = atOrUndefined(indices, 0);
return [
columnMetadata.relationMetadata != null ? CE_COLUMN_ATTRIBUTE.FK : void 0,
columnMetadata.isPrimary ? CE_COLUMN_ATTRIBUTE.PK : void 0,
index?.isUnique ? CE_COLUMN_ATTRIBUTE.UK : void 0
].filter((attribute) => attribute != null);
}
// src/typeorm/columns/getIsNullable.ts
function getIsNullable(metadata) {
if (metadata.isPrimary) {
return "";
}
if (metadata.isNullable === false) {
return "";
}
return "nullable";
}
// src/typeorm/columns/getColumnType.ts
import { isTrue as isTrue2 } from "my-easy-fp";
function getColumnType(columnMetadata, includeLength) {
const nullable = getIsNullable(columnMetadata);
if (typeof columnMetadata.type === "function") {
if (isTrue2(includeLength ?? false) && columnMetadata.length !== "") {
const name3 = columnMetadata.type.name.toString().toLowerCase().replace(/\s/g, "-");
const withNullable3 = nullable === "nullable" ? name3 : `*${name3}`;
return `${withNullable3}(${columnMetadata.length})`;
}
const name2 = columnMetadata.type.name.toString().toLowerCase().replace(/\s/g, "-");
const withNullable2 = nullable === "nullable" ? name2 : `*${name2}`;
return withNullable2;
}
if (isTrue2(includeLength ?? false) && columnMetadata.length !== "") {
const name2 = columnMetadata.type.toString().replace(/\s/g, "-");
const withNullable2 = nullable === "nullable" ? name2 : `*${name2}`;
return `${withNullable2}(${columnMetadata.length})`;
}
const name = columnMetadata.type.toString().replace(/\s/g, "-");
const withNullable = nullable === "nullable" ? name : `*${name}`;
return withNullable;
}
// src/typeorm/columns/getComment.ts
function getComment(option, comment) {
if (comment == null) {
return "";
}
if (option.format === CE_OUTPUT_FORMAT.MARKDOWN) {
return comment.replace(/\r\n/g, "\\\\r\\\\n").replace(/\n\r/g, "\\\\n\\\\r").replace(/\n/g, "\\\\n");
}
if (option.format === CE_OUTPUT_FORMAT.HTML) {
return comment.replace(/\r\n/g, "<br />").replace(/\n\r/g, "<br />").replace(/\n/g, "<br />");
}
return comment.replace(/\r\n/g, "<br />").replace(/\n\r/g, "<br />").replace(/\n/g, "<br />");
}
// src/typeorm/entities/getEntityName.ts
function getEntityName(entityMeta) {
if ("$kind" in entityMeta) {
if (entityMeta.dbName == null || entityMeta.dbName === "") {
return entityMeta.name;
}
return entityMeta.dbName;
}
const { tableName, name } = entityMeta;
if (tableName == null || tableName === "") {
return name;
}
return tableName;
}
// src/typeorm/columns/getColumnRecord.ts
function getColumnRecord(columnMetadata, option, metadata, indices) {
const entityName = getEntityName(columnMetadata.entityM