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 +23 -1
- package/README.md +23 -1
- package/dist/cli/index.js +82 -41
- package/dist/cli/index.js.map +1 -1
- package/package.json +4 -3
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
|
|
3
|
-
import { readFileSync as
|
|
4
|
-
import { join as
|
|
5
|
-
import { pathToFileURL as
|
|
6
|
-
import { pgGenerate as
|
|
7
|
-
import { mysqlGenerate as
|
|
8
|
-
import { sqliteGenerate as
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
|
20
|
+
return S;
|
|
20
21
|
case "sqlite":
|
|
21
|
-
return
|
|
22
|
+
return z;
|
|
22
23
|
default:
|
|
23
|
-
return
|
|
24
|
+
return b;
|
|
24
25
|
}
|
|
25
26
|
}
|
|
26
|
-
async function
|
|
27
|
-
const
|
|
28
|
-
return
|
|
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:
|
|
33
|
+
source: t
|
|
33
34
|
});
|
|
34
35
|
}
|
|
35
|
-
|
|
36
|
-
const
|
|
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
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
|
46
|
-
const
|
|
47
|
-
console.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
|
55
|
-
!e.output &&
|
|
56
|
-
} catch (
|
|
57
|
-
|
|
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
|
-
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
`Error: Invalid dialect "${e.dialect}". Valid options: ${
|
|
66
|
-
), process.exit(1)), await
|
|
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
|
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 { 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.
|
|
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"
|