drizzle-erd
Version:
Entity-relationship diagram (ERD) generator for Drizzle.
183 lines (172 loc) • 4.93 kB
JavaScript
// src/cli.ts
import { writeFileSync } from "fs";
import { Command } from "commander";
// src/utils/logger.ts
var Logger = class {
logStyle;
constructor(logStyle) {
this.logStyle = logStyle ?? "Default";
}
setLogStyle(logStyle) {
this.logStyle = logStyle;
}
createOutput(message, data) {
const output = [`[drizzle-erd]: ${message}`];
if (!data) return output;
if (data instanceof Error) {
output.push(data);
}
try {
output.push(JSON.stringify(data, null, 2));
} catch (_) {
output.push(data);
}
return output;
}
debug(message, data) {
if (this.logStyle === "Verbose") {
const output = this.createOutput(message, data);
console.debug(...output);
}
}
log(message, data) {
if (this.logStyle !== "Silent") {
const output = this.createOutput(message, data);
console.debug(...output);
}
}
error(message, data) {
if (this.logStyle !== "Silent") {
const output = this.createOutput(message, data);
console.error(...output);
}
}
};
var logger = new Logger("Default");
// src/infer.ts
import util from "util";
// src/utils/assert.ts
var AssertionError = class _AssertionError extends Error {
constructor(message) {
super(message);
if (Error.captureStackTrace) {
Error.captureStackTrace(this, _AssertionError);
} else {
this.stack = new Error(message).stack;
}
this.name = "AssertionError";
}
};
function assert(condition, message) {
if (!condition) {
throw new AssertionError(message);
}
}
// src/infer.ts
var onlyOne = (value, array) => array.filter((v) => v === value).length === 1;
var inferSchemaVariant = (schema) => {
const objectDump = util.inspect(schema, { depth: Infinity, getters: true });
const containsPgTable = objectDump.includes("PgTable");
const containsMySqlTable = objectDump.includes("MySqlTable");
const containsSQLiteTable = objectDump.includes("SQLiteTable");
const containsResults = [
containsPgTable,
containsMySqlTable,
containsSQLiteTable
];
assert(
containsResults.some((v) => !!v),
"Could not find any tables with a supported type (PG / MySQL / SQLite)"
);
assert(
onlyOne(true, containsResults),
"Seems to contain multiple table types (PG / MySQL / SQLite)"
);
if (containsPgTable) return "Postgres";
if (containsMySqlTable) return "MySQL";
if (containsSQLiteTable) return "SQLite";
throw new Error("Unable to determine schema type (PG / MySQL / SQLite)");
};
// src/dbml.ts
import {
mysqlGenerate,
pgGenerate,
sqliteGenerate
} from "@rorz/drizzle-dbml-generator";
var generateDbml = (schema, variant, relationMethod) => {
let relational = false;
if (relationMethod === "Explicit") relational = true;
switch (variant) {
case "Postgres":
return pgGenerate({ schema, relational });
case "MySQL":
return mysqlGenerate({ schema, relational });
case "SQLite":
return sqliteGenerate({ schema, relational });
}
};
// src/svg.ts
import { run } from "@rorz/dbml-renderer";
var generateSvg = (dbml) => {
const svg = run(dbml, "svg");
return svg;
};
// src/generate.ts
var generateErd = async ({
schema,
relationMethod = "ForeignKey",
logStyle = "Default"
}) => {
logger.setLogStyle(logStyle);
try {
const schemaVariant = inferSchemaVariant(schema);
logger.debug("\u26AA\uFE0F Generating DBML");
const dbml = generateDbml(schema, schemaVariant, relationMethod);
logger.debug("\u{1F7E2} Generated DBML");
logger.debug("\u26AA\uFE0F Generating SVG");
const svg = generateSvg(dbml);
logger.debug("\u{1F7E2} Generated SVG");
logger.debug("\u2705 ERD generated successfully");
return { dbml, svg };
} catch (error) {
logger.error(
"\u{1F534} Generating ERD failed with error:",
error instanceof Error ? error : { error: "UNKNOWN" }
);
throw error;
}
};
// src/resolve.ts
import { resolve } from "path";
import { require as require2 } from "tsx/cjs/api";
var resolveSchemaAt = async (filepath) => {
const absolutePath = resolve(filepath);
return require2(absolutePath, import.meta.url);
};
// src/cli.ts
var program = new Command();
program.description("Hello, ERD!");
program.option("--in <string>");
program.option("--out <string>");
program.option("--verbose");
program.parse();
var options = program.opts();
var { out: pathOut, in: pathIn, verbose: isVerbose } = options;
assert(
!!pathIn && typeof pathIn === "string",
"Must specify a path for your schema via the '--in' flag"
);
assert(
!!pathOut && typeof pathOut === "string",
"Must specify a path for an output SVG via the '--out' flag"
);
var run2 = async () => {
const schema = await resolveSchemaAt(pathIn);
const { svg } = await generateErd({
schema,
logStyle: isVerbose ? "Verbose" : void 0
});
writeFileSync(pathOut, svg);
};
void run2();