drizzle-docs-generator 0.1.0 → 0.2.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.
package/README.ja.md CHANGED
@@ -5,6 +5,14 @@
5
5
 
6
6
  Drizzle ORM スキーマから DBML を生成する CLI。JSDoc コメントを Note 句として出力できる。
7
7
 
8
+ **✨ 機能:**
9
+
10
+ - 📁 **ディレクトリインポート対応**: ディレクトリ内のすべてのスキーマファイルを自動インポート
11
+ - 🔄 **拡張子不要**: 拡張子なしのインポートに対応 (例: `import { users } from './users'`)
12
+ - 📝 **JSDoc コメント**: 自動的に DBML の Note 句に変換
13
+ - 🔗 **リレーション対応**: `relations()` または `defineRelations()` から参照を生成
14
+ - 👀 **Watch モード**: ファイル変更時に自動再生成
15
+
8
16
  [English README](./README.md)
9
17
 
10
18
  ## インストール
@@ -18,9 +26,12 @@ pnpm add -g drizzle-docs-generator
18
26
  ## 使い方
19
27
 
20
28
  ```bash
21
- # 基本
29
+ # 基本 - 単一ファイル
22
30
  drizzle-docs generate ./src/db/schema.ts -d postgresql
23
31
 
32
+ # ディレクトリ - ディレクトリ内のすべてのスキーマファイルをインポート
33
+ drizzle-docs generate ./src/db/schema/ -d postgresql
34
+
24
35
  # ファイル出力
25
36
  drizzle-docs generate ./src/db/schema.ts -d postgresql -o schema.dbml
26
37
 
@@ -83,6 +94,17 @@ const dbml = pgGenerate({
83
94
 
84
95
  - Node.js >= 24
85
96
  - Drizzle ORM v1 beta (1.0.0-beta.10+)
97
+ - ES Modules (ESM): プロジェクトで ESM を使用していること (`package.json` に `"type": "module"`)
98
+
99
+ ## 仕組み
100
+
101
+ このツールは [tsx](https://github.com/privatenumber/tsx) を使用してスキーマファイルを読み込むため:
102
+
103
+ ✅ **拡張子なしのインポートが動作**: `import { users } from './users'`
104
+ ✅ **TypeScript ファイルを直接読み込み**: コンパイル不要
105
+ ✅ **ディレクトリインポート**: ディレクトリ内のすべてのスキーマファイルを自動読み込み
106
+
107
+ スキーマファイルでは、ファイル拡張子を気にせず標準的な TypeScript のモジュール解決を使用できます。
86
108
 
87
109
  ## License
88
110
 
package/README.md CHANGED
@@ -5,6 +5,14 @@
5
5
 
6
6
  CLI tool to generate DBML from Drizzle ORM schemas. Extracts JSDoc comments and outputs them as Note clauses.
7
7
 
8
+ **✨ Features:**
9
+
10
+ - 📁 **Directory Import Support**: Import all schema files from a directory
11
+ - 🔄 **No File Extension Required**: Works with extensionless imports (e.g., `import { users } from './users'`)
12
+ - 📝 **JSDoc Comments**: Automatically extracts and converts to DBML Notes
13
+ - 🔗 **Relations Support**: Generate refs from `relations()` or `defineRelations()`
14
+ - 👀 **Watch Mode**: Auto-regenerate on file changes
15
+
8
16
  [日本語版READMEはこちら](./README.ja.md)
9
17
 
10
18
  ## Install
@@ -18,9 +26,12 @@ pnpm add -g drizzle-docs-generator
18
26
  ## Usage
19
27
 
20
28
  ```bash
21
- # Basic
29
+ # Basic - single file
22
30
  drizzle-docs generate ./src/db/schema.ts -d postgresql
23
31
 
32
+ # Directory - import all schema files from directory
33
+ drizzle-docs generate ./src/db/schema/ -d postgresql
34
+
24
35
  # Output to file
25
36
  drizzle-docs generate ./src/db/schema.ts -d postgresql -o schema.dbml
26
37
 
@@ -83,6 +94,17 @@ const dbml = pgGenerate({
83
94
 
84
95
  - Node.js >= 24
85
96
  - Drizzle ORM v1 beta (1.0.0-beta.10+)
97
+ - ES Modules (ESM): Your project must use ESM (`"type": "module"` in package.json)
98
+
99
+ ## How It Works
100
+
101
+ This tool uses [tsx](https://github.com/privatenumber/tsx) to load your schema files, which means:
102
+
103
+ ✅ **Extensionless imports work**: `import { users } from './users'`
104
+ ✅ **TypeScript files are loaded directly**: No need to compile first
105
+ ✅ **Directory imports**: Load all schema files from a directory automatically
106
+
107
+ Your schema files can use standard TypeScript module resolution without worrying about file extensions.
86
108
 
87
109
  ## License
88
110
 
package/dist/cli/index.js CHANGED
@@ -1,69 +1,110 @@
1
1
  #!/usr/bin/env node
2
- import { Command as m } from "commander";
3
- import { readFileSync as u, existsSync as f, watch as d } from "node:fs";
4
- import { join as g, resolve as i } from "node:path";
5
- import { pathToFileURL as p } from "node:url";
6
- import { pgGenerate as h } from "../generator/pg.js";
7
- import { mysqlGenerate as w } from "../generator/mysql.js";
8
- import { sqliteGenerate as y } from "../generator/sqlite.js";
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";
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
- const c = new m(), q = g(import.meta.dirname, "../../package.json"), s = JSON.parse(u(q, "utf-8"));
15
- c.name("drizzle-docs").description(s.description).version(s.version);
16
- function D(o) {
17
- switch (o) {
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) {
18
19
  case "mysql":
19
- return w;
20
+ return S;
20
21
  case "sqlite":
21
- return y;
22
+ return z;
22
23
  default:
23
- return h;
24
+ return b;
24
25
  }
25
26
  }
26
- async function l(o, e) {
27
- const r = p(o).href, t = e.watch ? `?t=${Date.now()}` : "", n = await import(r + t);
28
- return D(e.dialect)({
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)({
29
30
  schema: n,
30
31
  out: e.output,
31
32
  relational: e.relational,
32
- source: o
33
+ source: t
33
34
  });
34
35
  }
35
- async function $(o, e) {
36
- const r = i(process.cwd(), o);
37
- f(r) || (console.error(`Error: Schema file not found: ${r}`), process.exit(1));
36
+ function d(t) {
37
+ const e = [];
38
38
  try {
39
- const t = await l(r, e);
40
- !e.output && t ? console.log(t) : e.output && console.log(`DBML generated: ${e.output}`);
41
- } catch (t) {
42
- t instanceof Error ? console.error(`Error generating DBML: ${t.message}`) : console.error("Error generating DBML:", t), process.exit(1);
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);
43
+ }
44
+ } catch (o) {
45
+ o instanceof Error && console.error(`Error reading directory ${t}: ${o.message}`);
43
46
  }
47
+ return e;
44
48
  }
45
- function b(o, e) {
46
- const r = i(process.cwd(), o);
47
- console.log(`Watching for changes: ${r}`);
48
- let t = null;
49
- d(r, async (n) => {
50
- n === "change" && (t && clearTimeout(t), t = setTimeout(async () => {
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;
54
+ }
55
+ return [e];
56
+ }
57
+ async function F(t, e) {
58
+ const o = B(t);
59
+ try {
60
+ const r = {};
61
+ for (const a of o) {
62
+ const g = u(a).href, p = e.watch ? `?t=${Date.now()}` : "";
63
+ 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;
69
+ }
70
+ }
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);
81
+ }
82
+ }
83
+ function G(t, e) {
84
+ const o = f(process.cwd(), t);
85
+ console.log(`Watching for changes: ${o}`);
86
+ let r = null;
87
+ $(o, async (n) => {
88
+ n === "change" && (r && clearTimeout(r), r = setTimeout(async () => {
51
89
  console.log(`
52
90
  File changed, regenerating...`);
53
91
  try {
54
- const a = await l(r, e);
55
- !e.output && a ? console.log(a) : e.output && console.log(`DBML regenerated: ${e.output}`);
56
- } catch (a) {
57
- a instanceof Error ? console.error(`Error: ${a.message}`) : console.error("Error:", a);
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);
58
96
  }
59
97
  }, 100));
60
98
  });
61
99
  }
62
- c.command("generate").description("Generate DBML from Drizzle schema files").argument("<schema>", "Path to Drizzle schema file").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 (o, e) => {
63
- const r = ["postgresql", "mysql", "sqlite"];
64
- r.includes(e.dialect) || (console.error(
65
- `Error: Invalid dialect "${e.dialect}". Valid options: ${r.join(", ")}`
66
- ), process.exit(1)), await $(o, e), e.watch && b(o, e);
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) => {
101
+ const o = ["postgresql", "mysql", "sqlite"];
102
+ o.includes(e.dialect) || (console.error(
103
+ `Error: Invalid dialect "${e.dialect}". Valid options: ${o.join(", ")}`
104
+ ), process.exit(1)), await F(t, e), e.watch && G(t, e);
105
+ });
106
+ i.parse();
107
+ process.on("exit", () => {
108
+ j();
67
109
  });
68
- c.parse();
69
110
  //# 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, readFileSync, watch } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { pgGenerate, mysqlGenerate, sqliteGenerate } from \"../generator/index\";\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 * Run the generate command\n */\nasync function runGenerate(schema: string, options: GenerateCommandOptions): Promise<void> {\n const schemaPath = resolve(process.cwd(), schema);\n\n // Check if file exists\n if (!existsSync(schemaPath)) {\n console.error(`Error: Schema file not found: ${schemaPath}`);\n process.exit(1);\n }\n\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 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\")\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"],"names":["program","Command","packageJsonPath","join","packageJson","readFileSync","getGenerator","dialect","mysqlGenerate","sqliteGenerate","pgGenerate","generateDbml","schemaPath","options","schemaUrl","pathToFileURL","cacheBuster","schemaModule","runGenerate","schema","resolve","existsSync","dbml","error","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 { 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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "drizzle-docs-generator",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "A CLI tool that generates DBML files from Drizzle ORM schema definitions.",
5
5
  "keywords": [
6
6
  "cli",
@@ -38,13 +38,14 @@
38
38
  },
39
39
  "dependencies": {
40
40
  "commander": "^14.0.2",
41
- "drizzle-orm": "1.0.0-beta.10-42c284e"
41
+ "drizzle-orm": "1.0.0-beta.10-42c284e",
42
+ "tsx": "^4.21.0",
43
+ "typescript": "^5.9.3"
42
44
  },
43
45
  "devDependencies": {
44
46
  "@types/node": "^24.10.7",
45
47
  "oxfmt": "^0.23.0",
46
48
  "oxlint": "^1.38.0",
47
- "typescript": "^5.9.3",
48
49
  "vite": "^7.3.1",
49
50
  "vite-plugin-dts": "^4.5.4",
50
51
  "vitest": "^4.0.16"