drizzle-docs-generator 0.6.1 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +63 -61
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/resolve-schema-exports.d.ts +8 -0
- package/dist/cli/resolve-schema-exports.d.ts.map +1 -0
- package/dist/cli/resolve-schema-exports.js +8 -0
- package/dist/cli/resolve-schema-exports.js.map +1 -0
- package/dist/formatter/dbml.d.ts +8 -0
- package/dist/formatter/dbml.d.ts.map +1 -1
- package/dist/formatter/dbml.js +40 -24
- package/dist/formatter/dbml.js.map +1 -1
- package/package.json +3 -3
package/dist/cli/index.js
CHANGED
|
@@ -1,37 +1,39 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { Command as
|
|
3
|
-
import { readFileSync as
|
|
2
|
+
import { Command as q } from "commander";
|
|
3
|
+
import { readFileSync as G, existsSync as m, mkdirSync as w, writeFileSync as f, watch as j, lstatSync as O, readdirSync as P } from "node:fs";
|
|
4
4
|
import { join as u, dirname as g, resolve as F } from "node:path";
|
|
5
5
|
import { pathToFileURL as k } from "node:url";
|
|
6
|
-
import { PgGenerator as
|
|
7
|
-
import { MySqlGenerator as
|
|
8
|
-
import { SqliteGenerator as
|
|
6
|
+
import { PgGenerator as z, pgGenerate as C } from "../generator/pg.js";
|
|
7
|
+
import { MySqlGenerator as R, mysqlGenerate as T } from "../generator/mysql.js";
|
|
8
|
+
import { SqliteGenerator as W, sqliteGenerate as N } from "../generator/sqlite.js";
|
|
9
9
|
import { MarkdownFormatter as E } from "../formatter/markdown.js";
|
|
10
10
|
import { MermaidErDiagramFormatter as x } from "../formatter/mermaid.js";
|
|
11
|
-
import { register as
|
|
12
|
-
|
|
11
|
+
import { register as U } from "tsx/esm/api";
|
|
12
|
+
import { register as I } from "tsx/cjs/api";
|
|
13
|
+
import { resolveSchemaExports as b } from "./resolve-schema-exports.js";
|
|
14
|
+
const L = U(), B = I(), p = new q(), J = u(import.meta.dirname, "../../package.json"), $ = JSON.parse(G(J, "utf-8"));
|
|
13
15
|
p.name("drizzle-docs").description($.description).version($.version);
|
|
14
|
-
function
|
|
16
|
+
function M(r) {
|
|
15
17
|
switch (r) {
|
|
16
18
|
case "mysql":
|
|
17
|
-
return
|
|
19
|
+
return T;
|
|
18
20
|
case "sqlite":
|
|
19
|
-
return
|
|
21
|
+
return N;
|
|
20
22
|
default:
|
|
21
|
-
return
|
|
23
|
+
return C;
|
|
22
24
|
}
|
|
23
25
|
}
|
|
24
|
-
function
|
|
26
|
+
function D(r) {
|
|
25
27
|
switch (r) {
|
|
26
28
|
case "mysql":
|
|
27
|
-
return
|
|
29
|
+
return R;
|
|
28
30
|
case "sqlite":
|
|
29
|
-
return
|
|
31
|
+
return W;
|
|
30
32
|
default:
|
|
31
|
-
return
|
|
33
|
+
return z;
|
|
32
34
|
}
|
|
33
35
|
}
|
|
34
|
-
function
|
|
36
|
+
function A(r, e) {
|
|
35
37
|
const o = [];
|
|
36
38
|
if (!m(r))
|
|
37
39
|
return o;
|
|
@@ -43,47 +45,47 @@ function L(r, e) {
|
|
|
43
45
|
}
|
|
44
46
|
return o;
|
|
45
47
|
}
|
|
46
|
-
async function
|
|
47
|
-
const o = k(r).href, t = e.watch ? `?t=${Date.now()}` : "", a = await import(o + t);
|
|
48
|
+
async function V(r, e) {
|
|
49
|
+
const o = k(r).href, t = e.watch ? `?t=${Date.now()}` : "", a = await import(o + t), n = b(a);
|
|
48
50
|
if (e.format === "markdown") {
|
|
49
|
-
const
|
|
50
|
-
schema:
|
|
51
|
+
const s = D(e.dialect), i = new s({
|
|
52
|
+
schema: n,
|
|
51
53
|
source: r
|
|
52
54
|
}).toIntermediateSchema();
|
|
53
55
|
if (!e.singleFile && e.output) {
|
|
54
|
-
|
|
56
|
+
S(i, e.output, e);
|
|
55
57
|
return;
|
|
56
58
|
}
|
|
57
|
-
return h(
|
|
59
|
+
return h(i, e);
|
|
58
60
|
} else
|
|
59
|
-
return
|
|
60
|
-
schema:
|
|
61
|
+
return M(e.dialect)({
|
|
62
|
+
schema: n,
|
|
61
63
|
source: r
|
|
62
64
|
});
|
|
63
65
|
}
|
|
64
|
-
function
|
|
66
|
+
function v(r) {
|
|
65
67
|
const e = [];
|
|
66
68
|
try {
|
|
67
|
-
const o =
|
|
69
|
+
const o = P(r, { withFileTypes: !0 });
|
|
68
70
|
for (const t of o) {
|
|
69
71
|
const a = u(r, t.name);
|
|
70
|
-
t.isDirectory() ? e.push(...
|
|
72
|
+
t.isDirectory() ? e.push(...v(a)) : t.isFile() && /\.(ts|js|mts|mjs|cts|cjs)$/.test(t.name) && e.push(a);
|
|
71
73
|
}
|
|
72
74
|
} catch (o) {
|
|
73
75
|
o instanceof Error && console.error(`Error reading directory ${r}: ${o.message}`);
|
|
74
76
|
}
|
|
75
77
|
return e;
|
|
76
78
|
}
|
|
77
|
-
function
|
|
79
|
+
function H(r) {
|
|
78
80
|
const e = F(process.cwd(), r);
|
|
79
81
|
if (m(e) || (console.error(`Error: Schema path not found: ${e}`), process.exit(1)), O(e).isDirectory()) {
|
|
80
|
-
const t =
|
|
82
|
+
const t = v(e);
|
|
81
83
|
return t.length === 0 && (console.error(`Error: No schema files found in directory: ${e}`), process.exit(1)), t;
|
|
82
84
|
}
|
|
83
85
|
return [e];
|
|
84
86
|
}
|
|
85
|
-
function
|
|
86
|
-
return
|
|
87
|
+
function K(r, e, o) {
|
|
88
|
+
return M(o.dialect)({
|
|
87
89
|
schema: r,
|
|
88
90
|
source: e[0]
|
|
89
91
|
}) || "";
|
|
@@ -106,13 +108,13 @@ ${n}
|
|
|
106
108
|
}
|
|
107
109
|
return t;
|
|
108
110
|
}
|
|
109
|
-
function
|
|
111
|
+
function S(r, e, o) {
|
|
110
112
|
w(e, { recursive: !0 });
|
|
111
113
|
const t = new E({ linkFormat: "file" });
|
|
112
114
|
let n = `${t.generateIndex(r)}
|
|
113
115
|
`;
|
|
114
116
|
if (o.erDiagram) {
|
|
115
|
-
const
|
|
117
|
+
const c = new x({
|
|
116
118
|
includeColumns: o.columns
|
|
117
119
|
}).format(r);
|
|
118
120
|
n += `
|
|
@@ -121,56 +123,56 @@ function q(r, e, o) {
|
|
|
121
123
|
## ER Diagram
|
|
122
124
|
|
|
123
125
|
\`\`\`mermaid
|
|
124
|
-
${
|
|
126
|
+
${c}
|
|
125
127
|
\`\`\`
|
|
126
128
|
`;
|
|
127
129
|
}
|
|
128
130
|
f(u(e, "README.md"), n, "utf-8");
|
|
129
|
-
for (const
|
|
130
|
-
const
|
|
131
|
-
f(u(e, i), `# ${
|
|
131
|
+
for (const s of r.tables) {
|
|
132
|
+
const c = t.generateTableDoc(s, r), i = `${s.name}.md`;
|
|
133
|
+
f(u(e, i), `# ${s.name}
|
|
132
134
|
|
|
133
|
-
${
|
|
135
|
+
${c}
|
|
134
136
|
`, "utf-8");
|
|
135
137
|
}
|
|
136
138
|
}
|
|
137
|
-
function
|
|
139
|
+
function Q(r, e) {
|
|
138
140
|
const o = g(e);
|
|
139
141
|
w(o, { recursive: !0 }), f(e, r.endsWith(`
|
|
140
142
|
`) ? r : r + `
|
|
141
143
|
`, "utf-8");
|
|
142
144
|
}
|
|
143
|
-
async function
|
|
144
|
-
const o =
|
|
145
|
+
async function X(r, e) {
|
|
146
|
+
const o = H(r);
|
|
145
147
|
try {
|
|
146
148
|
const t = {};
|
|
147
149
|
for (const a of o) {
|
|
148
|
-
const n = k(a).href,
|
|
150
|
+
const n = k(a).href, s = e.watch ? `?t=${Date.now()}` : "";
|
|
149
151
|
try {
|
|
150
|
-
const
|
|
151
|
-
Object.assign(t,
|
|
152
|
-
} catch (
|
|
153
|
-
throw
|
|
154
|
-
Possible causes:`), console.error("- Syntax error in the schema file"), console.error("- Missing dependencies (make sure drizzle-orm is installed)"), console.error("- Circular dependencies between schema files")),
|
|
152
|
+
const c = await import(n + s);
|
|
153
|
+
Object.assign(t, b(c));
|
|
154
|
+
} catch (c) {
|
|
155
|
+
throw c instanceof Error && (console.error(`Error importing ${a}: ${c.message}`), console.error(`
|
|
156
|
+
Possible causes:`), console.error("- Syntax error in the schema file"), console.error("- Missing dependencies (make sure drizzle-orm is installed)"), console.error("- Circular dependencies between schema files")), c;
|
|
155
157
|
}
|
|
156
158
|
}
|
|
157
159
|
if (e.format === "markdown") {
|
|
158
|
-
const a =
|
|
160
|
+
const a = D(e.dialect), n = o[0];
|
|
159
161
|
if (!n)
|
|
160
162
|
throw new Error("No schema files found");
|
|
161
|
-
const
|
|
163
|
+
const s = o.length === 1 ? n : g(n), i = new a({
|
|
162
164
|
schema: t,
|
|
163
|
-
source:
|
|
165
|
+
source: s
|
|
164
166
|
}).toIntermediateSchema();
|
|
165
167
|
if (e.singleFile) {
|
|
166
168
|
const l = h(i, e);
|
|
167
169
|
e.output ? (!e.force && m(e.output) && (console.error(
|
|
168
170
|
`Error: Output file already exists: ${e.output}
|
|
169
171
|
Use --force to overwrite existing files.`
|
|
170
|
-
), process.exit(1)),
|
|
172
|
+
), process.exit(1)), Q(l, e.output), console.log(`Markdown generated: ${e.output}`)) : console.log(l);
|
|
171
173
|
} else if (e.output) {
|
|
172
174
|
if (!e.force) {
|
|
173
|
-
const l = i.tables.map((d) => d.name), y =
|
|
175
|
+
const l = i.tables.map((d) => d.name), y = A(e.output, l);
|
|
174
176
|
y.length > 0 && (console.error(
|
|
175
177
|
`Error: The following files already exist:
|
|
176
178
|
${y.map((d) => ` - ${d}`).join(`
|
|
@@ -178,13 +180,13 @@ ${y.map((d) => ` - ${d}`).join(`
|
|
|
178
180
|
Use --force to overwrite existing files.`
|
|
179
181
|
), process.exit(1));
|
|
180
182
|
}
|
|
181
|
-
|
|
183
|
+
S(i, e.output, e), console.log(`Markdown generated: ${e.output}/`);
|
|
182
184
|
} else {
|
|
183
185
|
const l = h(i, e);
|
|
184
186
|
console.log(l);
|
|
185
187
|
}
|
|
186
188
|
} else {
|
|
187
|
-
const a =
|
|
189
|
+
const a = K(t, o, e);
|
|
188
190
|
if (e.output) {
|
|
189
191
|
!e.force && m(e.output) && (console.error(
|
|
190
192
|
`Error: Output file already exists: ${e.output}
|
|
@@ -201,21 +203,21 @@ Use --force to overwrite existing files.`
|
|
|
201
203
|
t instanceof Error ? console.error(`Error generating output: ${t.message}`) : console.error("Error generating output:", t), process.exit(1);
|
|
202
204
|
}
|
|
203
205
|
}
|
|
204
|
-
function
|
|
206
|
+
function Y(r, e) {
|
|
205
207
|
const o = F(process.cwd(), r);
|
|
206
208
|
console.log(`Watching for changes: ${o}`);
|
|
207
209
|
let t = null;
|
|
208
|
-
|
|
210
|
+
j(o, async (a) => {
|
|
209
211
|
a === "change" && (t && clearTimeout(t), t = setTimeout(async () => {
|
|
210
212
|
console.log(`
|
|
211
213
|
File changed, regenerating...`);
|
|
212
214
|
try {
|
|
213
|
-
const n = await
|
|
215
|
+
const n = await V(o, e), s = e.format === "markdown" ? "Markdown" : "DBML", c = e.format === "markdown" && !e.singleFile && e.output;
|
|
214
216
|
if (!e.output && n)
|
|
215
217
|
console.log(n);
|
|
216
218
|
else if (e.output) {
|
|
217
|
-
const i =
|
|
218
|
-
console.log(`${
|
|
219
|
+
const i = c ? `${e.output}/` : e.output;
|
|
220
|
+
console.log(`${s} regenerated: ${i}`);
|
|
219
221
|
}
|
|
220
222
|
} catch (n) {
|
|
221
223
|
n instanceof Error ? console.error(`Error: ${n.message}`) : console.error("Error:", n);
|
|
@@ -231,10 +233,10 @@ p.command("generate").description("Generate documentation from Drizzle schema fi
|
|
|
231
233
|
const t = ["dbml", "markdown"];
|
|
232
234
|
t.includes(e.format) || (console.error(
|
|
233
235
|
`Error: Invalid format "${e.format}". Valid options: ${t.join(", ")}`
|
|
234
|
-
), process.exit(1)), e.format !== "markdown" && (e.singleFile && console.warn("Warning: --single-file is only applicable with --format markdown"), e.erDiagram || console.warn("Warning: --no-er-diagram is only applicable with --format markdown"), e.columns || console.warn("Warning: --no-columns is only applicable with --format markdown")), await
|
|
236
|
+
), process.exit(1)), e.format !== "markdown" && (e.singleFile && console.warn("Warning: --single-file is only applicable with --format markdown"), e.erDiagram || console.warn("Warning: --no-er-diagram is only applicable with --format markdown"), e.columns || console.warn("Warning: --no-columns is only applicable with --format markdown")), await X(r, e), e.watch && Y(r, e);
|
|
235
237
|
});
|
|
236
238
|
p.parse();
|
|
237
239
|
process.on("exit", () => {
|
|
238
|
-
|
|
240
|
+
L(), B();
|
|
239
241
|
});
|
|
240
242
|
//# sourceMappingURL=index.js.map
|
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * CLI for drizzle-docs-generator\n *\n * Generates DBML files from Drizzle ORM schema definitions.\n */\n\nimport { Command } from \"commander\";\nimport {\n existsSync,\n lstatSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n watch,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { pgGenerate } from \"../generator/pg\";\nimport { mysqlGenerate } from \"../generator/mysql\";\nimport { sqliteGenerate } from \"../generator/sqlite\";\nimport { PgGenerator } from \"../generator/pg\";\nimport { MySqlGenerator } from \"../generator/mysql\";\nimport { SqliteGenerator } from \"../generator/sqlite\";\nimport { MarkdownFormatter } from \"../formatter/markdown\";\nimport { MermaidErDiagramFormatter } from \"../formatter/mermaid\";\nimport { register } from \"tsx/esm/api\";\nimport type { IntermediateSchema } from \"../types\";\n\n// Register tsx loader to support TypeScript and extensionless imports\nconst unregister = register();\n\nconst program = new Command();\n\n// Use import.meta.dirname (Node 20.11+) to resolve package.json\n// This works correctly even after bundling\nconst packageJsonPath = join(import.meta.dirname, \"../../package.json\");\nconst packageJson = JSON.parse(readFileSync(packageJsonPath, \"utf-8\")) as {\n version: string;\n description: string;\n};\n\nprogram.name(\"drizzle-docs\").description(packageJson.description).version(packageJson.version);\n\ntype Dialect = \"postgresql\" | \"mysql\" | \"sqlite\";\ntype OutputFormat = \"dbml\" | \"markdown\";\n\ninterface GenerateCommandOptions {\n output?: string;\n dialect: Dialect;\n watch?: boolean;\n format: OutputFormat;\n singleFile?: boolean;\n erDiagram: boolean; // commander uses --no-er-diagram which sets erDiagram to false\n columns: boolean; // commander uses --no-columns which sets columns to false\n force?: boolean; // skip overwrite confirmation for existing files\n}\n\n/**\n * Get the generate function based on dialect\n */\nfunction getGenerateFunction(dialect: Dialect) {\n switch (dialect) {\n case \"mysql\":\n return mysqlGenerate;\n case \"sqlite\":\n return sqliteGenerate;\n case \"postgresql\":\n default:\n return pgGenerate;\n }\n}\n\n/**\n * Get the generator class based on dialect\n */\nfunction getGeneratorClass(dialect: Dialect) {\n switch (dialect) {\n case \"mysql\":\n return MySqlGenerator;\n case \"sqlite\":\n return SqliteGenerator;\n case \"postgresql\":\n default:\n return PgGenerator;\n }\n}\n\n/**\n * Check if output directory has existing files\n */\nfunction hasExistingFiles(outputDir: string, tableNames: string[]): string[] {\n const existingFiles: string[] = [];\n\n if (!existsSync(outputDir)) {\n return existingFiles;\n }\n\n const readmePath = join(outputDir, \"README.md\");\n if (existsSync(readmePath)) {\n existingFiles.push(readmePath);\n }\n\n for (const tableName of tableNames) {\n const tablePath = join(outputDir, `${tableName}.md`);\n if (existsSync(tablePath)) {\n existingFiles.push(tablePath);\n }\n }\n\n return existingFiles;\n}\n\n/**\n * Generate output from a schema file (for watch mode)\n * Returns the generated output string for single-file formats,\n * or writes multiple files directly for multi-file markdown\n */\nasync function generateFromSchema(\n schemaPath: string,\n options: GenerateCommandOptions,\n): Promise<string | undefined> {\n // Use file URL for dynamic import (required for ESM)\n const schemaUrl = pathToFileURL(schemaPath).href;\n\n // Dynamic import with cache busting for watch mode\n const cacheBuster = options.watch ? `?t=${Date.now()}` : \"\";\n const schemaModule = (await import(schemaUrl + cacheBuster)) as Record<string, unknown>;\n\n if (options.format === \"markdown\") {\n const GeneratorClass = getGeneratorClass(options.dialect);\n const generator = new GeneratorClass({\n schema: schemaModule,\n source: schemaPath,\n });\n const intermediateSchema = generator.toIntermediateSchema();\n\n // Handle multi-file output in watch mode\n if (!options.singleFile && options.output) {\n writeMarkdownMultipleFiles(intermediateSchema, options.output, options);\n return undefined;\n }\n\n return generateMarkdownOutput(intermediateSchema, options);\n } else {\n const generate = getGenerateFunction(options.dialect);\n return generate({\n schema: schemaModule,\n source: schemaPath,\n });\n }\n}\n\n/**\n * Find all TypeScript schema files in a directory\n */\nfunction findSchemaFiles(dirPath: string): string[] {\n const files: string[] = [];\n\n try {\n const entries = readdirSync(dirPath, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = join(dirPath, entry.name);\n\n if (entry.isDirectory()) {\n // Recursively search subdirectories\n files.push(...findSchemaFiles(fullPath));\n } else if (entry.isFile() && /\\.(ts|js|mts|mjs|cts|cjs)$/.test(entry.name)) {\n files.push(fullPath);\n }\n }\n } catch (error) {\n if (error instanceof Error) {\n console.error(`Error reading directory ${dirPath}: ${error.message}`);\n }\n }\n\n return files;\n}\n\n/**\n * Resolve schema path (file or directory)\n */\nfunction resolveSchemaPath(schema: string): string[] {\n const schemaPath = resolve(process.cwd(), schema);\n\n // Check if path exists\n if (!existsSync(schemaPath)) {\n console.error(`Error: Schema path not found: ${schemaPath}`);\n process.exit(1);\n }\n\n // Check if it's a directory\n const stats = lstatSync(schemaPath);\n if (stats.isDirectory()) {\n const schemaFiles = findSchemaFiles(schemaPath);\n if (schemaFiles.length === 0) {\n console.error(`Error: No schema files found in directory: ${schemaPath}`);\n process.exit(1);\n }\n return schemaFiles;\n }\n\n // Single file\n return [schemaPath];\n}\n\n/**\n * Generate DBML format output\n */\nfunction generateDbmlOutput(\n mergedSchema: Record<string, unknown>,\n schemaPaths: string[],\n options: GenerateCommandOptions,\n): string {\n const generate = getGenerateFunction(options.dialect);\n return (\n generate({\n schema: mergedSchema,\n source: schemaPaths[0],\n }) || \"\"\n );\n}\n\n/**\n * Generate Markdown format output\n */\nfunction generateMarkdownOutput(\n intermediateSchema: IntermediateSchema,\n options: GenerateCommandOptions,\n): string {\n const markdownFormatter = new MarkdownFormatter();\n const markdown = markdownFormatter.format(intermediateSchema);\n\n // Include ER diagram unless --no-er-diagram is specified\n if (options.erDiagram) {\n const mermaidFormatter = new MermaidErDiagramFormatter({\n includeColumns: options.columns,\n });\n const erDiagram = mermaidFormatter.format(intermediateSchema);\n\n return `${markdown}\\n\\n---\\n\\n## ER Diagram\\n\\n\\`\\`\\`mermaid\\n${erDiagram}\\n\\`\\`\\``;\n }\n\n return markdown;\n}\n\n/**\n * Write Markdown to multiple files (one per table)\n */\nfunction writeMarkdownMultipleFiles(\n intermediateSchema: IntermediateSchema,\n outputDir: string,\n options: GenerateCommandOptions,\n): void {\n // Ensure output directory exists\n mkdirSync(outputDir, { recursive: true });\n\n const markdownFormatter = new MarkdownFormatter({ linkFormat: \"file\" });\n\n // Write README.md with index\n const index = markdownFormatter.generateIndex(intermediateSchema);\n let readme = `${index}\\n`;\n\n // Add ER diagram to README unless disabled\n if (options.erDiagram) {\n const mermaidFormatter = new MermaidErDiagramFormatter({\n includeColumns: options.columns,\n });\n const erDiagram = mermaidFormatter.format(intermediateSchema);\n readme += `\\n---\\n\\n## ER Diagram\\n\\n\\`\\`\\`mermaid\\n${erDiagram}\\n\\`\\`\\`\\n`;\n }\n\n writeFileSync(join(outputDir, \"README.md\"), readme, \"utf-8\");\n\n // Write individual table files\n for (const table of intermediateSchema.tables) {\n const tableDoc = markdownFormatter.generateTableDoc(table, intermediateSchema);\n const fileName = `${table.name}.md`;\n writeFileSync(join(outputDir, fileName), `# ${table.name}\\n\\n${tableDoc}\\n`, \"utf-8\");\n }\n}\n\n/**\n * Write single Markdown file\n */\nfunction writeSingleMarkdownFile(content: string, outputPath: string): void {\n const dir = dirname(outputPath);\n mkdirSync(dir, { recursive: true });\n writeFileSync(outputPath, content.endsWith(\"\\n\") ? content : content + \"\\n\", \"utf-8\");\n}\n\n/**\n * Run the generate command\n */\nasync function runGenerate(schema: string, options: GenerateCommandOptions): Promise<void> {\n const schemaPaths = resolveSchemaPath(schema);\n\n try {\n // Merge all schema modules\n const mergedSchema: Record<string, unknown> = {};\n\n for (const schemaPath of schemaPaths) {\n const schemaUrl = pathToFileURL(schemaPath).href;\n const cacheBuster = options.watch ? `?t=${Date.now()}` : \"\";\n\n try {\n const schemaModule = (await import(schemaUrl + cacheBuster)) as Record<string, unknown>;\n Object.assign(mergedSchema, schemaModule);\n } catch (error) {\n if (error instanceof Error) {\n console.error(`Error importing ${schemaPath}: ${error.message}`);\n console.error(\"\\nPossible causes:\");\n console.error(\"- Syntax error in the schema file\");\n console.error(\"- Missing dependencies (make sure drizzle-orm is installed)\");\n console.error(\"- Circular dependencies between schema files\");\n }\n throw error;\n }\n }\n\n if (options.format === \"markdown\") {\n // Generate Markdown format\n const GeneratorClass = getGeneratorClass(options.dialect);\n // For multiple files, pass the directory path to extract comments from all files\n const firstFilePath = schemaPaths[0];\n if (!firstFilePath) {\n throw new Error(\"No schema files found\");\n }\n const sourcePath = schemaPaths.length === 1 ? firstFilePath : dirname(firstFilePath);\n const generator = new GeneratorClass({\n schema: mergedSchema,\n source: sourcePath,\n });\n const intermediateSchema = generator.toIntermediateSchema();\n\n if (options.singleFile) {\n // Single file Markdown output\n const content = generateMarkdownOutput(intermediateSchema, options);\n\n if (options.output) {\n // Check for existing file if --force is not specified\n if (!options.force && existsSync(options.output)) {\n console.error(\n `Error: Output file already exists: ${options.output}\\nUse --force to overwrite existing files.`,\n );\n process.exit(1);\n }\n writeSingleMarkdownFile(content, options.output);\n console.log(`Markdown generated: ${options.output}`);\n } else {\n console.log(content);\n }\n } else {\n // Multiple files Markdown output\n if (!options.output) {\n // If no output specified, default to stdout with single file format\n const content = generateMarkdownOutput(intermediateSchema, options);\n console.log(content);\n } else {\n // Check for existing files if --force is not specified\n if (!options.force) {\n const tableNames = intermediateSchema.tables.map((t) => t.name);\n const existingFiles = hasExistingFiles(options.output, tableNames);\n if (existingFiles.length > 0) {\n console.error(\n `Error: The following files already exist:\\n${existingFiles.map((f) => ` - ${f}`).join(\"\\n\")}\\nUse --force to overwrite existing files.`,\n );\n process.exit(1);\n }\n }\n writeMarkdownMultipleFiles(intermediateSchema, options.output, options);\n console.log(`Markdown generated: ${options.output}/`);\n }\n }\n } else {\n // Generate DBML format\n const dbml = generateDbmlOutput(mergedSchema, schemaPaths, options);\n\n if (options.output) {\n // Check for existing file if --force is not specified\n if (!options.force && existsSync(options.output)) {\n console.error(\n `Error: Output file already exists: ${options.output}\\nUse --force to overwrite existing files.`,\n );\n process.exit(1);\n }\n const dir = dirname(options.output);\n mkdirSync(dir, { recursive: true });\n writeFileSync(options.output, dbml.endsWith(\"\\n\") ? dbml : dbml + \"\\n\", \"utf-8\");\n console.log(`DBML generated: ${options.output}`);\n } else {\n console.log(dbml);\n }\n }\n } catch (error) {\n if (error instanceof Error) {\n console.error(`Error generating output: ${error.message}`);\n } else {\n console.error(\"Error generating output:\", error);\n }\n process.exit(1);\n }\n}\n\n/**\n * Watch mode: regenerate on file changes\n */\nfunction watchSchema(schema: string, options: GenerateCommandOptions): void {\n const schemaPath = resolve(process.cwd(), schema);\n\n console.log(`Watching for changes: ${schemaPath}`);\n\n let debounceTimer: NodeJS.Timeout | null = null;\n\n watch(schemaPath, async (eventType) => {\n if (eventType === \"change\") {\n // Debounce to avoid multiple triggers\n if (debounceTimer) {\n clearTimeout(debounceTimer);\n }\n\n debounceTimer = setTimeout(async () => {\n console.log(\"\\nFile changed, regenerating...\");\n try {\n const output = await generateFromSchema(schemaPath, options);\n const formatLabel = options.format === \"markdown\" ? \"Markdown\" : \"DBML\";\n const isMultiFile =\n options.format === \"markdown\" && !options.singleFile && options.output;\n\n if (!options.output && output) {\n console.log(output);\n } else if (options.output) {\n const outputLabel = isMultiFile ? `${options.output}/` : options.output;\n console.log(`${formatLabel} regenerated: ${outputLabel}`);\n }\n } catch (error) {\n if (error instanceof Error) {\n console.error(`Error: ${error.message}`);\n } else {\n console.error(\"Error:\", error);\n }\n }\n }, 100);\n }\n });\n}\n\nprogram\n .command(\"generate\")\n .description(\"Generate documentation from Drizzle schema files\")\n .argument(\"<schema>\", \"Path to Drizzle schema file or directory\")\n .option(\"-o, --output <path>\", \"Output file or directory path\")\n .option(\"-d, --dialect <dialect>\", \"Database dialect (postgresql, mysql, sqlite)\", \"postgresql\")\n .option(\"-f, --format <format>\", \"Output format (dbml, markdown)\", \"markdown\")\n .option(\"-w, --watch\", \"Watch for file changes and regenerate\")\n .option(\"--single-file\", \"Output Markdown as a single file (for markdown format)\")\n .option(\"--no-er-diagram\", \"Exclude ER diagram from Markdown output\")\n .option(\"--no-columns\", \"Exclude columns from Mermaid ER diagram\")\n .option(\"--force\", \"Overwrite existing files without confirmation\")\n .action(async (schema: string, options: GenerateCommandOptions) => {\n // Validate dialect\n const validDialects: Dialect[] = [\"postgresql\", \"mysql\", \"sqlite\"];\n if (!validDialects.includes(options.dialect)) {\n console.error(\n `Error: Invalid dialect \"${options.dialect}\". Valid options: ${validDialects.join(\", \")}`,\n );\n process.exit(1);\n }\n\n // Validate format\n const validFormats: OutputFormat[] = [\"dbml\", \"markdown\"];\n if (!validFormats.includes(options.format)) {\n console.error(\n `Error: Invalid format \"${options.format}\". Valid options: ${validFormats.join(\", \")}`,\n );\n process.exit(1);\n }\n\n // Warn if --single-file, --no-er-diagram, or --no-columns used with non-markdown format\n if (options.format !== \"markdown\") {\n if (options.singleFile) {\n console.warn(\"Warning: --single-file is only applicable with --format markdown\");\n }\n if (!options.erDiagram) {\n console.warn(\"Warning: --no-er-diagram is only applicable with --format markdown\");\n }\n if (!options.columns) {\n console.warn(\"Warning: --no-columns is only applicable with --format markdown\");\n }\n }\n\n // Initial generation\n await runGenerate(schema, options);\n\n // Start watch mode if requested\n if (options.watch) {\n watchSchema(schema, options);\n }\n });\n\nprogram.parse();\n\n// Cleanup: Unregister tsx loader when process exits\nprocess.on(\"exit\", () => {\n unregister();\n});\n"],"names":["unregister","register","program","Command","packageJsonPath","join","packageJson","readFileSync","getGenerateFunction","dialect","mysqlGenerate","sqliteGenerate","pgGenerate","getGeneratorClass","MySqlGenerator","SqliteGenerator","PgGenerator","hasExistingFiles","outputDir","tableNames","existingFiles","existsSync","readmePath","tableName","tablePath","generateFromSchema","schemaPath","options","schemaUrl","pathToFileURL","cacheBuster","schemaModule","GeneratorClass","intermediateSchema","writeMarkdownMultipleFiles","generateMarkdownOutput","findSchemaFiles","dirPath","files","entries","readdirSync","entry","fullPath","error","resolveSchemaPath","schema","resolve","lstatSync","schemaFiles","generateDbmlOutput","mergedSchema","schemaPaths","markdown","MarkdownFormatter","erDiagram","MermaidErDiagramFormatter","mkdirSync","markdownFormatter","readme","writeFileSync","table","tableDoc","fileName","writeSingleMarkdownFile","content","outputPath","dir","dirname","runGenerate","firstFilePath","sourcePath","t","f","dbml","watchSchema","debounceTimer","watch","eventType","output","formatLabel","isMultiFile","outputLabel","validDialects","validFormats"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * CLI for drizzle-docs-generator\n *\n * Generates DBML files from Drizzle ORM schema definitions.\n */\n\nimport { Command } from \"commander\";\nimport {\n existsSync,\n lstatSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n watch,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { pgGenerate } from \"../generator/pg\";\nimport { mysqlGenerate } from \"../generator/mysql\";\nimport { sqliteGenerate } from \"../generator/sqlite\";\nimport { PgGenerator } from \"../generator/pg\";\nimport { MySqlGenerator } from \"../generator/mysql\";\nimport { SqliteGenerator } from \"../generator/sqlite\";\nimport { MarkdownFormatter } from \"../formatter/markdown\";\nimport { MermaidErDiagramFormatter } from \"../formatter/mermaid\";\nimport { register } from \"tsx/esm/api\";\nimport { register as registerCjs } from \"tsx/cjs/api\";\nimport type { IntermediateSchema } from \"../types\";\nimport { resolveSchemaExports } from \"./resolve-schema-exports\";\n\n// Register both ESM and CJS tsx loader hooks.\n// ESM hooks handle import() calls, CJS hooks handle require() calls.\n// Both are needed to prevent \"Cannot require() ES Module in a cycle\" errors\n// that occur in ESM projects (e.g., NestJS) where require(esm) creates cycles.\nconst unregister = register();\nconst unregisterCjs = registerCjs();\n\nconst program = new Command();\n\n// Use import.meta.dirname (Node 20.11+) to resolve package.json\n// This works correctly even after bundling\nconst packageJsonPath = join(import.meta.dirname, \"../../package.json\");\nconst packageJson = JSON.parse(readFileSync(packageJsonPath, \"utf-8\")) as {\n version: string;\n description: string;\n};\n\nprogram.name(\"drizzle-docs\").description(packageJson.description).version(packageJson.version);\n\ntype Dialect = \"postgresql\" | \"mysql\" | \"sqlite\";\ntype OutputFormat = \"dbml\" | \"markdown\";\n\ninterface GenerateCommandOptions {\n output?: string;\n dialect: Dialect;\n watch?: boolean;\n format: OutputFormat;\n singleFile?: boolean;\n erDiagram: boolean; // commander uses --no-er-diagram which sets erDiagram to false\n columns: boolean; // commander uses --no-columns which sets columns to false\n force?: boolean; // skip overwrite confirmation for existing files\n}\n\n/**\n * Get the generate function based on dialect\n */\nfunction getGenerateFunction(dialect: Dialect) {\n switch (dialect) {\n case \"mysql\":\n return mysqlGenerate;\n case \"sqlite\":\n return sqliteGenerate;\n case \"postgresql\":\n default:\n return pgGenerate;\n }\n}\n\n/**\n * Get the generator class based on dialect\n */\nfunction getGeneratorClass(dialect: Dialect) {\n switch (dialect) {\n case \"mysql\":\n return MySqlGenerator;\n case \"sqlite\":\n return SqliteGenerator;\n case \"postgresql\":\n default:\n return PgGenerator;\n }\n}\n\n/**\n * Check if output directory has existing files\n */\nfunction hasExistingFiles(outputDir: string, tableNames: string[]): string[] {\n const existingFiles: string[] = [];\n\n if (!existsSync(outputDir)) {\n return existingFiles;\n }\n\n const readmePath = join(outputDir, \"README.md\");\n if (existsSync(readmePath)) {\n existingFiles.push(readmePath);\n }\n\n for (const tableName of tableNames) {\n const tablePath = join(outputDir, `${tableName}.md`);\n if (existsSync(tablePath)) {\n existingFiles.push(tablePath);\n }\n }\n\n return existingFiles;\n}\n\n/**\n * Generate output from a schema file (for watch mode)\n * Returns the generated output string for single-file formats,\n * or writes multiple files directly for multi-file markdown\n */\nasync function generateFromSchema(\n schemaPath: string,\n options: GenerateCommandOptions,\n): Promise<string | undefined> {\n // Use file URL for dynamic import (required for ESM)\n const schemaUrl = pathToFileURL(schemaPath).href;\n\n // Dynamic import with cache busting for watch mode\n const cacheBuster = options.watch ? `?t=${Date.now()}` : \"\";\n const rawModule = (await import(schemaUrl + cacheBuster)) as Record<string, unknown>;\n const schemaModule = resolveSchemaExports(rawModule);\n\n if (options.format === \"markdown\") {\n const GeneratorClass = getGeneratorClass(options.dialect);\n const generator = new GeneratorClass({\n schema: schemaModule,\n source: schemaPath,\n });\n const intermediateSchema = generator.toIntermediateSchema();\n\n // Handle multi-file output in watch mode\n if (!options.singleFile && options.output) {\n writeMarkdownMultipleFiles(intermediateSchema, options.output, options);\n return undefined;\n }\n\n return generateMarkdownOutput(intermediateSchema, options);\n } else {\n const generate = getGenerateFunction(options.dialect);\n return generate({\n schema: schemaModule,\n source: schemaPath,\n });\n }\n}\n\n/**\n * Find all TypeScript schema files in a directory\n */\nfunction findSchemaFiles(dirPath: string): string[] {\n const files: string[] = [];\n\n try {\n const entries = readdirSync(dirPath, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = join(dirPath, entry.name);\n\n if (entry.isDirectory()) {\n // Recursively search subdirectories\n files.push(...findSchemaFiles(fullPath));\n } else if (entry.isFile() && /\\.(ts|js|mts|mjs|cts|cjs)$/.test(entry.name)) {\n files.push(fullPath);\n }\n }\n } catch (error) {\n if (error instanceof Error) {\n console.error(`Error reading directory ${dirPath}: ${error.message}`);\n }\n }\n\n return files;\n}\n\n/**\n * Resolve schema path (file or directory)\n */\nfunction resolveSchemaPath(schema: string): string[] {\n const schemaPath = resolve(process.cwd(), schema);\n\n // Check if path exists\n if (!existsSync(schemaPath)) {\n console.error(`Error: Schema path not found: ${schemaPath}`);\n process.exit(1);\n }\n\n // Check if it's a directory\n const stats = lstatSync(schemaPath);\n if (stats.isDirectory()) {\n const schemaFiles = findSchemaFiles(schemaPath);\n if (schemaFiles.length === 0) {\n console.error(`Error: No schema files found in directory: ${schemaPath}`);\n process.exit(1);\n }\n return schemaFiles;\n }\n\n // Single file\n return [schemaPath];\n}\n\n/**\n * Generate DBML format output\n */\nfunction generateDbmlOutput(\n mergedSchema: Record<string, unknown>,\n schemaPaths: string[],\n options: GenerateCommandOptions,\n): string {\n const generate = getGenerateFunction(options.dialect);\n return (\n generate({\n schema: mergedSchema,\n source: schemaPaths[0],\n }) || \"\"\n );\n}\n\n/**\n * Generate Markdown format output\n */\nfunction generateMarkdownOutput(\n intermediateSchema: IntermediateSchema,\n options: GenerateCommandOptions,\n): string {\n const markdownFormatter = new MarkdownFormatter();\n const markdown = markdownFormatter.format(intermediateSchema);\n\n // Include ER diagram unless --no-er-diagram is specified\n if (options.erDiagram) {\n const mermaidFormatter = new MermaidErDiagramFormatter({\n includeColumns: options.columns,\n });\n const erDiagram = mermaidFormatter.format(intermediateSchema);\n\n return `${markdown}\\n\\n---\\n\\n## ER Diagram\\n\\n\\`\\`\\`mermaid\\n${erDiagram}\\n\\`\\`\\``;\n }\n\n return markdown;\n}\n\n/**\n * Write Markdown to multiple files (one per table)\n */\nfunction writeMarkdownMultipleFiles(\n intermediateSchema: IntermediateSchema,\n outputDir: string,\n options: GenerateCommandOptions,\n): void {\n // Ensure output directory exists\n mkdirSync(outputDir, { recursive: true });\n\n const markdownFormatter = new MarkdownFormatter({ linkFormat: \"file\" });\n\n // Write README.md with index\n const index = markdownFormatter.generateIndex(intermediateSchema);\n let readme = `${index}\\n`;\n\n // Add ER diagram to README unless disabled\n if (options.erDiagram) {\n const mermaidFormatter = new MermaidErDiagramFormatter({\n includeColumns: options.columns,\n });\n const erDiagram = mermaidFormatter.format(intermediateSchema);\n readme += `\\n---\\n\\n## ER Diagram\\n\\n\\`\\`\\`mermaid\\n${erDiagram}\\n\\`\\`\\`\\n`;\n }\n\n writeFileSync(join(outputDir, \"README.md\"), readme, \"utf-8\");\n\n // Write individual table files\n for (const table of intermediateSchema.tables) {\n const tableDoc = markdownFormatter.generateTableDoc(table, intermediateSchema);\n const fileName = `${table.name}.md`;\n writeFileSync(join(outputDir, fileName), `# ${table.name}\\n\\n${tableDoc}\\n`, \"utf-8\");\n }\n}\n\n/**\n * Write single Markdown file\n */\nfunction writeSingleMarkdownFile(content: string, outputPath: string): void {\n const dir = dirname(outputPath);\n mkdirSync(dir, { recursive: true });\n writeFileSync(outputPath, content.endsWith(\"\\n\") ? content : content + \"\\n\", \"utf-8\");\n}\n\n/**\n * Run the generate command\n */\nasync function runGenerate(schema: string, options: GenerateCommandOptions): Promise<void> {\n const schemaPaths = resolveSchemaPath(schema);\n\n try {\n // Merge all schema modules\n const mergedSchema: Record<string, unknown> = {};\n\n for (const schemaPath of schemaPaths) {\n const schemaUrl = pathToFileURL(schemaPath).href;\n const cacheBuster = options.watch ? `?t=${Date.now()}` : \"\";\n\n try {\n const rawModule = (await import(schemaUrl + cacheBuster)) as Record<string, unknown>;\n Object.assign(mergedSchema, resolveSchemaExports(rawModule));\n } catch (error) {\n if (error instanceof Error) {\n console.error(`Error importing ${schemaPath}: ${error.message}`);\n console.error(\"\\nPossible causes:\");\n console.error(\"- Syntax error in the schema file\");\n console.error(\"- Missing dependencies (make sure drizzle-orm is installed)\");\n console.error(\"- Circular dependencies between schema files\");\n }\n throw error;\n }\n }\n\n if (options.format === \"markdown\") {\n // Generate Markdown format\n const GeneratorClass = getGeneratorClass(options.dialect);\n // For multiple files, pass the directory path to extract comments from all files\n const firstFilePath = schemaPaths[0];\n if (!firstFilePath) {\n throw new Error(\"No schema files found\");\n }\n const sourcePath = schemaPaths.length === 1 ? firstFilePath : dirname(firstFilePath);\n const generator = new GeneratorClass({\n schema: mergedSchema,\n source: sourcePath,\n });\n const intermediateSchema = generator.toIntermediateSchema();\n\n if (options.singleFile) {\n // Single file Markdown output\n const content = generateMarkdownOutput(intermediateSchema, options);\n\n if (options.output) {\n // Check for existing file if --force is not specified\n if (!options.force && existsSync(options.output)) {\n console.error(\n `Error: Output file already exists: ${options.output}\\nUse --force to overwrite existing files.`,\n );\n process.exit(1);\n }\n writeSingleMarkdownFile(content, options.output);\n console.log(`Markdown generated: ${options.output}`);\n } else {\n console.log(content);\n }\n } else {\n // Multiple files Markdown output\n if (!options.output) {\n // If no output specified, default to stdout with single file format\n const content = generateMarkdownOutput(intermediateSchema, options);\n console.log(content);\n } else {\n // Check for existing files if --force is not specified\n if (!options.force) {\n const tableNames = intermediateSchema.tables.map((t) => t.name);\n const existingFiles = hasExistingFiles(options.output, tableNames);\n if (existingFiles.length > 0) {\n console.error(\n `Error: The following files already exist:\\n${existingFiles.map((f) => ` - ${f}`).join(\"\\n\")}\\nUse --force to overwrite existing files.`,\n );\n process.exit(1);\n }\n }\n writeMarkdownMultipleFiles(intermediateSchema, options.output, options);\n console.log(`Markdown generated: ${options.output}/`);\n }\n }\n } else {\n // Generate DBML format\n const dbml = generateDbmlOutput(mergedSchema, schemaPaths, options);\n\n if (options.output) {\n // Check for existing file if --force is not specified\n if (!options.force && existsSync(options.output)) {\n console.error(\n `Error: Output file already exists: ${options.output}\\nUse --force to overwrite existing files.`,\n );\n process.exit(1);\n }\n const dir = dirname(options.output);\n mkdirSync(dir, { recursive: true });\n writeFileSync(options.output, dbml.endsWith(\"\\n\") ? dbml : dbml + \"\\n\", \"utf-8\");\n console.log(`DBML generated: ${options.output}`);\n } else {\n console.log(dbml);\n }\n }\n } catch (error) {\n if (error instanceof Error) {\n console.error(`Error generating output: ${error.message}`);\n } else {\n console.error(\"Error generating output:\", error);\n }\n process.exit(1);\n }\n}\n\n/**\n * Watch mode: regenerate on file changes\n */\nfunction watchSchema(schema: string, options: GenerateCommandOptions): void {\n const schemaPath = resolve(process.cwd(), schema);\n\n console.log(`Watching for changes: ${schemaPath}`);\n\n let debounceTimer: NodeJS.Timeout | null = null;\n\n watch(schemaPath, async (eventType) => {\n if (eventType === \"change\") {\n // Debounce to avoid multiple triggers\n if (debounceTimer) {\n clearTimeout(debounceTimer);\n }\n\n debounceTimer = setTimeout(async () => {\n console.log(\"\\nFile changed, regenerating...\");\n try {\n const output = await generateFromSchema(schemaPath, options);\n const formatLabel = options.format === \"markdown\" ? \"Markdown\" : \"DBML\";\n const isMultiFile =\n options.format === \"markdown\" && !options.singleFile && options.output;\n\n if (!options.output && output) {\n console.log(output);\n } else if (options.output) {\n const outputLabel = isMultiFile ? `${options.output}/` : options.output;\n console.log(`${formatLabel} regenerated: ${outputLabel}`);\n }\n } catch (error) {\n if (error instanceof Error) {\n console.error(`Error: ${error.message}`);\n } else {\n console.error(\"Error:\", error);\n }\n }\n }, 100);\n }\n });\n}\n\nprogram\n .command(\"generate\")\n .description(\"Generate documentation from Drizzle schema files\")\n .argument(\"<schema>\", \"Path to Drizzle schema file or directory\")\n .option(\"-o, --output <path>\", \"Output file or directory path\")\n .option(\"-d, --dialect <dialect>\", \"Database dialect (postgresql, mysql, sqlite)\", \"postgresql\")\n .option(\"-f, --format <format>\", \"Output format (dbml, markdown)\", \"markdown\")\n .option(\"-w, --watch\", \"Watch for file changes and regenerate\")\n .option(\"--single-file\", \"Output Markdown as a single file (for markdown format)\")\n .option(\"--no-er-diagram\", \"Exclude ER diagram from Markdown output\")\n .option(\"--no-columns\", \"Exclude columns from Mermaid ER diagram\")\n .option(\"--force\", \"Overwrite existing files without confirmation\")\n .action(async (schema: string, options: GenerateCommandOptions) => {\n // Validate dialect\n const validDialects: Dialect[] = [\"postgresql\", \"mysql\", \"sqlite\"];\n if (!validDialects.includes(options.dialect)) {\n console.error(\n `Error: Invalid dialect \"${options.dialect}\". Valid options: ${validDialects.join(\", \")}`,\n );\n process.exit(1);\n }\n\n // Validate format\n const validFormats: OutputFormat[] = [\"dbml\", \"markdown\"];\n if (!validFormats.includes(options.format)) {\n console.error(\n `Error: Invalid format \"${options.format}\". Valid options: ${validFormats.join(\", \")}`,\n );\n process.exit(1);\n }\n\n // Warn if --single-file, --no-er-diagram, or --no-columns used with non-markdown format\n if (options.format !== \"markdown\") {\n if (options.singleFile) {\n console.warn(\"Warning: --single-file is only applicable with --format markdown\");\n }\n if (!options.erDiagram) {\n console.warn(\"Warning: --no-er-diagram is only applicable with --format markdown\");\n }\n if (!options.columns) {\n console.warn(\"Warning: --no-columns is only applicable with --format markdown\");\n }\n }\n\n // Initial generation\n await runGenerate(schema, options);\n\n // Start watch mode if requested\n if (options.watch) {\n watchSchema(schema, options);\n }\n });\n\nprogram.parse();\n\n// Cleanup: Unregister tsx loaders when process exits\nprocess.on(\"exit\", () => {\n unregister();\n unregisterCjs();\n});\n"],"names":["unregister","register","unregisterCjs","registerCjs","program","Command","packageJsonPath","join","packageJson","readFileSync","getGenerateFunction","dialect","mysqlGenerate","sqliteGenerate","pgGenerate","getGeneratorClass","MySqlGenerator","SqliteGenerator","PgGenerator","hasExistingFiles","outputDir","tableNames","existingFiles","existsSync","readmePath","tableName","tablePath","generateFromSchema","schemaPath","options","schemaUrl","pathToFileURL","cacheBuster","rawModule","schemaModule","resolveSchemaExports","GeneratorClass","intermediateSchema","writeMarkdownMultipleFiles","generateMarkdownOutput","findSchemaFiles","dirPath","files","entries","readdirSync","entry","fullPath","error","resolveSchemaPath","schema","resolve","lstatSync","schemaFiles","generateDbmlOutput","mergedSchema","schemaPaths","markdown","MarkdownFormatter","erDiagram","MermaidErDiagramFormatter","mkdirSync","markdownFormatter","readme","writeFileSync","table","tableDoc","fileName","writeSingleMarkdownFile","content","outputPath","dir","dirname","runGenerate","firstFilePath","sourcePath","t","f","dbml","watchSchema","debounceTimer","watch","eventType","output","formatLabel","isMultiFile","outputLabel","validDialects","validFormats"],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve schema module exports, handling CJS default export wrapping.
|
|
3
|
+
* When a CJS module is loaded via import(), its exports are wrapped in
|
|
4
|
+
* a `default` property. This function unwraps them so table definitions
|
|
5
|
+
* are accessible as top-level properties.
|
|
6
|
+
*/
|
|
7
|
+
export declare function resolveSchemaExports(mod: Record<string, unknown>): Record<string, unknown>;
|
|
8
|
+
//# sourceMappingURL=resolve-schema-exports.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-schema-exports.d.ts","sourceRoot":"","sources":["../../src/cli/resolve-schema-exports.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAM1F"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-schema-exports.js","sources":["../../src/cli/resolve-schema-exports.ts"],"sourcesContent":["/**\n * Resolve schema module exports, handling CJS default export wrapping.\n * When a CJS module is loaded via import(), its exports are wrapped in\n * a `default` property. This function unwraps them so table definitions\n * are accessible as top-level properties.\n */\nexport function resolveSchemaExports(mod: Record<string, unknown>): Record<string, unknown> {\n const { default: defaultExport, ...namedExports } = mod;\n if (defaultExport && typeof defaultExport === \"object\" && !Array.isArray(defaultExport)) {\n return { ...(defaultExport as Record<string, unknown>), ...namedExports };\n }\n return mod;\n}\n"],"names":["resolveSchemaExports","mod","defaultExport","namedExports"],"mappings":"AAMO,SAASA,EAAqBC,GAAuD;AAC1F,QAAM,EAAE,SAASC,GAAe,GAAGC,MAAiBF;AACpD,SAAIC,KAAiB,OAAOA,KAAkB,YAAY,CAAC,MAAM,QAAQA,CAAa,IAC7E,EAAE,GAAIA,GAA2C,GAAGC,EAAA,IAEtDF;AACT;"}
|
package/dist/formatter/dbml.d.ts
CHANGED
|
@@ -70,6 +70,14 @@ export declare class DbmlFormatter implements OutputFormatter {
|
|
|
70
70
|
* Convert IntermediateRelationType to DBML relation symbol
|
|
71
71
|
*/
|
|
72
72
|
private getRelationType;
|
|
73
|
+
/**
|
|
74
|
+
* Normalize SQL type for DBML compatibility
|
|
75
|
+
*
|
|
76
|
+
* Converts types with "with time zone" suffix to their short form
|
|
77
|
+
* (e.g., "timestamp(3) with time zone" -> "timestamptz(3)")
|
|
78
|
+
* because DBML parsers cannot handle the multi-word suffix.
|
|
79
|
+
*/
|
|
80
|
+
private normalizeType;
|
|
73
81
|
/**
|
|
74
82
|
* Escape a name for DBML
|
|
75
83
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dbml.d.ts","sourceRoot":"","sources":["../../src/formatter/dbml.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAOnB,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAYjE;;;;;GAKG;AACH,qBAAa,aAAc,YAAW,eAAe;IACnD,OAAO,CAAC,OAAO,CAA6B;IAE5C;;;;OAIG;gBACS,OAAO,GAAE,gBAAqB;IAI1C;;;;;OAKG;IACH,MAAM,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM;IAuB1C;;OAEG;IACH,OAAO,CAAC,UAAU;IAWlB;;OAEG;IACH,OAAO,CAAC,WAAW;IA8BnB;;OAEG;IACH,OAAO,CAAC,YAAY;IAapB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAyB3B;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,kBAAkB;IA4B1B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAc1B;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IA6C1B;;OAEG;IACH,OAAO,CAAC,cAAc;IAsBtB;;OAEG;IACH,OAAO,CAAC,eAAe;IAavB;;;;OAIG;IACH,OAAO,CAAC,UAAU;IAIlB;;OAEG;IACH,OAAO,CAAC,YAAY;CAGrB"}
|
|
1
|
+
{"version":3,"file":"dbml.d.ts","sourceRoot":"","sources":["../../src/formatter/dbml.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAOnB,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAYjE;;;;;GAKG;AACH,qBAAa,aAAc,YAAW,eAAe;IACnD,OAAO,CAAC,OAAO,CAA6B;IAE5C;;;;OAIG;gBACS,OAAO,GAAE,gBAAqB;IAI1C;;;;;OAKG;IACH,MAAM,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM;IAuB1C;;OAEG;IACH,OAAO,CAAC,UAAU;IAWlB;;OAEG;IACH,OAAO,CAAC,WAAW;IA8BnB;;OAEG;IACH,OAAO,CAAC,YAAY;IAapB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAyB3B;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,kBAAkB;IA4B1B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAc1B;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IA6C1B;;OAEG;IACH,OAAO,CAAC,cAAc;IAsBtB;;OAEG;IACH,OAAO,CAAC,eAAe;IAavB;;;;;;OAMG;IACH,OAAO,CAAC,aAAa;IAUrB;;;;OAIG;IACH,OAAO,CAAC,UAAU;IAIlB;;OAEG;IACH,OAAO,CAAC,YAAY;CAGrB"}
|
package/dist/formatter/dbml.js
CHANGED
|
@@ -36,8 +36,8 @@ class l {
|
|
|
36
36
|
formatEnum(e, t) {
|
|
37
37
|
const n = this.escapeName(t.name);
|
|
38
38
|
e.line(`Enum ${n} {`), e.indent();
|
|
39
|
-
for (const
|
|
40
|
-
e.line(
|
|
39
|
+
for (const s of t.values)
|
|
40
|
+
e.line(s);
|
|
41
41
|
e.dedent(), e.line("}");
|
|
42
42
|
}
|
|
43
43
|
/**
|
|
@@ -46,17 +46,17 @@ class l {
|
|
|
46
46
|
formatTable(e, t) {
|
|
47
47
|
const n = this.escapeName(t.name);
|
|
48
48
|
e.line(`Table ${n} {`), e.indent();
|
|
49
|
-
for (const
|
|
50
|
-
this.formatColumn(e,
|
|
51
|
-
const
|
|
52
|
-
this.options.includeIndexes && (
|
|
49
|
+
for (const a of t.columns)
|
|
50
|
+
this.formatColumn(e, a);
|
|
51
|
+
const s = t.indexes.length > 0, o = t.constraints.filter((a) => a.type === "primary_key"), r = t.constraints.filter((a) => a.type === "unique"), i = o.length > 0 || r.length > 0;
|
|
52
|
+
this.options.includeIndexes && (s || i) && this.formatIndexesBlock(e, t.indexes, o, r), this.options.includeComments && t.comment && (e.line(), e.line(`Note: '${this.escapeString(t.comment)}'`)), e.dedent(), e.line("}");
|
|
53
53
|
}
|
|
54
54
|
/**
|
|
55
55
|
* Format a column definition to DBML
|
|
56
56
|
*/
|
|
57
57
|
formatColumn(e, t) {
|
|
58
|
-
const n = this.escapeName(t.name),
|
|
59
|
-
|
|
58
|
+
const n = this.escapeName(t.name), s = this.normalizeType(t.type), r = this.getColumnAttributes(t).join(", ");
|
|
59
|
+
r ? e.line(`${n} ${s} [${r}]`) : e.line(`${n} ${s}`);
|
|
60
60
|
}
|
|
61
61
|
/**
|
|
62
62
|
* Get column attributes for DBML
|
|
@@ -102,21 +102,21 @@ class l {
|
|
|
102
102
|
*
|
|
103
103
|
* Includes primary key constraints, unique constraints, and regular indexes.
|
|
104
104
|
*/
|
|
105
|
-
formatIndexesBlock(e, t, n,
|
|
105
|
+
formatIndexesBlock(e, t, n, s) {
|
|
106
106
|
e.line(), e.line("indexes {"), e.indent();
|
|
107
|
-
for (const
|
|
108
|
-
const
|
|
109
|
-
e.line(`(${
|
|
107
|
+
for (const o of n) {
|
|
108
|
+
const r = o.columns.map((i) => this.escapeName(i)).join(", ");
|
|
109
|
+
e.line(`(${r}) [pk]`);
|
|
110
110
|
}
|
|
111
|
-
for (const
|
|
112
|
-
const
|
|
113
|
-
e.line(`(${
|
|
111
|
+
for (const o of s) {
|
|
112
|
+
const r = o.columns.map((i) => this.escapeName(i)).join(", ");
|
|
113
|
+
e.line(`(${r}) [unique]`);
|
|
114
114
|
}
|
|
115
|
-
for (const
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
const
|
|
119
|
-
e.line(`(${
|
|
115
|
+
for (const o of t) {
|
|
116
|
+
const r = o.columns.map((c) => this.escapeName(c)).join(", "), i = [];
|
|
117
|
+
o.unique && i.push("unique"), o.name && i.push(`name: '${o.name}'`), o.type && i.push(`type: ${o.type}`);
|
|
118
|
+
const a = i.length > 0 ? ` [${i.join(", ")}]` : "";
|
|
119
|
+
e.line(`(${r})${a}`);
|
|
120
120
|
}
|
|
121
121
|
e.dedent(), e.line("}");
|
|
122
122
|
}
|
|
@@ -124,10 +124,10 @@ class l {
|
|
|
124
124
|
* Format a relation definition to DBML Ref
|
|
125
125
|
*/
|
|
126
126
|
formatRelation(e, t) {
|
|
127
|
-
const n = `${this.escapeName(t.fromTable)}.${t.fromColumns.map((
|
|
128
|
-
let
|
|
129
|
-
const
|
|
130
|
-
t.onDelete && t.onDelete.toLowerCase() !== "no action" &&
|
|
127
|
+
const n = `${this.escapeName(t.fromTable)}.${t.fromColumns.map((a) => this.escapeName(a)).join(", ")}`, s = `${this.escapeName(t.toTable)}.${t.toColumns.map((a) => this.escapeName(a)).join(", ")}`, o = this.getRelationType(t.type);
|
|
128
|
+
let r = `Ref: ${n} ${o} ${s}`;
|
|
129
|
+
const i = [];
|
|
130
|
+
t.onDelete && t.onDelete.toLowerCase() !== "no action" && i.push(`delete: ${t.onDelete.toLowerCase()}`), t.onUpdate && t.onUpdate.toLowerCase() !== "no action" && i.push(`update: ${t.onUpdate.toLowerCase()}`), i.length > 0 && (r += ` [${i.join(", ")}]`), e.line(r);
|
|
131
131
|
}
|
|
132
132
|
/**
|
|
133
133
|
* Convert IntermediateRelationType to DBML relation symbol
|
|
@@ -144,6 +144,22 @@ class l {
|
|
|
144
144
|
return "<>";
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* Normalize SQL type for DBML compatibility
|
|
149
|
+
*
|
|
150
|
+
* Converts types with "with time zone" suffix to their short form
|
|
151
|
+
* (e.g., "timestamp(3) with time zone" -> "timestamptz(3)")
|
|
152
|
+
* because DBML parsers cannot handle the multi-word suffix.
|
|
153
|
+
*/
|
|
154
|
+
normalizeType(e) {
|
|
155
|
+
return e.replace(
|
|
156
|
+
/^(timestamp)\s*(\([^)]*\))?\s+with time zone$/i,
|
|
157
|
+
(t, n, s) => s ? `timestamptz${s}` : "timestamptz"
|
|
158
|
+
).replace(
|
|
159
|
+
/^(time)\s*(\([^)]*\))?\s+with time zone$/i,
|
|
160
|
+
(t, n, s) => s ? `timetz${s}` : "timetz"
|
|
161
|
+
);
|
|
162
|
+
}
|
|
147
163
|
/**
|
|
148
164
|
* Escape a name for DBML
|
|
149
165
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dbml.js","sources":["../../src/formatter/dbml.ts"],"sourcesContent":["import type {\n IntermediateSchema,\n TableDefinition,\n ColumnDefinition,\n IndexDefinition,\n ConstraintDefinition,\n RelationDefinition,\n EnumDefinition,\n} from \"../types\";\nimport type { OutputFormatter, FormatterOptions } from \"./types\";\nimport { DbmlBuilder } from \"./dbml-builder\";\n\n/**\n * Default formatter options\n */\nconst DEFAULT_OPTIONS: Required<FormatterOptions> = {\n includeComments: true,\n includeIndexes: true,\n includeConstraints: true,\n};\n\n/**\n * DbmlFormatter converts IntermediateSchema to DBML format\n *\n * This formatter wraps the existing DbmlBuilder to provide DBML output\n * from the database-agnostic IntermediateSchema representation.\n */\nexport class DbmlFormatter implements OutputFormatter {\n private options: Required<FormatterOptions>;\n\n /**\n * Create a new DbmlFormatter\n *\n * @param options - Formatter options\n */\n constructor(options: FormatterOptions = {}) {\n this.options = { ...DEFAULT_OPTIONS, ...options };\n }\n\n /**\n * Format the intermediate schema into DBML\n *\n * @param schema - The intermediate schema to format\n * @returns DBML string\n */\n format(schema: IntermediateSchema): string {\n const dbml = new DbmlBuilder();\n\n // Generate enums (PostgreSQL specific)\n for (const enumDef of schema.enums) {\n this.formatEnum(dbml, enumDef);\n dbml.line();\n }\n\n // Generate tables\n for (const table of schema.tables) {\n this.formatTable(dbml, table);\n dbml.line();\n }\n\n // Generate relations\n for (const relation of schema.relations) {\n this.formatRelation(dbml, relation);\n }\n\n return dbml.build().trim();\n }\n\n /**\n * Format an enum definition to DBML\n */\n private formatEnum(dbml: DbmlBuilder, enumDef: EnumDefinition): void {\n const name = this.escapeName(enumDef.name);\n dbml.line(`Enum ${name} {`);\n dbml.indent();\n for (const value of enumDef.values) {\n dbml.line(value);\n }\n dbml.dedent();\n dbml.line(\"}\");\n }\n\n /**\n * Format a table definition to DBML\n */\n private formatTable(dbml: DbmlBuilder, table: TableDefinition): void {\n const tableName = this.escapeName(table.name);\n dbml.line(`Table ${tableName} {`);\n dbml.indent();\n\n // Generate columns\n for (const column of table.columns) {\n this.formatColumn(dbml, column);\n }\n\n // Generate indexes block if enabled and there are indexes or constraints to show\n const hasIndexes = table.indexes.length > 0;\n const pkConstraints = table.constraints.filter((c) => c.type === \"primary_key\");\n const ucConstraints = table.constraints.filter((c) => c.type === \"unique\");\n const hasConstraints = pkConstraints.length > 0 || ucConstraints.length > 0;\n\n if (this.options.includeIndexes && (hasIndexes || hasConstraints)) {\n this.formatIndexesBlock(dbml, table.indexes, pkConstraints, ucConstraints);\n }\n\n // Add table-level Note if comment exists and comments are enabled\n if (this.options.includeComments && table.comment) {\n dbml.line();\n dbml.line(`Note: '${this.escapeString(table.comment)}'`);\n }\n\n dbml.dedent();\n dbml.line(\"}\");\n }\n\n /**\n * Format a column definition to DBML\n */\n private formatColumn(dbml: DbmlBuilder, column: ColumnDefinition): void {\n const name = this.escapeName(column.name);\n const type = column.type;\n const attrs = this.getColumnAttributes(column);\n const attrStr = attrs.join(\", \");\n\n if (attrStr) {\n dbml.line(`${name} ${type} [${attrStr}]`);\n } else {\n dbml.line(`${name} ${type}`);\n }\n }\n\n /**\n * Get column attributes for DBML\n */\n private getColumnAttributes(column: ColumnDefinition): string[] {\n const attrs: string[] = [];\n\n if (column.primaryKey) {\n attrs.push(\"primary key\");\n }\n if (!column.nullable) {\n attrs.push(\"not null\");\n }\n if (column.unique) {\n attrs.push(\"unique\");\n }\n if (column.autoIncrement) {\n attrs.push(\"increment\");\n }\n if (column.defaultValue !== undefined) {\n attrs.push(`default: ${this.formatDefaultValue(column.defaultValue)}`);\n }\n if (this.options.includeComments && column.comment) {\n attrs.push(`note: '${this.escapeString(column.comment)}'`);\n }\n\n return attrs;\n }\n\n /**\n * Format a default value for DBML\n *\n * The value comes from IntermediateSchema in \"raw\" format:\n * - SQL expressions: no wrapping (e.g., \"now()\", \"gen_random_uuid()\")\n * - Strings: already wrapped with single quotes (e.g., \"'user'\")\n * - Numbers/booleans: as-is (e.g., \"true\", \"42\")\n * - null: \"null\"\n *\n * DBML syntax requires:\n * - SQL functions/expressions: wrapped in backticks (e.g., `now()`)\n * - Strings: single quotes (e.g., 'user')\n * - Numbers/booleans/null: as-is\n */\n private formatDefaultValue(value: string): string {\n // If it looks like a SQL expression (contains parentheses or is a known function)\n // Wrap in backticks for dbdiagram.io compatibility\n if (value.includes(\"(\") || value.includes(\"::\") || this.isKnownSqlFunction(value)) {\n return `\\`${value}\\``;\n }\n // If it's null\n if (value === \"null\" || value === \"NULL\") {\n return \"null\";\n }\n // If it's a boolean\n if (value === \"true\" || value === \"false\") {\n return value;\n }\n // If it's a number\n if (/^-?\\d+(\\.\\d+)?$/.test(value)) {\n return value;\n }\n // If already wrapped in single quotes (from IntermediateSchema)\n if (value.startsWith(\"'\") && value.endsWith(\"'\")) {\n // Convert '' escape (SQL standard) to \\' escape (DBML)\n const inner = value.slice(1, -1).replace(/''/g, \"\\\\'\");\n return `'${inner}'`;\n }\n // Otherwise treat as string (shouldn't happen with proper IntermediateSchema)\n return `'${value.replace(/'/g, \"\\\\'\")}'`;\n }\n\n /**\n * Check if a value is a known SQL function/expression\n */\n private isKnownSqlFunction(value: string): boolean {\n const sqlKeywords = [\n \"now\",\n \"current_timestamp\",\n \"current_date\",\n \"current_time\",\n \"gen_random_uuid\",\n \"uuid_generate_v4\",\n \"autoincrement\",\n \"auto_increment\",\n ];\n return sqlKeywords.some((kw) => value.toLowerCase().includes(kw));\n }\n\n /**\n * Format indexes block to DBML\n *\n * Includes primary key constraints, unique constraints, and regular indexes.\n */\n private formatIndexesBlock(\n dbml: DbmlBuilder,\n indexes: IndexDefinition[],\n pkConstraints: ConstraintDefinition[],\n ucConstraints: ConstraintDefinition[],\n ): void {\n dbml.line();\n dbml.line(\"indexes {\");\n dbml.indent();\n\n // Primary key constraints\n for (const pk of pkConstraints) {\n const columns = pk.columns.map((c) => this.escapeName(c)).join(\", \");\n dbml.line(`(${columns}) [pk]`);\n }\n\n // Unique constraints\n for (const uc of ucConstraints) {\n const columns = uc.columns.map((c) => this.escapeName(c)).join(\", \");\n dbml.line(`(${columns}) [unique]`);\n }\n\n // Regular indexes\n for (const index of indexes) {\n const columns = index.columns.map((c) => this.escapeName(c)).join(\", \");\n const attrs: string[] = [];\n\n if (index.unique) {\n attrs.push(\"unique\");\n }\n if (index.name) {\n attrs.push(`name: '${index.name}'`);\n }\n if (index.type) {\n attrs.push(`type: ${index.type}`);\n }\n\n const attrStr = attrs.length > 0 ? ` [${attrs.join(\", \")}]` : \"\";\n dbml.line(`(${columns})${attrStr}`);\n }\n\n dbml.dedent();\n dbml.line(\"}\");\n }\n\n /**\n * Format a relation definition to DBML Ref\n */\n private formatRelation(dbml: DbmlBuilder, relation: RelationDefinition): void {\n const from = `${this.escapeName(relation.fromTable)}.${relation.fromColumns.map((c) => this.escapeName(c)).join(\", \")}`;\n const to = `${this.escapeName(relation.toTable)}.${relation.toColumns.map((c) => this.escapeName(c)).join(\", \")}`;\n const type = this.getRelationType(relation.type);\n\n let refLine = `Ref: ${from} ${type} ${to}`;\n\n const attrs: string[] = [];\n if (relation.onDelete && relation.onDelete.toLowerCase() !== \"no action\") {\n attrs.push(`delete: ${relation.onDelete.toLowerCase()}`);\n }\n if (relation.onUpdate && relation.onUpdate.toLowerCase() !== \"no action\") {\n attrs.push(`update: ${relation.onUpdate.toLowerCase()}`);\n }\n\n if (attrs.length > 0) {\n refLine += ` [${attrs.join(\", \")}]`;\n }\n\n dbml.line(refLine);\n }\n\n /**\n * Convert IntermediateRelationType to DBML relation symbol\n */\n private getRelationType(type: RelationDefinition[\"type\"]): \"<\" | \">\" | \"-\" | \"<>\" {\n switch (type) {\n case \"one-to-one\":\n return \"-\";\n case \"one-to-many\":\n return \"<\";\n case \"many-to-one\":\n return \">\";\n case \"many-to-many\":\n return \"<>\";\n }\n }\n\n /**\n * Escape a name for DBML\n *\n * DBML uses double quotes for all database types\n */\n private escapeName(name: string): string {\n return `\"${name}\"`;\n }\n\n /**\n * Escape a string for use in DBML single-quoted strings\n */\n private escapeString(str: string): string {\n return str.replace(/\\\\/g, \"\\\\\\\\\").replace(/'/g, \"\\\\'\").replace(/\\n/g, \"\\\\n\");\n }\n}\n"],"names":["DEFAULT_OPTIONS","DbmlFormatter","options","schema","dbml","DbmlBuilder","enumDef","table","relation","name","value","tableName","column","hasIndexes","pkConstraints","c","ucConstraints","hasConstraints","type","attrStr","attrs","kw","indexes","pk","columns","uc","index","from","to","refLine","str"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dbml.js","sources":["../../src/formatter/dbml.ts"],"sourcesContent":["import type {\n IntermediateSchema,\n TableDefinition,\n ColumnDefinition,\n IndexDefinition,\n ConstraintDefinition,\n RelationDefinition,\n EnumDefinition,\n} from \"../types\";\nimport type { OutputFormatter, FormatterOptions } from \"./types\";\nimport { DbmlBuilder } from \"./dbml-builder\";\n\n/**\n * Default formatter options\n */\nconst DEFAULT_OPTIONS: Required<FormatterOptions> = {\n includeComments: true,\n includeIndexes: true,\n includeConstraints: true,\n};\n\n/**\n * DbmlFormatter converts IntermediateSchema to DBML format\n *\n * This formatter wraps the existing DbmlBuilder to provide DBML output\n * from the database-agnostic IntermediateSchema representation.\n */\nexport class DbmlFormatter implements OutputFormatter {\n private options: Required<FormatterOptions>;\n\n /**\n * Create a new DbmlFormatter\n *\n * @param options - Formatter options\n */\n constructor(options: FormatterOptions = {}) {\n this.options = { ...DEFAULT_OPTIONS, ...options };\n }\n\n /**\n * Format the intermediate schema into DBML\n *\n * @param schema - The intermediate schema to format\n * @returns DBML string\n */\n format(schema: IntermediateSchema): string {\n const dbml = new DbmlBuilder();\n\n // Generate enums (PostgreSQL specific)\n for (const enumDef of schema.enums) {\n this.formatEnum(dbml, enumDef);\n dbml.line();\n }\n\n // Generate tables\n for (const table of schema.tables) {\n this.formatTable(dbml, table);\n dbml.line();\n }\n\n // Generate relations\n for (const relation of schema.relations) {\n this.formatRelation(dbml, relation);\n }\n\n return dbml.build().trim();\n }\n\n /**\n * Format an enum definition to DBML\n */\n private formatEnum(dbml: DbmlBuilder, enumDef: EnumDefinition): void {\n const name = this.escapeName(enumDef.name);\n dbml.line(`Enum ${name} {`);\n dbml.indent();\n for (const value of enumDef.values) {\n dbml.line(value);\n }\n dbml.dedent();\n dbml.line(\"}\");\n }\n\n /**\n * Format a table definition to DBML\n */\n private formatTable(dbml: DbmlBuilder, table: TableDefinition): void {\n const tableName = this.escapeName(table.name);\n dbml.line(`Table ${tableName} {`);\n dbml.indent();\n\n // Generate columns\n for (const column of table.columns) {\n this.formatColumn(dbml, column);\n }\n\n // Generate indexes block if enabled and there are indexes or constraints to show\n const hasIndexes = table.indexes.length > 0;\n const pkConstraints = table.constraints.filter((c) => c.type === \"primary_key\");\n const ucConstraints = table.constraints.filter((c) => c.type === \"unique\");\n const hasConstraints = pkConstraints.length > 0 || ucConstraints.length > 0;\n\n if (this.options.includeIndexes && (hasIndexes || hasConstraints)) {\n this.formatIndexesBlock(dbml, table.indexes, pkConstraints, ucConstraints);\n }\n\n // Add table-level Note if comment exists and comments are enabled\n if (this.options.includeComments && table.comment) {\n dbml.line();\n dbml.line(`Note: '${this.escapeString(table.comment)}'`);\n }\n\n dbml.dedent();\n dbml.line(\"}\");\n }\n\n /**\n * Format a column definition to DBML\n */\n private formatColumn(dbml: DbmlBuilder, column: ColumnDefinition): void {\n const name = this.escapeName(column.name);\n const type = this.normalizeType(column.type);\n const attrs = this.getColumnAttributes(column);\n const attrStr = attrs.join(\", \");\n\n if (attrStr) {\n dbml.line(`${name} ${type} [${attrStr}]`);\n } else {\n dbml.line(`${name} ${type}`);\n }\n }\n\n /**\n * Get column attributes for DBML\n */\n private getColumnAttributes(column: ColumnDefinition): string[] {\n const attrs: string[] = [];\n\n if (column.primaryKey) {\n attrs.push(\"primary key\");\n }\n if (!column.nullable) {\n attrs.push(\"not null\");\n }\n if (column.unique) {\n attrs.push(\"unique\");\n }\n if (column.autoIncrement) {\n attrs.push(\"increment\");\n }\n if (column.defaultValue !== undefined) {\n attrs.push(`default: ${this.formatDefaultValue(column.defaultValue)}`);\n }\n if (this.options.includeComments && column.comment) {\n attrs.push(`note: '${this.escapeString(column.comment)}'`);\n }\n\n return attrs;\n }\n\n /**\n * Format a default value for DBML\n *\n * The value comes from IntermediateSchema in \"raw\" format:\n * - SQL expressions: no wrapping (e.g., \"now()\", \"gen_random_uuid()\")\n * - Strings: already wrapped with single quotes (e.g., \"'user'\")\n * - Numbers/booleans: as-is (e.g., \"true\", \"42\")\n * - null: \"null\"\n *\n * DBML syntax requires:\n * - SQL functions/expressions: wrapped in backticks (e.g., `now()`)\n * - Strings: single quotes (e.g., 'user')\n * - Numbers/booleans/null: as-is\n */\n private formatDefaultValue(value: string): string {\n // If it looks like a SQL expression (contains parentheses or is a known function)\n // Wrap in backticks for dbdiagram.io compatibility\n if (value.includes(\"(\") || value.includes(\"::\") || this.isKnownSqlFunction(value)) {\n return `\\`${value}\\``;\n }\n // If it's null\n if (value === \"null\" || value === \"NULL\") {\n return \"null\";\n }\n // If it's a boolean\n if (value === \"true\" || value === \"false\") {\n return value;\n }\n // If it's a number\n if (/^-?\\d+(\\.\\d+)?$/.test(value)) {\n return value;\n }\n // If already wrapped in single quotes (from IntermediateSchema)\n if (value.startsWith(\"'\") && value.endsWith(\"'\")) {\n // Convert '' escape (SQL standard) to \\' escape (DBML)\n const inner = value.slice(1, -1).replace(/''/g, \"\\\\'\");\n return `'${inner}'`;\n }\n // Otherwise treat as string (shouldn't happen with proper IntermediateSchema)\n return `'${value.replace(/'/g, \"\\\\'\")}'`;\n }\n\n /**\n * Check if a value is a known SQL function/expression\n */\n private isKnownSqlFunction(value: string): boolean {\n const sqlKeywords = [\n \"now\",\n \"current_timestamp\",\n \"current_date\",\n \"current_time\",\n \"gen_random_uuid\",\n \"uuid_generate_v4\",\n \"autoincrement\",\n \"auto_increment\",\n ];\n return sqlKeywords.some((kw) => value.toLowerCase().includes(kw));\n }\n\n /**\n * Format indexes block to DBML\n *\n * Includes primary key constraints, unique constraints, and regular indexes.\n */\n private formatIndexesBlock(\n dbml: DbmlBuilder,\n indexes: IndexDefinition[],\n pkConstraints: ConstraintDefinition[],\n ucConstraints: ConstraintDefinition[],\n ): void {\n dbml.line();\n dbml.line(\"indexes {\");\n dbml.indent();\n\n // Primary key constraints\n for (const pk of pkConstraints) {\n const columns = pk.columns.map((c) => this.escapeName(c)).join(\", \");\n dbml.line(`(${columns}) [pk]`);\n }\n\n // Unique constraints\n for (const uc of ucConstraints) {\n const columns = uc.columns.map((c) => this.escapeName(c)).join(\", \");\n dbml.line(`(${columns}) [unique]`);\n }\n\n // Regular indexes\n for (const index of indexes) {\n const columns = index.columns.map((c) => this.escapeName(c)).join(\", \");\n const attrs: string[] = [];\n\n if (index.unique) {\n attrs.push(\"unique\");\n }\n if (index.name) {\n attrs.push(`name: '${index.name}'`);\n }\n if (index.type) {\n attrs.push(`type: ${index.type}`);\n }\n\n const attrStr = attrs.length > 0 ? ` [${attrs.join(\", \")}]` : \"\";\n dbml.line(`(${columns})${attrStr}`);\n }\n\n dbml.dedent();\n dbml.line(\"}\");\n }\n\n /**\n * Format a relation definition to DBML Ref\n */\n private formatRelation(dbml: DbmlBuilder, relation: RelationDefinition): void {\n const from = `${this.escapeName(relation.fromTable)}.${relation.fromColumns.map((c) => this.escapeName(c)).join(\", \")}`;\n const to = `${this.escapeName(relation.toTable)}.${relation.toColumns.map((c) => this.escapeName(c)).join(\", \")}`;\n const type = this.getRelationType(relation.type);\n\n let refLine = `Ref: ${from} ${type} ${to}`;\n\n const attrs: string[] = [];\n if (relation.onDelete && relation.onDelete.toLowerCase() !== \"no action\") {\n attrs.push(`delete: ${relation.onDelete.toLowerCase()}`);\n }\n if (relation.onUpdate && relation.onUpdate.toLowerCase() !== \"no action\") {\n attrs.push(`update: ${relation.onUpdate.toLowerCase()}`);\n }\n\n if (attrs.length > 0) {\n refLine += ` [${attrs.join(\", \")}]`;\n }\n\n dbml.line(refLine);\n }\n\n /**\n * Convert IntermediateRelationType to DBML relation symbol\n */\n private getRelationType(type: RelationDefinition[\"type\"]): \"<\" | \">\" | \"-\" | \"<>\" {\n switch (type) {\n case \"one-to-one\":\n return \"-\";\n case \"one-to-many\":\n return \"<\";\n case \"many-to-one\":\n return \">\";\n case \"many-to-many\":\n return \"<>\";\n }\n }\n\n /**\n * Normalize SQL type for DBML compatibility\n *\n * Converts types with \"with time zone\" suffix to their short form\n * (e.g., \"timestamp(3) with time zone\" -> \"timestamptz(3)\")\n * because DBML parsers cannot handle the multi-word suffix.\n */\n private normalizeType(type: string): string {\n return type\n .replace(/^(timestamp)\\s*(\\([^)]*\\))?\\s+with time zone$/i, (_match, _base, precision) =>\n precision ? `timestamptz${precision}` : \"timestamptz\",\n )\n .replace(/^(time)\\s*(\\([^)]*\\))?\\s+with time zone$/i, (_match, _base, precision) =>\n precision ? `timetz${precision}` : \"timetz\",\n );\n }\n\n /**\n * Escape a name for DBML\n *\n * DBML uses double quotes for all database types\n */\n private escapeName(name: string): string {\n return `\"${name}\"`;\n }\n\n /**\n * Escape a string for use in DBML single-quoted strings\n */\n private escapeString(str: string): string {\n return str.replace(/\\\\/g, \"\\\\\\\\\").replace(/'/g, \"\\\\'\").replace(/\\n/g, \"\\\\n\");\n }\n}\n"],"names":["DEFAULT_OPTIONS","DbmlFormatter","options","schema","dbml","DbmlBuilder","enumDef","table","relation","name","value","tableName","column","hasIndexes","pkConstraints","c","ucConstraints","hasConstraints","type","attrStr","attrs","kw","indexes","pk","columns","uc","index","from","to","refLine","_match","_base","precision","str"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "drizzle-docs-generator",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "A CLI tool that generates DBML files from Drizzle ORM schema definitions.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -39,14 +39,14 @@
|
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"commander": "^14.0.3",
|
|
42
|
-
"drizzle-orm": "1.0.0-beta.
|
|
42
|
+
"drizzle-orm": "1.0.0-beta.15-859cf75",
|
|
43
43
|
"tsx": "^4.21.0",
|
|
44
44
|
"typescript": "^5.9.3"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@types/node": "^24.10.7",
|
|
48
48
|
"@vitest/coverage-v8": "^4.0.18",
|
|
49
|
-
"oxfmt": "^0.
|
|
49
|
+
"oxfmt": "^0.34.0",
|
|
50
50
|
"oxlint": "^1.42.0",
|
|
51
51
|
"vite": "^7.3.1",
|
|
52
52
|
"vite-plugin-dts": "^4.5.4",
|