UNPKG

drizzle-erd

Version:

Entity-relationship diagram (ERD) generator for Drizzle.

183 lines (172 loc) 4.93 kB
#!/usr/bin/env node // 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();