drizzle-docs-generator 0.2.0 → 0.3.0

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.
Files changed (61) hide show
  1. package/README.ja.md +50 -13
  2. package/README.md +45 -8
  3. package/dist/adapter/index.d.ts +10 -0
  4. package/dist/adapter/index.d.ts.map +1 -0
  5. package/dist/adapter/types.d.ts +40 -0
  6. package/dist/adapter/types.d.ts.map +1 -0
  7. package/dist/adapter/v0-adapter.d.ts +73 -0
  8. package/dist/adapter/v0-adapter.d.ts.map +1 -0
  9. package/dist/adapter/v0-adapter.js +136 -0
  10. package/dist/adapter/v0-adapter.js.map +1 -0
  11. package/dist/adapter/v1-adapter.d.ts +40 -0
  12. package/dist/adapter/v1-adapter.d.ts.map +1 -0
  13. package/dist/adapter/v1-adapter.js +83 -0
  14. package/dist/adapter/v1-adapter.js.map +1 -0
  15. package/dist/cli/index.js +198 -66
  16. package/dist/cli/index.js.map +1 -1
  17. package/dist/cli/integration-test-utils.d.ts +19 -0
  18. package/dist/cli/integration-test-utils.d.ts.map +1 -0
  19. package/dist/formatter/dbml-builder.d.ts +29 -0
  20. package/dist/formatter/dbml-builder.d.ts.map +1 -0
  21. package/dist/formatter/dbml-builder.js +39 -0
  22. package/dist/formatter/dbml-builder.js.map +1 -0
  23. package/dist/formatter/dbml.d.ts +81 -0
  24. package/dist/formatter/dbml.d.ts.map +1 -0
  25. package/dist/formatter/dbml.js +163 -0
  26. package/dist/formatter/dbml.js.map +1 -0
  27. package/dist/formatter/index.d.ts +7 -0
  28. package/dist/formatter/index.d.ts.map +1 -0
  29. package/dist/formatter/markdown.d.ts +125 -0
  30. package/dist/formatter/markdown.d.ts.map +1 -0
  31. package/dist/formatter/markdown.js +235 -0
  32. package/dist/formatter/markdown.js.map +1 -0
  33. package/dist/formatter/mermaid.d.ts +102 -0
  34. package/dist/formatter/mermaid.d.ts.map +1 -0
  35. package/dist/formatter/mermaid.js +177 -0
  36. package/dist/formatter/mermaid.js.map +1 -0
  37. package/dist/formatter/types.d.ts +37 -0
  38. package/dist/formatter/types.d.ts.map +1 -0
  39. package/dist/generator/common.d.ts +109 -211
  40. package/dist/generator/common.d.ts.map +1 -1
  41. package/dist/generator/common.js +252 -481
  42. package/dist/generator/common.js.map +1 -1
  43. package/dist/generator/index.d.ts +2 -1
  44. package/dist/generator/index.d.ts.map +1 -1
  45. package/dist/generator/mysql.js +3 -3
  46. package/dist/generator/pg.d.ts +8 -7
  47. package/dist/generator/pg.d.ts.map +1 -1
  48. package/dist/generator/pg.js +29 -31
  49. package/dist/generator/pg.js.map +1 -1
  50. package/dist/generator/sqlite.js +3 -3
  51. package/dist/index.d.ts +3 -0
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +16 -9
  54. package/dist/index.js.map +1 -1
  55. package/dist/test-utils/cli-runner.d.ts +4 -0
  56. package/dist/test-utils/cli-runner.d.ts.map +1 -1
  57. package/dist/test-utils/dbml-validator.d.ts +37 -0
  58. package/dist/test-utils/dbml-validator.d.ts.map +1 -1
  59. package/dist/types.d.ts +132 -0
  60. package/dist/types.d.ts.map +1 -1
  61. package/package.json +3 -2
package/dist/cli/index.js CHANGED
@@ -1,110 +1,242 @@
1
1
  #!/usr/bin/env node
2
- import { Command as y } from "commander";
3
- import { readFileSync as w, watch as $, existsSync as D, lstatSync as E, readdirSync as q } from "node:fs";
4
- import { join as m, resolve as f } from "node:path";
5
- import { pathToFileURL as u } from "node:url";
6
- import { pgGenerate as b } from "../generator/pg.js";
7
- import { mysqlGenerate as S } from "../generator/mysql.js";
8
- import { sqliteGenerate as z } from "../generator/sqlite.js";
2
+ import { Command as D } from "commander";
3
+ import { readFileSync as q, existsSync as l, mkdirSync as g, writeFileSync as f, watch as v, lstatSync as S, readdirSync as G } from "node:fs";
4
+ import { join as m, dirname as p, resolve as y } from "node:path";
5
+ import { pathToFileURL as $ } from "node:url";
6
+ import { PgGenerator as O, pgGenerate as j } from "../generator/pg.js";
7
+ import { MySqlGenerator as z, mysqlGenerate as P } from "../generator/mysql.js";
8
+ import { SqliteGenerator as T, sqliteGenerate as U } from "../generator/sqlite.js";
9
9
  import "drizzle-orm";
10
10
  import "drizzle-orm/pg-core";
11
11
  import "drizzle-orm/mysql-core";
12
12
  import "drizzle-orm/sqlite-core";
13
13
  import "typescript";
14
- import { register as M } from "tsx/esm/api";
15
- const j = M(), i = new y(), v = m(import.meta.dirname, "../../package.json"), l = JSON.parse(w(v, "utf-8"));
16
- i.name("drizzle-docs").description(l.description).version(l.version);
17
- function h(t) {
18
- switch (t) {
14
+ import { MarkdownFormatter as F } from "../formatter/markdown.js";
15
+ import { MermaidErDiagramFormatter as k } from "../formatter/mermaid.js";
16
+ import { register as R } from "tsx/esm/api";
17
+ const W = R(), h = new D(), C = m(import.meta.dirname, "../../package.json"), w = JSON.parse(q(C, "utf-8"));
18
+ h.name("drizzle-docs").description(w.description).version(w.version);
19
+ function E(r) {
20
+ switch (r) {
19
21
  case "mysql":
20
- return S;
22
+ return P;
21
23
  case "sqlite":
24
+ return U;
25
+ default:
26
+ return j;
27
+ }
28
+ }
29
+ function x(r) {
30
+ switch (r) {
31
+ case "mysql":
22
32
  return z;
33
+ case "sqlite":
34
+ return T;
23
35
  default:
24
- return b;
36
+ return O;
25
37
  }
26
38
  }
27
- async function x(t, e) {
28
- const o = u(t).href, r = e.watch ? `?t=${Date.now()}` : "", n = await import(o + r);
29
- return h(e.dialect)({
30
- schema: n,
31
- out: e.output,
32
- relational: e.relational,
33
- source: t
34
- });
39
+ function I(r, e) {
40
+ const o = [];
41
+ if (!l(r))
42
+ return o;
43
+ const t = m(r, "README.md");
44
+ l(t) && o.push(t);
45
+ for (const n of e) {
46
+ const a = m(r, `${n}.md`);
47
+ l(a) && o.push(a);
48
+ }
49
+ return o;
35
50
  }
36
- function d(t) {
51
+ async function L(r, e) {
52
+ const o = $(r).href, t = e.watch ? `?t=${Date.now()}` : "", n = await import(o + t);
53
+ if (e.format === "markdown") {
54
+ const a = x(e.dialect), s = new a({
55
+ schema: n,
56
+ relational: e.relational,
57
+ source: r
58
+ }).toIntermediateSchema();
59
+ if (!e.singleFile && e.output) {
60
+ M(s, e.output, e);
61
+ return;
62
+ }
63
+ return d(s, e);
64
+ } else
65
+ return E(e.dialect)({
66
+ schema: n,
67
+ relational: e.relational,
68
+ source: r
69
+ });
70
+ }
71
+ function b(r) {
37
72
  const e = [];
38
73
  try {
39
- const o = q(t, { withFileTypes: !0 });
40
- for (const r of o) {
41
- const n = m(t, r.name);
42
- r.isDirectory() ? e.push(...d(n)) : r.isFile() && /\.(ts|js|mts|mjs|cts|cjs)$/.test(r.name) && e.push(n);
74
+ const o = G(r, { withFileTypes: !0 });
75
+ for (const t of o) {
76
+ const n = m(r, t.name);
77
+ t.isDirectory() ? e.push(...b(n)) : t.isFile() && /\.(ts|js|mts|mjs|cts|cjs)$/.test(t.name) && e.push(n);
43
78
  }
44
79
  } catch (o) {
45
- o instanceof Error && console.error(`Error reading directory ${t}: ${o.message}`);
80
+ o instanceof Error && console.error(`Error reading directory ${r}: ${o.message}`);
46
81
  }
47
82
  return e;
48
83
  }
49
- function B(t) {
50
- const e = f(process.cwd(), t);
51
- if (D(e) || (console.error(`Error: Schema path not found: ${e}`), process.exit(1)), E(e).isDirectory()) {
52
- const r = d(e);
53
- return r.length === 0 && (console.error(`Error: No schema files found in directory: ${e}`), process.exit(1)), r;
84
+ function N(r) {
85
+ const e = y(process.cwd(), r);
86
+ if (l(e) || (console.error(`Error: Schema path not found: ${e}`), process.exit(1)), S(e).isDirectory()) {
87
+ const t = b(e);
88
+ return t.length === 0 && (console.error(`Error: No schema files found in directory: ${e}`), process.exit(1)), t;
54
89
  }
55
90
  return [e];
56
91
  }
57
- async function F(t, e) {
58
- const o = B(t);
92
+ function B(r, e, o) {
93
+ return E(o.dialect)({
94
+ schema: r,
95
+ relational: o.relational,
96
+ source: e[0]
97
+ }) || "";
98
+ }
99
+ function d(r, e) {
100
+ const t = new F().format(r);
101
+ if (e.erDiagram) {
102
+ const a = new k().format(r);
103
+ return `${t}
104
+
105
+ ---
106
+
107
+ ## ER Diagram
108
+
109
+ \`\`\`mermaid
110
+ ${a}
111
+ \`\`\``;
112
+ }
113
+ return t;
114
+ }
115
+ function M(r, e, o) {
116
+ g(e, { recursive: !0 });
117
+ const t = new F({ linkFormat: "file" });
118
+ let a = `${t.generateIndex(r)}
119
+ `;
120
+ if (o.erDiagram) {
121
+ const s = new k().format(r);
122
+ a += `
123
+ ---
124
+
125
+ ## ER Diagram
126
+
127
+ \`\`\`mermaid
128
+ ${s}
129
+ \`\`\`
130
+ `;
131
+ }
132
+ f(m(e, "README.md"), a, "utf-8");
133
+ for (const i of r.tables) {
134
+ const s = t.generateTableDoc(i, r), c = `${i.name}.md`;
135
+ f(m(e, c), `# ${i.name}
136
+
137
+ ${s}
138
+ `, "utf-8");
139
+ }
140
+ }
141
+ function J(r, e) {
142
+ const o = p(e);
143
+ g(o, { recursive: !0 }), f(e, r.endsWith(`
144
+ `) ? r : r + `
145
+ `, "utf-8");
146
+ }
147
+ async function A(r, e) {
148
+ const o = N(r);
59
149
  try {
60
- const r = {};
61
- for (const a of o) {
62
- const g = u(a).href, p = e.watch ? `?t=${Date.now()}` : "";
150
+ const t = {};
151
+ for (const n of o) {
152
+ const a = $(n).href, i = e.watch ? `?t=${Date.now()}` : "";
63
153
  try {
64
- const c = await import(g + p);
65
- Object.assign(r, c);
66
- } catch (c) {
67
- throw c instanceof Error && (console.error(`Error importing ${a}: ${c.message}`), console.error(`
68
- 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;
154
+ const s = await import(a + i);
155
+ Object.assign(t, s);
156
+ } catch (s) {
157
+ throw s instanceof Error && (console.error(`Error importing ${n}: ${s.message}`), console.error(`
158
+ 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")), s;
69
159
  }
70
160
  }
71
- const s = h(e.dialect)({
72
- schema: r,
73
- out: e.output,
74
- relational: e.relational,
75
- source: o[0]
76
- // Use first file for source path
77
- });
78
- !e.output && s ? console.log(s) : e.output && console.log(`DBML generated: ${e.output}`);
79
- } catch (r) {
80
- r instanceof Error ? console.error(`Error generating DBML: ${r.message}`) : console.error("Error generating DBML:", r), process.exit(1);
161
+ if (e.format === "markdown") {
162
+ const n = x(e.dialect), i = new n({
163
+ schema: t,
164
+ relational: e.relational,
165
+ source: o[0]
166
+ }).toIntermediateSchema();
167
+ if (e.singleFile) {
168
+ const s = d(i, e);
169
+ e.output ? (!e.force && l(e.output) && (console.error(
170
+ `Error: Output file already exists: ${e.output}
171
+ Use --force to overwrite existing files.`
172
+ ), process.exit(1)), J(s, e.output), console.log(`Markdown generated: ${e.output}`)) : console.log(s);
173
+ } else if (e.output) {
174
+ if (!e.force) {
175
+ const s = i.tables.map((u) => u.name), c = I(e.output, s);
176
+ c.length > 0 && (console.error(
177
+ `Error: The following files already exist:
178
+ ${c.map((u) => ` - ${u}`).join(`
179
+ `)}
180
+ Use --force to overwrite existing files.`
181
+ ), process.exit(1));
182
+ }
183
+ M(i, e.output, e), console.log(`Markdown generated: ${e.output}/`);
184
+ } else {
185
+ const s = d(i, e);
186
+ console.log(s);
187
+ }
188
+ } else {
189
+ const n = B(t, o, e);
190
+ if (e.output) {
191
+ !e.force && l(e.output) && (console.error(
192
+ `Error: Output file already exists: ${e.output}
193
+ Use --force to overwrite existing files.`
194
+ ), process.exit(1));
195
+ const a = p(e.output);
196
+ g(a, { recursive: !0 }), f(e.output, n.endsWith(`
197
+ `) ? n : n + `
198
+ `, "utf-8"), console.log(`DBML generated: ${e.output}`);
199
+ } else
200
+ console.log(n);
201
+ }
202
+ } catch (t) {
203
+ t instanceof Error ? console.error(`Error generating output: ${t.message}`) : console.error("Error generating output:", t), process.exit(1);
81
204
  }
82
205
  }
83
- function G(t, e) {
84
- const o = f(process.cwd(), t);
206
+ function V(r, e) {
207
+ const o = y(process.cwd(), r);
85
208
  console.log(`Watching for changes: ${o}`);
86
- let r = null;
87
- $(o, async (n) => {
88
- n === "change" && (r && clearTimeout(r), r = setTimeout(async () => {
209
+ let t = null;
210
+ v(o, async (n) => {
211
+ n === "change" && (t && clearTimeout(t), t = setTimeout(async () => {
89
212
  console.log(`
90
213
  File changed, regenerating...`);
91
214
  try {
92
- const s = await x(o, e);
93
- !e.output && s ? console.log(s) : e.output && console.log(`DBML regenerated: ${e.output}`);
94
- } catch (s) {
95
- s instanceof Error ? console.error(`Error: ${s.message}`) : console.error("Error:", s);
215
+ const a = await L(o, e), i = e.format === "markdown" ? "Markdown" : "DBML", s = e.format === "markdown" && !e.singleFile && e.output;
216
+ if (!e.output && a)
217
+ console.log(a);
218
+ else if (e.output) {
219
+ const c = s ? `${e.output}/` : e.output;
220
+ console.log(`${i} regenerated: ${c}`);
221
+ }
222
+ } catch (a) {
223
+ a instanceof Error ? console.error(`Error: ${a.message}`) : console.error("Error:", a);
96
224
  }
97
225
  }, 100));
98
226
  });
99
227
  }
100
- i.command("generate").description("Generate DBML from Drizzle schema files").argument("<schema>", "Path to Drizzle schema file or directory").option("-o, --output <file>", "Output file path").option("-d, --dialect <dialect>", "Database dialect (postgresql, mysql, sqlite)", "postgresql").option("-r, --relational", "Use relations() definitions instead of foreign keys").option("-w, --watch", "Watch for file changes and regenerate").action(async (t, e) => {
228
+ h.command("generate").description("Generate documentation from Drizzle schema files").argument("<schema>", "Path to Drizzle schema file or directory").option("-o, --output <path>", "Output file or directory path").option("-d, --dialect <dialect>", "Database dialect (postgresql, mysql, sqlite)", "postgresql").option("-f, --format <format>", "Output format (dbml, markdown)", "dbml").option("-r, --relational", "Use relations() definitions instead of foreign keys").option("-w, --watch", "Watch for file changes and regenerate").option("--single-file", "Output Markdown as a single file (for markdown format)").option("--no-er-diagram", "Exclude ER diagram from Markdown output").option("--force", "Overwrite existing files without confirmation").action(async (r, e) => {
101
229
  const o = ["postgresql", "mysql", "sqlite"];
102
230
  o.includes(e.dialect) || (console.error(
103
231
  `Error: Invalid dialect "${e.dialect}". Valid options: ${o.join(", ")}`
104
- ), process.exit(1)), await F(t, e), e.watch && G(t, e);
232
+ ), process.exit(1));
233
+ const t = ["dbml", "markdown"];
234
+ t.includes(e.format) || (console.error(
235
+ `Error: Invalid format "${e.format}". Valid options: ${t.join(", ")}`
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")), await A(r, e), e.watch && V(r, e);
105
237
  });
106
- i.parse();
238
+ h.parse();
107
239
  process.on("exit", () => {
108
- j();
240
+ W();
109
241
  });
110
242
  //# sourceMappingURL=index.js.map
@@ -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 { existsSync, lstatSync, readdirSync, readFileSync, watch } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { pgGenerate, mysqlGenerate, sqliteGenerate } from \"../generator/index\";\nimport { register } from \"tsx/esm/api\";\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\";\n\ninterface GenerateCommandOptions {\n output?: string;\n dialect: Dialect;\n relational?: boolean;\n watch?: boolean;\n}\n\n/**\n * Get the generate function based on dialect\n */\nfunction getGenerator(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 * Generate DBML from a schema file\n */\nasync function generateDbml(\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 const generate = getGenerator(options.dialect);\n\n const dbml = generate({\n schema: schemaModule,\n out: options.output,\n relational: options.relational,\n source: schemaPath,\n });\n\n return dbml;\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 * 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 const generate = getGenerator(options.dialect);\n const dbml = generate({\n schema: mergedSchema,\n out: options.output,\n relational: options.relational,\n source: schemaPaths[0], // Use first file for source path\n });\n\n if (!options.output && dbml) {\n console.log(dbml);\n } else if (options.output) {\n console.log(`DBML generated: ${options.output}`);\n }\n } catch (error) {\n if (error instanceof Error) {\n console.error(`Error generating DBML: ${error.message}`);\n } else {\n console.error(\"Error generating DBML:\", error);\n }\n process.exit(1);\n }\n}\n\n/**\n * Watch mode: regenerate DBML 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 dbml = await generateDbml(schemaPath, options);\n\n if (!options.output && dbml) {\n console.log(dbml);\n } else if (options.output) {\n console.log(`DBML regenerated: ${options.output}`);\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 DBML from Drizzle schema files\")\n .argument(\"<schema>\", \"Path to Drizzle schema file or directory\")\n .option(\"-o, --output <file>\", \"Output file path\")\n .option(\"-d, --dialect <dialect>\", \"Database dialect (postgresql, mysql, sqlite)\", \"postgresql\")\n .option(\"-r, --relational\", \"Use relations() definitions instead of foreign keys\")\n .option(\"-w, --watch\", \"Watch for file changes and regenerate\")\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 // 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","getGenerator","dialect","mysqlGenerate","sqliteGenerate","pgGenerate","generateDbml","schemaPath","options","schemaUrl","pathToFileURL","cacheBuster","schemaModule","findSchemaFiles","dirPath","files","entries","readdirSync","entry","fullPath","error","resolveSchemaPath","schema","resolve","existsSync","lstatSync","schemaFiles","runGenerate","schemaPaths","mergedSchema","dbml","watchSchema","debounceTimer","watch","eventType","validDialects"],"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, mysqlGenerate, sqliteGenerate } from \"../generator/index\";\nimport { PgGenerator, MySqlGenerator, SqliteGenerator } from \"../generator/index\";\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 relational?: boolean;\n watch?: boolean;\n format: OutputFormat;\n singleFile?: boolean;\n erDiagram: boolean; // commander uses --no-er-diagram which sets erDiagram 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 relational: options.relational,\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 relational: options.relational,\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 relational: options.relational,\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 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 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 const generator = new GeneratorClass({\n schema: mergedSchema,\n relational: options.relational,\n source: schemaPaths[0],\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 (default)\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)\", \"dbml\")\n .option(\"-r, --relational\", \"Use relations() definitions instead of foreign keys\")\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(\"--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 or --no-er-diagram 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 }\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","t","f","dbml","watchSchema","debounceTimer","watch","eventType","output","formatLabel","isMultiFile","outputLabel","validDialects","validFormats"],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import { join } from 'node:path';
2
+ import { existsSync, mkdirSync, rmSync, readFileSync, writeFileSync } from 'node:fs';
3
+ export declare const EXAMPLES_DIR: string;
4
+ export declare const PG_SCHEMA_V0: string;
5
+ export declare const PG_SCHEMA_V1: string;
6
+ export declare const MYSQL_SCHEMA_V0: string;
7
+ export declare const MYSQL_SCHEMA_V1: string;
8
+ export declare const SQLITE_SCHEMA_V0: string;
9
+ export declare const SQLITE_SCHEMA_V1: string;
10
+ export declare const TEST_OUTPUT_DIR: string;
11
+ export declare const EXPECTED_TABLES: string[];
12
+ /**
13
+ * Setup function to ensure example schemas exist and create output directory.
14
+ * Call this in beforeAll() of tests that need schema files.
15
+ */
16
+ export declare function setupIntegrationTest(): void;
17
+ export { existsSync, mkdirSync, rmSync, readFileSync, writeFileSync };
18
+ export { join };
19
+ //# sourceMappingURL=integration-test-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integration-test-utils.d.ts","sourceRoot":"","sources":["../../src/cli/integration-test-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAIrF,eAAO,MAAM,YAAY,QAA8C,CAAC;AACxE,eAAO,MAAM,YAAY,QAAqC,CAAC;AAC/D,eAAO,MAAM,YAAY,QAAwC,CAAC;AAClE,eAAO,MAAM,eAAe,QAAwC,CAAC;AACrE,eAAO,MAAM,eAAe,QAA2C,CAAC;AACxE,eAAO,MAAM,gBAAgB,QAAyC,CAAC;AACvE,eAAO,MAAM,gBAAgB,QAA4C,CAAC;AAG1E,eAAO,MAAM,eAAe,QAA2D,CAAC;AAGxF,eAAO,MAAM,eAAe,UAAsD,CAAC;AAEnF;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAa3C;AAGD,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC;AACtE,OAAO,EAAE,IAAI,EAAE,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Simple DBML string builder
3
+ */
4
+ export declare class DbmlBuilder {
5
+ private lines;
6
+ private indentLevel;
7
+ /**
8
+ * Increase the indentation level by one
9
+ * @returns This instance for method chaining
10
+ */
11
+ indent(): this;
12
+ /**
13
+ * Decrease the indentation level by one (minimum 0)
14
+ * @returns This instance for method chaining
15
+ */
16
+ dedent(): this;
17
+ /**
18
+ * Add a line with the current indentation level
19
+ * @param content - The content to add (empty string adds a blank line)
20
+ * @returns This instance for method chaining
21
+ */
22
+ line(content?: string): this;
23
+ /**
24
+ * Build the final DBML string from all added lines
25
+ * @returns The complete DBML content as a string
26
+ */
27
+ build(): string;
28
+ }
29
+ //# sourceMappingURL=dbml-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dbml-builder.d.ts","sourceRoot":"","sources":["../../src/formatter/dbml-builder.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,WAAW,CAAK;IAExB;;;OAGG;IACH,MAAM,IAAI,IAAI;IAKd;;;OAGG;IACH,MAAM,IAAI,IAAI;IAKd;;;;OAIG;IACH,IAAI,CAAC,OAAO,GAAE,MAAW,GAAG,IAAI;IAMhC;;;OAGG;IACH,KAAK,IAAI,MAAM;CAGhB"}
@@ -0,0 +1,39 @@
1
+ class t {
2
+ lines = [];
3
+ indentLevel = 0;
4
+ /**
5
+ * Increase the indentation level by one
6
+ * @returns This instance for method chaining
7
+ */
8
+ indent() {
9
+ return this.indentLevel++, this;
10
+ }
11
+ /**
12
+ * Decrease the indentation level by one (minimum 0)
13
+ * @returns This instance for method chaining
14
+ */
15
+ dedent() {
16
+ return this.indentLevel = Math.max(0, this.indentLevel - 1), this;
17
+ }
18
+ /**
19
+ * Add a line with the current indentation level
20
+ * @param content - The content to add (empty string adds a blank line)
21
+ * @returns This instance for method chaining
22
+ */
23
+ line(e = "") {
24
+ const n = " ".repeat(this.indentLevel);
25
+ return this.lines.push(e ? `${n}${e}` : ""), this;
26
+ }
27
+ /**
28
+ * Build the final DBML string from all added lines
29
+ * @returns The complete DBML content as a string
30
+ */
31
+ build() {
32
+ return this.lines.join(`
33
+ `);
34
+ }
35
+ }
36
+ export {
37
+ t as DbmlBuilder
38
+ };
39
+ //# sourceMappingURL=dbml-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dbml-builder.js","sources":["../../src/formatter/dbml-builder.ts"],"sourcesContent":["/**\n * Simple DBML string builder\n */\nexport class DbmlBuilder {\n private lines: string[] = [];\n private indentLevel = 0;\n\n /**\n * Increase the indentation level by one\n * @returns This instance for method chaining\n */\n indent(): this {\n this.indentLevel++;\n return this;\n }\n\n /**\n * Decrease the indentation level by one (minimum 0)\n * @returns This instance for method chaining\n */\n dedent(): this {\n this.indentLevel = Math.max(0, this.indentLevel - 1);\n return this;\n }\n\n /**\n * Add a line with the current indentation level\n * @param content - The content to add (empty string adds a blank line)\n * @returns This instance for method chaining\n */\n line(content: string = \"\"): this {\n const indent = \" \".repeat(this.indentLevel);\n this.lines.push(content ? `${indent}${content}` : \"\");\n return this;\n }\n\n /**\n * Build the final DBML string from all added lines\n * @returns The complete DBML content as a string\n */\n build(): string {\n return this.lines.join(\"\\n\");\n }\n}\n"],"names":["DbmlBuilder","content","indent"],"mappings":"AAGO,MAAMA,EAAY;AAAA,EACf,QAAkB,CAAA;AAAA,EAClB,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA,EAMtB,SAAe;AACb,gBAAK,eACE;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,SAAe;AACb,gBAAK,cAAc,KAAK,IAAI,GAAG,KAAK,cAAc,CAAC,GAC5C;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,KAAKC,IAAkB,IAAU;AAC/B,UAAMC,IAAS,KAAK,OAAO,KAAK,WAAW;AAC3C,gBAAK,MAAM,KAAKD,IAAU,GAAGC,CAAM,GAAGD,CAAO,KAAK,EAAE,GAC7C;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,QAAgB;AACd,WAAO,KAAK,MAAM,KAAK;AAAA,CAAI;AAAA,EAC7B;AACF;"}
@@ -0,0 +1,81 @@
1
+ import { IntermediateSchema } from '../types';
2
+ import { OutputFormatter, FormatterOptions } from './types';
3
+ /**
4
+ * DbmlFormatter converts IntermediateSchema to DBML format
5
+ *
6
+ * This formatter wraps the existing DbmlBuilder to provide DBML output
7
+ * from the database-agnostic IntermediateSchema representation.
8
+ */
9
+ export declare class DbmlFormatter implements OutputFormatter {
10
+ private options;
11
+ private databaseType;
12
+ /**
13
+ * Create a new DbmlFormatter
14
+ *
15
+ * @param options - Formatter options
16
+ */
17
+ constructor(options?: FormatterOptions);
18
+ /**
19
+ * Format the intermediate schema into DBML
20
+ *
21
+ * @param schema - The intermediate schema to format
22
+ * @returns DBML string
23
+ */
24
+ format(schema: IntermediateSchema): string;
25
+ /**
26
+ * Format an enum definition to DBML
27
+ */
28
+ private formatEnum;
29
+ /**
30
+ * Format a table definition to DBML
31
+ */
32
+ private formatTable;
33
+ /**
34
+ * Format a column definition to DBML
35
+ */
36
+ private formatColumn;
37
+ /**
38
+ * Get column attributes for DBML
39
+ */
40
+ private getColumnAttributes;
41
+ /**
42
+ * Format a default value for DBML
43
+ *
44
+ * The value comes from IntermediateSchema in "raw" format:
45
+ * - SQL expressions: no wrapping (e.g., "now()", "gen_random_uuid()")
46
+ * - Strings: already wrapped with single quotes (e.g., "'user'")
47
+ * - Numbers/booleans: as-is (e.g., "true", "42")
48
+ * - null: "null"
49
+ */
50
+ private formatDefaultValue;
51
+ /**
52
+ * Check if a value is a known SQL function/expression
53
+ */
54
+ private isKnownSqlFunction;
55
+ /**
56
+ * Format indexes block to DBML
57
+ *
58
+ * Includes primary key constraints, unique constraints, and regular indexes.
59
+ */
60
+ private formatIndexesBlock;
61
+ /**
62
+ * Format a relation definition to DBML Ref
63
+ */
64
+ private formatRelation;
65
+ /**
66
+ * Convert IntermediateRelationType to DBML relation symbol
67
+ */
68
+ private getRelationType;
69
+ /**
70
+ * Escape a name for DBML based on database type
71
+ *
72
+ * - PostgreSQL/SQLite: Use double quotes ("name")
73
+ * - MySQL: Use backticks (`name`)
74
+ */
75
+ private escapeName;
76
+ /**
77
+ * Escape a string for use in DBML single-quoted strings
78
+ */
79
+ private escapeString;
80
+ }
81
+ //# sourceMappingURL=dbml.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dbml.d.ts","sourceRoot":"","sources":["../../src/formatter/dbml.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAQnB,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;IAC5C,OAAO,CAAC,YAAY,CAA8B;IAElD;;;;OAIG;gBACS,OAAO,GAAE,gBAAqB;IAI1C;;;;;OAKG;IACH,MAAM,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM;IA0B1C;;OAEG;IACH,OAAO,CAAC,UAAU;IAWlB;;OAEG;IACH,OAAO,CAAC,WAAW;IA8BnB;;OAEG;IACH,OAAO,CAAC,YAAY;IAapB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAyB3B;;;;;;;;OAQG;IACH,OAAO,CAAC,kBAAkB;IA2B1B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAc1B;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IA6C1B;;OAEG;IACH,OAAO,CAAC,cAAc;IAsBtB;;OAEG;IACH,OAAO,CAAC,eAAe;IAavB;;;;;OAKG;IACH,OAAO,CAAC,UAAU;IAQlB;;OAEG;IACH,OAAO,CAAC,YAAY;CAGrB"}