duckerd 0.1.1 → 0.1.3
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.md +23 -1
- package/dist/index.js +19 -8
- package/dist/lib/metadata.js +19 -3
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ Generate an ERD diagram of the database schemas.
|
|
|
26
26
|
|
|
27
27
|
- `-d, --database <path>`: Path to the database file
|
|
28
28
|
- `-t, --theme [theme]`: Theme of the chart (choices: `default`, `forest`, `dark`, `neutral`, default: `default`)
|
|
29
|
-
- `-o, --output
|
|
29
|
+
- `-o, --output <path>`: Path to the output file
|
|
30
30
|
- `-w, --width [width]`: Width of the page (default: `1024`)
|
|
31
31
|
- `-H, --height [height]`: Height of the page (default: `768`)
|
|
32
32
|
- `-f, --outputFormat [format]`: Output format for the generated image (choices: `svg`, `png`, `pdf`, default: `png`)
|
|
@@ -36,3 +36,25 @@ Generate an ERD diagram of the database schemas.
|
|
|
36
36
|
```bash
|
|
37
37
|
duckerd -d ./mydb.duckdb -o ./erd.png -f png -t neutral -w 1600
|
|
38
38
|
```
|
|
39
|
+
|
|
40
|
+
**It's possible that you need to re-run the command after the first execution, because the DuckERD CLI automatically installs the `@mermaid-js/mermaid-cli` package globally when it's missing on your system.**
|
|
41
|
+
|
|
42
|
+
## Usage example
|
|
43
|
+
|
|
44
|
+
### Download the example database
|
|
45
|
+
For this example we use the [AWS IAM database](https://raw.githubusercontent.com/tobilg/aws-iam-data/main/data/db/iam.duckdb) from the [AWS IAM Data](https://github.com/tobilg/aws-iam-data) project.
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
curl -LO https://raw.githubusercontent.com/tobilg/aws-iam-data/main/data/db/iam.duckdb
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Generate the ERD
|
|
52
|
+
Then, we can generate the ERD as PNG with the `neutral` theme and otherwise default settings:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
duckerd -d ./iam.duckdb -f png -t neutral
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Result
|
|
59
|
+
|
|
60
|
+

|
package/dist/index.js
CHANGED
|
@@ -16,13 +16,23 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
|
|
16
16
|
}) : function(o, v) {
|
|
17
17
|
o["default"] = v;
|
|
18
18
|
});
|
|
19
|
-
var __importStar = (this && this.__importStar) || function (
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
};
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
26
36
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
37
|
const commander_1 = require("commander");
|
|
28
38
|
const path = __importStar(require("path"));
|
|
@@ -67,7 +77,8 @@ const run = async (dbPath, options) => {
|
|
|
67
77
|
return;
|
|
68
78
|
}
|
|
69
79
|
if (stderr) {
|
|
70
|
-
console.error(`ERD generation
|
|
80
|
+
console.error(`ERD generation: ${stderr}`);
|
|
81
|
+
console.error(`Please re-run the command!`);
|
|
71
82
|
return;
|
|
72
83
|
}
|
|
73
84
|
console.log(`ERD diagram generated: ${outputFile}`);
|
package/dist/lib/metadata.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.sanitizeDataType = exports.generateMermaidCodeForAllDBs = exports.getMetadata = void 0;
|
|
3
|
+
exports.addCommentIfNecessary = exports.sanitizeDataType = exports.generateMermaidCodeForAllDBs = exports.getMetadata = void 0;
|
|
4
4
|
// Database metadata query
|
|
5
5
|
const databaseMetadataQuery = `SELECT database_name as databaseName FROM duckdb_databases() WHERE database_name NOT IN ('system', 'temp') ORDER BY database_name`;
|
|
6
6
|
// Schema metadata query
|
|
@@ -84,7 +84,7 @@ const generateMermaidCodeForAllDBs = (metadata) => {
|
|
|
84
84
|
// Add tables, columns and constraints
|
|
85
85
|
mermaidCode += tables.map((table) => {
|
|
86
86
|
return `"${table.databaseName}.${table.name}" {
|
|
87
|
-
${table.columns.map((column) => ` ${(0, exports.sanitizeDataType)(column.dataType.toUpperCase())} ${column.name} ${table.constraints?.filter((constraint) => constraint.columnName === column.name).map((constraint) => ((constraint.constraintType === "PRIMARY KEY" ? "PK" : "") || (constraint.constraintType === "FOREIGN KEY" ? "FK" : "") || "")).filter(str => str)}`).join("\n")}
|
|
87
|
+
${table.columns.map((column) => ` ${(0, exports.sanitizeDataType)(column.dataType.toUpperCase())} ${column.name} ${table.constraints?.filter((constraint) => constraint.columnName === column.name).map((constraint) => ((constraint.constraintType === "PRIMARY KEY" ? "PK" : "") || (constraint.constraintType === "FOREIGN KEY" ? "FK" : "") || "")).filter(str => str)}${(0, exports.addCommentIfNecessary)(column.dataType)}`).join("\n")}
|
|
88
88
|
}\n${table.constraints?.filter((constraint) => constraint.constraintType === "FOREIGN KEY").map((constraint) => ` "${table.databaseName}.${constraint.sql.match(/(?:^|)REFERENCES\s([^*]+?)\b\(/i)[1]}" ||--o{ "${table.databaseName}.${table.name}" : has`).join("\n")}`;
|
|
89
89
|
}).join("\n ");
|
|
90
90
|
return mermaidCode;
|
|
@@ -99,6 +99,22 @@ ${table.columns.map((column) => ` ${(0, exports.sanitizeDataType)(column.da
|
|
|
99
99
|
};
|
|
100
100
|
exports.generateMermaidCodeForAllDBs = generateMermaidCodeForAllDBs;
|
|
101
101
|
const sanitizeDataType = (dataType) => {
|
|
102
|
-
|
|
102
|
+
if (dataType.startsWith("STRUCT(")) {
|
|
103
|
+
return dataType.replace("STRUCT(", "STRUCT").replace(")", "");
|
|
104
|
+
}
|
|
105
|
+
if (dataType.startsWith("ENUM(")) {
|
|
106
|
+
return "ENUM";
|
|
107
|
+
}
|
|
108
|
+
if (dataType.includes(",")) {
|
|
109
|
+
return dataType.replace(",", "_");
|
|
110
|
+
}
|
|
111
|
+
return dataType;
|
|
103
112
|
};
|
|
104
113
|
exports.sanitizeDataType = sanitizeDataType;
|
|
114
|
+
const addCommentIfNecessary = (dataType) => {
|
|
115
|
+
if (dataType.startsWith("ENUM(")) {
|
|
116
|
+
return ` "${dataType.replace("ENUM(", "").replace(")", "").split(",").map((value) => value.trim().replace(/'/g, "").replace(/"/g, "")).join(", ")}"`;
|
|
117
|
+
}
|
|
118
|
+
return "";
|
|
119
|
+
};
|
|
120
|
+
exports.addCommentIfNecessary = addCommentIfNecessary;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "duckerd",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "A CLI tool for generating ERD diagrams from DuckDB databases",
|
|
5
5
|
"author": "TobiLG <tobilg@gmail.com>",
|
|
6
6
|
"repository": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"duckerd": "dist/index.js"
|
|
17
17
|
},
|
|
18
18
|
"scripts": {
|
|
19
|
-
"build": "tsc",
|
|
19
|
+
"build": "rm -rf dist && tsc",
|
|
20
20
|
"start": "ts-node src/index.ts"
|
|
21
21
|
},
|
|
22
22
|
"engines": {
|
|
@@ -24,12 +24,12 @@
|
|
|
24
24
|
},
|
|
25
25
|
"keywords": ["duckdb", "erd", "diagram", "cli"],
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"commander": "^
|
|
28
|
-
"duckdb-async": "^1.
|
|
27
|
+
"commander": "^13.1.0",
|
|
28
|
+
"duckdb-async": "^1.2.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@types/node": "^18.11.9",
|
|
32
|
-
"ts-node": "^10.9.
|
|
33
|
-
"typescript": "^5.
|
|
32
|
+
"ts-node": "^10.9.2",
|
|
33
|
+
"typescript": "^5.8.2"
|
|
34
34
|
}
|
|
35
35
|
}
|