@nonsoo/prisma-mermaid 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -64,6 +64,32 @@ await generateMermaidERD({
64
64
  });
65
65
  ```
66
66
 
67
+ ## Styling Diagrams
68
+
69
+ This library ships with sensible default styles for Mermaid ERD and Class diagrams. However, if you need more control, both `generateMermaidClass` and `generateMermaidERD` accept a configuration object that lets you customize the final diagram output.
70
+
71
+ The configuration `type` is exported as `MermaidDiagramConfig`. You can import it directly and pass the config object to the generator functions.
72
+
73
+ ```ts
74
+ import {
75
+ type MermaidDiagramConfig,
76
+ generateMermaidERD,
77
+ } from "@nonsoo/prisma-mermaid";
78
+
79
+ const config = {
80
+ type: "mermaid-erd",
81
+ config: {
82
+ layout: "elk",
83
+ },
84
+ } satisfies MermaidDiagramConfig;
85
+
86
+ await generateMermaidERD({
87
+ schemaPath: "./prisma/schema.prisma",
88
+ output: "./diagrams/erdDiagram.mmd",
89
+ config,
90
+ });
91
+ ```
92
+
67
93
  ## Purpose
68
94
 
69
95
  Documentation should evolve alongside the code it describes. Diagrams-as-code tools such as Mermaid make it easier for teams to maintain clear, accurate diagrams as their systems grow and change. However, creating these diagrams manually — especially for database schemas — still introduces friction and the risk of diagrams falling out of sync with the system.
package/build/index.cjs CHANGED
@@ -41,13 +41,18 @@ var import_node_fs = require("fs");
41
41
  var import_node_path = __toESM(require("path"), 1);
42
42
 
43
43
  // src/constants/mermaid.ts
44
+ var DEFAULT_BASE_NODE_SPACING = 100;
45
+ var DEFAULT_BASE_EDGE_SPACING = 150;
44
46
  var mermaidERDiagramConfig = {
45
47
  theme: "neutral",
48
+ layout: "dagre",
49
+ look: "classic",
50
+ panZoom: true,
46
51
  themeVariables: {
47
52
  fontSize: "20px",
48
53
  fontFamily: "Arial",
49
54
  padding: "12px",
50
- lineHeight: "1.4"
55
+ lineHeight: 1.4
51
56
  },
52
57
  flowchart: {
53
58
  nodeSpacing: 80,
@@ -57,9 +62,12 @@ var mermaidERDiagramConfig = {
57
62
  };
58
63
  var mermaidClassDiagramConfig = {
59
64
  theme: "neutral",
65
+ layout: "dagre",
66
+ look: "classic",
67
+ panZoom: true,
60
68
  themeVariables: {
61
69
  fontFamily: "Arial",
62
- lineHeight: "1.4"
70
+ lineHeight: 1.4
63
71
  },
64
72
  flowchart: {
65
73
  nodeSpacing: 300,
@@ -70,10 +78,9 @@ var mermaidClassDiagramConfig = {
70
78
  hideEmptyMembersBox: true
71
79
  }
72
80
  };
73
- var DEFAULT_BASE_NODE_SPACING = 100;
74
- var DEFAULT_BASE_EDGE_SPACING = 150;
75
81
 
76
82
  // src/utils/mermaid.ts
83
+ var import_js_yaml = require("js-yaml");
77
84
  var generateDiagramSpacing = ({
78
85
  baseEdge,
79
86
  baseNode,
@@ -102,8 +109,17 @@ var generateMermaidConfig = (config, models) => {
102
109
  nodeSpacing
103
110
  };
104
111
  }
105
- const json = JSON.stringify(config, null, 2);
106
- return `%%{init: ${json}}%%
112
+ const { title, ...rest } = config;
113
+ const diagramConfig = {
114
+ config: rest,
115
+ ...title ? { title } : {}
116
+ };
117
+ const yamlString = (0, import_js_yaml.dump)(diagramConfig, {
118
+ indent: 2,
119
+ lineWidth: 1e3
120
+ });
121
+ return `---
122
+ ${yamlString}---
107
123
  `;
108
124
  };
109
125
 
@@ -169,7 +185,8 @@ var { getDMMF } = import_internals.default;
169
185
  var generateDiagram = async ({
170
186
  outputPath,
171
187
  schemaPath,
172
- generatorPrismaDocument
188
+ generatorPrismaDocument,
189
+ config
173
190
  }) => {
174
191
  const outputDir = outputPath ? import_node_path.default.resolve(outputPath) : import_node_path.default.join(`${process.cwd()}/src/generated/diagrams`);
175
192
  try {
@@ -178,18 +195,23 @@ var generateDiagram = async ({
178
195
  });
179
196
  const models = prismaDocument.datamodel.models;
180
197
  const enums = prismaDocument.datamodel.enums;
198
+ const userGeneratedConfig = config?.type === "mermaid-class" ? config?.config : {};
199
+ const diagramConfig = {
200
+ ...mermaidClassDiagramConfig,
201
+ ...userGeneratedConfig
202
+ };
181
203
  const mermaidLines = [
204
+ generateMermaidConfig(diagramConfig, models),
182
205
  "%% --------------------------------------------",
183
206
  "%% Auto-generated Mermaid Class Diagram. Do Not Edit Directly.",
184
207
  "%% --------------------------------------------\n",
185
- generateMermaidConfig(mermaidClassDiagramConfig, models),
186
208
  "classDiagram"
187
209
  ];
188
210
  const relationships = {};
189
211
  models.forEach((model) => {
190
- mermaidLines.push(`class ${model.name} {`);
212
+ mermaidLines.push(` class ${model.name} {`);
191
213
  model.fields.forEach((field) => {
192
- mermaidLines.push(` ${field.type} ${field.name}`);
214
+ mermaidLines.push(` ${field.type} ${field.name}`);
193
215
  if (field.relationName) {
194
216
  if (!relationships[field.relationName]) {
195
217
  relationships[field.relationName] = [];
@@ -202,21 +224,21 @@ var generateDiagram = async ({
202
224
  });
203
225
  }
204
226
  });
205
- mermaidLines.push("}");
227
+ mermaidLines.push(" }");
206
228
  });
207
229
  enums.forEach((enumDef) => {
208
- mermaidLines.push(`class ${enumDef.name} {`);
230
+ mermaidLines.push(` class ${enumDef.name} {`);
209
231
  enumDef.values.forEach((val) => {
210
- mermaidLines.push(` <<enumeration>> ${val.name}`);
232
+ mermaidLines.push(` <<enumeration>> ${val.name}`);
211
233
  });
212
- mermaidLines.push("}");
234
+ mermaidLines.push(" }");
213
235
  });
214
236
  const relationLines = generateRelationships({ relationships });
215
237
  const output = mermaidLines.concat(relationLines).join("\n");
216
238
  (0, import_node_fs.mkdirSync)(outputDir, { recursive: true });
217
239
  const outFile = import_node_path.default.join(outputDir, "mermaidClassDiagram.mmd");
218
240
  (0, import_node_fs.writeFileSync)(outFile, output, "utf-8");
219
- console.log(`Mermaid Class Diagram written to: ${outFile}`);
241
+ console.log(`Mermaid Class Diagram generated at: ${outFile}`);
220
242
  return outFile;
221
243
  } catch (e) {
222
244
  console.error("Failed to generate Mermaid Class Diagram.", e);
@@ -240,14 +262,8 @@ var generateCardinality2 = ({
240
262
  }
241
263
  return isRequired ? "||" : "o|";
242
264
  };
243
- var getKeyConstraints = (isId, fieldName, foreignKeys, nativeTypes) => {
265
+ var getKeyConstraints = (isId, fieldName, foreignKeys) => {
244
266
  if (isId) return "PK";
245
- if (nativeTypes) {
246
- const allNativeTypes = nativeTypes.flatMap((nativeType) => nativeType);
247
- if (!isId && allNativeTypes.includes("UniqueIdentifier")) {
248
- return "FK";
249
- }
250
- }
251
267
  if (!isId && foreignKeys.has(fieldName)) return "FK";
252
268
  return "";
253
269
  };
@@ -302,13 +318,32 @@ var generateRelationships2 = ({
302
318
  }
303
319
  return relationLines;
304
320
  };
321
+ var validateForeignKeys = ({
322
+ foreignKeys,
323
+ foreignKeyLocation,
324
+ mermaidLines
325
+ }) => {
326
+ if (foreignKeys.size > 0) {
327
+ for (const key of foreignKeys) {
328
+ const keyIndexMermaidLines = foreignKeyLocation.get(key);
329
+ if (!keyIndexMermaidLines) continue;
330
+ const currentLine = mermaidLines[keyIndexMermaidLines];
331
+ if (!currentLine) continue;
332
+ const lineArray = currentLine.split(" ");
333
+ lineArray[2] = "FK";
334
+ const finalLine = lineArray.join(" ");
335
+ mermaidLines[keyIndexMermaidLines] = finalLine;
336
+ }
337
+ }
338
+ };
305
339
 
306
340
  // src/lib/MermaidERD/prismaMermaidErd.ts
307
341
  var { getDMMF: getDMMF2 } = import_internals2.default;
308
342
  var generateDiagram2 = async ({
309
343
  outputPath,
310
344
  schemaPath,
311
- generatorPrismaDocument
345
+ generatorPrismaDocument,
346
+ config
312
347
  }) => {
313
348
  const outputDir = outputPath ? import_node_path2.default.resolve(outputPath) : import_node_path2.default.join(`${process.cwd()}/src/generated/diagrams`);
314
349
  try {
@@ -317,17 +352,22 @@ var generateDiagram2 = async ({
317
352
  });
318
353
  const schemaModels = prismaDocument.datamodel.models;
319
354
  const schemaEnums = prismaDocument.datamodel.enums;
320
- console.dir(schemaEnums, { depth: null });
355
+ const userGeneratedConfig = config?.type === "mermaid-erd" ? config?.config : {};
356
+ const diagramConfig = {
357
+ ...mermaidERDiagramConfig,
358
+ ...userGeneratedConfig
359
+ };
321
360
  const mermaidLines = [
361
+ generateMermaidConfig(diagramConfig, schemaModels),
322
362
  "%% --------------------------------------------",
323
363
  "%% Auto-generated Mermaid ER Diagram. Do Not Edit Directly.",
324
364
  "%% --------------------------------------------\n",
325
- generateMermaidConfig(mermaidERDiagramConfig, schemaModels),
326
365
  "erDiagram"
327
366
  ];
328
367
  const relationships = {};
329
368
  schemaModels.forEach((model) => {
330
369
  mermaidLines.push(` ${model.name} {`);
370
+ const foreignKeyLocation = /* @__PURE__ */ new Map();
331
371
  const foreignKeys = /* @__PURE__ */ new Set();
332
372
  model.fields.forEach((field) => {
333
373
  if (field.relationFromFields && field.relationFromFields.length > 0) {
@@ -339,10 +379,10 @@ var generateDiagram2 = async ({
339
379
  ` ${field.type} ${field.name} ${getKeyConstraints(
340
380
  field.isId,
341
381
  field.name,
342
- foreignKeys,
343
- field.nativeType
382
+ foreignKeys
344
383
  )} ${getOptionalitySymbol(field.isRequired)}`
345
384
  );
385
+ foreignKeyLocation.set(field.name, mermaidLines.length - 1);
346
386
  if (field.relationName) {
347
387
  if (!relationships[field.relationName]) {
348
388
  relationships[field.relationName] = [];
@@ -355,6 +395,11 @@ var generateDiagram2 = async ({
355
395
  });
356
396
  }
357
397
  });
398
+ validateForeignKeys({
399
+ foreignKeyLocation,
400
+ foreignKeys,
401
+ mermaidLines
402
+ });
358
403
  mermaidLines.push(` }`);
359
404
  });
360
405
  schemaEnums.forEach((enumDef) => {
package/build/index.d.cts CHANGED
@@ -1,7 +1,52 @@
1
1
  import { DMMF } from '@prisma/generator-helper';
2
2
 
3
+ type ConfigTheme = "default" | "redux-dark" | "forest" | "neutral" | "mc" | "base" | "redux" | "redux-dark";
4
+ type ConfigLook = "classic" | "handDrawn" | "neo";
5
+ type ConfigLayout = "dagre" | "elk";
6
+ type ConfigThemeVariables = {
7
+ fontSize: `${number}px`;
8
+ fontFamily: string;
9
+ padding: `${number}px`;
10
+ lineHeight: number;
11
+ nodeSpacing: number;
12
+ edgeSpacing: number;
13
+ };
14
+ type ConfigFlowchart = {
15
+ nodeSpacing: number;
16
+ rankSpacing: number;
17
+ htmlLabels: boolean;
18
+ };
19
+ type MermaidERDiagramConfig = {
20
+ type: Exclude<PrismaGeneratorsKeys, "mermaid-class">;
21
+ config: Partial<{
22
+ theme: ConfigTheme;
23
+ layout: ConfigLayout;
24
+ look: ConfigLook;
25
+ panZoom: boolean;
26
+ themeVariables: Partial<ConfigThemeVariables>;
27
+ flowchart: Partial<ConfigFlowchart>;
28
+ title: string;
29
+ }>;
30
+ };
31
+ type MermaidClassDiagramConfig = {
32
+ type: Exclude<PrismaGeneratorsKeys, "mermaid-erd">;
33
+ config: Partial<{
34
+ theme: ConfigTheme;
35
+ layout: ConfigLayout;
36
+ look: ConfigLook;
37
+ themeVariables: Partial<ConfigThemeVariables>;
38
+ flowchart: Partial<ConfigFlowchart>;
39
+ panZoom: boolean;
40
+ class: Partial<{
41
+ hideEmptyMembersBox: boolean;
42
+ }>;
43
+ title: string;
44
+ }>;
45
+ };
46
+ type MermaidDiagramConfig = MermaidERDiagramConfig | MermaidClassDiagramConfig;
3
47
  type GenerateDiagramOptions = {
4
48
  generatorPrismaDocument?: DMMF.Document;
49
+ config?: MermaidDiagramConfig;
5
50
  schemaPath: string;
6
51
  outputPath: string | undefined;
7
52
  };
@@ -44,7 +89,7 @@ type PrismaGeneratorsKeys = "mermaid-erd" | "mermaid-class";
44
89
  * or, if `outputPath` is omitted:
45
90
  * `<projectRoot>/src/generated/diagrams/mermaidClassDiagram.mmd`
46
91
  */
47
- declare const generateDiagram$1: ({ outputPath, schemaPath, generatorPrismaDocument, }: GenerateDiagramOptions) => Promise<string>;
92
+ declare const generateDiagram$1: ({ outputPath, schemaPath, generatorPrismaDocument, config, }: GenerateDiagramOptions) => Promise<string>;
48
93
 
49
94
  /**
50
95
  * Generates a Mermaid ERD (Entity-Relationship Diagram) from a Prisma schema.
@@ -58,6 +103,6 @@ declare const generateDiagram$1: ({ outputPath, schemaPath, generatorPrismaDocum
58
103
  * or, if no output path is provided:
59
104
  * `<projectRoot>/src/generated/diagrams/mermaidErdDiagram.mmd`
60
105
  */
61
- declare const generateDiagram: ({ outputPath, schemaPath, generatorPrismaDocument, }: GenerateDiagramOptions) => Promise<string>;
106
+ declare const generateDiagram: ({ outputPath, schemaPath, generatorPrismaDocument, config, }: GenerateDiagramOptions) => Promise<string>;
62
107
 
63
- export { type ClassCardinality, type ERDCardinality, type GenerateCardinality, type GenerateCardinalityOptions, type GenerateDiagram, type GenerateDiagramOptions, type GenerateDiagramSpacingOptions, type GenerateRelationshipOptions, type GenerateRelationships, type PrismaGeneratorsKeys, type Relationships, generateDiagram$1 as generateMermaidClass, generateDiagram as generateMermaidERD };
108
+ export { type ClassCardinality, type ERDCardinality, type GenerateCardinality, type GenerateCardinalityOptions, type GenerateDiagram, type GenerateDiagramOptions, type GenerateDiagramSpacingOptions, type GenerateRelationshipOptions, type GenerateRelationships, type MermaidClassDiagramConfig, type MermaidDiagramConfig, type MermaidERDiagramConfig, type PrismaGeneratorsKeys, type Relationships, generateDiagram$1 as generateMermaidClass, generateDiagram as generateMermaidERD };
package/build/index.d.ts CHANGED
@@ -1,7 +1,52 @@
1
1
  import { DMMF } from '@prisma/generator-helper';
2
2
 
3
+ type ConfigTheme = "default" | "redux-dark" | "forest" | "neutral" | "mc" | "base" | "redux" | "redux-dark";
4
+ type ConfigLook = "classic" | "handDrawn" | "neo";
5
+ type ConfigLayout = "dagre" | "elk";
6
+ type ConfigThemeVariables = {
7
+ fontSize: `${number}px`;
8
+ fontFamily: string;
9
+ padding: `${number}px`;
10
+ lineHeight: number;
11
+ nodeSpacing: number;
12
+ edgeSpacing: number;
13
+ };
14
+ type ConfigFlowchart = {
15
+ nodeSpacing: number;
16
+ rankSpacing: number;
17
+ htmlLabels: boolean;
18
+ };
19
+ type MermaidERDiagramConfig = {
20
+ type: Exclude<PrismaGeneratorsKeys, "mermaid-class">;
21
+ config: Partial<{
22
+ theme: ConfigTheme;
23
+ layout: ConfigLayout;
24
+ look: ConfigLook;
25
+ panZoom: boolean;
26
+ themeVariables: Partial<ConfigThemeVariables>;
27
+ flowchart: Partial<ConfigFlowchart>;
28
+ title: string;
29
+ }>;
30
+ };
31
+ type MermaidClassDiagramConfig = {
32
+ type: Exclude<PrismaGeneratorsKeys, "mermaid-erd">;
33
+ config: Partial<{
34
+ theme: ConfigTheme;
35
+ layout: ConfigLayout;
36
+ look: ConfigLook;
37
+ themeVariables: Partial<ConfigThemeVariables>;
38
+ flowchart: Partial<ConfigFlowchart>;
39
+ panZoom: boolean;
40
+ class: Partial<{
41
+ hideEmptyMembersBox: boolean;
42
+ }>;
43
+ title: string;
44
+ }>;
45
+ };
46
+ type MermaidDiagramConfig = MermaidERDiagramConfig | MermaidClassDiagramConfig;
3
47
  type GenerateDiagramOptions = {
4
48
  generatorPrismaDocument?: DMMF.Document;
49
+ config?: MermaidDiagramConfig;
5
50
  schemaPath: string;
6
51
  outputPath: string | undefined;
7
52
  };
@@ -44,7 +89,7 @@ type PrismaGeneratorsKeys = "mermaid-erd" | "mermaid-class";
44
89
  * or, if `outputPath` is omitted:
45
90
  * `<projectRoot>/src/generated/diagrams/mermaidClassDiagram.mmd`
46
91
  */
47
- declare const generateDiagram$1: ({ outputPath, schemaPath, generatorPrismaDocument, }: GenerateDiagramOptions) => Promise<string>;
92
+ declare const generateDiagram$1: ({ outputPath, schemaPath, generatorPrismaDocument, config, }: GenerateDiagramOptions) => Promise<string>;
48
93
 
49
94
  /**
50
95
  * Generates a Mermaid ERD (Entity-Relationship Diagram) from a Prisma schema.
@@ -58,6 +103,6 @@ declare const generateDiagram$1: ({ outputPath, schemaPath, generatorPrismaDocum
58
103
  * or, if no output path is provided:
59
104
  * `<projectRoot>/src/generated/diagrams/mermaidErdDiagram.mmd`
60
105
  */
61
- declare const generateDiagram: ({ outputPath, schemaPath, generatorPrismaDocument, }: GenerateDiagramOptions) => Promise<string>;
106
+ declare const generateDiagram: ({ outputPath, schemaPath, generatorPrismaDocument, config, }: GenerateDiagramOptions) => Promise<string>;
62
107
 
63
- export { type ClassCardinality, type ERDCardinality, type GenerateCardinality, type GenerateCardinalityOptions, type GenerateDiagram, type GenerateDiagramOptions, type GenerateDiagramSpacingOptions, type GenerateRelationshipOptions, type GenerateRelationships, type PrismaGeneratorsKeys, type Relationships, generateDiagram$1 as generateMermaidClass, generateDiagram as generateMermaidERD };
108
+ export { type ClassCardinality, type ERDCardinality, type GenerateCardinality, type GenerateCardinalityOptions, type GenerateDiagram, type GenerateDiagramOptions, type GenerateDiagramSpacingOptions, type GenerateRelationshipOptions, type GenerateRelationships, type MermaidClassDiagramConfig, type MermaidDiagramConfig, type MermaidERDiagramConfig, type PrismaGeneratorsKeys, type Relationships, generateDiagram$1 as generateMermaidClass, generateDiagram as generateMermaidERD };
package/build/index.js CHANGED
@@ -4,13 +4,18 @@ import { readFileSync, writeFileSync, mkdirSync } from "fs";
4
4
  import path from "path";
5
5
 
6
6
  // src/constants/mermaid.ts
7
+ var DEFAULT_BASE_NODE_SPACING = 100;
8
+ var DEFAULT_BASE_EDGE_SPACING = 150;
7
9
  var mermaidERDiagramConfig = {
8
10
  theme: "neutral",
11
+ layout: "dagre",
12
+ look: "classic",
13
+ panZoom: true,
9
14
  themeVariables: {
10
15
  fontSize: "20px",
11
16
  fontFamily: "Arial",
12
17
  padding: "12px",
13
- lineHeight: "1.4"
18
+ lineHeight: 1.4
14
19
  },
15
20
  flowchart: {
16
21
  nodeSpacing: 80,
@@ -20,9 +25,12 @@ var mermaidERDiagramConfig = {
20
25
  };
21
26
  var mermaidClassDiagramConfig = {
22
27
  theme: "neutral",
28
+ layout: "dagre",
29
+ look: "classic",
30
+ panZoom: true,
23
31
  themeVariables: {
24
32
  fontFamily: "Arial",
25
- lineHeight: "1.4"
33
+ lineHeight: 1.4
26
34
  },
27
35
  flowchart: {
28
36
  nodeSpacing: 300,
@@ -33,10 +41,9 @@ var mermaidClassDiagramConfig = {
33
41
  hideEmptyMembersBox: true
34
42
  }
35
43
  };
36
- var DEFAULT_BASE_NODE_SPACING = 100;
37
- var DEFAULT_BASE_EDGE_SPACING = 150;
38
44
 
39
45
  // src/utils/mermaid.ts
46
+ import { dump } from "js-yaml";
40
47
  var generateDiagramSpacing = ({
41
48
  baseEdge,
42
49
  baseNode,
@@ -65,8 +72,17 @@ var generateMermaidConfig = (config, models) => {
65
72
  nodeSpacing
66
73
  };
67
74
  }
68
- const json = JSON.stringify(config, null, 2);
69
- return `%%{init: ${json}}%%
75
+ const { title, ...rest } = config;
76
+ const diagramConfig = {
77
+ config: rest,
78
+ ...title ? { title } : {}
79
+ };
80
+ const yamlString = dump(diagramConfig, {
81
+ indent: 2,
82
+ lineWidth: 1e3
83
+ });
84
+ return `---
85
+ ${yamlString}---
70
86
  `;
71
87
  };
72
88
 
@@ -132,7 +148,8 @@ var { getDMMF } = pkg;
132
148
  var generateDiagram = async ({
133
149
  outputPath,
134
150
  schemaPath,
135
- generatorPrismaDocument
151
+ generatorPrismaDocument,
152
+ config
136
153
  }) => {
137
154
  const outputDir = outputPath ? path.resolve(outputPath) : path.join(`${process.cwd()}/src/generated/diagrams`);
138
155
  try {
@@ -141,18 +158,23 @@ var generateDiagram = async ({
141
158
  });
142
159
  const models = prismaDocument.datamodel.models;
143
160
  const enums = prismaDocument.datamodel.enums;
161
+ const userGeneratedConfig = config?.type === "mermaid-class" ? config?.config : {};
162
+ const diagramConfig = {
163
+ ...mermaidClassDiagramConfig,
164
+ ...userGeneratedConfig
165
+ };
144
166
  const mermaidLines = [
167
+ generateMermaidConfig(diagramConfig, models),
145
168
  "%% --------------------------------------------",
146
169
  "%% Auto-generated Mermaid Class Diagram. Do Not Edit Directly.",
147
170
  "%% --------------------------------------------\n",
148
- generateMermaidConfig(mermaidClassDiagramConfig, models),
149
171
  "classDiagram"
150
172
  ];
151
173
  const relationships = {};
152
174
  models.forEach((model) => {
153
- mermaidLines.push(`class ${model.name} {`);
175
+ mermaidLines.push(` class ${model.name} {`);
154
176
  model.fields.forEach((field) => {
155
- mermaidLines.push(` ${field.type} ${field.name}`);
177
+ mermaidLines.push(` ${field.type} ${field.name}`);
156
178
  if (field.relationName) {
157
179
  if (!relationships[field.relationName]) {
158
180
  relationships[field.relationName] = [];
@@ -165,21 +187,21 @@ var generateDiagram = async ({
165
187
  });
166
188
  }
167
189
  });
168
- mermaidLines.push("}");
190
+ mermaidLines.push(" }");
169
191
  });
170
192
  enums.forEach((enumDef) => {
171
- mermaidLines.push(`class ${enumDef.name} {`);
193
+ mermaidLines.push(` class ${enumDef.name} {`);
172
194
  enumDef.values.forEach((val) => {
173
- mermaidLines.push(` <<enumeration>> ${val.name}`);
195
+ mermaidLines.push(` <<enumeration>> ${val.name}`);
174
196
  });
175
- mermaidLines.push("}");
197
+ mermaidLines.push(" }");
176
198
  });
177
199
  const relationLines = generateRelationships({ relationships });
178
200
  const output = mermaidLines.concat(relationLines).join("\n");
179
201
  mkdirSync(outputDir, { recursive: true });
180
202
  const outFile = path.join(outputDir, "mermaidClassDiagram.mmd");
181
203
  writeFileSync(outFile, output, "utf-8");
182
- console.log(`Mermaid Class Diagram written to: ${outFile}`);
204
+ console.log(`Mermaid Class Diagram generated at: ${outFile}`);
183
205
  return outFile;
184
206
  } catch (e) {
185
207
  console.error("Failed to generate Mermaid Class Diagram.", e);
@@ -203,14 +225,8 @@ var generateCardinality2 = ({
203
225
  }
204
226
  return isRequired ? "||" : "o|";
205
227
  };
206
- var getKeyConstraints = (isId, fieldName, foreignKeys, nativeTypes) => {
228
+ var getKeyConstraints = (isId, fieldName, foreignKeys) => {
207
229
  if (isId) return "PK";
208
- if (nativeTypes) {
209
- const allNativeTypes = nativeTypes.flatMap((nativeType) => nativeType);
210
- if (!isId && allNativeTypes.includes("UniqueIdentifier")) {
211
- return "FK";
212
- }
213
- }
214
230
  if (!isId && foreignKeys.has(fieldName)) return "FK";
215
231
  return "";
216
232
  };
@@ -265,13 +281,32 @@ var generateRelationships2 = ({
265
281
  }
266
282
  return relationLines;
267
283
  };
284
+ var validateForeignKeys = ({
285
+ foreignKeys,
286
+ foreignKeyLocation,
287
+ mermaidLines
288
+ }) => {
289
+ if (foreignKeys.size > 0) {
290
+ for (const key of foreignKeys) {
291
+ const keyIndexMermaidLines = foreignKeyLocation.get(key);
292
+ if (!keyIndexMermaidLines) continue;
293
+ const currentLine = mermaidLines[keyIndexMermaidLines];
294
+ if (!currentLine) continue;
295
+ const lineArray = currentLine.split(" ");
296
+ lineArray[2] = "FK";
297
+ const finalLine = lineArray.join(" ");
298
+ mermaidLines[keyIndexMermaidLines] = finalLine;
299
+ }
300
+ }
301
+ };
268
302
 
269
303
  // src/lib/MermaidERD/prismaMermaidErd.ts
270
304
  var { getDMMF: getDMMF2 } = pkg2;
271
305
  var generateDiagram2 = async ({
272
306
  outputPath,
273
307
  schemaPath,
274
- generatorPrismaDocument
308
+ generatorPrismaDocument,
309
+ config
275
310
  }) => {
276
311
  const outputDir = outputPath ? path2.resolve(outputPath) : path2.join(`${process.cwd()}/src/generated/diagrams`);
277
312
  try {
@@ -280,17 +315,22 @@ var generateDiagram2 = async ({
280
315
  });
281
316
  const schemaModels = prismaDocument.datamodel.models;
282
317
  const schemaEnums = prismaDocument.datamodel.enums;
283
- console.dir(schemaEnums, { depth: null });
318
+ const userGeneratedConfig = config?.type === "mermaid-erd" ? config?.config : {};
319
+ const diagramConfig = {
320
+ ...mermaidERDiagramConfig,
321
+ ...userGeneratedConfig
322
+ };
284
323
  const mermaidLines = [
324
+ generateMermaidConfig(diagramConfig, schemaModels),
285
325
  "%% --------------------------------------------",
286
326
  "%% Auto-generated Mermaid ER Diagram. Do Not Edit Directly.",
287
327
  "%% --------------------------------------------\n",
288
- generateMermaidConfig(mermaidERDiagramConfig, schemaModels),
289
328
  "erDiagram"
290
329
  ];
291
330
  const relationships = {};
292
331
  schemaModels.forEach((model) => {
293
332
  mermaidLines.push(` ${model.name} {`);
333
+ const foreignKeyLocation = /* @__PURE__ */ new Map();
294
334
  const foreignKeys = /* @__PURE__ */ new Set();
295
335
  model.fields.forEach((field) => {
296
336
  if (field.relationFromFields && field.relationFromFields.length > 0) {
@@ -302,10 +342,10 @@ var generateDiagram2 = async ({
302
342
  ` ${field.type} ${field.name} ${getKeyConstraints(
303
343
  field.isId,
304
344
  field.name,
305
- foreignKeys,
306
- field.nativeType
345
+ foreignKeys
307
346
  )} ${getOptionalitySymbol(field.isRequired)}`
308
347
  );
348
+ foreignKeyLocation.set(field.name, mermaidLines.length - 1);
309
349
  if (field.relationName) {
310
350
  if (!relationships[field.relationName]) {
311
351
  relationships[field.relationName] = [];
@@ -318,6 +358,11 @@ var generateDiagram2 = async ({
318
358
  });
319
359
  }
320
360
  });
361
+ validateForeignKeys({
362
+ foreignKeyLocation,
363
+ foreignKeys,
364
+ mermaidLines
365
+ });
321
366
  mermaidLines.push(` }`);
322
367
  });
323
368
  schemaEnums.forEach((enumDef) => {
@@ -1382,13 +1382,18 @@ var import_node_fs = require("fs");
1382
1382
  var import_node_path = __toESM(require("path"), 1);
1383
1383
 
1384
1384
  // src/constants/mermaid.ts
1385
+ var DEFAULT_BASE_NODE_SPACING = 100;
1386
+ var DEFAULT_BASE_EDGE_SPACING = 150;
1385
1387
  var mermaidERDiagramConfig = {
1386
1388
  theme: "neutral",
1389
+ layout: "dagre",
1390
+ look: "classic",
1391
+ panZoom: true,
1387
1392
  themeVariables: {
1388
1393
  fontSize: "20px",
1389
1394
  fontFamily: "Arial",
1390
1395
  padding: "12px",
1391
- lineHeight: "1.4"
1396
+ lineHeight: 1.4
1392
1397
  },
1393
1398
  flowchart: {
1394
1399
  nodeSpacing: 80,
@@ -1398,9 +1403,12 @@ var mermaidERDiagramConfig = {
1398
1403
  };
1399
1404
  var mermaidClassDiagramConfig = {
1400
1405
  theme: "neutral",
1406
+ layout: "dagre",
1407
+ look: "classic",
1408
+ panZoom: true,
1401
1409
  themeVariables: {
1402
1410
  fontFamily: "Arial",
1403
- lineHeight: "1.4"
1411
+ lineHeight: 1.4
1404
1412
  },
1405
1413
  flowchart: {
1406
1414
  nodeSpacing: 300,
@@ -1411,10 +1419,9 @@ var mermaidClassDiagramConfig = {
1411
1419
  hideEmptyMembersBox: true
1412
1420
  }
1413
1421
  };
1414
- var DEFAULT_BASE_NODE_SPACING = 100;
1415
- var DEFAULT_BASE_EDGE_SPACING = 150;
1416
1422
 
1417
1423
  // src/utils/mermaid.ts
1424
+ var import_js_yaml = require("js-yaml");
1418
1425
  var generateDiagramSpacing = ({
1419
1426
  baseEdge,
1420
1427
  baseNode,
@@ -1443,8 +1450,17 @@ var generateMermaidConfig = (config, models) => {
1443
1450
  nodeSpacing
1444
1451
  };
1445
1452
  }
1446
- const json = JSON.stringify(config, null, 2);
1447
- return `%%{init: ${json}}%%
1453
+ const { title, ...rest } = config;
1454
+ const diagramConfig = {
1455
+ config: rest,
1456
+ ...title ? { title } : {}
1457
+ };
1458
+ const yamlString = (0, import_js_yaml.dump)(diagramConfig, {
1459
+ indent: 2,
1460
+ lineWidth: 1e3
1461
+ });
1462
+ return `---
1463
+ ${yamlString}---
1448
1464
  `;
1449
1465
  };
1450
1466
 
@@ -1510,7 +1526,8 @@ var { getDMMF } = import_internals.default;
1510
1526
  var generateDiagram = async ({
1511
1527
  outputPath,
1512
1528
  schemaPath,
1513
- generatorPrismaDocument
1529
+ generatorPrismaDocument,
1530
+ config
1514
1531
  }) => {
1515
1532
  const outputDir = outputPath ? import_node_path.default.resolve(outputPath) : import_node_path.default.join(`${process.cwd()}/src/generated/diagrams`);
1516
1533
  try {
@@ -1519,18 +1536,23 @@ var generateDiagram = async ({
1519
1536
  });
1520
1537
  const models = prismaDocument.datamodel.models;
1521
1538
  const enums = prismaDocument.datamodel.enums;
1539
+ const userGeneratedConfig = config?.type === "mermaid-class" ? config?.config : {};
1540
+ const diagramConfig = {
1541
+ ...mermaidClassDiagramConfig,
1542
+ ...userGeneratedConfig
1543
+ };
1522
1544
  const mermaidLines = [
1545
+ generateMermaidConfig(diagramConfig, models),
1523
1546
  "%% --------------------------------------------",
1524
1547
  "%% Auto-generated Mermaid Class Diagram. Do Not Edit Directly.",
1525
1548
  "%% --------------------------------------------\n",
1526
- generateMermaidConfig(mermaidClassDiagramConfig, models),
1527
1549
  "classDiagram"
1528
1550
  ];
1529
1551
  const relationships = {};
1530
1552
  models.forEach((model) => {
1531
- mermaidLines.push(`class ${model.name} {`);
1553
+ mermaidLines.push(` class ${model.name} {`);
1532
1554
  model.fields.forEach((field) => {
1533
- mermaidLines.push(` ${field.type} ${field.name}`);
1555
+ mermaidLines.push(` ${field.type} ${field.name}`);
1534
1556
  if (field.relationName) {
1535
1557
  if (!relationships[field.relationName]) {
1536
1558
  relationships[field.relationName] = [];
@@ -1543,21 +1565,21 @@ var generateDiagram = async ({
1543
1565
  });
1544
1566
  }
1545
1567
  });
1546
- mermaidLines.push("}");
1568
+ mermaidLines.push(" }");
1547
1569
  });
1548
1570
  enums.forEach((enumDef) => {
1549
- mermaidLines.push(`class ${enumDef.name} {`);
1571
+ mermaidLines.push(` class ${enumDef.name} {`);
1550
1572
  enumDef.values.forEach((val) => {
1551
- mermaidLines.push(` <<enumeration>> ${val.name}`);
1573
+ mermaidLines.push(` <<enumeration>> ${val.name}`);
1552
1574
  });
1553
- mermaidLines.push("}");
1575
+ mermaidLines.push(" }");
1554
1576
  });
1555
1577
  const relationLines = generateRelationships({ relationships });
1556
1578
  const output = mermaidLines.concat(relationLines).join("\n");
1557
1579
  (0, import_node_fs.mkdirSync)(outputDir, { recursive: true });
1558
1580
  const outFile = import_node_path.default.join(outputDir, "mermaidClassDiagram.mmd");
1559
1581
  (0, import_node_fs.writeFileSync)(outFile, output, "utf-8");
1560
- console.log(`Mermaid Class Diagram written to: ${outFile}`);
1582
+ console.log(`Mermaid Class Diagram generated at: ${outFile}`);
1561
1583
  return outFile;
1562
1584
  } catch (e) {
1563
1585
  console.error("Failed to generate Mermaid Class Diagram.", e);
@@ -1581,14 +1603,8 @@ var generateCardinality2 = ({
1581
1603
  }
1582
1604
  return isRequired ? "||" : "o|";
1583
1605
  };
1584
- var getKeyConstraints = (isId, fieldName, foreignKeys, nativeTypes) => {
1606
+ var getKeyConstraints = (isId, fieldName, foreignKeys) => {
1585
1607
  if (isId) return "PK";
1586
- if (nativeTypes) {
1587
- const allNativeTypes = nativeTypes.flatMap((nativeType) => nativeType);
1588
- if (!isId && allNativeTypes.includes("UniqueIdentifier")) {
1589
- return "FK";
1590
- }
1591
- }
1592
1608
  if (!isId && foreignKeys.has(fieldName)) return "FK";
1593
1609
  return "";
1594
1610
  };
@@ -1643,13 +1659,32 @@ var generateRelationships2 = ({
1643
1659
  }
1644
1660
  return relationLines;
1645
1661
  };
1662
+ var validateForeignKeys = ({
1663
+ foreignKeys,
1664
+ foreignKeyLocation,
1665
+ mermaidLines
1666
+ }) => {
1667
+ if (foreignKeys.size > 0) {
1668
+ for (const key of foreignKeys) {
1669
+ const keyIndexMermaidLines = foreignKeyLocation.get(key);
1670
+ if (!keyIndexMermaidLines) continue;
1671
+ const currentLine = mermaidLines[keyIndexMermaidLines];
1672
+ if (!currentLine) continue;
1673
+ const lineArray = currentLine.split(" ");
1674
+ lineArray[2] = "FK";
1675
+ const finalLine = lineArray.join(" ");
1676
+ mermaidLines[keyIndexMermaidLines] = finalLine;
1677
+ }
1678
+ }
1679
+ };
1646
1680
 
1647
1681
  // src/lib/MermaidERD/prismaMermaidErd.ts
1648
1682
  var { getDMMF: getDMMF2 } = import_internals2.default;
1649
1683
  var generateDiagram2 = async ({
1650
1684
  outputPath,
1651
1685
  schemaPath,
1652
- generatorPrismaDocument
1686
+ generatorPrismaDocument,
1687
+ config
1653
1688
  }) => {
1654
1689
  const outputDir = outputPath ? import_node_path2.default.resolve(outputPath) : import_node_path2.default.join(`${process.cwd()}/src/generated/diagrams`);
1655
1690
  try {
@@ -1658,17 +1693,22 @@ var generateDiagram2 = async ({
1658
1693
  });
1659
1694
  const schemaModels = prismaDocument.datamodel.models;
1660
1695
  const schemaEnums = prismaDocument.datamodel.enums;
1661
- console.dir(schemaEnums, { depth: null });
1696
+ const userGeneratedConfig = config?.type === "mermaid-erd" ? config?.config : {};
1697
+ const diagramConfig = {
1698
+ ...mermaidERDiagramConfig,
1699
+ ...userGeneratedConfig
1700
+ };
1662
1701
  const mermaidLines = [
1702
+ generateMermaidConfig(diagramConfig, schemaModels),
1663
1703
  "%% --------------------------------------------",
1664
1704
  "%% Auto-generated Mermaid ER Diagram. Do Not Edit Directly.",
1665
1705
  "%% --------------------------------------------\n",
1666
- generateMermaidConfig(mermaidERDiagramConfig, schemaModels),
1667
1706
  "erDiagram"
1668
1707
  ];
1669
1708
  const relationships = {};
1670
1709
  schemaModels.forEach((model) => {
1671
1710
  mermaidLines.push(` ${model.name} {`);
1711
+ const foreignKeyLocation = /* @__PURE__ */ new Map();
1672
1712
  const foreignKeys = /* @__PURE__ */ new Set();
1673
1713
  model.fields.forEach((field) => {
1674
1714
  if (field.relationFromFields && field.relationFromFields.length > 0) {
@@ -1680,10 +1720,10 @@ var generateDiagram2 = async ({
1680
1720
  ` ${field.type} ${field.name} ${getKeyConstraints(
1681
1721
  field.isId,
1682
1722
  field.name,
1683
- foreignKeys,
1684
- field.nativeType
1723
+ foreignKeys
1685
1724
  )} ${getOptionalitySymbol(field.isRequired)}`
1686
1725
  );
1726
+ foreignKeyLocation.set(field.name, mermaidLines.length - 1);
1687
1727
  if (field.relationName) {
1688
1728
  if (!relationships[field.relationName]) {
1689
1729
  relationships[field.relationName] = [];
@@ -1696,6 +1736,11 @@ var generateDiagram2 = async ({
1696
1736
  });
1697
1737
  }
1698
1738
  });
1739
+ validateForeignKeys({
1740
+ foreignKeyLocation,
1741
+ foreignKeys,
1742
+ mermaidLines
1743
+ });
1699
1744
  mermaidLines.push(` }`);
1700
1745
  });
1701
1746
  schemaEnums.forEach((enumDef) => {
@@ -1727,7 +1772,7 @@ var prismaGenerators = /* @__PURE__ */ new Map([
1727
1772
  // package.json
1728
1773
  var package_default = {
1729
1774
  name: "@nonsoo/prisma-mermaid",
1730
- version: "0.1.2",
1775
+ version: "0.2.0",
1731
1776
  description: "A Prisma generator that generates Mermaid Class or ER diagrams from your Prisma schema.",
1732
1777
  main: "build/index.js",
1733
1778
  bin: {
@@ -1777,6 +1822,7 @@ var package_default = {
1777
1822
  devDependencies: {
1778
1823
  "@changesets/cli": "^2.29.7",
1779
1824
  "@eslint/js": "^9.39.1",
1825
+ "@types/js-yaml": "^4.0.9",
1780
1826
  "@types/node": "^24.10.1",
1781
1827
  eslint: "^9.39.1",
1782
1828
  "eslint-config-prettier": "^10.1.8",
@@ -1792,7 +1838,8 @@ var package_default = {
1792
1838
  "@prisma/client": "^6.0.0 || ^7.0.0"
1793
1839
  },
1794
1840
  dependencies: {
1795
- "@prisma/internals": "^7.0.0"
1841
+ "@prisma/internals": "^7.0.0",
1842
+ "js-yaml": "^4.1.1"
1796
1843
  }
1797
1844
  };
1798
1845
 
@@ -1814,12 +1861,12 @@ var package_default = {
1814
1861
  return;
1815
1862
  }
1816
1863
  if (Array.isArray(formats)) {
1817
- for (const format of formats) {
1864
+ for await (const format of formats) {
1818
1865
  const mermaidGenerator2 = prismaGenerators.get(
1819
1866
  format
1820
1867
  );
1821
1868
  if (!mermaidGenerator2) continue;
1822
- mermaidGenerator2({
1869
+ await mermaidGenerator2({
1823
1870
  schemaPath,
1824
1871
  outputPath: outputDir,
1825
1872
  generatorPrismaDocument
@@ -1831,7 +1878,7 @@ var package_default = {
1831
1878
  formats
1832
1879
  );
1833
1880
  if (!mermaidGenerator) return;
1834
- mermaidGenerator({
1881
+ await mermaidGenerator({
1835
1882
  schemaPath,
1836
1883
  outputPath: outputDir,
1837
1884
  generatorPrismaDocument
@@ -1387,13 +1387,18 @@ import { readFileSync, writeFileSync, mkdirSync } from "fs";
1387
1387
  import path from "path";
1388
1388
 
1389
1389
  // src/constants/mermaid.ts
1390
+ var DEFAULT_BASE_NODE_SPACING = 100;
1391
+ var DEFAULT_BASE_EDGE_SPACING = 150;
1390
1392
  var mermaidERDiagramConfig = {
1391
1393
  theme: "neutral",
1394
+ layout: "dagre",
1395
+ look: "classic",
1396
+ panZoom: true,
1392
1397
  themeVariables: {
1393
1398
  fontSize: "20px",
1394
1399
  fontFamily: "Arial",
1395
1400
  padding: "12px",
1396
- lineHeight: "1.4"
1401
+ lineHeight: 1.4
1397
1402
  },
1398
1403
  flowchart: {
1399
1404
  nodeSpacing: 80,
@@ -1403,9 +1408,12 @@ var mermaidERDiagramConfig = {
1403
1408
  };
1404
1409
  var mermaidClassDiagramConfig = {
1405
1410
  theme: "neutral",
1411
+ layout: "dagre",
1412
+ look: "classic",
1413
+ panZoom: true,
1406
1414
  themeVariables: {
1407
1415
  fontFamily: "Arial",
1408
- lineHeight: "1.4"
1416
+ lineHeight: 1.4
1409
1417
  },
1410
1418
  flowchart: {
1411
1419
  nodeSpacing: 300,
@@ -1416,10 +1424,9 @@ var mermaidClassDiagramConfig = {
1416
1424
  hideEmptyMembersBox: true
1417
1425
  }
1418
1426
  };
1419
- var DEFAULT_BASE_NODE_SPACING = 100;
1420
- var DEFAULT_BASE_EDGE_SPACING = 150;
1421
1427
 
1422
1428
  // src/utils/mermaid.ts
1429
+ import { dump } from "js-yaml";
1423
1430
  var generateDiagramSpacing = ({
1424
1431
  baseEdge,
1425
1432
  baseNode,
@@ -1448,8 +1455,17 @@ var generateMermaidConfig = (config, models) => {
1448
1455
  nodeSpacing
1449
1456
  };
1450
1457
  }
1451
- const json = JSON.stringify(config, null, 2);
1452
- return `%%{init: ${json}}%%
1458
+ const { title, ...rest } = config;
1459
+ const diagramConfig = {
1460
+ config: rest,
1461
+ ...title ? { title } : {}
1462
+ };
1463
+ const yamlString = dump(diagramConfig, {
1464
+ indent: 2,
1465
+ lineWidth: 1e3
1466
+ });
1467
+ return `---
1468
+ ${yamlString}---
1453
1469
  `;
1454
1470
  };
1455
1471
 
@@ -1515,7 +1531,8 @@ var { getDMMF } = pkg;
1515
1531
  var generateDiagram = async ({
1516
1532
  outputPath,
1517
1533
  schemaPath,
1518
- generatorPrismaDocument
1534
+ generatorPrismaDocument,
1535
+ config
1519
1536
  }) => {
1520
1537
  const outputDir = outputPath ? path.resolve(outputPath) : path.join(`${process.cwd()}/src/generated/diagrams`);
1521
1538
  try {
@@ -1524,18 +1541,23 @@ var generateDiagram = async ({
1524
1541
  });
1525
1542
  const models = prismaDocument.datamodel.models;
1526
1543
  const enums = prismaDocument.datamodel.enums;
1544
+ const userGeneratedConfig = config?.type === "mermaid-class" ? config?.config : {};
1545
+ const diagramConfig = {
1546
+ ...mermaidClassDiagramConfig,
1547
+ ...userGeneratedConfig
1548
+ };
1527
1549
  const mermaidLines = [
1550
+ generateMermaidConfig(diagramConfig, models),
1528
1551
  "%% --------------------------------------------",
1529
1552
  "%% Auto-generated Mermaid Class Diagram. Do Not Edit Directly.",
1530
1553
  "%% --------------------------------------------\n",
1531
- generateMermaidConfig(mermaidClassDiagramConfig, models),
1532
1554
  "classDiagram"
1533
1555
  ];
1534
1556
  const relationships = {};
1535
1557
  models.forEach((model) => {
1536
- mermaidLines.push(`class ${model.name} {`);
1558
+ mermaidLines.push(` class ${model.name} {`);
1537
1559
  model.fields.forEach((field) => {
1538
- mermaidLines.push(` ${field.type} ${field.name}`);
1560
+ mermaidLines.push(` ${field.type} ${field.name}`);
1539
1561
  if (field.relationName) {
1540
1562
  if (!relationships[field.relationName]) {
1541
1563
  relationships[field.relationName] = [];
@@ -1548,21 +1570,21 @@ var generateDiagram = async ({
1548
1570
  });
1549
1571
  }
1550
1572
  });
1551
- mermaidLines.push("}");
1573
+ mermaidLines.push(" }");
1552
1574
  });
1553
1575
  enums.forEach((enumDef) => {
1554
- mermaidLines.push(`class ${enumDef.name} {`);
1576
+ mermaidLines.push(` class ${enumDef.name} {`);
1555
1577
  enumDef.values.forEach((val) => {
1556
- mermaidLines.push(` <<enumeration>> ${val.name}`);
1578
+ mermaidLines.push(` <<enumeration>> ${val.name}`);
1557
1579
  });
1558
- mermaidLines.push("}");
1580
+ mermaidLines.push(" }");
1559
1581
  });
1560
1582
  const relationLines = generateRelationships({ relationships });
1561
1583
  const output = mermaidLines.concat(relationLines).join("\n");
1562
1584
  mkdirSync(outputDir, { recursive: true });
1563
1585
  const outFile = path.join(outputDir, "mermaidClassDiagram.mmd");
1564
1586
  writeFileSync(outFile, output, "utf-8");
1565
- console.log(`Mermaid Class Diagram written to: ${outFile}`);
1587
+ console.log(`Mermaid Class Diagram generated at: ${outFile}`);
1566
1588
  return outFile;
1567
1589
  } catch (e) {
1568
1590
  console.error("Failed to generate Mermaid Class Diagram.", e);
@@ -1586,14 +1608,8 @@ var generateCardinality2 = ({
1586
1608
  }
1587
1609
  return isRequired ? "||" : "o|";
1588
1610
  };
1589
- var getKeyConstraints = (isId, fieldName, foreignKeys, nativeTypes) => {
1611
+ var getKeyConstraints = (isId, fieldName, foreignKeys) => {
1590
1612
  if (isId) return "PK";
1591
- if (nativeTypes) {
1592
- const allNativeTypes = nativeTypes.flatMap((nativeType) => nativeType);
1593
- if (!isId && allNativeTypes.includes("UniqueIdentifier")) {
1594
- return "FK";
1595
- }
1596
- }
1597
1613
  if (!isId && foreignKeys.has(fieldName)) return "FK";
1598
1614
  return "";
1599
1615
  };
@@ -1648,13 +1664,32 @@ var generateRelationships2 = ({
1648
1664
  }
1649
1665
  return relationLines;
1650
1666
  };
1667
+ var validateForeignKeys = ({
1668
+ foreignKeys,
1669
+ foreignKeyLocation,
1670
+ mermaidLines
1671
+ }) => {
1672
+ if (foreignKeys.size > 0) {
1673
+ for (const key of foreignKeys) {
1674
+ const keyIndexMermaidLines = foreignKeyLocation.get(key);
1675
+ if (!keyIndexMermaidLines) continue;
1676
+ const currentLine = mermaidLines[keyIndexMermaidLines];
1677
+ if (!currentLine) continue;
1678
+ const lineArray = currentLine.split(" ");
1679
+ lineArray[2] = "FK";
1680
+ const finalLine = lineArray.join(" ");
1681
+ mermaidLines[keyIndexMermaidLines] = finalLine;
1682
+ }
1683
+ }
1684
+ };
1651
1685
 
1652
1686
  // src/lib/MermaidERD/prismaMermaidErd.ts
1653
1687
  var { getDMMF: getDMMF2 } = pkg2;
1654
1688
  var generateDiagram2 = async ({
1655
1689
  outputPath,
1656
1690
  schemaPath,
1657
- generatorPrismaDocument
1691
+ generatorPrismaDocument,
1692
+ config
1658
1693
  }) => {
1659
1694
  const outputDir = outputPath ? path2.resolve(outputPath) : path2.join(`${process.cwd()}/src/generated/diagrams`);
1660
1695
  try {
@@ -1663,17 +1698,22 @@ var generateDiagram2 = async ({
1663
1698
  });
1664
1699
  const schemaModels = prismaDocument.datamodel.models;
1665
1700
  const schemaEnums = prismaDocument.datamodel.enums;
1666
- console.dir(schemaEnums, { depth: null });
1701
+ const userGeneratedConfig = config?.type === "mermaid-erd" ? config?.config : {};
1702
+ const diagramConfig = {
1703
+ ...mermaidERDiagramConfig,
1704
+ ...userGeneratedConfig
1705
+ };
1667
1706
  const mermaidLines = [
1707
+ generateMermaidConfig(diagramConfig, schemaModels),
1668
1708
  "%% --------------------------------------------",
1669
1709
  "%% Auto-generated Mermaid ER Diagram. Do Not Edit Directly.",
1670
1710
  "%% --------------------------------------------\n",
1671
- generateMermaidConfig(mermaidERDiagramConfig, schemaModels),
1672
1711
  "erDiagram"
1673
1712
  ];
1674
1713
  const relationships = {};
1675
1714
  schemaModels.forEach((model) => {
1676
1715
  mermaidLines.push(` ${model.name} {`);
1716
+ const foreignKeyLocation = /* @__PURE__ */ new Map();
1677
1717
  const foreignKeys = /* @__PURE__ */ new Set();
1678
1718
  model.fields.forEach((field) => {
1679
1719
  if (field.relationFromFields && field.relationFromFields.length > 0) {
@@ -1685,10 +1725,10 @@ var generateDiagram2 = async ({
1685
1725
  ` ${field.type} ${field.name} ${getKeyConstraints(
1686
1726
  field.isId,
1687
1727
  field.name,
1688
- foreignKeys,
1689
- field.nativeType
1728
+ foreignKeys
1690
1729
  )} ${getOptionalitySymbol(field.isRequired)}`
1691
1730
  );
1731
+ foreignKeyLocation.set(field.name, mermaidLines.length - 1);
1692
1732
  if (field.relationName) {
1693
1733
  if (!relationships[field.relationName]) {
1694
1734
  relationships[field.relationName] = [];
@@ -1701,6 +1741,11 @@ var generateDiagram2 = async ({
1701
1741
  });
1702
1742
  }
1703
1743
  });
1744
+ validateForeignKeys({
1745
+ foreignKeyLocation,
1746
+ foreignKeys,
1747
+ mermaidLines
1748
+ });
1704
1749
  mermaidLines.push(` }`);
1705
1750
  });
1706
1751
  schemaEnums.forEach((enumDef) => {
@@ -1732,7 +1777,7 @@ var prismaGenerators = /* @__PURE__ */ new Map([
1732
1777
  // package.json
1733
1778
  var package_default = {
1734
1779
  name: "@nonsoo/prisma-mermaid",
1735
- version: "0.1.2",
1780
+ version: "0.2.0",
1736
1781
  description: "A Prisma generator that generates Mermaid Class or ER diagrams from your Prisma schema.",
1737
1782
  main: "build/index.js",
1738
1783
  bin: {
@@ -1782,6 +1827,7 @@ var package_default = {
1782
1827
  devDependencies: {
1783
1828
  "@changesets/cli": "^2.29.7",
1784
1829
  "@eslint/js": "^9.39.1",
1830
+ "@types/js-yaml": "^4.0.9",
1785
1831
  "@types/node": "^24.10.1",
1786
1832
  eslint: "^9.39.1",
1787
1833
  "eslint-config-prettier": "^10.1.8",
@@ -1797,7 +1843,8 @@ var package_default = {
1797
1843
  "@prisma/client": "^6.0.0 || ^7.0.0"
1798
1844
  },
1799
1845
  dependencies: {
1800
- "@prisma/internals": "^7.0.0"
1846
+ "@prisma/internals": "^7.0.0",
1847
+ "js-yaml": "^4.1.1"
1801
1848
  }
1802
1849
  };
1803
1850
 
@@ -1819,12 +1866,12 @@ var package_default = {
1819
1866
  return;
1820
1867
  }
1821
1868
  if (Array.isArray(formats)) {
1822
- for (const format of formats) {
1869
+ for await (const format of formats) {
1823
1870
  const mermaidGenerator2 = prismaGenerators.get(
1824
1871
  format
1825
1872
  );
1826
1873
  if (!mermaidGenerator2) continue;
1827
- mermaidGenerator2({
1874
+ await mermaidGenerator2({
1828
1875
  schemaPath,
1829
1876
  outputPath: outputDir,
1830
1877
  generatorPrismaDocument
@@ -1836,7 +1883,7 @@ var package_default = {
1836
1883
  formats
1837
1884
  );
1838
1885
  if (!mermaidGenerator) return;
1839
- mermaidGenerator({
1886
+ await mermaidGenerator({
1840
1887
  schemaPath,
1841
1888
  outputPath: outputDir,
1842
1889
  generatorPrismaDocument
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@nonsoo/prisma-mermaid",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "A Prisma generator that generates Mermaid Class or ER diagrams from your Prisma schema.",
5
5
  "main": "build/index.js",
6
6
  "bin": {
7
- "prisma-mermaid": "./build/lib/PrismaMermaidGenerators/bin.cjs"
7
+ "prisma-mermaid": "build/lib/PrismaMermaidGenerators/bin.cjs"
8
8
  },
9
9
  "types": "build/lib/PrismaMermaidGenerators/index.d.js",
10
10
  "files": [
@@ -34,7 +34,7 @@
34
34
  "type": "module",
35
35
  "repository": {
36
36
  "type": "git",
37
- "url": "https://github.com/nonsoo/prisma-mermaid.git"
37
+ "url": "git+https://github.com/nonsoo/prisma-mermaid.git"
38
38
  },
39
39
  "engines": {
40
40
  "node": ">=22.0.0"
@@ -50,6 +50,7 @@
50
50
  "devDependencies": {
51
51
  "@changesets/cli": "^2.29.7",
52
52
  "@eslint/js": "^9.39.1",
53
+ "@types/js-yaml": "^4.0.9",
53
54
  "@types/node": "^24.10.1",
54
55
  "eslint": "^9.39.1",
55
56
  "eslint-config-prettier": "^10.1.8",
@@ -65,6 +66,7 @@
65
66
  "@prisma/client": "^6.0.0 || ^7.0.0"
66
67
  },
67
68
  "dependencies": {
68
- "@prisma/internals": "^7.0.0"
69
+ "@prisma/internals": "^7.0.0",
70
+ "js-yaml": "^4.1.1"
69
71
  }
70
72
  }