drizzle-docs-generator 0.4.0 → 0.5.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 +60 -57
- package/README.md +60 -57
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/integration-test-utils.d.ts.map +1 -1
- package/dist/formatter/dbml.d.ts +7 -4
- package/dist/formatter/dbml.d.ts.map +1 -1
- package/dist/formatter/dbml.js +10 -8
- package/dist/formatter/dbml.js.map +1 -1
- package/dist/formatter/markdown.d.ts +6 -0
- package/dist/formatter/markdown.d.ts.map +1 -1
- package/dist/formatter/markdown.js +10 -1
- package/dist/formatter/markdown.js.map +1 -1
- package/dist/parser/comments.js +55 -50
- package/dist/parser/comments.js.map +1 -1
- package/package.json +3 -3
package/README.ja.md
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
# drizzle-docs-generator
|
|
2
2
|
|
|
3
|
-
[](https://opensource.org/licenses/MIT)
|
|
3
|
+
[](https://nodei.co/npm/drizzle-docs-generator/)
|
|
5
4
|
|
|
6
|
-
Drizzle ORM スキーマから DBML
|
|
5
|
+
Drizzle ORM スキーマから DBML と Markdown ドキュメントを生成する CLI。JSDoc コメントを Note 句として出力できる。
|
|
7
6
|
|
|
8
7
|
**機能:**
|
|
9
8
|
|
|
@@ -12,62 +11,97 @@ Drizzle ORM スキーマから DBML を生成する CLI。JSDoc コメントを
|
|
|
12
11
|
- **JSDoc コメント**: 自動的に DBML の Note 句に変換
|
|
13
12
|
- **リレーション対応**: `relations()` または `defineRelations()` から参照を生成
|
|
14
13
|
- **Watch モード**: ファイル変更時に自動再生成
|
|
15
|
-
- **複数の出力形式**:
|
|
14
|
+
- **複数の出力形式**: Markdown (デフォルト) および ER 図付き DBML
|
|
16
15
|
|
|
17
16
|
[English README](./README.md)
|
|
18
17
|
|
|
19
18
|
## インストール
|
|
20
19
|
|
|
20
|
+
### ローカルインストール(推奨)
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# 開発依存関係としてインストール
|
|
24
|
+
npm install --save-dev drizzle-docs-generator
|
|
25
|
+
# or
|
|
26
|
+
pnpm add -D drizzle-docs-generator
|
|
27
|
+
|
|
28
|
+
# npx で実行
|
|
29
|
+
npx drizzle-docs generate ./src/db/schema.ts -d postgresql
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### グローバルインストール
|
|
33
|
+
|
|
21
34
|
```bash
|
|
22
35
|
npm install -g drizzle-docs-generator
|
|
23
36
|
# or
|
|
24
37
|
pnpm add -g drizzle-docs-generator
|
|
38
|
+
|
|
39
|
+
drizzle-docs generate ./src/db/schema.ts -d postgresql
|
|
25
40
|
```
|
|
26
41
|
|
|
27
42
|
## 使い方
|
|
28
43
|
|
|
29
|
-
###
|
|
44
|
+
### 基本的な使用方法
|
|
30
45
|
|
|
31
46
|
```bash
|
|
32
|
-
#
|
|
33
|
-
drizzle-docs generate ./src/db/schema.ts -d postgresql
|
|
47
|
+
# Markdown 出力 (デフォルト)
|
|
48
|
+
drizzle-docs generate ./src/db/schema.ts -d postgresql -o ./docs
|
|
34
49
|
|
|
35
|
-
#
|
|
36
|
-
drizzle-docs generate ./src/db/schema
|
|
50
|
+
# DBML 出力
|
|
51
|
+
drizzle-docs generate ./src/db/schema.ts -d postgresql -f dbml -o schema.dbml
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 出力形式のオプション
|
|
55
|
+
|
|
56
|
+
#### Markdown 形式 (デフォルト)
|
|
57
|
+
|
|
58
|
+
デフォルトの出力形式は **Markdown** で、ER 図付きの複数ファイルを生成します。
|
|
59
|
+
|
|
60
|
+
**Markdown 形式固有のオプション:**
|
|
61
|
+
|
|
62
|
+
| オプション | 説明 |
|
|
63
|
+
| ----------------- | -------------------------------------- |
|
|
64
|
+
| `--single-file` | 複数ファイルではなく単一ファイルで出力 |
|
|
65
|
+
| `--no-er-diagram` | 出力から ER 図を除外 |
|
|
66
|
+
|
|
67
|
+
**例:**
|
|
37
68
|
|
|
38
|
-
|
|
39
|
-
|
|
69
|
+
```bash
|
|
70
|
+
# ER 図付き複数ファイル (デフォルト)
|
|
71
|
+
drizzle-docs generate ./src/db/schema.ts -d postgresql -o ./docs
|
|
40
72
|
|
|
41
|
-
#
|
|
42
|
-
drizzle-docs generate ./src/db/schema.ts -d postgresql -
|
|
73
|
+
# 単一ファイル Markdown
|
|
74
|
+
drizzle-docs generate ./src/db/schema.ts -d postgresql --single-file -o schema.md
|
|
43
75
|
|
|
44
|
-
#
|
|
45
|
-
drizzle-docs generate ./src/db/schema.ts -d postgresql -
|
|
76
|
+
# ER 図なしの複数ファイル
|
|
77
|
+
drizzle-docs generate ./src/db/schema.ts -d postgresql --no-er-diagram -o ./docs
|
|
46
78
|
```
|
|
47
79
|
|
|
48
|
-
|
|
80
|
+
#### DBML 形式
|
|
81
|
+
|
|
82
|
+
`-f dbml` または `--format dbml` オプションを使用して DBML 形式を生成します。
|
|
83
|
+
|
|
84
|
+
**例:**
|
|
49
85
|
|
|
50
86
|
```bash
|
|
51
|
-
#
|
|
52
|
-
drizzle-docs generate ./src/db/schema.ts -d postgresql -f
|
|
87
|
+
# ファイルに出力
|
|
88
|
+
drizzle-docs generate ./src/db/schema.ts -d postgresql -f dbml -o schema.dbml
|
|
53
89
|
|
|
54
|
-
#
|
|
55
|
-
drizzle-docs generate ./src/db/schema
|
|
90
|
+
# ディレクトリ - ディレクトリ内のすべてのスキーマファイルをインポート
|
|
91
|
+
drizzle-docs generate ./src/db/schema/ -d postgresql -f dbml -o schema.dbml
|
|
56
92
|
|
|
57
|
-
#
|
|
58
|
-
drizzle-docs generate ./src/db/schema.ts -d postgresql -f
|
|
93
|
+
# Watch モード
|
|
94
|
+
drizzle-docs generate ./src/db/schema.ts -d postgresql -f dbml -w
|
|
59
95
|
```
|
|
60
96
|
|
|
61
|
-
|
|
97
|
+
#### 共通オプション
|
|
62
98
|
|
|
63
99
|
| オプション | 説明 |
|
|
64
100
|
| ------------------------- | ----------------------------------------------------- |
|
|
65
101
|
| `-o, --output <path>` | 出力ファイルまたはディレクトリパス |
|
|
66
102
|
| `-d, --dialect <dialect>` | DB 種別: `postgresql` (デフォルト), `mysql`, `sqlite` |
|
|
67
|
-
| `-f, --format <format>` | 出力形式: `
|
|
103
|
+
| `-f, --format <format>` | 出力形式: `markdown` (デフォルト), `dbml` |
|
|
68
104
|
| `-w, --watch` | ファイル変更時に自動再生成 |
|
|
69
|
-
| `--single-file` | Markdown を単一ファイルで出力 (markdown のみ) |
|
|
70
|
-
| `--no-er-diagram` | ER 図を Markdown 出力から除外 |
|
|
71
105
|
| `--force` | 確認なしで既存ファイルを上書き |
|
|
72
106
|
|
|
73
107
|
### リレーション検出
|
|
@@ -119,37 +153,6 @@ Table users {
|
|
|
119
153
|
|
|
120
154
|
詳細なサンプル出力は [examples/](./examples/) を参照してください。
|
|
121
155
|
|
|
122
|
-
## API
|
|
123
|
-
|
|
124
|
-
```typescript
|
|
125
|
-
import { pgGenerate } from "drizzle-docs-generator";
|
|
126
|
-
import * as schema from "./schema";
|
|
127
|
-
|
|
128
|
-
const dbml = pgGenerate({
|
|
129
|
-
schema,
|
|
130
|
-
source: "./schema.ts", // JSDoc コメントと v0 relations() 検出用
|
|
131
|
-
out: "./output.dbml", // optional
|
|
132
|
-
});
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
`mysqlGenerate`, `sqliteGenerate` も同様。
|
|
136
|
-
|
|
137
|
-
## 動作環境
|
|
138
|
-
|
|
139
|
-
- Node.js >= 24
|
|
140
|
-
- Drizzle ORM v1 beta (1.0.0-beta.10+)
|
|
141
|
-
- ES Modules (ESM): プロジェクトで ESM を使用していること (`package.json` に `"type": "module"`)
|
|
142
|
-
|
|
143
|
-
## 仕組み
|
|
144
|
-
|
|
145
|
-
このツールは [tsx](https://github.com/privatenumber/tsx) を使用してスキーマファイルを読み込むため:
|
|
146
|
-
|
|
147
|
-
✅ **拡張子なしのインポートが動作**: `import { users } from './users'`
|
|
148
|
-
✅ **TypeScript ファイルを直接読み込み**: コンパイル不要
|
|
149
|
-
✅ **ディレクトリインポート**: ディレクトリ内のすべてのスキーマファイルを自動読み込み
|
|
150
|
-
|
|
151
|
-
スキーマファイルでは、ファイル拡張子を気にせず標準的な TypeScript のモジュール解決を使用できます。
|
|
152
|
-
|
|
153
156
|
## License
|
|
154
157
|
|
|
155
158
|
MIT
|
package/README.md
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
# drizzle-docs-generator
|
|
2
2
|
|
|
3
|
-
[](https://opensource.org/licenses/MIT)
|
|
3
|
+
[](https://nodei.co/npm/drizzle-docs-generator/)
|
|
5
4
|
|
|
6
|
-
CLI tool to generate DBML from Drizzle ORM schemas. Extracts JSDoc comments and outputs them as Note clauses.
|
|
5
|
+
CLI tool to generate DBML and Markdown documentation from Drizzle ORM schemas. Extracts JSDoc comments and outputs them as Note clauses.
|
|
7
6
|
|
|
8
7
|
**Features:**
|
|
9
8
|
|
|
@@ -12,62 +11,97 @@ CLI tool to generate DBML from Drizzle ORM schemas. Extracts JSDoc comments and
|
|
|
12
11
|
- **JSDoc Comments**: Automatically extracts and converts to DBML Notes
|
|
13
12
|
- **Relations Support**: Generate refs from `relations()` or `defineRelations()`
|
|
14
13
|
- **Watch Mode**: Auto-regenerate on file changes
|
|
15
|
-
- **Multiple Output Formats**:
|
|
14
|
+
- **Multiple Output Formats**: Markdown (default) and DBML with ER diagrams
|
|
16
15
|
|
|
17
16
|
[日本語版READMEはこちら](./README.ja.md)
|
|
18
17
|
|
|
19
18
|
## Install
|
|
20
19
|
|
|
20
|
+
### Local Install (recommended)
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# As a dev dependency
|
|
24
|
+
npm install --save-dev drizzle-docs-generator
|
|
25
|
+
# or
|
|
26
|
+
pnpm add -D drizzle-docs-generator
|
|
27
|
+
|
|
28
|
+
# Then use with npx
|
|
29
|
+
npx drizzle-docs generate ./src/db/schema.ts -d postgresql
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Global Install
|
|
33
|
+
|
|
21
34
|
```bash
|
|
22
35
|
npm install -g drizzle-docs-generator
|
|
23
36
|
# or
|
|
24
37
|
pnpm add -g drizzle-docs-generator
|
|
38
|
+
|
|
39
|
+
drizzle-docs generate ./src/db/schema.ts -d postgresql
|
|
25
40
|
```
|
|
26
41
|
|
|
27
42
|
## Usage
|
|
28
43
|
|
|
29
|
-
###
|
|
44
|
+
### Basic Usage
|
|
30
45
|
|
|
31
46
|
```bash
|
|
32
|
-
#
|
|
33
|
-
drizzle-docs generate ./src/db/schema.ts -d postgresql
|
|
47
|
+
# Markdown output (default)
|
|
48
|
+
drizzle-docs generate ./src/db/schema.ts -d postgresql -o ./docs
|
|
34
49
|
|
|
35
|
-
#
|
|
36
|
-
drizzle-docs generate ./src/db/schema
|
|
50
|
+
# DBML output
|
|
51
|
+
drizzle-docs generate ./src/db/schema.ts -d postgresql -f dbml -o schema.dbml
|
|
52
|
+
```
|
|
37
53
|
|
|
38
|
-
|
|
39
|
-
drizzle-docs generate ./src/db/schema.ts -d postgresql -o schema.dbml
|
|
54
|
+
### Output Format Options
|
|
40
55
|
|
|
41
|
-
|
|
42
|
-
drizzle-docs generate ./src/db/schema.ts -d postgresql -r
|
|
56
|
+
#### Markdown Format (Default)
|
|
43
57
|
|
|
44
|
-
|
|
45
|
-
|
|
58
|
+
The default output format is **Markdown**, which generates multiple files with an ER diagram.
|
|
59
|
+
|
|
60
|
+
**Options specific to Markdown format:**
|
|
61
|
+
|
|
62
|
+
| Option | Description |
|
|
63
|
+
| ----------------- | ------------------------------------------- |
|
|
64
|
+
| `--single-file` | Output as a single file instead of multiple |
|
|
65
|
+
| `--no-er-diagram` | Exclude ER diagram from output |
|
|
66
|
+
|
|
67
|
+
**Examples:**
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# Multiple files with ER diagram (default)
|
|
71
|
+
drizzle-docs generate ./src/db/schema.ts -d postgresql -o ./docs
|
|
72
|
+
|
|
73
|
+
# Single file Markdown
|
|
74
|
+
drizzle-docs generate ./src/db/schema.ts -d postgresql --single-file -o schema.md
|
|
75
|
+
|
|
76
|
+
# Multiple files without ER diagram
|
|
77
|
+
drizzle-docs generate ./src/db/schema.ts -d postgresql --no-er-diagram -o ./docs
|
|
46
78
|
```
|
|
47
79
|
|
|
48
|
-
|
|
80
|
+
#### DBML Format
|
|
81
|
+
|
|
82
|
+
Use the `-f dbml` or `--format dbml` option to generate DBML format.
|
|
83
|
+
|
|
84
|
+
**Examples:**
|
|
49
85
|
|
|
50
86
|
```bash
|
|
51
|
-
#
|
|
52
|
-
drizzle-docs generate ./src/db/schema.ts -d postgresql -f
|
|
87
|
+
# Output to file
|
|
88
|
+
drizzle-docs generate ./src/db/schema.ts -d postgresql -f dbml -o schema.dbml
|
|
53
89
|
|
|
54
|
-
#
|
|
55
|
-
drizzle-docs generate ./src/db/schema
|
|
90
|
+
# Directory - import all schema files from directory
|
|
91
|
+
drizzle-docs generate ./src/db/schema/ -d postgresql -f dbml -o schema.dbml
|
|
56
92
|
|
|
57
|
-
#
|
|
58
|
-
drizzle-docs generate ./src/db/schema.ts -d postgresql -f
|
|
93
|
+
# Watch mode
|
|
94
|
+
drizzle-docs generate ./src/db/schema.ts -d postgresql -f dbml -w
|
|
59
95
|
```
|
|
60
96
|
|
|
61
|
-
|
|
97
|
+
#### Common Options
|
|
62
98
|
|
|
63
99
|
| Option | Description |
|
|
64
100
|
| ------------------------- | --------------------------------------------------- |
|
|
65
101
|
| `-o, --output <path>` | Output file or directory path |
|
|
66
102
|
| `-d, --dialect <dialect>` | Database: `postgresql` (default), `mysql`, `sqlite` |
|
|
67
|
-
| `-f, --format <format>` | Output format: `
|
|
103
|
+
| `-f, --format <format>` | Output format: `markdown` (default), `dbml` |
|
|
68
104
|
| `-w, --watch` | Regenerate on file changes |
|
|
69
|
-
| `--single-file` | Output Markdown as a single file (markdown only) |
|
|
70
|
-
| `--no-er-diagram` | Exclude ER diagram from Markdown output |
|
|
71
105
|
| `--force` | Overwrite existing files without confirmation |
|
|
72
106
|
|
|
73
107
|
### Relation Detection
|
|
@@ -119,37 +153,6 @@ Users table
|
|
|
119
153
|
|
|
120
154
|
See [examples/](./examples/) for more detailed output samples.
|
|
121
155
|
|
|
122
|
-
## API
|
|
123
|
-
|
|
124
|
-
```typescript
|
|
125
|
-
import { pgGenerate } from "drizzle-docs-generator";
|
|
126
|
-
import * as schema from "./schema";
|
|
127
|
-
|
|
128
|
-
const dbml = pgGenerate({
|
|
129
|
-
schema,
|
|
130
|
-
source: "./schema.ts", // for JSDoc comments and v0 relations() detection
|
|
131
|
-
out: "./output.dbml", // optional
|
|
132
|
-
});
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
`mysqlGenerate`, `sqliteGenerate` are also available.
|
|
136
|
-
|
|
137
|
-
## Requirements
|
|
138
|
-
|
|
139
|
-
- Node.js >= 24
|
|
140
|
-
- Drizzle ORM v1 beta (1.0.0-beta.10+)
|
|
141
|
-
- ES Modules (ESM): Your project must use ESM (`"type": "module"` in package.json)
|
|
142
|
-
|
|
143
|
-
## How It Works
|
|
144
|
-
|
|
145
|
-
This tool uses [tsx](https://github.com/privatenumber/tsx) to load your schema files, which means:
|
|
146
|
-
|
|
147
|
-
✅ **Extensionless imports work**: `import { users } from './users'`
|
|
148
|
-
✅ **TypeScript files are loaded directly**: No need to compile first
|
|
149
|
-
✅ **Directory imports**: Load all schema files from a directory automatically
|
|
150
|
-
|
|
151
|
-
Your schema files can use standard TypeScript module resolution without worrying about file extensions.
|
|
152
|
-
|
|
153
156
|
## License
|
|
154
157
|
|
|
155
158
|
MIT
|
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * CLI for drizzle-docs-generator\n *\n * Generates DBML files from Drizzle ORM schema definitions.\n */\n\nimport { Command } from \"commander\";\nimport {\n existsSync,\n lstatSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n watch,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { pgGenerate } from \"../generator/pg\";\nimport { mysqlGenerate } from \"../generator/mysql\";\nimport { sqliteGenerate } from \"../generator/sqlite\";\nimport { PgGenerator } from \"../generator/pg\";\nimport { MySqlGenerator } from \"../generator/mysql\";\nimport { SqliteGenerator } from \"../generator/sqlite\";\nimport { MarkdownFormatter } from \"../formatter/markdown\";\nimport { MermaidErDiagramFormatter } from \"../formatter/mermaid\";\nimport { register } from \"tsx/esm/api\";\nimport type { IntermediateSchema } from \"../types\";\n\n// Register tsx loader to support TypeScript and extensionless imports\nconst unregister = register();\n\nconst program = new Command();\n\n// Use import.meta.dirname (Node 20.11+) to resolve package.json\n// This works correctly even after bundling\nconst packageJsonPath = join(import.meta.dirname, \"../../package.json\");\nconst packageJson = JSON.parse(readFileSync(packageJsonPath, \"utf-8\")) as {\n version: string;\n description: string;\n};\n\nprogram.name(\"drizzle-docs\").description(packageJson.description).version(packageJson.version);\n\ntype Dialect = \"postgresql\" | \"mysql\" | \"sqlite\";\ntype OutputFormat = \"dbml\" | \"markdown\";\n\ninterface GenerateCommandOptions {\n output?: string;\n dialect: Dialect;\n watch?: boolean;\n format: OutputFormat;\n singleFile?: boolean;\n erDiagram: boolean; // commander uses --no-er-diagram which sets erDiagram to false\n force?: boolean; // skip overwrite confirmation for existing files\n}\n\n/**\n * Get the generate function based on dialect\n */\nfunction getGenerateFunction(dialect: Dialect) {\n switch (dialect) {\n case \"mysql\":\n return mysqlGenerate;\n case \"sqlite\":\n return sqliteGenerate;\n case \"postgresql\":\n default:\n return pgGenerate;\n }\n}\n\n/**\n * Get the generator class based on dialect\n */\nfunction getGeneratorClass(dialect: Dialect) {\n switch (dialect) {\n case \"mysql\":\n return MySqlGenerator;\n case \"sqlite\":\n return SqliteGenerator;\n case \"postgresql\":\n default:\n return PgGenerator;\n }\n}\n\n/**\n * Check if output directory has existing files\n */\nfunction hasExistingFiles(outputDir: string, tableNames: string[]): string[] {\n const existingFiles: string[] = [];\n\n if (!existsSync(outputDir)) {\n return existingFiles;\n }\n\n const readmePath = join(outputDir, \"README.md\");\n if (existsSync(readmePath)) {\n existingFiles.push(readmePath);\n }\n\n for (const tableName of tableNames) {\n const tablePath = join(outputDir, `${tableName}.md`);\n if (existsSync(tablePath)) {\n existingFiles.push(tablePath);\n }\n }\n\n return existingFiles;\n}\n\n/**\n * Generate output from a schema file (for watch mode)\n * Returns the generated output string for single-file formats,\n * or writes multiple files directly for multi-file markdown\n */\nasync function generateFromSchema(\n schemaPath: string,\n options: GenerateCommandOptions,\n): Promise<string | undefined> {\n // Use file URL for dynamic import (required for ESM)\n const schemaUrl = pathToFileURL(schemaPath).href;\n\n // Dynamic import with cache busting for watch mode\n const cacheBuster = options.watch ? `?t=${Date.now()}` : \"\";\n const schemaModule = (await import(schemaUrl + cacheBuster)) as Record<string, unknown>;\n\n if (options.format === \"markdown\") {\n const GeneratorClass = getGeneratorClass(options.dialect);\n const generator = new GeneratorClass({\n schema: schemaModule,\n source: schemaPath,\n });\n const intermediateSchema = generator.toIntermediateSchema();\n\n // Handle multi-file output in watch mode\n if (!options.singleFile && options.output) {\n writeMarkdownMultipleFiles(intermediateSchema, options.output, options);\n return undefined;\n }\n\n return generateMarkdownOutput(intermediateSchema, options);\n } else {\n const generate = getGenerateFunction(options.dialect);\n return generate({\n schema: schemaModule,\n source: schemaPath,\n });\n }\n}\n\n/**\n * Find all TypeScript schema files in a directory\n */\nfunction findSchemaFiles(dirPath: string): string[] {\n const files: string[] = [];\n\n try {\n const entries = readdirSync(dirPath, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = join(dirPath, entry.name);\n\n if (entry.isDirectory()) {\n // Recursively search subdirectories\n files.push(...findSchemaFiles(fullPath));\n } else if (entry.isFile() && /\\.(ts|js|mts|mjs|cts|cjs)$/.test(entry.name)) {\n files.push(fullPath);\n }\n }\n } catch (error) {\n if (error instanceof Error) {\n console.error(`Error reading directory ${dirPath}: ${error.message}`);\n }\n }\n\n return files;\n}\n\n/**\n * Resolve schema path (file or directory)\n */\nfunction resolveSchemaPath(schema: string): string[] {\n const schemaPath = resolve(process.cwd(), schema);\n\n // Check if path exists\n if (!existsSync(schemaPath)) {\n console.error(`Error: Schema path not found: ${schemaPath}`);\n process.exit(1);\n }\n\n // Check if it's a directory\n const stats = lstatSync(schemaPath);\n if (stats.isDirectory()) {\n const schemaFiles = findSchemaFiles(schemaPath);\n if (schemaFiles.length === 0) {\n console.error(`Error: No schema files found in directory: ${schemaPath}`);\n process.exit(1);\n }\n return schemaFiles;\n }\n\n // Single file\n return [schemaPath];\n}\n\n/**\n * Generate DBML format output\n */\nfunction generateDbmlOutput(\n mergedSchema: Record<string, unknown>,\n schemaPaths: string[],\n options: GenerateCommandOptions,\n): string {\n const generate = getGenerateFunction(options.dialect);\n return (\n generate({\n schema: mergedSchema,\n source: schemaPaths[0],\n }) || \"\"\n );\n}\n\n/**\n * Generate Markdown format output\n */\nfunction generateMarkdownOutput(\n intermediateSchema: IntermediateSchema,\n options: GenerateCommandOptions,\n): string {\n const markdownFormatter = new MarkdownFormatter();\n const markdown = markdownFormatter.format(intermediateSchema);\n\n // Include ER diagram unless --no-er-diagram is specified\n if (options.erDiagram) {\n const mermaidFormatter = new MermaidErDiagramFormatter();\n 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 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(\"-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","dbml","watchSchema","debounceTimer","watch","eventType","output","formatLabel","isMultiFile","outputLabel","validDialects","validFormats"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * CLI for drizzle-docs-generator\n *\n * Generates DBML files from Drizzle ORM schema definitions.\n */\n\nimport { Command } from \"commander\";\nimport {\n existsSync,\n lstatSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n watch,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { pgGenerate } from \"../generator/pg\";\nimport { mysqlGenerate } from \"../generator/mysql\";\nimport { sqliteGenerate } from \"../generator/sqlite\";\nimport { PgGenerator } from \"../generator/pg\";\nimport { MySqlGenerator } from \"../generator/mysql\";\nimport { SqliteGenerator } from \"../generator/sqlite\";\nimport { MarkdownFormatter } from \"../formatter/markdown\";\nimport { MermaidErDiagramFormatter } from \"../formatter/mermaid\";\nimport { register } from \"tsx/esm/api\";\nimport type { IntermediateSchema } from \"../types\";\n\n// Register tsx loader to support TypeScript and extensionless imports\nconst unregister = register();\n\nconst program = new Command();\n\n// Use import.meta.dirname (Node 20.11+) to resolve package.json\n// This works correctly even after bundling\nconst packageJsonPath = join(import.meta.dirname, \"../../package.json\");\nconst packageJson = JSON.parse(readFileSync(packageJsonPath, \"utf-8\")) as {\n version: string;\n description: string;\n};\n\nprogram.name(\"drizzle-docs\").description(packageJson.description).version(packageJson.version);\n\ntype Dialect = \"postgresql\" | \"mysql\" | \"sqlite\";\ntype OutputFormat = \"dbml\" | \"markdown\";\n\ninterface GenerateCommandOptions {\n output?: string;\n dialect: Dialect;\n watch?: boolean;\n format: OutputFormat;\n singleFile?: boolean;\n erDiagram: boolean; // commander uses --no-er-diagram which sets erDiagram to false\n force?: boolean; // skip overwrite confirmation for existing files\n}\n\n/**\n * Get the generate function based on dialect\n */\nfunction getGenerateFunction(dialect: Dialect) {\n switch (dialect) {\n case \"mysql\":\n return mysqlGenerate;\n case \"sqlite\":\n return sqliteGenerate;\n case \"postgresql\":\n default:\n return pgGenerate;\n }\n}\n\n/**\n * Get the generator class based on dialect\n */\nfunction getGeneratorClass(dialect: Dialect) {\n switch (dialect) {\n case \"mysql\":\n return MySqlGenerator;\n case \"sqlite\":\n return SqliteGenerator;\n case \"postgresql\":\n default:\n return PgGenerator;\n }\n}\n\n/**\n * Check if output directory has existing files\n */\nfunction hasExistingFiles(outputDir: string, tableNames: string[]): string[] {\n const existingFiles: string[] = [];\n\n if (!existsSync(outputDir)) {\n return existingFiles;\n }\n\n const readmePath = join(outputDir, \"README.md\");\n if (existsSync(readmePath)) {\n existingFiles.push(readmePath);\n }\n\n for (const tableName of tableNames) {\n const tablePath = join(outputDir, `${tableName}.md`);\n if (existsSync(tablePath)) {\n existingFiles.push(tablePath);\n }\n }\n\n return existingFiles;\n}\n\n/**\n * Generate output from a schema file (for watch mode)\n * Returns the generated output string for single-file formats,\n * or writes multiple files directly for multi-file markdown\n */\nasync function generateFromSchema(\n schemaPath: string,\n options: GenerateCommandOptions,\n): Promise<string | undefined> {\n // Use file URL for dynamic import (required for ESM)\n const schemaUrl = pathToFileURL(schemaPath).href;\n\n // Dynamic import with cache busting for watch mode\n const cacheBuster = options.watch ? `?t=${Date.now()}` : \"\";\n const schemaModule = (await import(schemaUrl + cacheBuster)) as Record<string, unknown>;\n\n if (options.format === \"markdown\") {\n const GeneratorClass = getGeneratorClass(options.dialect);\n const generator = new GeneratorClass({\n schema: schemaModule,\n source: schemaPath,\n });\n const intermediateSchema = generator.toIntermediateSchema();\n\n // Handle multi-file output in watch mode\n if (!options.singleFile && options.output) {\n writeMarkdownMultipleFiles(intermediateSchema, options.output, options);\n return undefined;\n }\n\n return generateMarkdownOutput(intermediateSchema, options);\n } else {\n const generate = getGenerateFunction(options.dialect);\n return generate({\n schema: schemaModule,\n source: schemaPath,\n });\n }\n}\n\n/**\n * Find all TypeScript schema files in a directory\n */\nfunction findSchemaFiles(dirPath: string): string[] {\n const files: string[] = [];\n\n try {\n const entries = readdirSync(dirPath, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = join(dirPath, entry.name);\n\n if (entry.isDirectory()) {\n // Recursively search subdirectories\n files.push(...findSchemaFiles(fullPath));\n } else if (entry.isFile() && /\\.(ts|js|mts|mjs|cts|cjs)$/.test(entry.name)) {\n files.push(fullPath);\n }\n }\n } catch (error) {\n if (error instanceof Error) {\n console.error(`Error reading directory ${dirPath}: ${error.message}`);\n }\n }\n\n return files;\n}\n\n/**\n * Resolve schema path (file or directory)\n */\nfunction resolveSchemaPath(schema: string): string[] {\n const schemaPath = resolve(process.cwd(), schema);\n\n // Check if path exists\n if (!existsSync(schemaPath)) {\n console.error(`Error: Schema path not found: ${schemaPath}`);\n process.exit(1);\n }\n\n // Check if it's a directory\n const stats = lstatSync(schemaPath);\n if (stats.isDirectory()) {\n const schemaFiles = findSchemaFiles(schemaPath);\n if (schemaFiles.length === 0) {\n console.error(`Error: No schema files found in directory: ${schemaPath}`);\n process.exit(1);\n }\n return schemaFiles;\n }\n\n // Single file\n return [schemaPath];\n}\n\n/**\n * Generate DBML format output\n */\nfunction generateDbmlOutput(\n mergedSchema: Record<string, unknown>,\n schemaPaths: string[],\n options: GenerateCommandOptions,\n): string {\n const generate = getGenerateFunction(options.dialect);\n return (\n generate({\n schema: mergedSchema,\n source: schemaPaths[0],\n }) || \"\"\n );\n}\n\n/**\n * Generate Markdown format output\n */\nfunction generateMarkdownOutput(\n intermediateSchema: IntermediateSchema,\n options: GenerateCommandOptions,\n): string {\n const markdownFormatter = new MarkdownFormatter();\n const markdown = markdownFormatter.format(intermediateSchema);\n\n // Include ER diagram unless --no-er-diagram is specified\n if (options.erDiagram) {\n const mermaidFormatter = new MermaidErDiagramFormatter();\n 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 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\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(\"-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","dbml","watchSchema","debounceTimer","watch","eventType","output","formatLabel","isMultiFile","outputLabel","validDialects","validFormats"],"mappings":"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"}
|
|
@@ -1 +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,
|
|
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,QAAwC,CAAC;AAClE,eAAO,MAAM,YAAY,QAAwC,CAAC;AAClE,eAAO,MAAM,eAAe,QAA2C,CAAC;AACxE,eAAO,MAAM,eAAe,QAA2C,CAAC;AACxE,eAAO,MAAM,gBAAgB,QAA4C,CAAC;AAC1E,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"}
|
package/dist/formatter/dbml.d.ts
CHANGED
|
@@ -8,7 +8,6 @@ import { OutputFormatter, FormatterOptions } from './types';
|
|
|
8
8
|
*/
|
|
9
9
|
export declare class DbmlFormatter implements OutputFormatter {
|
|
10
10
|
private options;
|
|
11
|
-
private databaseType;
|
|
12
11
|
/**
|
|
13
12
|
* Create a new DbmlFormatter
|
|
14
13
|
*
|
|
@@ -46,6 +45,11 @@ export declare class DbmlFormatter implements OutputFormatter {
|
|
|
46
45
|
* - Strings: already wrapped with single quotes (e.g., "'user'")
|
|
47
46
|
* - Numbers/booleans: as-is (e.g., "true", "42")
|
|
48
47
|
* - null: "null"
|
|
48
|
+
*
|
|
49
|
+
* DBML syntax requires:
|
|
50
|
+
* - SQL functions/expressions: wrapped in backticks (e.g., `now()`)
|
|
51
|
+
* - Strings: single quotes (e.g., 'user')
|
|
52
|
+
* - Numbers/booleans/null: as-is
|
|
49
53
|
*/
|
|
50
54
|
private formatDefaultValue;
|
|
51
55
|
/**
|
|
@@ -67,10 +71,9 @@ export declare class DbmlFormatter implements OutputFormatter {
|
|
|
67
71
|
*/
|
|
68
72
|
private getRelationType;
|
|
69
73
|
/**
|
|
70
|
-
* Escape a name for DBML
|
|
74
|
+
* Escape a name for DBML
|
|
71
75
|
*
|
|
72
|
-
*
|
|
73
|
-
* - MySQL: Use backticks (`name`)
|
|
76
|
+
* DBML uses double quotes for all database types
|
|
74
77
|
*/
|
|
75
78
|
private escapeName;
|
|
76
79
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dbml.d.ts","sourceRoot":"","sources":["../../src/formatter/dbml.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,
|
|
1
|
+
{"version":3,"file":"dbml.d.ts","sourceRoot":"","sources":["../../src/formatter/dbml.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAOnB,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAYjE;;;;;GAKG;AACH,qBAAa,aAAc,YAAW,eAAe;IACnD,OAAO,CAAC,OAAO,CAA6B;IAE5C;;;;OAIG;gBACS,OAAO,GAAE,gBAAqB;IAI1C;;;;;OAKG;IACH,MAAM,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM;IAuB1C;;OAEG;IACH,OAAO,CAAC,UAAU;IAWlB;;OAEG;IACH,OAAO,CAAC,WAAW;IA8BnB;;OAEG;IACH,OAAO,CAAC,YAAY;IAapB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAyB3B;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,kBAAkB;IA4B1B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAc1B;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IA6C1B;;OAEG;IACH,OAAO,CAAC,cAAc;IAsBtB;;OAEG;IACH,OAAO,CAAC,eAAe;IAavB;;;;OAIG;IACH,OAAO,CAAC,UAAU;IAIlB;;OAEG;IACH,OAAO,CAAC,YAAY;CAGrB"}
|
package/dist/formatter/dbml.js
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
import { DbmlBuilder as u } from "./dbml-builder.js";
|
|
2
|
-
const
|
|
2
|
+
const m = {
|
|
3
3
|
includeComments: !0,
|
|
4
4
|
includeIndexes: !0,
|
|
5
5
|
includeConstraints: !0
|
|
6
6
|
};
|
|
7
7
|
class l {
|
|
8
8
|
options;
|
|
9
|
-
databaseType = "postgresql";
|
|
10
9
|
/**
|
|
11
10
|
* Create a new DbmlFormatter
|
|
12
11
|
*
|
|
13
12
|
* @param options - Formatter options
|
|
14
13
|
*/
|
|
15
14
|
constructor(e = {}) {
|
|
16
|
-
this.options = { ...
|
|
15
|
+
this.options = { ...m, ...e };
|
|
17
16
|
}
|
|
18
17
|
/**
|
|
19
18
|
* Format the intermediate schema into DBML
|
|
@@ -23,7 +22,6 @@ class l {
|
|
|
23
22
|
*/
|
|
24
23
|
format(e) {
|
|
25
24
|
const t = new u();
|
|
26
|
-
this.databaseType = e.databaseType;
|
|
27
25
|
for (const n of e.enums)
|
|
28
26
|
this.formatEnum(t, n), t.line();
|
|
29
27
|
for (const n of e.tables)
|
|
@@ -75,6 +73,11 @@ class l {
|
|
|
75
73
|
* - Strings: already wrapped with single quotes (e.g., "'user'")
|
|
76
74
|
* - Numbers/booleans: as-is (e.g., "true", "42")
|
|
77
75
|
* - null: "null"
|
|
76
|
+
*
|
|
77
|
+
* DBML syntax requires:
|
|
78
|
+
* - SQL functions/expressions: wrapped in backticks (e.g., `now()`)
|
|
79
|
+
* - Strings: single quotes (e.g., 'user')
|
|
80
|
+
* - Numbers/booleans/null: as-is
|
|
78
81
|
*/
|
|
79
82
|
formatDefaultValue(e) {
|
|
80
83
|
return e.includes("(") || e.includes("::") || this.isKnownSqlFunction(e) ? `\`${e}\`` : e === "null" || e === "NULL" ? "null" : e === "true" || e === "false" || /^-?\d+(\.\d+)?$/.test(e) ? e : e.startsWith("'") && e.endsWith("'") ? `'${e.slice(1, -1).replace(/''/g, "\\'")}'` : `'${e.replace(/'/g, "\\'")}'`;
|
|
@@ -142,13 +145,12 @@ class l {
|
|
|
142
145
|
}
|
|
143
146
|
}
|
|
144
147
|
/**
|
|
145
|
-
* Escape a name for DBML
|
|
148
|
+
* Escape a name for DBML
|
|
146
149
|
*
|
|
147
|
-
*
|
|
148
|
-
* - MySQL: Use backticks (`name`)
|
|
150
|
+
* DBML uses double quotes for all database types
|
|
149
151
|
*/
|
|
150
152
|
escapeName(e) {
|
|
151
|
-
return
|
|
153
|
+
return `"${e}"`;
|
|
152
154
|
}
|
|
153
155
|
/**
|
|
154
156
|
* Escape a string for use in DBML single-quoted strings
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dbml.js","sources":["../../src/formatter/dbml.ts"],"sourcesContent":["import type {\n IntermediateSchema,\n TableDefinition,\n ColumnDefinition,\n IndexDefinition,\n ConstraintDefinition,\n RelationDefinition,\n EnumDefinition,\n DatabaseType,\n} from \"../types\";\nimport type { OutputFormatter, FormatterOptions } from \"./types\";\nimport { DbmlBuilder } from \"./dbml-builder\";\n\n/**\n * Default formatter options\n */\nconst DEFAULT_OPTIONS: Required<FormatterOptions> = {\n includeComments: true,\n includeIndexes: true,\n includeConstraints: true,\n};\n\n/**\n * DbmlFormatter converts IntermediateSchema to DBML format\n *\n * This formatter wraps the existing DbmlBuilder to provide DBML output\n * from the database-agnostic IntermediateSchema representation.\n */\nexport class DbmlFormatter implements OutputFormatter {\n private options: Required<FormatterOptions>;\n private databaseType: DatabaseType = \"postgresql\";\n\n /**\n * Create a new DbmlFormatter\n *\n * @param options - Formatter options\n */\n constructor(options: FormatterOptions = {}) {\n this.options = { ...DEFAULT_OPTIONS, ...options };\n }\n\n /**\n * Format the intermediate schema into DBML\n *\n * @param schema - The intermediate schema to format\n * @returns DBML string\n */\n format(schema: IntermediateSchema): string {\n const dbml = new DbmlBuilder();\n\n // Store database type for name escaping\n this.databaseType = schema.databaseType;\n\n // Generate enums (PostgreSQL specific)\n for (const enumDef of schema.enums) {\n this.formatEnum(dbml, enumDef);\n dbml.line();\n }\n\n // Generate tables\n for (const table of schema.tables) {\n this.formatTable(dbml, table);\n dbml.line();\n }\n\n // Generate relations\n for (const relation of schema.relations) {\n this.formatRelation(dbml, relation);\n }\n\n return dbml.build().trim();\n }\n\n /**\n * Format an enum definition to DBML\n */\n private formatEnum(dbml: DbmlBuilder, enumDef: EnumDefinition): void {\n const name = this.escapeName(enumDef.name);\n dbml.line(`Enum ${name} {`);\n dbml.indent();\n for (const value of enumDef.values) {\n dbml.line(value);\n }\n dbml.dedent();\n dbml.line(\"}\");\n }\n\n /**\n * Format a table definition to DBML\n */\n private formatTable(dbml: DbmlBuilder, table: TableDefinition): void {\n const tableName = this.escapeName(table.name);\n dbml.line(`Table ${tableName} {`);\n dbml.indent();\n\n // Generate columns\n for (const column of table.columns) {\n this.formatColumn(dbml, column);\n }\n\n // Generate indexes block if enabled and there are indexes or constraints to show\n const hasIndexes = table.indexes.length > 0;\n const pkConstraints = table.constraints.filter((c) => c.type === \"primary_key\");\n const ucConstraints = table.constraints.filter((c) => c.type === \"unique\");\n const hasConstraints = pkConstraints.length > 0 || ucConstraints.length > 0;\n\n if (this.options.includeIndexes && (hasIndexes || hasConstraints)) {\n this.formatIndexesBlock(dbml, table.indexes, pkConstraints, ucConstraints);\n }\n\n // Add table-level Note if comment exists and comments are enabled\n if (this.options.includeComments && table.comment) {\n dbml.line();\n dbml.line(`Note: '${this.escapeString(table.comment)}'`);\n }\n\n dbml.dedent();\n dbml.line(\"}\");\n }\n\n /**\n * Format a column definition to DBML\n */\n private formatColumn(dbml: DbmlBuilder, column: ColumnDefinition): void {\n const name = this.escapeName(column.name);\n const type = column.type;\n const attrs = this.getColumnAttributes(column);\n const attrStr = attrs.join(\", \");\n\n if (attrStr) {\n dbml.line(`${name} ${type} [${attrStr}]`);\n } else {\n dbml.line(`${name} ${type}`);\n }\n }\n\n /**\n * Get column attributes for DBML\n */\n private getColumnAttributes(column: ColumnDefinition): string[] {\n const attrs: string[] = [];\n\n if (column.primaryKey) {\n attrs.push(\"primary key\");\n }\n if (!column.nullable) {\n attrs.push(\"not null\");\n }\n if (column.unique) {\n attrs.push(\"unique\");\n }\n if (column.autoIncrement) {\n attrs.push(\"increment\");\n }\n if (column.defaultValue !== undefined) {\n attrs.push(`default: ${this.formatDefaultValue(column.defaultValue)}`);\n }\n if (this.options.includeComments && column.comment) {\n attrs.push(`note: '${this.escapeString(column.comment)}'`);\n }\n\n return attrs;\n }\n\n /**\n * Format a default value for DBML\n *\n * The value comes from IntermediateSchema in \"raw\" format:\n * - SQL expressions: no wrapping (e.g., \"now()\", \"gen_random_uuid()\")\n * - Strings: already wrapped with single quotes (e.g., \"'user'\")\n * - Numbers/booleans: as-is (e.g., \"true\", \"42\")\n * - null: \"null\"\n */\n private formatDefaultValue(value: string): string {\n // If it looks like a SQL expression (contains parentheses or is a known function)\n if (value.includes(\"(\") || value.includes(\"::\") || this.isKnownSqlFunction(value)) {\n return `\\`${value}\\``;\n }\n // If it's null\n if (value === \"null\" || value === \"NULL\") {\n return \"null\";\n }\n // If it's a boolean\n if (value === \"true\" || value === \"false\") {\n return value;\n }\n // If it's a number\n if (/^-?\\d+(\\.\\d+)?$/.test(value)) {\n return value;\n }\n // If already wrapped in single quotes (from IntermediateSchema)\n if (value.startsWith(\"'\") && value.endsWith(\"'\")) {\n // Convert '' escape (SQL standard) to \\' escape (DBML)\n const inner = value.slice(1, -1).replace(/''/g, \"\\\\'\");\n return `'${inner}'`;\n }\n // Otherwise treat as string (shouldn't happen with proper IntermediateSchema)\n return `'${value.replace(/'/g, \"\\\\'\")}'`;\n }\n\n /**\n * Check if a value is a known SQL function/expression\n */\n private isKnownSqlFunction(value: string): boolean {\n const sqlKeywords = [\n \"now\",\n \"current_timestamp\",\n \"current_date\",\n \"current_time\",\n \"gen_random_uuid\",\n \"uuid_generate_v4\",\n \"autoincrement\",\n \"auto_increment\",\n ];\n return sqlKeywords.some((kw) => value.toLowerCase().includes(kw));\n }\n\n /**\n * Format indexes block to DBML\n *\n * Includes primary key constraints, unique constraints, and regular indexes.\n */\n private formatIndexesBlock(\n dbml: DbmlBuilder,\n indexes: IndexDefinition[],\n pkConstraints: ConstraintDefinition[],\n ucConstraints: ConstraintDefinition[],\n ): void {\n dbml.line();\n dbml.line(\"indexes {\");\n dbml.indent();\n\n // Primary key constraints\n for (const pk of pkConstraints) {\n const columns = pk.columns.map((c) => this.escapeName(c)).join(\", \");\n dbml.line(`(${columns}) [pk]`);\n }\n\n // Unique constraints\n for (const uc of ucConstraints) {\n const columns = uc.columns.map((c) => this.escapeName(c)).join(\", \");\n dbml.line(`(${columns}) [unique]`);\n }\n\n // Regular indexes\n for (const index of indexes) {\n const columns = index.columns.map((c) => this.escapeName(c)).join(\", \");\n const attrs: string[] = [];\n\n if (index.unique) {\n attrs.push(\"unique\");\n }\n if (index.name) {\n attrs.push(`name: '${index.name}'`);\n }\n if (index.type) {\n attrs.push(`type: ${index.type}`);\n }\n\n const attrStr = attrs.length > 0 ? ` [${attrs.join(\", \")}]` : \"\";\n dbml.line(`(${columns})${attrStr}`);\n }\n\n dbml.dedent();\n dbml.line(\"}\");\n }\n\n /**\n * Format a relation definition to DBML Ref\n */\n private formatRelation(dbml: DbmlBuilder, relation: RelationDefinition): void {\n const from = `${this.escapeName(relation.fromTable)}.${relation.fromColumns.map((c) => this.escapeName(c)).join(\", \")}`;\n const to = `${this.escapeName(relation.toTable)}.${relation.toColumns.map((c) => this.escapeName(c)).join(\", \")}`;\n const type = this.getRelationType(relation.type);\n\n let refLine = `Ref: ${from} ${type} ${to}`;\n\n const attrs: string[] = [];\n if (relation.onDelete && relation.onDelete.toLowerCase() !== \"no action\") {\n attrs.push(`delete: ${relation.onDelete.toLowerCase()}`);\n }\n if (relation.onUpdate && relation.onUpdate.toLowerCase() !== \"no action\") {\n attrs.push(`update: ${relation.onUpdate.toLowerCase()}`);\n }\n\n if (attrs.length > 0) {\n refLine += ` [${attrs.join(\", \")}]`;\n }\n\n dbml.line(refLine);\n }\n\n /**\n * Convert IntermediateRelationType to DBML relation symbol\n */\n private getRelationType(type: RelationDefinition[\"type\"]): \"<\" | \">\" | \"-\" | \"<>\" {\n switch (type) {\n case \"one-to-one\":\n return \"-\";\n case \"one-to-many\":\n return \"<\";\n case \"many-to-one\":\n return \">\";\n case \"many-to-many\":\n return \"<>\";\n }\n }\n\n /**\n * Escape a name for DBML based on database type\n *\n * - PostgreSQL/SQLite: Use double quotes (\"name\")\n * - MySQL: Use backticks (`name`)\n */\n private escapeName(name: string): string {\n if (this.databaseType === \"mysql\") {\n return `\\`${name}\\``;\n }\n // PostgreSQL and SQLite use double quotes\n return `\"${name}\"`;\n }\n\n /**\n * Escape a string for use in DBML single-quoted strings\n */\n private escapeString(str: string): string {\n return str.replace(/\\\\/g, \"\\\\\\\\\").replace(/'/g, \"\\\\'\").replace(/\\n/g, \"\\\\n\");\n }\n}\n"],"names":["DEFAULT_OPTIONS","DbmlFormatter","options","schema","dbml","DbmlBuilder","enumDef","table","relation","name","value","tableName","column","hasIndexes","pkConstraints","c","ucConstraints","hasConstraints","type","attrStr","attrs","kw","indexes","pk","columns","uc","index","from","to","refLine","str"],"mappings":";AAgBA,MAAMA,IAA8C;AAAA,EAClD,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,oBAAoB;AACtB;AAQO,MAAMC,EAAyC;AAAA,EAC5C;AAAA,EACA,eAA6B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOrC,YAAYC,IAA4B,IAAI;AAC1C,SAAK,UAAU,EAAE,GAAGF,GAAiB,GAAGE,EAAA;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAOC,GAAoC;AACzC,UAAMC,IAAO,IAAIC,EAAA;AAGjB,SAAK,eAAeF,EAAO;AAG3B,eAAWG,KAAWH,EAAO;AAC3B,WAAK,WAAWC,GAAME,CAAO,GAC7BF,EAAK,KAAA;AAIP,eAAWG,KAASJ,EAAO;AACzB,WAAK,YAAYC,GAAMG,CAAK,GAC5BH,EAAK,KAAA;AAIP,eAAWI,KAAYL,EAAO;AAC5B,WAAK,eAAeC,GAAMI,CAAQ;AAGpC,WAAOJ,EAAK,MAAA,EAAQ,KAAA;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA,EAKQ,WAAWA,GAAmBE,GAA+B;AACnE,UAAMG,IAAO,KAAK,WAAWH,EAAQ,IAAI;AACzC,IAAAF,EAAK,KAAK,QAAQK,CAAI,IAAI,GAC1BL,EAAK,OAAA;AACL,eAAWM,KAASJ,EAAQ;AAC1B,MAAAF,EAAK,KAAKM,CAAK;AAEjB,IAAAN,EAAK,OAAA,GACLA,EAAK,KAAK,GAAG;AAAA,EACf;AAAA;AAAA;AAAA;AAAA,EAKQ,YAAYA,GAAmBG,GAA8B;AACnE,UAAMI,IAAY,KAAK,WAAWJ,EAAM,IAAI;AAC5C,IAAAH,EAAK,KAAK,SAASO,CAAS,IAAI,GAChCP,EAAK,OAAA;AAGL,eAAWQ,KAAUL,EAAM;AACzB,WAAK,aAAaH,GAAMQ,CAAM;AAIhC,UAAMC,IAAaN,EAAM,QAAQ,SAAS,GACpCO,IAAgBP,EAAM,YAAY,OAAO,CAACQ,MAAMA,EAAE,SAAS,aAAa,GACxEC,IAAgBT,EAAM,YAAY,OAAO,CAACQ,MAAMA,EAAE,SAAS,QAAQ,GACnEE,IAAiBH,EAAc,SAAS,KAAKE,EAAc,SAAS;AAE1E,IAAI,KAAK,QAAQ,mBAAmBH,KAAcI,MAChD,KAAK,mBAAmBb,GAAMG,EAAM,SAASO,GAAeE,CAAa,GAIvE,KAAK,QAAQ,mBAAmBT,EAAM,YACxCH,EAAK,KAAA,GACLA,EAAK,KAAK,UAAU,KAAK,aAAaG,EAAM,OAAO,CAAC,GAAG,IAGzDH,EAAK,OAAA,GACLA,EAAK,KAAK,GAAG;AAAA,EACf;AAAA;AAAA;AAAA;AAAA,EAKQ,aAAaA,GAAmBQ,GAAgC;AACtE,UAAMH,IAAO,KAAK,WAAWG,EAAO,IAAI,GAClCM,IAAON,EAAO,MAEdO,IADQ,KAAK,oBAAoBP,CAAM,EACvB,KAAK,IAAI;AAE/B,IAAIO,IACFf,EAAK,KAAK,GAAGK,CAAI,IAAIS,CAAI,KAAKC,CAAO,GAAG,IAExCf,EAAK,KAAK,GAAGK,CAAI,IAAIS,CAAI,EAAE;AAAA,EAE/B;AAAA;AAAA;AAAA;AAAA,EAKQ,oBAAoBN,GAAoC;AAC9D,UAAMQ,IAAkB,CAAA;AAExB,WAAIR,EAAO,cACTQ,EAAM,KAAK,aAAa,GAErBR,EAAO,YACVQ,EAAM,KAAK,UAAU,GAEnBR,EAAO,UACTQ,EAAM,KAAK,QAAQ,GAEjBR,EAAO,iBACTQ,EAAM,KAAK,WAAW,GAEpBR,EAAO,iBAAiB,UAC1BQ,EAAM,KAAK,YAAY,KAAK,mBAAmBR,EAAO,YAAY,CAAC,EAAE,GAEnE,KAAK,QAAQ,mBAAmBA,EAAO,WACzCQ,EAAM,KAAK,UAAU,KAAK,aAAaR,EAAO,OAAO,CAAC,GAAG,GAGpDQ;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWQ,mBAAmBV,GAAuB;AAEhD,WAAIA,EAAM,SAAS,GAAG,KAAKA,EAAM,SAAS,IAAI,KAAK,KAAK,mBAAmBA,CAAK,IACvE,KAAKA,CAAK,OAGfA,MAAU,UAAUA,MAAU,SACzB,SAGLA,MAAU,UAAUA,MAAU,WAI9B,kBAAkB,KAAKA,CAAK,IACvBA,IAGLA,EAAM,WAAW,GAAG,KAAKA,EAAM,SAAS,GAAG,IAGtC,IADOA,EAAM,MAAM,GAAG,EAAE,EAAE,QAAQ,OAAO,KAAK,CACrC,MAGX,IAAIA,EAAM,QAAQ,MAAM,KAAK,CAAC;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA,EAKQ,mBAAmBA,GAAwB;AAWjD,WAVoB;AAAA,MAClB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA,EAEiB,KAAK,CAACW,MAAOX,EAAM,YAAA,EAAc,SAASW,CAAE,CAAC;AAAA,EAClE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,mBACNjB,GACAkB,GACAR,GACAE,GACM;AACN,IAAAZ,EAAK,KAAA,GACLA,EAAK,KAAK,WAAW,GACrBA,EAAK,OAAA;AAGL,eAAWmB,KAAMT,GAAe;AAC9B,YAAMU,IAAUD,EAAG,QAAQ,IAAI,CAACR,MAAM,KAAK,WAAWA,CAAC,CAAC,EAAE,KAAK,IAAI;AACnE,MAAAX,EAAK,KAAK,IAAIoB,CAAO,QAAQ;AAAA,IAC/B;AAGA,eAAWC,KAAMT,GAAe;AAC9B,YAAMQ,IAAUC,EAAG,QAAQ,IAAI,CAACV,MAAM,KAAK,WAAWA,CAAC,CAAC,EAAE,KAAK,IAAI;AACnE,MAAAX,EAAK,KAAK,IAAIoB,CAAO,YAAY;AAAA,IACnC;AAGA,eAAWE,KAASJ,GAAS;AAC3B,YAAME,IAAUE,EAAM,QAAQ,IAAI,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,EAAE,KAAK,IAAI,GAChEN,IAAkB,CAAA;AAExB,MAAIM,EAAM,UACRN,EAAM,KAAK,QAAQ,GAEjBM,EAAM,QACRN,EAAM,KAAK,UAAUM,EAAM,IAAI,GAAG,GAEhCA,EAAM,QACRN,EAAM,KAAK,SAASM,EAAM,IAAI,EAAE;AAGlC,YAAMP,IAAUC,EAAM,SAAS,IAAI,KAAKA,EAAM,KAAK,IAAI,CAAC,MAAM;AAC9D,MAAAhB,EAAK,KAAK,IAAIoB,CAAO,IAAIL,CAAO,EAAE;AAAA,IACpC;AAEA,IAAAf,EAAK,OAAA,GACLA,EAAK,KAAK,GAAG;AAAA,EACf;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAeA,GAAmBI,GAAoC;AAC5E,UAAMmB,IAAO,GAAG,KAAK,WAAWnB,EAAS,SAAS,CAAC,IAAIA,EAAS,YAAY,IAAI,CAACO,MAAM,KAAK,WAAWA,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,IAC/Ga,IAAK,GAAG,KAAK,WAAWpB,EAAS,OAAO,CAAC,IAAIA,EAAS,UAAU,IAAI,CAACO,MAAM,KAAK,WAAWA,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,IACzGG,IAAO,KAAK,gBAAgBV,EAAS,IAAI;AAE/C,QAAIqB,IAAU,QAAQF,CAAI,IAAIT,CAAI,IAAIU,CAAE;AAExC,UAAMR,IAAkB,CAAA;AACxB,IAAIZ,EAAS,YAAYA,EAAS,SAAS,YAAA,MAAkB,eAC3DY,EAAM,KAAK,WAAWZ,EAAS,SAAS,YAAA,CAAa,EAAE,GAErDA,EAAS,YAAYA,EAAS,SAAS,YAAA,MAAkB,eAC3DY,EAAM,KAAK,WAAWZ,EAAS,SAAS,YAAA,CAAa,EAAE,GAGrDY,EAAM,SAAS,MACjBS,KAAW,KAAKT,EAAM,KAAK,IAAI,CAAC,MAGlChB,EAAK,KAAKyB,CAAO;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAgBX,GAA0D;AAChF,YAAQA,GAAA;AAAA,MACN,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,IAAA;AAAA,EAEb;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,WAAWT,GAAsB;AACvC,WAAI,KAAK,iBAAiB,UACjB,KAAKA,CAAI,OAGX,IAAIA,CAAI;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA,EAKQ,aAAaqB,GAAqB;AACxC,WAAOA,EAAI,QAAQ,OAAO,MAAM,EAAE,QAAQ,MAAM,KAAK,EAAE,QAAQ,OAAO,KAAK;AAAA,EAC7E;AACF;"}
|
|
1
|
+
{"version":3,"file":"dbml.js","sources":["../../src/formatter/dbml.ts"],"sourcesContent":["import type {\n IntermediateSchema,\n TableDefinition,\n ColumnDefinition,\n IndexDefinition,\n ConstraintDefinition,\n RelationDefinition,\n EnumDefinition,\n} from \"../types\";\nimport type { OutputFormatter, FormatterOptions } from \"./types\";\nimport { DbmlBuilder } from \"./dbml-builder\";\n\n/**\n * Default formatter options\n */\nconst DEFAULT_OPTIONS: Required<FormatterOptions> = {\n includeComments: true,\n includeIndexes: true,\n includeConstraints: true,\n};\n\n/**\n * DbmlFormatter converts IntermediateSchema to DBML format\n *\n * This formatter wraps the existing DbmlBuilder to provide DBML output\n * from the database-agnostic IntermediateSchema representation.\n */\nexport class DbmlFormatter implements OutputFormatter {\n private options: Required<FormatterOptions>;\n\n /**\n * Create a new DbmlFormatter\n *\n * @param options - Formatter options\n */\n constructor(options: FormatterOptions = {}) {\n this.options = { ...DEFAULT_OPTIONS, ...options };\n }\n\n /**\n * Format the intermediate schema into DBML\n *\n * @param schema - The intermediate schema to format\n * @returns DBML string\n */\n format(schema: IntermediateSchema): string {\n const dbml = new DbmlBuilder();\n\n // Generate enums (PostgreSQL specific)\n for (const enumDef of schema.enums) {\n this.formatEnum(dbml, enumDef);\n dbml.line();\n }\n\n // Generate tables\n for (const table of schema.tables) {\n this.formatTable(dbml, table);\n dbml.line();\n }\n\n // Generate relations\n for (const relation of schema.relations) {\n this.formatRelation(dbml, relation);\n }\n\n return dbml.build().trim();\n }\n\n /**\n * Format an enum definition to DBML\n */\n private formatEnum(dbml: DbmlBuilder, enumDef: EnumDefinition): void {\n const name = this.escapeName(enumDef.name);\n dbml.line(`Enum ${name} {`);\n dbml.indent();\n for (const value of enumDef.values) {\n dbml.line(value);\n }\n dbml.dedent();\n dbml.line(\"}\");\n }\n\n /**\n * Format a table definition to DBML\n */\n private formatTable(dbml: DbmlBuilder, table: TableDefinition): void {\n const tableName = this.escapeName(table.name);\n dbml.line(`Table ${tableName} {`);\n dbml.indent();\n\n // Generate columns\n for (const column of table.columns) {\n this.formatColumn(dbml, column);\n }\n\n // Generate indexes block if enabled and there are indexes or constraints to show\n const hasIndexes = table.indexes.length > 0;\n const pkConstraints = table.constraints.filter((c) => c.type === \"primary_key\");\n const ucConstraints = table.constraints.filter((c) => c.type === \"unique\");\n const hasConstraints = pkConstraints.length > 0 || ucConstraints.length > 0;\n\n if (this.options.includeIndexes && (hasIndexes || hasConstraints)) {\n this.formatIndexesBlock(dbml, table.indexes, pkConstraints, ucConstraints);\n }\n\n // Add table-level Note if comment exists and comments are enabled\n if (this.options.includeComments && table.comment) {\n dbml.line();\n dbml.line(`Note: '${this.escapeString(table.comment)}'`);\n }\n\n dbml.dedent();\n dbml.line(\"}\");\n }\n\n /**\n * Format a column definition to DBML\n */\n private formatColumn(dbml: DbmlBuilder, column: ColumnDefinition): void {\n const name = this.escapeName(column.name);\n const type = column.type;\n const attrs = this.getColumnAttributes(column);\n const attrStr = attrs.join(\", \");\n\n if (attrStr) {\n dbml.line(`${name} ${type} [${attrStr}]`);\n } else {\n dbml.line(`${name} ${type}`);\n }\n }\n\n /**\n * Get column attributes for DBML\n */\n private getColumnAttributes(column: ColumnDefinition): string[] {\n const attrs: string[] = [];\n\n if (column.primaryKey) {\n attrs.push(\"primary key\");\n }\n if (!column.nullable) {\n attrs.push(\"not null\");\n }\n if (column.unique) {\n attrs.push(\"unique\");\n }\n if (column.autoIncrement) {\n attrs.push(\"increment\");\n }\n if (column.defaultValue !== undefined) {\n attrs.push(`default: ${this.formatDefaultValue(column.defaultValue)}`);\n }\n if (this.options.includeComments && column.comment) {\n attrs.push(`note: '${this.escapeString(column.comment)}'`);\n }\n\n return attrs;\n }\n\n /**\n * Format a default value for DBML\n *\n * The value comes from IntermediateSchema in \"raw\" format:\n * - SQL expressions: no wrapping (e.g., \"now()\", \"gen_random_uuid()\")\n * - Strings: already wrapped with single quotes (e.g., \"'user'\")\n * - Numbers/booleans: as-is (e.g., \"true\", \"42\")\n * - null: \"null\"\n *\n * DBML syntax requires:\n * - SQL functions/expressions: wrapped in backticks (e.g., `now()`)\n * - Strings: single quotes (e.g., 'user')\n * - Numbers/booleans/null: as-is\n */\n private formatDefaultValue(value: string): string {\n // If it looks like a SQL expression (contains parentheses or is a known function)\n // Wrap in backticks for dbdiagram.io compatibility\n if (value.includes(\"(\") || value.includes(\"::\") || this.isKnownSqlFunction(value)) {\n return `\\`${value}\\``;\n }\n // If it's null\n if (value === \"null\" || value === \"NULL\") {\n return \"null\";\n }\n // If it's a boolean\n if (value === \"true\" || value === \"false\") {\n return value;\n }\n // If it's a number\n if (/^-?\\d+(\\.\\d+)?$/.test(value)) {\n return value;\n }\n // If already wrapped in single quotes (from IntermediateSchema)\n if (value.startsWith(\"'\") && value.endsWith(\"'\")) {\n // Convert '' escape (SQL standard) to \\' escape (DBML)\n const inner = value.slice(1, -1).replace(/''/g, \"\\\\'\");\n return `'${inner}'`;\n }\n // Otherwise treat as string (shouldn't happen with proper IntermediateSchema)\n return `'${value.replace(/'/g, \"\\\\'\")}'`;\n }\n\n /**\n * Check if a value is a known SQL function/expression\n */\n private isKnownSqlFunction(value: string): boolean {\n const sqlKeywords = [\n \"now\",\n \"current_timestamp\",\n \"current_date\",\n \"current_time\",\n \"gen_random_uuid\",\n \"uuid_generate_v4\",\n \"autoincrement\",\n \"auto_increment\",\n ];\n return sqlKeywords.some((kw) => value.toLowerCase().includes(kw));\n }\n\n /**\n * Format indexes block to DBML\n *\n * Includes primary key constraints, unique constraints, and regular indexes.\n */\n private formatIndexesBlock(\n dbml: DbmlBuilder,\n indexes: IndexDefinition[],\n pkConstraints: ConstraintDefinition[],\n ucConstraints: ConstraintDefinition[],\n ): void {\n dbml.line();\n dbml.line(\"indexes {\");\n dbml.indent();\n\n // Primary key constraints\n for (const pk of pkConstraints) {\n const columns = pk.columns.map((c) => this.escapeName(c)).join(\", \");\n dbml.line(`(${columns}) [pk]`);\n }\n\n // Unique constraints\n for (const uc of ucConstraints) {\n const columns = uc.columns.map((c) => this.escapeName(c)).join(\", \");\n dbml.line(`(${columns}) [unique]`);\n }\n\n // Regular indexes\n for (const index of indexes) {\n const columns = index.columns.map((c) => this.escapeName(c)).join(\", \");\n const attrs: string[] = [];\n\n if (index.unique) {\n attrs.push(\"unique\");\n }\n if (index.name) {\n attrs.push(`name: '${index.name}'`);\n }\n if (index.type) {\n attrs.push(`type: ${index.type}`);\n }\n\n const attrStr = attrs.length > 0 ? ` [${attrs.join(\", \")}]` : \"\";\n dbml.line(`(${columns})${attrStr}`);\n }\n\n dbml.dedent();\n dbml.line(\"}\");\n }\n\n /**\n * Format a relation definition to DBML Ref\n */\n private formatRelation(dbml: DbmlBuilder, relation: RelationDefinition): void {\n const from = `${this.escapeName(relation.fromTable)}.${relation.fromColumns.map((c) => this.escapeName(c)).join(\", \")}`;\n const to = `${this.escapeName(relation.toTable)}.${relation.toColumns.map((c) => this.escapeName(c)).join(\", \")}`;\n const type = this.getRelationType(relation.type);\n\n let refLine = `Ref: ${from} ${type} ${to}`;\n\n const attrs: string[] = [];\n if (relation.onDelete && relation.onDelete.toLowerCase() !== \"no action\") {\n attrs.push(`delete: ${relation.onDelete.toLowerCase()}`);\n }\n if (relation.onUpdate && relation.onUpdate.toLowerCase() !== \"no action\") {\n attrs.push(`update: ${relation.onUpdate.toLowerCase()}`);\n }\n\n if (attrs.length > 0) {\n refLine += ` [${attrs.join(\", \")}]`;\n }\n\n dbml.line(refLine);\n }\n\n /**\n * Convert IntermediateRelationType to DBML relation symbol\n */\n private getRelationType(type: RelationDefinition[\"type\"]): \"<\" | \">\" | \"-\" | \"<>\" {\n switch (type) {\n case \"one-to-one\":\n return \"-\";\n case \"one-to-many\":\n return \"<\";\n case \"many-to-one\":\n return \">\";\n case \"many-to-many\":\n return \"<>\";\n }\n }\n\n /**\n * Escape a name for DBML\n *\n * DBML uses double quotes for all database types\n */\n private escapeName(name: string): string {\n return `\"${name}\"`;\n }\n\n /**\n * Escape a string for use in DBML single-quoted strings\n */\n private escapeString(str: string): string {\n return str.replace(/\\\\/g, \"\\\\\\\\\").replace(/'/g, \"\\\\'\").replace(/\\n/g, \"\\\\n\");\n }\n}\n"],"names":["DEFAULT_OPTIONS","DbmlFormatter","options","schema","dbml","DbmlBuilder","enumDef","table","relation","name","value","tableName","column","hasIndexes","pkConstraints","c","ucConstraints","hasConstraints","type","attrStr","attrs","kw","indexes","pk","columns","uc","index","from","to","refLine","str"],"mappings":"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"}
|
|
@@ -119,8 +119,14 @@ export declare class MarkdownFormatter implements OutputFormatter {
|
|
|
119
119
|
private createTableLink;
|
|
120
120
|
/**
|
|
121
121
|
* Escape special Markdown characters in a string
|
|
122
|
+
* Converts newlines to spaces for use in table cells
|
|
122
123
|
*/
|
|
123
124
|
private escapeMarkdown;
|
|
125
|
+
/**
|
|
126
|
+
* Escape special Markdown characters and convert newlines to <br> tags
|
|
127
|
+
* Used for better readability in text sections (not in tables)
|
|
128
|
+
*/
|
|
129
|
+
private escapeMarkdownWithBreaks;
|
|
124
130
|
}
|
|
125
131
|
export {};
|
|
126
132
|
//# sourceMappingURL=markdown.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/formatter/markdown.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,eAAe,EAMhB,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEjE;;GAEG;AACH,KAAK,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEpC;;GAEG;AACH,MAAM,WAAW,wBAAyB,SAAQ,gBAAgB;IAChE;;;OAGG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;OAGG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB;AAaD;;;;;;;;;GASG;AACH,qBAAa,iBAAkB,YAAW,eAAe;IACvD,OAAO,CAAC,OAAO,CAAqC;IAEpD;;;;OAIG;gBACS,OAAO,GAAE,wBAA6B;IAIlD;;;;;;;OAOG;IACH,MAAM,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM;IAyB1C;;;;;OAKG;IACH,aAAa,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM;IA4BjD;;;;;;OAMG;IACH,gBAAgB,CAAC,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,kBAAkB,GAAG,MAAM;IAsC5E;;OAEG;IACH,OAAO,CAAC,oBAAoB;IA6C5B;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAoBhC;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAqB5B;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAiC9B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAoB5B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAOzB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAazB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAa1B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAS3B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAW5B;;OAEG;IACH,OAAO,CAAC,0BAA0B;IAelC;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAU1B;;OAEG;IACH,OAAO,CAAC,OAAO;IAOf;;OAEG;IACH,OAAO,CAAC,eAAe;IAQvB
|
|
1
|
+
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/formatter/markdown.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,eAAe,EAMhB,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEjE;;GAEG;AACH,KAAK,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEpC;;GAEG;AACH,MAAM,WAAW,wBAAyB,SAAQ,gBAAgB;IAChE;;;OAGG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;OAGG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB;AAaD;;;;;;;;;GASG;AACH,qBAAa,iBAAkB,YAAW,eAAe;IACvD,OAAO,CAAC,OAAO,CAAqC;IAEpD;;;;OAIG;gBACS,OAAO,GAAE,wBAA6B;IAIlD;;;;;;;OAOG;IACH,MAAM,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM;IAyB1C;;;;;OAKG;IACH,aAAa,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM;IA4BjD;;;;;;OAMG;IACH,gBAAgB,CAAC,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,kBAAkB,GAAG,MAAM;IAsC5E;;OAEG;IACH,OAAO,CAAC,oBAAoB;IA6C5B;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAoBhC;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAqB5B;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAiC9B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAoB5B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAOzB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAazB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAa1B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAS3B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAW5B;;OAEG;IACH,OAAO,CAAC,0BAA0B;IAelC;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAU1B;;OAEG;IACH,OAAO,CAAC,OAAO;IAOf;;OAEG;IACH,OAAO,CAAC,eAAe;IAQvB;;;OAGG;IACH,OAAO,CAAC,cAAc;IAItB;;;OAGG;IACH,OAAO,CAAC,wBAAwB;CAGjC"}
|
|
@@ -59,7 +59,7 @@ class T {
|
|
|
59
59
|
*/
|
|
60
60
|
generateTableDoc(e, n) {
|
|
61
61
|
const t = [];
|
|
62
|
-
t.push(`## ${e.name}`), t.push(""), this.options.includeComments && e.comment && (t.push(this.
|
|
62
|
+
t.push(`## ${e.name}`), t.push(""), this.options.includeComments && e.comment && (t.push(this.escapeMarkdownWithBreaks(e.comment)), t.push("")), t.push(this.generateColumnsTable(e.columns, e.name, n)), this.options.includeConstraints && e.constraints.length > 0 && (t.push(""), t.push(this.generateConstraintsTable(e.constraints))), this.options.includeIndexes && e.indexes.length > 0 && (t.push(""), t.push(this.generateIndexesTable(e.indexes)));
|
|
63
63
|
const s = this.getTableRelations(e.name, n.relations);
|
|
64
64
|
return s.length > 0 && (t.push(""), t.push(this.generateRelationsTable(s, e.name))), t.join(`
|
|
65
65
|
`);
|
|
@@ -224,10 +224,19 @@ class T {
|
|
|
224
224
|
}
|
|
225
225
|
/**
|
|
226
226
|
* Escape special Markdown characters in a string
|
|
227
|
+
* Converts newlines to spaces for use in table cells
|
|
227
228
|
*/
|
|
228
229
|
escapeMarkdown(e) {
|
|
229
230
|
return e.replace(/\|/g, "\\|").replace(/\n/g, " ");
|
|
230
231
|
}
|
|
232
|
+
/**
|
|
233
|
+
* Escape special Markdown characters and convert newlines to <br> tags
|
|
234
|
+
* Used for better readability in text sections (not in tables)
|
|
235
|
+
*/
|
|
236
|
+
escapeMarkdownWithBreaks(e) {
|
|
237
|
+
return e.replace(/\|/g, "\\|").replace(/\n/g, `
|
|
238
|
+
`);
|
|
239
|
+
}
|
|
231
240
|
}
|
|
232
241
|
export {
|
|
233
242
|
T as MarkdownFormatter
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown.js","sources":["../../src/formatter/markdown.ts"],"sourcesContent":["import type {\n IntermediateSchema,\n TableDefinition,\n ColumnDefinition,\n IndexDefinition,\n ConstraintDefinition,\n RelationDefinition,\n EnumDefinition,\n} from \"../types\";\nimport type { OutputFormatter, FormatterOptions } from \"./types\";\n\n/**\n * Link format for table references\n */\ntype LinkFormat = \"anchor\" | \"file\";\n\n/**\n * Options for MarkdownFormatter\n */\nexport interface MarkdownFormatterOptions extends FormatterOptions {\n /**\n * Whether to use relative links for table references\n * @default true\n */\n useRelativeLinks?: boolean;\n /**\n * Link format: \"anchor\" for #table-name, \"file\" for ./table-name.md\n * @default \"anchor\"\n */\n linkFormat?: LinkFormat;\n}\n\n/**\n * Default formatter options\n */\nconst DEFAULT_OPTIONS: Required<MarkdownFormatterOptions> = {\n includeComments: true,\n includeIndexes: true,\n includeConstraints: true,\n useRelativeLinks: true,\n linkFormat: \"anchor\",\n};\n\n/**\n * MarkdownFormatter converts IntermediateSchema to tbls-style Markdown format\n *\n * This formatter generates human-readable Markdown documentation from\n * the database-agnostic IntermediateSchema representation.\n *\n * Output includes:\n * - Table index (README.md style)\n * - Individual table documentation with columns, constraints, indexes, and relations\n */\nexport class MarkdownFormatter implements OutputFormatter {\n private options: Required<MarkdownFormatterOptions>;\n\n /**\n * Create a new MarkdownFormatter\n *\n * @param options - Formatter options\n */\n constructor(options: MarkdownFormatterOptions = {}) {\n this.options = { ...DEFAULT_OPTIONS, ...options };\n }\n\n /**\n * Format the intermediate schema into a single Markdown document\n *\n * This generates a complete document containing the index and all table docs.\n *\n * @param schema - The intermediate schema to format\n * @returns Markdown string\n */\n format(schema: IntermediateSchema): string {\n const lines: string[] = [];\n\n // Generate index section\n lines.push(this.generateIndex(schema));\n\n // Generate enum documentation if any\n if (schema.enums.length > 0) {\n lines.push(\"\");\n lines.push(\"---\");\n lines.push(\"\");\n lines.push(this.generateEnumsSection(schema.enums));\n }\n\n // Generate table documentation\n for (const table of schema.tables) {\n lines.push(\"\");\n lines.push(\"---\");\n lines.push(\"\");\n lines.push(this.generateTableDoc(table, schema));\n }\n\n return lines.join(\"\\n\").trim();\n }\n\n /**\n * Generate the index section (README.md style)\n *\n * @param schema - The intermediate schema\n * @returns Markdown string for the index\n */\n generateIndex(schema: IntermediateSchema): string {\n const lines: string[] = [];\n\n lines.push(\"# Tables\");\n lines.push(\"\");\n\n if (schema.tables.length === 0) {\n lines.push(\"No tables defined.\");\n return lines.join(\"\\n\");\n }\n\n // Table header\n lines.push(\"| Name | Columns | Comment |\");\n lines.push(\"|------|---------|---------|\");\n\n // Table rows\n for (const table of schema.tables) {\n const name = this.options.useRelativeLinks ? this.createTableLink(table.name) : table.name;\n const columnCount = table.columns.length;\n const comment =\n this.options.includeComments && table.comment ? this.escapeMarkdown(table.comment) : \"\";\n\n lines.push(`| ${name} | ${columnCount} | ${comment} |`);\n }\n\n return lines.join(\"\\n\");\n }\n\n /**\n * Generate documentation for a single table\n *\n * @param table - The table definition\n * @param schema - The full schema (for relation lookups)\n * @returns Markdown string for the table documentation\n */\n generateTableDoc(table: TableDefinition, schema: IntermediateSchema): string {\n const lines: string[] = [];\n\n // Table heading with anchor\n lines.push(`## ${table.name}`);\n lines.push(\"\");\n\n // Table comment\n if (this.options.includeComments && table.comment) {\n lines.push(this.escapeMarkdown(table.comment));\n lines.push(\"\");\n }\n\n // Columns section\n lines.push(this.generateColumnsTable(table.columns, table.name, schema));\n\n // Constraints section\n if (this.options.includeConstraints && table.constraints.length > 0) {\n lines.push(\"\");\n lines.push(this.generateConstraintsTable(table.constraints));\n }\n\n // Indexes section\n if (this.options.includeIndexes && table.indexes.length > 0) {\n lines.push(\"\");\n lines.push(this.generateIndexesTable(table.indexes));\n }\n\n // Relations section\n const tableRelations = this.getTableRelations(table.name, schema.relations);\n if (tableRelations.length > 0) {\n lines.push(\"\");\n lines.push(this.generateRelationsTable(tableRelations, table.name));\n }\n\n return lines.join(\"\\n\");\n }\n\n /**\n * Generate the columns table for a table\n */\n private generateColumnsTable(\n columns: ColumnDefinition[],\n tableName: string,\n schema: IntermediateSchema,\n ): string {\n const lines: string[] = [];\n\n lines.push(\"### Columns\");\n lines.push(\"\");\n\n if (columns.length === 0) {\n lines.push(\"No columns defined.\");\n return lines.join(\"\\n\");\n }\n\n // Build column info with relations\n const columnInfo = columns.map((col) => {\n const children = this.getChildRelations(tableName, col.name, schema.relations);\n const parents = this.getParentRelations(tableName, col.name, schema.relations);\n return { column: col, children, parents };\n });\n\n // Table header\n lines.push(\"| Name | Type | Default | Nullable | Children | Parents | Comment |\");\n lines.push(\"|------|------|---------|----------|----------|---------|---------|\");\n\n // Table rows\n for (const { column, children, parents } of columnInfo) {\n const name = column.primaryKey ? `**${column.name}**` : column.name;\n const type = this.escapeMarkdown(column.type);\n const defaultVal = column.defaultValue !== undefined ? `\\`${column.defaultValue}\\`` : \"-\";\n const nullable = column.nullable ? \"YES\" : \"NO\";\n const childrenStr = children.length > 0 ? this.formatRelationLinks(children) : \"-\";\n const parentsStr = parents.length > 0 ? this.formatRelationLinks(parents) : \"-\";\n const comment =\n this.options.includeComments && column.comment ? this.escapeMarkdown(column.comment) : \"-\";\n\n lines.push(\n `| ${name} | ${type} | ${defaultVal} | ${nullable} | ${childrenStr} | ${parentsStr} | ${comment} |`,\n );\n }\n\n return lines.join(\"\\n\");\n }\n\n /**\n * Generate the constraints table\n */\n private generateConstraintsTable(constraints: ConstraintDefinition[]): string {\n const lines: string[] = [];\n\n lines.push(\"### Constraints\");\n lines.push(\"\");\n\n lines.push(\"| Name | Type | Definition |\");\n lines.push(\"|------|------|------------|\");\n\n for (const constraint of constraints) {\n const name = constraint.name || \"-\";\n const type = this.formatConstraintType(constraint.type);\n const definition = this.formatConstraintDefinition(constraint);\n\n lines.push(`| ${name} | ${type} | ${definition} |`);\n }\n\n return lines.join(\"\\n\");\n }\n\n /**\n * Generate the indexes table\n */\n private generateIndexesTable(indexes: IndexDefinition[]): string {\n const lines: string[] = [];\n\n lines.push(\"### Indexes\");\n lines.push(\"\");\n\n lines.push(\"| Name | Columns | Unique | Type |\");\n lines.push(\"|------|---------|--------|------|\");\n\n for (const index of indexes) {\n const name = index.name || \"-\";\n const columns = index.columns.join(\", \");\n const unique = index.unique ? \"YES\" : \"NO\";\n const type = index.type || \"-\";\n\n lines.push(`| ${name} | ${columns} | ${unique} | ${type} |`);\n }\n\n return lines.join(\"\\n\");\n }\n\n /**\n * Generate the relations table for a specific table\n */\n private generateRelationsTable(relations: RelationDefinition[], tableName: string): string {\n const lines: string[] = [];\n\n lines.push(\"### Relations\");\n lines.push(\"\");\n\n lines.push(\"| Parent | Child | Type |\");\n lines.push(\"|--------|-------|------|\");\n\n for (const relation of relations) {\n const isParent = relation.toTable === tableName;\n const parent = `${relation.toTable}.${relation.toColumns.join(\", \")}`;\n const child = `${relation.fromTable}.${relation.fromColumns.join(\", \")}`;\n const type = this.formatRelationType(relation.type);\n\n // Add links if enabled\n const parentLink = this.options.useRelativeLinks\n ? this.createTableLink(relation.toTable, parent)\n : parent;\n const childLink = this.options.useRelativeLinks\n ? this.createTableLink(relation.fromTable, child)\n : child;\n\n // Highlight the current table\n const parentDisplay = isParent ? `**${parentLink}**` : parentLink;\n const childDisplay = !isParent ? `**${childLink}**` : childLink;\n\n lines.push(`| ${parentDisplay} | ${childDisplay} | ${type} |`);\n }\n\n return lines.join(\"\\n\");\n }\n\n /**\n * Generate documentation for enums\n */\n private generateEnumsSection(enums: EnumDefinition[]): string {\n const lines: string[] = [];\n\n lines.push(\"# Enums\");\n lines.push(\"\");\n\n for (const enumDef of enums) {\n lines.push(`## ${enumDef.name}`);\n lines.push(\"\");\n lines.push(\"| Value |\");\n lines.push(\"|-------|\");\n for (const value of enumDef.values) {\n lines.push(`| ${value} |`);\n }\n lines.push(\"\");\n }\n\n return lines.join(\"\\n\").trim();\n }\n\n /**\n * Get all relations involving a specific table\n */\n private getTableRelations(\n tableName: string,\n relations: RelationDefinition[],\n ): RelationDefinition[] {\n return relations.filter((r) => r.fromTable === tableName || r.toTable === tableName);\n }\n\n /**\n * Get child relations for a specific column (where this column is referenced)\n */\n private getChildRelations(\n tableName: string,\n columnName: string,\n relations: RelationDefinition[],\n ): Array<{ table: string; column: string }> {\n return relations\n .filter((r) => r.toTable === tableName && r.toColumns.includes(columnName))\n .map((r) => ({\n table: r.fromTable,\n column: r.fromColumns.join(\", \"),\n }));\n }\n\n /**\n * Get parent relations for a specific column (columns this column references)\n */\n private getParentRelations(\n tableName: string,\n columnName: string,\n relations: RelationDefinition[],\n ): Array<{ table: string; column: string }> {\n return relations\n .filter((r) => r.fromTable === tableName && r.fromColumns.includes(columnName))\n .map((r) => ({\n table: r.toTable,\n column: r.toColumns.join(\", \"),\n }));\n }\n\n /**\n * Format relation links for display in columns table\n */\n private formatRelationLinks(relations: Array<{ table: string; column: string }>): string {\n return relations\n .map((r) => {\n const text = `${r.table}.${r.column}`;\n return this.options.useRelativeLinks ? this.createTableLink(r.table, text) : text;\n })\n .join(\", \");\n }\n\n /**\n * Format constraint type for display\n */\n private formatConstraintType(type: ConstraintDefinition[\"type\"]): string {\n const typeMap: Record<ConstraintDefinition[\"type\"], string> = {\n primary_key: \"PRIMARY KEY\",\n foreign_key: \"FOREIGN KEY\",\n unique: \"UNIQUE\",\n check: \"CHECK\",\n not_null: \"NOT NULL\",\n };\n return typeMap[type] || type;\n }\n\n /**\n * Format constraint definition for display\n */\n private formatConstraintDefinition(constraint: ConstraintDefinition): string {\n if (constraint.definition) {\n return `\\`${constraint.definition}\\``;\n }\n\n const columns = constraint.columns.join(\", \");\n\n if (constraint.type === \"foreign_key\" && constraint.referencedTable) {\n const refColumns = constraint.referencedColumns?.join(\", \") || \"\";\n return `(${columns}) → ${constraint.referencedTable}(${refColumns})`;\n }\n\n return `(${columns})`;\n }\n\n /**\n * Format relation type for display\n */\n private formatRelationType(type: RelationDefinition[\"type\"]): string {\n const typeMap: Record<RelationDefinition[\"type\"], string> = {\n \"one-to-one\": \"One to One\",\n \"one-to-many\": \"One to Many\",\n \"many-to-one\": \"Many to One\",\n \"many-to-many\": \"Many to Many\",\n };\n return typeMap[type] || type;\n }\n\n /**\n * Create a URL-safe slug from a string\n */\n private slugify(str: string): string {\n return str\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n }\n\n /**\n * Create a table link based on the configured link format\n */\n private createTableLink(tableName: string, displayText?: string): string {\n const text = displayText || tableName;\n if (this.options.linkFormat === \"file\") {\n return `[${text}](./${tableName}.md)`;\n }\n return `[${text}](#${this.slugify(tableName)})`;\n }\n\n /**\n * Escape special Markdown characters in a string\n */\n private escapeMarkdown(str: string): string {\n return str.replace(/\\|/g, \"\\\\|\").replace(/\\n/g, \" \");\n }\n}\n"],"names":["DEFAULT_OPTIONS","MarkdownFormatter","options","schema","lines","table","name","columnCount","comment","tableRelations","columns","tableName","columnInfo","col","children","parents","column","type","defaultVal","nullable","childrenStr","parentsStr","constraints","constraint","definition","indexes","index","unique","relations","relation","isParent","parent","child","parentLink","childLink","parentDisplay","childDisplay","enums","enumDef","value","r","columnName","text","refColumns","str","displayText"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"markdown.js","sources":["../../src/formatter/markdown.ts"],"sourcesContent":["import type {\n IntermediateSchema,\n TableDefinition,\n ColumnDefinition,\n IndexDefinition,\n ConstraintDefinition,\n RelationDefinition,\n EnumDefinition,\n} from \"../types\";\nimport type { OutputFormatter, FormatterOptions } from \"./types\";\n\n/**\n * Link format for table references\n */\ntype LinkFormat = \"anchor\" | \"file\";\n\n/**\n * Options for MarkdownFormatter\n */\nexport interface MarkdownFormatterOptions extends FormatterOptions {\n /**\n * Whether to use relative links for table references\n * @default true\n */\n useRelativeLinks?: boolean;\n /**\n * Link format: \"anchor\" for #table-name, \"file\" for ./table-name.md\n * @default \"anchor\"\n */\n linkFormat?: LinkFormat;\n}\n\n/**\n * Default formatter options\n */\nconst DEFAULT_OPTIONS: Required<MarkdownFormatterOptions> = {\n includeComments: true,\n includeIndexes: true,\n includeConstraints: true,\n useRelativeLinks: true,\n linkFormat: \"anchor\",\n};\n\n/**\n * MarkdownFormatter converts IntermediateSchema to tbls-style Markdown format\n *\n * This formatter generates human-readable Markdown documentation from\n * the database-agnostic IntermediateSchema representation.\n *\n * Output includes:\n * - Table index (README.md style)\n * - Individual table documentation with columns, constraints, indexes, and relations\n */\nexport class MarkdownFormatter implements OutputFormatter {\n private options: Required<MarkdownFormatterOptions>;\n\n /**\n * Create a new MarkdownFormatter\n *\n * @param options - Formatter options\n */\n constructor(options: MarkdownFormatterOptions = {}) {\n this.options = { ...DEFAULT_OPTIONS, ...options };\n }\n\n /**\n * Format the intermediate schema into a single Markdown document\n *\n * This generates a complete document containing the index and all table docs.\n *\n * @param schema - The intermediate schema to format\n * @returns Markdown string\n */\n format(schema: IntermediateSchema): string {\n const lines: string[] = [];\n\n // Generate index section\n lines.push(this.generateIndex(schema));\n\n // Generate enum documentation if any\n if (schema.enums.length > 0) {\n lines.push(\"\");\n lines.push(\"---\");\n lines.push(\"\");\n lines.push(this.generateEnumsSection(schema.enums));\n }\n\n // Generate table documentation\n for (const table of schema.tables) {\n lines.push(\"\");\n lines.push(\"---\");\n lines.push(\"\");\n lines.push(this.generateTableDoc(table, schema));\n }\n\n return lines.join(\"\\n\").trim();\n }\n\n /**\n * Generate the index section (README.md style)\n *\n * @param schema - The intermediate schema\n * @returns Markdown string for the index\n */\n generateIndex(schema: IntermediateSchema): string {\n const lines: string[] = [];\n\n lines.push(\"# Tables\");\n lines.push(\"\");\n\n if (schema.tables.length === 0) {\n lines.push(\"No tables defined.\");\n return lines.join(\"\\n\");\n }\n\n // Table header\n lines.push(\"| Name | Columns | Comment |\");\n lines.push(\"|------|---------|---------|\");\n\n // Table rows\n for (const table of schema.tables) {\n const name = this.options.useRelativeLinks ? this.createTableLink(table.name) : table.name;\n const columnCount = table.columns.length;\n const comment =\n this.options.includeComments && table.comment ? this.escapeMarkdown(table.comment) : \"\";\n\n lines.push(`| ${name} | ${columnCount} | ${comment} |`);\n }\n\n return lines.join(\"\\n\");\n }\n\n /**\n * Generate documentation for a single table\n *\n * @param table - The table definition\n * @param schema - The full schema (for relation lookups)\n * @returns Markdown string for the table documentation\n */\n generateTableDoc(table: TableDefinition, schema: IntermediateSchema): string {\n const lines: string[] = [];\n\n // Table heading with anchor\n lines.push(`## ${table.name}`);\n lines.push(\"\");\n\n // Table comment (preserve newlines as <br> for better readability)\n if (this.options.includeComments && table.comment) {\n lines.push(this.escapeMarkdownWithBreaks(table.comment));\n lines.push(\"\");\n }\n\n // Columns section\n lines.push(this.generateColumnsTable(table.columns, table.name, schema));\n\n // Constraints section\n if (this.options.includeConstraints && table.constraints.length > 0) {\n lines.push(\"\");\n lines.push(this.generateConstraintsTable(table.constraints));\n }\n\n // Indexes section\n if (this.options.includeIndexes && table.indexes.length > 0) {\n lines.push(\"\");\n lines.push(this.generateIndexesTable(table.indexes));\n }\n\n // Relations section\n const tableRelations = this.getTableRelations(table.name, schema.relations);\n if (tableRelations.length > 0) {\n lines.push(\"\");\n lines.push(this.generateRelationsTable(tableRelations, table.name));\n }\n\n return lines.join(\"\\n\");\n }\n\n /**\n * Generate the columns table for a table\n */\n private generateColumnsTable(\n columns: ColumnDefinition[],\n tableName: string,\n schema: IntermediateSchema,\n ): string {\n const lines: string[] = [];\n\n lines.push(\"### Columns\");\n lines.push(\"\");\n\n if (columns.length === 0) {\n lines.push(\"No columns defined.\");\n return lines.join(\"\\n\");\n }\n\n // Build column info with relations\n const columnInfo = columns.map((col) => {\n const children = this.getChildRelations(tableName, col.name, schema.relations);\n const parents = this.getParentRelations(tableName, col.name, schema.relations);\n return { column: col, children, parents };\n });\n\n // Table header\n lines.push(\"| Name | Type | Default | Nullable | Children | Parents | Comment |\");\n lines.push(\"|------|------|---------|----------|----------|---------|---------|\");\n\n // Table rows\n for (const { column, children, parents } of columnInfo) {\n const name = column.primaryKey ? `**${column.name}**` : column.name;\n const type = this.escapeMarkdown(column.type);\n const defaultVal = column.defaultValue !== undefined ? `\\`${column.defaultValue}\\`` : \"-\";\n const nullable = column.nullable ? \"YES\" : \"NO\";\n const childrenStr = children.length > 0 ? this.formatRelationLinks(children) : \"-\";\n const parentsStr = parents.length > 0 ? this.formatRelationLinks(parents) : \"-\";\n const comment =\n this.options.includeComments && column.comment ? this.escapeMarkdown(column.comment) : \"-\";\n\n lines.push(\n `| ${name} | ${type} | ${defaultVal} | ${nullable} | ${childrenStr} | ${parentsStr} | ${comment} |`,\n );\n }\n\n return lines.join(\"\\n\");\n }\n\n /**\n * Generate the constraints table\n */\n private generateConstraintsTable(constraints: ConstraintDefinition[]): string {\n const lines: string[] = [];\n\n lines.push(\"### Constraints\");\n lines.push(\"\");\n\n lines.push(\"| Name | Type | Definition |\");\n lines.push(\"|------|------|------------|\");\n\n for (const constraint of constraints) {\n const name = constraint.name || \"-\";\n const type = this.formatConstraintType(constraint.type);\n const definition = this.formatConstraintDefinition(constraint);\n\n lines.push(`| ${name} | ${type} | ${definition} |`);\n }\n\n return lines.join(\"\\n\");\n }\n\n /**\n * Generate the indexes table\n */\n private generateIndexesTable(indexes: IndexDefinition[]): string {\n const lines: string[] = [];\n\n lines.push(\"### Indexes\");\n lines.push(\"\");\n\n lines.push(\"| Name | Columns | Unique | Type |\");\n lines.push(\"|------|---------|--------|------|\");\n\n for (const index of indexes) {\n const name = index.name || \"-\";\n const columns = index.columns.join(\", \");\n const unique = index.unique ? \"YES\" : \"NO\";\n const type = index.type || \"-\";\n\n lines.push(`| ${name} | ${columns} | ${unique} | ${type} |`);\n }\n\n return lines.join(\"\\n\");\n }\n\n /**\n * Generate the relations table for a specific table\n */\n private generateRelationsTable(relations: RelationDefinition[], tableName: string): string {\n const lines: string[] = [];\n\n lines.push(\"### Relations\");\n lines.push(\"\");\n\n lines.push(\"| Parent | Child | Type |\");\n lines.push(\"|--------|-------|------|\");\n\n for (const relation of relations) {\n const isParent = relation.toTable === tableName;\n const parent = `${relation.toTable}.${relation.toColumns.join(\", \")}`;\n const child = `${relation.fromTable}.${relation.fromColumns.join(\", \")}`;\n const type = this.formatRelationType(relation.type);\n\n // Add links if enabled\n const parentLink = this.options.useRelativeLinks\n ? this.createTableLink(relation.toTable, parent)\n : parent;\n const childLink = this.options.useRelativeLinks\n ? this.createTableLink(relation.fromTable, child)\n : child;\n\n // Highlight the current table\n const parentDisplay = isParent ? `**${parentLink}**` : parentLink;\n const childDisplay = !isParent ? `**${childLink}**` : childLink;\n\n lines.push(`| ${parentDisplay} | ${childDisplay} | ${type} |`);\n }\n\n return lines.join(\"\\n\");\n }\n\n /**\n * Generate documentation for enums\n */\n private generateEnumsSection(enums: EnumDefinition[]): string {\n const lines: string[] = [];\n\n lines.push(\"# Enums\");\n lines.push(\"\");\n\n for (const enumDef of enums) {\n lines.push(`## ${enumDef.name}`);\n lines.push(\"\");\n lines.push(\"| Value |\");\n lines.push(\"|-------|\");\n for (const value of enumDef.values) {\n lines.push(`| ${value} |`);\n }\n lines.push(\"\");\n }\n\n return lines.join(\"\\n\").trim();\n }\n\n /**\n * Get all relations involving a specific table\n */\n private getTableRelations(\n tableName: string,\n relations: RelationDefinition[],\n ): RelationDefinition[] {\n return relations.filter((r) => r.fromTable === tableName || r.toTable === tableName);\n }\n\n /**\n * Get child relations for a specific column (where this column is referenced)\n */\n private getChildRelations(\n tableName: string,\n columnName: string,\n relations: RelationDefinition[],\n ): Array<{ table: string; column: string }> {\n return relations\n .filter((r) => r.toTable === tableName && r.toColumns.includes(columnName))\n .map((r) => ({\n table: r.fromTable,\n column: r.fromColumns.join(\", \"),\n }));\n }\n\n /**\n * Get parent relations for a specific column (columns this column references)\n */\n private getParentRelations(\n tableName: string,\n columnName: string,\n relations: RelationDefinition[],\n ): Array<{ table: string; column: string }> {\n return relations\n .filter((r) => r.fromTable === tableName && r.fromColumns.includes(columnName))\n .map((r) => ({\n table: r.toTable,\n column: r.toColumns.join(\", \"),\n }));\n }\n\n /**\n * Format relation links for display in columns table\n */\n private formatRelationLinks(relations: Array<{ table: string; column: string }>): string {\n return relations\n .map((r) => {\n const text = `${r.table}.${r.column}`;\n return this.options.useRelativeLinks ? this.createTableLink(r.table, text) : text;\n })\n .join(\", \");\n }\n\n /**\n * Format constraint type for display\n */\n private formatConstraintType(type: ConstraintDefinition[\"type\"]): string {\n const typeMap: Record<ConstraintDefinition[\"type\"], string> = {\n primary_key: \"PRIMARY KEY\",\n foreign_key: \"FOREIGN KEY\",\n unique: \"UNIQUE\",\n check: \"CHECK\",\n not_null: \"NOT NULL\",\n };\n return typeMap[type] || type;\n }\n\n /**\n * Format constraint definition for display\n */\n private formatConstraintDefinition(constraint: ConstraintDefinition): string {\n if (constraint.definition) {\n return `\\`${constraint.definition}\\``;\n }\n\n const columns = constraint.columns.join(\", \");\n\n if (constraint.type === \"foreign_key\" && constraint.referencedTable) {\n const refColumns = constraint.referencedColumns?.join(\", \") || \"\";\n return `(${columns}) → ${constraint.referencedTable}(${refColumns})`;\n }\n\n return `(${columns})`;\n }\n\n /**\n * Format relation type for display\n */\n private formatRelationType(type: RelationDefinition[\"type\"]): string {\n const typeMap: Record<RelationDefinition[\"type\"], string> = {\n \"one-to-one\": \"One to One\",\n \"one-to-many\": \"One to Many\",\n \"many-to-one\": \"Many to One\",\n \"many-to-many\": \"Many to Many\",\n };\n return typeMap[type] || type;\n }\n\n /**\n * Create a URL-safe slug from a string\n */\n private slugify(str: string): string {\n return str\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n }\n\n /**\n * Create a table link based on the configured link format\n */\n private createTableLink(tableName: string, displayText?: string): string {\n const text = displayText || tableName;\n if (this.options.linkFormat === \"file\") {\n return `[${text}](./${tableName}.md)`;\n }\n return `[${text}](#${this.slugify(tableName)})`;\n }\n\n /**\n * Escape special Markdown characters in a string\n * Converts newlines to spaces for use in table cells\n */\n private escapeMarkdown(str: string): string {\n return str.replace(/\\|/g, \"\\\\|\").replace(/\\n/g, \" \");\n }\n\n /**\n * Escape special Markdown characters and convert newlines to <br> tags\n * Used for better readability in text sections (not in tables)\n */\n private escapeMarkdownWithBreaks(str: string): string {\n return str.replace(/\\|/g, \"\\\\|\").replace(/\\n/g, \" \\n\");\n }\n}\n"],"names":["DEFAULT_OPTIONS","MarkdownFormatter","options","schema","lines","table","name","columnCount","comment","tableRelations","columns","tableName","columnInfo","col","children","parents","column","type","defaultVal","nullable","childrenStr","parentsStr","constraints","constraint","definition","indexes","index","unique","relations","relation","isParent","parent","child","parentLink","childLink","parentDisplay","childDisplay","enums","enumDef","value","r","columnName","text","refColumns","str","displayText"],"mappings":"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"}
|
package/dist/parser/comments.js
CHANGED
|
@@ -1,71 +1,71 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import { readFileSync as
|
|
3
|
-
import { join as
|
|
1
|
+
import * as o from "typescript";
|
|
2
|
+
import { readFileSync as x, statSync as h, readdirSync as b } from "node:fs";
|
|
3
|
+
import { join as C } from "node:path";
|
|
4
4
|
function p(t) {
|
|
5
|
-
const n =
|
|
5
|
+
const n = h(t);
|
|
6
6
|
if (n.isFile())
|
|
7
7
|
return t.endsWith(".ts") ? [t] : [];
|
|
8
8
|
if (n.isDirectory()) {
|
|
9
|
-
const
|
|
10
|
-
for (const e of
|
|
11
|
-
const
|
|
12
|
-
e.isDirectory() ?
|
|
9
|
+
const s = [], i = b(t, { withFileTypes: !0 });
|
|
10
|
+
for (const e of i) {
|
|
11
|
+
const r = C(t, e.name);
|
|
12
|
+
e.isDirectory() ? s.push(...p(r)) : e.isFile() && e.name.endsWith(".ts") && !e.name.endsWith(".test.ts") && s.push(r);
|
|
13
13
|
}
|
|
14
|
-
return
|
|
14
|
+
return s;
|
|
15
15
|
}
|
|
16
16
|
return [];
|
|
17
17
|
}
|
|
18
18
|
function L(t) {
|
|
19
|
-
const n = { tables: {} },
|
|
20
|
-
for (const
|
|
21
|
-
const e =
|
|
22
|
-
d(
|
|
19
|
+
const n = { tables: {} }, s = p(t);
|
|
20
|
+
for (const i of s) {
|
|
21
|
+
const e = x(i, "utf-8"), r = o.createSourceFile(i, e, o.ScriptTarget.Latest, !0);
|
|
22
|
+
d(r, r, n);
|
|
23
23
|
}
|
|
24
24
|
return n;
|
|
25
25
|
}
|
|
26
|
-
function d(t, n,
|
|
27
|
-
if (
|
|
28
|
-
const
|
|
26
|
+
function d(t, n, s) {
|
|
27
|
+
if (o.isVariableStatement(t)) {
|
|
28
|
+
const i = g(t, n);
|
|
29
29
|
for (const e of t.declarationList.declarations)
|
|
30
|
-
if (
|
|
31
|
-
const
|
|
30
|
+
if (o.isIdentifier(e.name) && e.initializer && o.isCallExpression(e.initializer)) {
|
|
31
|
+
const r = y(
|
|
32
32
|
e.name.text,
|
|
33
33
|
e.initializer,
|
|
34
34
|
n,
|
|
35
|
-
|
|
35
|
+
i
|
|
36
36
|
);
|
|
37
|
-
|
|
37
|
+
r && (s.tables[r.tableName] = r.tableComment);
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
|
-
|
|
40
|
+
o.forEachChild(t, (i) => d(i, n, s));
|
|
41
41
|
}
|
|
42
|
-
function
|
|
42
|
+
function y(t, n, s, i) {
|
|
43
43
|
const e = T(n);
|
|
44
44
|
if (!S(e))
|
|
45
45
|
return;
|
|
46
|
-
const
|
|
47
|
-
if (!
|
|
46
|
+
const r = n.arguments[0];
|
|
47
|
+
if (!r || !o.isStringLiteral(r))
|
|
48
48
|
return;
|
|
49
|
-
const c =
|
|
50
|
-
if (
|
|
51
|
-
for (const a of
|
|
52
|
-
if (
|
|
53
|
-
const
|
|
54
|
-
|
|
49
|
+
const c = r.text, l = n.arguments[1], m = {};
|
|
50
|
+
if (l && o.isObjectLiteralExpression(l)) {
|
|
51
|
+
for (const a of l.properties)
|
|
52
|
+
if (o.isPropertyAssignment(a) && o.isIdentifier(a.name)) {
|
|
53
|
+
const f = v(a.initializer), u = g(a, s);
|
|
54
|
+
f && u && (m[f] = { comment: u });
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
return {
|
|
58
58
|
tableName: c,
|
|
59
59
|
tableComment: {
|
|
60
|
-
comment:
|
|
61
|
-
columns:
|
|
60
|
+
comment: i,
|
|
61
|
+
columns: m
|
|
62
62
|
}
|
|
63
63
|
};
|
|
64
64
|
}
|
|
65
65
|
function T(t) {
|
|
66
|
-
if (
|
|
66
|
+
if (o.isIdentifier(t.expression))
|
|
67
67
|
return t.expression.text;
|
|
68
|
-
if (
|
|
68
|
+
if (o.isPropertyAccessExpression(t.expression))
|
|
69
69
|
return t.expression.name.text;
|
|
70
70
|
}
|
|
71
71
|
function S(t) {
|
|
@@ -73,39 +73,44 @@ function S(t) {
|
|
|
73
73
|
}
|
|
74
74
|
function v(t) {
|
|
75
75
|
let n = t;
|
|
76
|
-
for (;
|
|
77
|
-
if (
|
|
76
|
+
for (; o.isCallExpression(n); )
|
|
77
|
+
if (o.isPropertyAccessExpression(n.expression))
|
|
78
78
|
n = n.expression.expression;
|
|
79
|
-
else if (
|
|
80
|
-
const
|
|
81
|
-
return
|
|
79
|
+
else if (o.isIdentifier(n.expression)) {
|
|
80
|
+
const s = n.arguments[0];
|
|
81
|
+
return s && o.isStringLiteral(s) ? s.text : void 0;
|
|
82
82
|
} else
|
|
83
83
|
return;
|
|
84
84
|
}
|
|
85
|
-
function
|
|
86
|
-
const
|
|
85
|
+
function g(t, n) {
|
|
86
|
+
const s = n.getFullText(), i = t.getFullStart(), e = o.getLeadingCommentRanges(s, i);
|
|
87
87
|
if (!(!e || e.length === 0)) {
|
|
88
|
-
for (const
|
|
89
|
-
const c =
|
|
88
|
+
for (const r of e) {
|
|
89
|
+
const c = s.slice(r.pos, r.end);
|
|
90
90
|
if (c.startsWith("/**"))
|
|
91
91
|
return D(c);
|
|
92
92
|
}
|
|
93
|
-
for (const
|
|
94
|
-
const c =
|
|
93
|
+
for (const r of e) {
|
|
94
|
+
const c = s.slice(r.pos, r.end);
|
|
95
95
|
if (c.startsWith("//"))
|
|
96
96
|
return c.slice(2).trim();
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
function D(t) {
|
|
101
|
-
const
|
|
102
|
-
`).map((e) => e.replace(/^\s*\*\s?/, "").trim()),
|
|
103
|
-
for (const e of
|
|
101
|
+
const s = t.slice(3, -2).split(`
|
|
102
|
+
`).map((e) => e.replace(/^\s*\*\s?/, "").trim()), i = [];
|
|
103
|
+
for (const e of s) {
|
|
104
104
|
if (e.startsWith("@"))
|
|
105
105
|
break;
|
|
106
|
-
|
|
106
|
+
i.push(e);
|
|
107
107
|
}
|
|
108
|
-
|
|
108
|
+
for (; i.length > 0 && i[i.length - 1] === ""; )
|
|
109
|
+
i.pop();
|
|
110
|
+
for (; i.length > 0 && i[0] === ""; )
|
|
111
|
+
i.shift();
|
|
112
|
+
return i.join(`
|
|
113
|
+
`).trim();
|
|
109
114
|
}
|
|
110
115
|
export {
|
|
111
116
|
L as extractComments
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"comments.js","sources":["../../src/parser/comments.ts"],"sourcesContent":["import * as ts from \"typescript\";\nimport { readFileSync, statSync, readdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\n/**\n * Comments for a single column\n */\nexport interface ColumnComment {\n comment: string;\n}\n\n/**\n * Comments for a single table\n */\nexport interface TableComment {\n comment?: string;\n columns: Record<string, ColumnComment>;\n}\n\n/**\n * All extracted comments from a schema file\n */\nexport interface SchemaComments {\n tables: Record<string, TableComment>;\n}\n\n/**\n * Get all TypeScript files from a path (file or directory)\n */\nfunction getTypeScriptFiles(sourcePath: string): string[] {\n const stat = statSync(sourcePath);\n\n if (stat.isFile()) {\n return sourcePath.endsWith(\".ts\") ? [sourcePath] : [];\n }\n\n if (stat.isDirectory()) {\n const files: string[] = [];\n const entries = readdirSync(sourcePath, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = join(sourcePath, entry.name);\n if (entry.isDirectory()) {\n files.push(...getTypeScriptFiles(fullPath));\n } else if (entry.isFile() && entry.name.endsWith(\".ts\") && !entry.name.endsWith(\".test.ts\")) {\n files.push(fullPath);\n }\n }\n\n return files;\n }\n\n return [];\n}\n\n/**\n * Extract JSDoc comments from a Drizzle schema source file or directory\n *\n * Parses TypeScript source files and extracts:\n * - JSDoc comments on table definitions (e.g., pgTable, mysqlTable, sqliteTable)\n * - JSDoc comments on column definitions within tables\n *\n * @param sourcePath - Path to the TypeScript schema file or directory\n * @returns Extracted comments organized by table and column\n */\nexport function extractComments(sourcePath: string): SchemaComments {\n const comments: SchemaComments = { tables: {} };\n const files = getTypeScriptFiles(sourcePath);\n\n for (const filePath of files) {\n const sourceCode = readFileSync(filePath, \"utf-8\");\n const sourceFile = ts.createSourceFile(filePath, sourceCode, ts.ScriptTarget.Latest, true);\n\n // Visit all nodes in the source file\n visitNode(sourceFile, sourceFile, comments);\n }\n\n return comments;\n}\n\n/**\n * Recursively visit AST nodes to find table and column definitions\n */\nfunction visitNode(node: ts.Node, sourceFile: ts.SourceFile, comments: SchemaComments): void {\n // Look for variable declarations that define tables\n if (ts.isVariableStatement(node)) {\n const jsDocComment = getJsDocComment(node, sourceFile);\n\n for (const declaration of node.declarationList.declarations) {\n if (\n ts.isIdentifier(declaration.name) &&\n declaration.initializer &&\n ts.isCallExpression(declaration.initializer)\n ) {\n const tableInfo = parseTableDefinition(\n declaration.name.text,\n declaration.initializer,\n sourceFile,\n jsDocComment,\n );\n if (tableInfo) {\n comments.tables[tableInfo.tableName] = tableInfo.tableComment;\n }\n }\n }\n }\n\n ts.forEachChild(node, (child) => visitNode(child, sourceFile, comments));\n}\n\n/**\n * Parse a table definition call expression (e.g., pgTable(\"users\", { ... }))\n */\nfunction parseTableDefinition(\n _variableName: string,\n callExpr: ts.CallExpression,\n sourceFile: ts.SourceFile,\n tableJsDoc: string | undefined,\n): { tableName: string; tableComment: TableComment } | undefined {\n const funcName = getCallExpressionName(callExpr);\n\n // Check if this is a table definition function\n if (!isTableDefinitionFunction(funcName)) {\n return undefined;\n }\n\n // Get table name from first argument\n const tableNameArg = callExpr.arguments[0];\n if (!tableNameArg || !ts.isStringLiteral(tableNameArg)) {\n return undefined;\n }\n const tableName = tableNameArg.text;\n\n // Get column definitions from second argument\n const columnsArg = callExpr.arguments[1];\n const columnComments: Record<string, ColumnComment> = {};\n\n if (columnsArg && ts.isObjectLiteralExpression(columnsArg)) {\n for (const property of columnsArg.properties) {\n if (ts.isPropertyAssignment(property) && ts.isIdentifier(property.name)) {\n const columnName = extractColumnName(property.initializer);\n const columnJsDoc = getJsDocComment(property, sourceFile);\n\n if (columnName && columnJsDoc) {\n columnComments[columnName] = { comment: columnJsDoc };\n }\n }\n }\n }\n\n return {\n tableName,\n tableComment: {\n comment: tableJsDoc,\n columns: columnComments,\n },\n };\n}\n\n/**\n * Get the function name from a call expression\n */\nfunction getCallExpressionName(callExpr: ts.CallExpression): string | undefined {\n if (ts.isIdentifier(callExpr.expression)) {\n return callExpr.expression.text;\n }\n if (ts.isPropertyAccessExpression(callExpr.expression)) {\n return callExpr.expression.name.text;\n }\n return undefined;\n}\n\n/**\n * Check if a function name is a table definition function\n */\nfunction isTableDefinitionFunction(funcName: string | undefined): boolean {\n if (!funcName) return false;\n return [\"pgTable\", \"mysqlTable\", \"sqliteTable\"].includes(funcName);\n}\n\n/**\n * Extract the actual column name from a column definition\n * e.g., serial(\"id\") -> \"id\", text(\"name\") -> \"name\"\n */\nfunction extractColumnName(expr: ts.Expression): string | undefined {\n // Handle chained calls like serial(\"id\").primaryKey()\n let current = expr;\n\n while (ts.isCallExpression(current)) {\n if (ts.isPropertyAccessExpression(current.expression)) {\n // This is a method call like .primaryKey(), go deeper\n current = current.expression.expression;\n } else if (ts.isIdentifier(current.expression)) {\n // This is the base call like serial(\"id\")\n const firstArg = current.arguments[0];\n if (firstArg && ts.isStringLiteral(firstArg)) {\n return firstArg.text;\n }\n return undefined;\n } else {\n return undefined;\n }\n }\n\n return undefined;\n}\n\n/**\n * Get JSDoc comment from a node\n */\nfunction getJsDocComment(node: ts.Node, sourceFile: ts.SourceFile): string | undefined {\n const fullText = sourceFile.getFullText();\n const nodeStart = node.getFullStart();\n const leadingComments = ts.getLeadingCommentRanges(fullText, nodeStart);\n\n if (!leadingComments || leadingComments.length === 0) {\n return undefined;\n }\n\n // Find JSDoc comment (starts with /**)\n for (const comment of leadingComments) {\n const commentText = fullText.slice(comment.pos, comment.end);\n if (commentText.startsWith(\"/**\")) {\n return parseJsDocComment(commentText);\n }\n }\n\n // Fall back to single-line comments\n for (const comment of leadingComments) {\n const commentText = fullText.slice(comment.pos, comment.end);\n if (commentText.startsWith(\"//\")) {\n return commentText.slice(2).trim();\n }\n }\n\n return undefined;\n}\n\n/**\n * Parse JSDoc comment text to extract the description\n */\nfunction parseJsDocComment(commentText: string): string {\n // Remove /** and */\n let text = commentText.slice(3, -2);\n\n // Split into lines and process\n const lines = text.split(\"\\n\").map((line) => {\n // Remove leading * and whitespace\n return line.replace(/^\\s*\\*\\s?/, \"\").trim();\n });\n\n // Filter out @tags and empty lines at start/end\n const contentLines: string[] = [];\n for (const line of lines) {\n // Stop at first @tag\n if (line.startsWith(\"@\")) {\n break;\n }\n contentLines.push(line);\n }\n\n // Join and trim\n return contentLines.join(\" \").trim();\n}\n"],"names":["getTypeScriptFiles","sourcePath","stat","statSync","files","entries","readdirSync","entry","fullPath","join","extractComments","comments","filePath","sourceCode","readFileSync","sourceFile","ts","visitNode","node","jsDocComment","getJsDocComment","declaration","tableInfo","parseTableDefinition","child","_variableName","callExpr","tableJsDoc","funcName","getCallExpressionName","isTableDefinitionFunction","tableNameArg","tableName","columnsArg","columnComments","property","columnName","extractColumnName","columnJsDoc","expr","current","firstArg","fullText","nodeStart","leadingComments","comment","commentText","parseJsDocComment","lines","line","contentLines"],"mappings":";;;AA6BA,SAASA,EAAmBC,GAA8B;AACxD,QAAMC,IAAOC,EAASF,CAAU;AAEhC,MAAIC,EAAK;AACP,WAAOD,EAAW,SAAS,KAAK,IAAI,CAACA,CAAU,IAAI,CAAA;AAGrD,MAAIC,EAAK,eAAe;AACtB,UAAME,IAAkB,CAAA,GAClBC,IAAUC,EAAYL,GAAY,EAAE,eAAe,IAAM;AAE/D,eAAWM,KAASF,GAAS;AAC3B,YAAMG,IAAWC,EAAKR,GAAYM,EAAM,IAAI;AAC5C,MAAIA,EAAM,gBACRH,EAAM,KAAK,GAAGJ,EAAmBQ,CAAQ,CAAC,IACjCD,EAAM,OAAA,KAAYA,EAAM,KAAK,SAAS,KAAK,KAAK,CAACA,EAAM,KAAK,SAAS,UAAU,KACxFH,EAAM,KAAKI,CAAQ;AAAA,IAEvB;AAEA,WAAOJ;AAAA,EACT;AAEA,SAAO,CAAA;AACT;AAYO,SAASM,EAAgBT,GAAoC;AAClE,QAAMU,IAA2B,EAAE,QAAQ,GAAC,GACtCP,IAAQJ,EAAmBC,CAAU;AAE3C,aAAWW,KAAYR,GAAO;AAC5B,UAAMS,IAAaC,EAAaF,GAAU,OAAO,GAC3CG,IAAaC,EAAG,iBAAiBJ,GAAUC,GAAYG,EAAG,aAAa,QAAQ,EAAI;AAGzF,IAAAC,EAAUF,GAAYA,GAAYJ,CAAQ;AAAA,EAC5C;AAEA,SAAOA;AACT;AAKA,SAASM,EAAUC,GAAeH,GAA2BJ,GAAgC;AAE3F,MAAIK,EAAG,oBAAoBE,CAAI,GAAG;AAChC,UAAMC,IAAeC,EAAgBF,GAAMH,CAAU;AAErD,eAAWM,KAAeH,EAAK,gBAAgB;AAC7C,UACEF,EAAG,aAAaK,EAAY,IAAI,KAChCA,EAAY,eACZL,EAAG,iBAAiBK,EAAY,WAAW,GAC3C;AACA,cAAMC,IAAYC;AAAA,UAChBF,EAAY,KAAK;AAAA,UACjBA,EAAY;AAAA,UACZN;AAAA,UACAI;AAAA,QAAA;AAEF,QAAIG,MACFX,EAAS,OAAOW,EAAU,SAAS,IAAIA,EAAU;AAAA,MAErD;AAAA,EAEJ;AAEA,EAAAN,EAAG,aAAaE,GAAM,CAACM,MAAUP,EAAUO,GAAOT,GAAYJ,CAAQ,CAAC;AACzE;AAKA,SAASY,EACPE,GACAC,GACAX,GACAY,GAC+D;AAC/D,QAAMC,IAAWC,EAAsBH,CAAQ;AAG/C,MAAI,CAACI,EAA0BF,CAAQ;AACrC;AAIF,QAAMG,IAAeL,EAAS,UAAU,CAAC;AACzC,MAAI,CAACK,KAAgB,CAACf,EAAG,gBAAgBe,CAAY;AACnD;AAEF,QAAMC,IAAYD,EAAa,MAGzBE,IAAaP,EAAS,UAAU,CAAC,GACjCQ,IAAgD,CAAA;AAEtD,MAAID,KAAcjB,EAAG,0BAA0BiB,CAAU;AACvD,eAAWE,KAAYF,EAAW;AAChC,UAAIjB,EAAG,qBAAqBmB,CAAQ,KAAKnB,EAAG,aAAamB,EAAS,IAAI,GAAG;AACvE,cAAMC,IAAaC,EAAkBF,EAAS,WAAW,GACnDG,IAAclB,EAAgBe,GAAUpB,CAAU;AAExD,QAAIqB,KAAcE,MAChBJ,EAAeE,CAAU,IAAI,EAAE,SAASE,EAAA;AAAA,MAE5C;AAAA;AAIJ,SAAO;AAAA,IACL,WAAAN;AAAA,IACA,cAAc;AAAA,MACZ,SAASL;AAAA,MACT,SAASO;AAAA,IAAA;AAAA,EACX;AAEJ;AAKA,SAASL,EAAsBH,GAAiD;AAC9E,MAAIV,EAAG,aAAaU,EAAS,UAAU;AACrC,WAAOA,EAAS,WAAW;AAE7B,MAAIV,EAAG,2BAA2BU,EAAS,UAAU;AACnD,WAAOA,EAAS,WAAW,KAAK;AAGpC;AAKA,SAASI,EAA0BF,GAAuC;AACxE,SAAKA,IACE,CAAC,WAAW,cAAc,aAAa,EAAE,SAASA,CAAQ,IAD3C;AAExB;AAMA,SAASS,EAAkBE,GAAyC;AAElE,MAAIC,IAAUD;AAEd,SAAOvB,EAAG,iBAAiBwB,CAAO;AAChC,QAAIxB,EAAG,2BAA2BwB,EAAQ,UAAU;AAElD,MAAAA,IAAUA,EAAQ,WAAW;AAAA,aACpBxB,EAAG,aAAawB,EAAQ,UAAU,GAAG;AAE9C,YAAMC,IAAWD,EAAQ,UAAU,CAAC;AACpC,aAAIC,KAAYzB,EAAG,gBAAgByB,CAAQ,IAClCA,EAAS,OAElB;AAAA,IACF;AACE;AAKN;AAKA,SAASrB,EAAgBF,GAAeH,GAA+C;AACrF,QAAM2B,IAAW3B,EAAW,YAAA,GACtB4B,IAAYzB,EAAK,aAAA,GACjB0B,IAAkB5B,EAAG,wBAAwB0B,GAAUC,CAAS;AAEtE,MAAI,GAACC,KAAmBA,EAAgB,WAAW,IAKnD;AAAA,eAAWC,KAAWD,GAAiB;AACrC,YAAME,IAAcJ,EAAS,MAAMG,EAAQ,KAAKA,EAAQ,GAAG;AAC3D,UAAIC,EAAY,WAAW,KAAK;AAC9B,eAAOC,EAAkBD,CAAW;AAAA,IAExC;AAGA,eAAWD,KAAWD,GAAiB;AACrC,YAAME,IAAcJ,EAAS,MAAMG,EAAQ,KAAKA,EAAQ,GAAG;AAC3D,UAAIC,EAAY,WAAW,IAAI;AAC7B,eAAOA,EAAY,MAAM,CAAC,EAAE,KAAA;AAAA,IAEhC;AAAA;AAGF;AAKA,SAASC,EAAkBD,GAA6B;AAKtD,QAAME,IAHKF,EAAY,MAAM,GAAG,EAAE,EAGf,MAAM;AAAA,CAAI,EAAE,IAAI,CAACG,MAE3BA,EAAK,QAAQ,aAAa,EAAE,EAAE,KAAA,CACtC,GAGKC,IAAyB,CAAA;AAC/B,aAAWD,KAAQD,GAAO;AAExB,QAAIC,EAAK,WAAW,GAAG;AACrB;AAEF,IAAAC,EAAa,KAAKD,CAAI;AAAA,EACxB;AAGA,SAAOC,EAAa,KAAK,GAAG,EAAE,KAAA;AAChC;"}
|
|
1
|
+
{"version":3,"file":"comments.js","sources":["../../src/parser/comments.ts"],"sourcesContent":["import * as ts from \"typescript\";\nimport { readFileSync, statSync, readdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\n/**\n * Comments for a single column\n */\nexport interface ColumnComment {\n comment: string;\n}\n\n/**\n * Comments for a single table\n */\nexport interface TableComment {\n comment?: string;\n columns: Record<string, ColumnComment>;\n}\n\n/**\n * All extracted comments from a schema file\n */\nexport interface SchemaComments {\n tables: Record<string, TableComment>;\n}\n\n/**\n * Get all TypeScript files from a path (file or directory)\n */\nfunction getTypeScriptFiles(sourcePath: string): string[] {\n const stat = statSync(sourcePath);\n\n if (stat.isFile()) {\n return sourcePath.endsWith(\".ts\") ? [sourcePath] : [];\n }\n\n if (stat.isDirectory()) {\n const files: string[] = [];\n const entries = readdirSync(sourcePath, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = join(sourcePath, entry.name);\n if (entry.isDirectory()) {\n files.push(...getTypeScriptFiles(fullPath));\n } else if (entry.isFile() && entry.name.endsWith(\".ts\") && !entry.name.endsWith(\".test.ts\")) {\n files.push(fullPath);\n }\n }\n\n return files;\n }\n\n return [];\n}\n\n/**\n * Extract JSDoc comments from a Drizzle schema source file or directory\n *\n * Parses TypeScript source files and extracts:\n * - JSDoc comments on table definitions (e.g., pgTable, mysqlTable, sqliteTable)\n * - JSDoc comments on column definitions within tables\n *\n * @param sourcePath - Path to the TypeScript schema file or directory\n * @returns Extracted comments organized by table and column\n */\nexport function extractComments(sourcePath: string): SchemaComments {\n const comments: SchemaComments = { tables: {} };\n const files = getTypeScriptFiles(sourcePath);\n\n for (const filePath of files) {\n const sourceCode = readFileSync(filePath, \"utf-8\");\n const sourceFile = ts.createSourceFile(filePath, sourceCode, ts.ScriptTarget.Latest, true);\n\n // Visit all nodes in the source file\n visitNode(sourceFile, sourceFile, comments);\n }\n\n return comments;\n}\n\n/**\n * Recursively visit AST nodes to find table and column definitions\n */\nfunction visitNode(node: ts.Node, sourceFile: ts.SourceFile, comments: SchemaComments): void {\n // Look for variable declarations that define tables\n if (ts.isVariableStatement(node)) {\n const jsDocComment = getJsDocComment(node, sourceFile);\n\n for (const declaration of node.declarationList.declarations) {\n if (\n ts.isIdentifier(declaration.name) &&\n declaration.initializer &&\n ts.isCallExpression(declaration.initializer)\n ) {\n const tableInfo = parseTableDefinition(\n declaration.name.text,\n declaration.initializer,\n sourceFile,\n jsDocComment,\n );\n if (tableInfo) {\n comments.tables[tableInfo.tableName] = tableInfo.tableComment;\n }\n }\n }\n }\n\n ts.forEachChild(node, (child) => visitNode(child, sourceFile, comments));\n}\n\n/**\n * Parse a table definition call expression (e.g., pgTable(\"users\", { ... }))\n */\nfunction parseTableDefinition(\n _variableName: string,\n callExpr: ts.CallExpression,\n sourceFile: ts.SourceFile,\n tableJsDoc: string | undefined,\n): { tableName: string; tableComment: TableComment } | undefined {\n const funcName = getCallExpressionName(callExpr);\n\n // Check if this is a table definition function\n if (!isTableDefinitionFunction(funcName)) {\n return undefined;\n }\n\n // Get table name from first argument\n const tableNameArg = callExpr.arguments[0];\n if (!tableNameArg || !ts.isStringLiteral(tableNameArg)) {\n return undefined;\n }\n const tableName = tableNameArg.text;\n\n // Get column definitions from second argument\n const columnsArg = callExpr.arguments[1];\n const columnComments: Record<string, ColumnComment> = {};\n\n if (columnsArg && ts.isObjectLiteralExpression(columnsArg)) {\n for (const property of columnsArg.properties) {\n if (ts.isPropertyAssignment(property) && ts.isIdentifier(property.name)) {\n const columnName = extractColumnName(property.initializer);\n const columnJsDoc = getJsDocComment(property, sourceFile);\n\n if (columnName && columnJsDoc) {\n columnComments[columnName] = { comment: columnJsDoc };\n }\n }\n }\n }\n\n return {\n tableName,\n tableComment: {\n comment: tableJsDoc,\n columns: columnComments,\n },\n };\n}\n\n/**\n * Get the function name from a call expression\n */\nfunction getCallExpressionName(callExpr: ts.CallExpression): string | undefined {\n if (ts.isIdentifier(callExpr.expression)) {\n return callExpr.expression.text;\n }\n if (ts.isPropertyAccessExpression(callExpr.expression)) {\n return callExpr.expression.name.text;\n }\n return undefined;\n}\n\n/**\n * Check if a function name is a table definition function\n */\nfunction isTableDefinitionFunction(funcName: string | undefined): boolean {\n if (!funcName) return false;\n return [\"pgTable\", \"mysqlTable\", \"sqliteTable\"].includes(funcName);\n}\n\n/**\n * Extract the actual column name from a column definition\n * e.g., serial(\"id\") -> \"id\", text(\"name\") -> \"name\"\n */\nfunction extractColumnName(expr: ts.Expression): string | undefined {\n // Handle chained calls like serial(\"id\").primaryKey()\n let current = expr;\n\n while (ts.isCallExpression(current)) {\n if (ts.isPropertyAccessExpression(current.expression)) {\n // This is a method call like .primaryKey(), go deeper\n current = current.expression.expression;\n } else if (ts.isIdentifier(current.expression)) {\n // This is the base call like serial(\"id\")\n const firstArg = current.arguments[0];\n if (firstArg && ts.isStringLiteral(firstArg)) {\n return firstArg.text;\n }\n return undefined;\n } else {\n return undefined;\n }\n }\n\n return undefined;\n}\n\n/**\n * Get JSDoc comment from a node\n */\nfunction getJsDocComment(node: ts.Node, sourceFile: ts.SourceFile): string | undefined {\n const fullText = sourceFile.getFullText();\n const nodeStart = node.getFullStart();\n const leadingComments = ts.getLeadingCommentRanges(fullText, nodeStart);\n\n if (!leadingComments || leadingComments.length === 0) {\n return undefined;\n }\n\n // Find JSDoc comment (starts with /**)\n for (const comment of leadingComments) {\n const commentText = fullText.slice(comment.pos, comment.end);\n if (commentText.startsWith(\"/**\")) {\n return parseJsDocComment(commentText);\n }\n }\n\n // Fall back to single-line comments\n for (const comment of leadingComments) {\n const commentText = fullText.slice(comment.pos, comment.end);\n if (commentText.startsWith(\"//\")) {\n return commentText.slice(2).trim();\n }\n }\n\n return undefined;\n}\n\n/**\n * Parse JSDoc comment text to extract the description\n *\n * Preserves newlines in the output for proper formatting in DBML and Markdown.\n */\nfunction parseJsDocComment(commentText: string): string {\n // Remove /** and */\n let text = commentText.slice(3, -2);\n\n // Split into lines and process\n const lines = text.split(\"\\n\").map((line) => {\n // Remove leading * and whitespace\n return line.replace(/^\\s*\\*\\s?/, \"\").trim();\n });\n\n // Filter out @tags and empty lines at start/end\n const contentLines: string[] = [];\n for (const line of lines) {\n // Stop at first @tag\n if (line.startsWith(\"@\")) {\n break;\n }\n contentLines.push(line);\n }\n\n // Remove trailing empty lines\n while (contentLines.length > 0 && contentLines[contentLines.length - 1] === \"\") {\n contentLines.pop();\n }\n\n // Remove leading empty lines\n while (contentLines.length > 0 && contentLines[0] === \"\") {\n contentLines.shift();\n }\n\n // Join with newlines to preserve formatting\n return contentLines.join(\"\\n\").trim();\n}\n"],"names":["getTypeScriptFiles","sourcePath","stat","statSync","files","entries","readdirSync","entry","fullPath","join","extractComments","comments","filePath","sourceCode","readFileSync","sourceFile","ts","visitNode","node","jsDocComment","getJsDocComment","declaration","tableInfo","parseTableDefinition","child","_variableName","callExpr","tableJsDoc","funcName","getCallExpressionName","isTableDefinitionFunction","tableNameArg","tableName","columnsArg","columnComments","property","columnName","extractColumnName","columnJsDoc","expr","current","firstArg","fullText","nodeStart","leadingComments","comment","commentText","parseJsDocComment","lines","line","contentLines"],"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.5.0",
|
|
4
4
|
"description": "A CLI tool that generates DBML files from Drizzle ORM schema definitions.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -61,11 +61,11 @@
|
|
|
61
61
|
"dev": "vite build --watch",
|
|
62
62
|
"test": "vitest",
|
|
63
63
|
"test:run": "vitest run",
|
|
64
|
-
"test:integration": "vitest run --config vitest.integration.config.ts",
|
|
64
|
+
"test:integration": "rm -rf src/cli/__integration_test_output__ && vitest run --config vitest.integration.config.ts",
|
|
65
65
|
"lint": "oxlint",
|
|
66
66
|
"format": "oxfmt --write",
|
|
67
67
|
"format:check": "oxfmt --check",
|
|
68
68
|
"typecheck": "tsc --noEmit",
|
|
69
|
-
"generate:examples": "node dist/cli/index.js generate examples/pg/schema.ts -d postgresql -f dbml -o examples/pg/schema.dbml --force && node dist/cli/index.js generate examples/pg/schema.ts -d postgresql -f markdown -o examples/pg/markdown --force && node dist/cli/index.js generate examples/mysql/schema.ts -d mysql -f dbml -o examples/mysql/schema.dbml --force && node dist/cli/index.js generate examples/mysql/schema.ts -d mysql -f markdown -o examples/mysql/markdown --force && node dist/cli/index.js generate examples/sqlite/schema.ts -d sqlite -f dbml -o examples/sqlite/schema.dbml --force && node dist/cli/index.js generate examples/sqlite/schema.ts -d sqlite -f markdown -o examples/sqlite/markdown --force"
|
|
69
|
+
"generate:examples": "node dist/cli/index.js generate examples/pg/v0/schema.ts -d postgresql -f dbml -o examples/pg/v0/schema.dbml --force && node dist/cli/index.js generate examples/pg/v0/schema.ts -d postgresql -f markdown -o examples/pg/v0/markdown --force && node dist/cli/index.js generate examples/pg/v1/schema.ts -d postgresql -f dbml -o examples/pg/v1/schema.dbml --force && node dist/cli/index.js generate examples/pg/v1/schema.ts -d postgresql -f markdown -o examples/pg/v1/markdown --force && node dist/cli/index.js generate examples/mysql/v0/schema.ts -d mysql -f dbml -o examples/mysql/v0/schema.dbml --force && node dist/cli/index.js generate examples/mysql/v0/schema.ts -d mysql -f markdown -o examples/mysql/v0/markdown --force && node dist/cli/index.js generate examples/mysql/v1/schema.ts -d mysql -f dbml -o examples/mysql/v1/schema.dbml --force && node dist/cli/index.js generate examples/mysql/v1/schema.ts -d mysql -f markdown -o examples/mysql/v1/markdown --force && node dist/cli/index.js generate examples/sqlite/v0/schema.ts -d sqlite -f dbml -o examples/sqlite/v0/schema.dbml --force && node dist/cli/index.js generate examples/sqlite/v0/schema.ts -d sqlite -f markdown -o examples/sqlite/v0/markdown --force && node dist/cli/index.js generate examples/sqlite/v1/schema.ts -d sqlite -f dbml -o examples/sqlite/v1/schema.dbml --force && node dist/cli/index.js generate examples/sqlite/v1/schema.ts -d sqlite -f markdown -o examples/sqlite/v1/markdown --force"
|
|
70
70
|
}
|
|
71
71
|
}
|