@nonsoo/prisma-mermaid 0.1.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/build/index.js ADDED
@@ -0,0 +1,345 @@
1
+ // src/lib/MermaidClass/prismaMermaidClass.ts
2
+ import pkg from "@prisma/internals";
3
+ import { readFileSync, writeFileSync, mkdirSync } from "fs";
4
+ import path from "path";
5
+
6
+ // src/constants/mermaid.ts
7
+ var mermaidERDiagramConfig = {
8
+ theme: "neutral",
9
+ themeVariables: {
10
+ fontSize: "20px",
11
+ fontFamily: "Arial",
12
+ padding: "12px",
13
+ lineHeight: "1.4"
14
+ },
15
+ flowchart: {
16
+ nodeSpacing: 80,
17
+ rankSpacing: 120,
18
+ htmlLabels: true
19
+ }
20
+ };
21
+ var mermaidClassDiagramConfig = {
22
+ theme: "neutral",
23
+ themeVariables: {
24
+ fontFamily: "Arial",
25
+ lineHeight: "1.4"
26
+ },
27
+ flowchart: {
28
+ nodeSpacing: 300,
29
+ rankSpacing: 120,
30
+ htmlLabels: true
31
+ },
32
+ class: {
33
+ hideEmptyMembersBox: true
34
+ }
35
+ };
36
+ var DEFAULT_BASE_NODE_SPACING = 100;
37
+ var DEFAULT_BASE_EDGE_SPACING = 150;
38
+
39
+ // src/utils/mermaid.ts
40
+ var generateDiagramSpacing = ({
41
+ baseEdge,
42
+ baseNode,
43
+ models
44
+ }) => {
45
+ const totalFields = models.reduce((sum, m) => sum + m.fields.length, 0);
46
+ const totalRelations = models.reduce(
47
+ (sum, m) => sum + m.fields.filter((f) => f.relationName && f.relationFromFields?.length).length,
48
+ 0
49
+ );
50
+ return {
51
+ nodeSpacing: baseNode + models.length * 6 + totalFields * 2,
52
+ edgeSpacing: baseEdge + models.length * 4 + totalRelations * 4
53
+ };
54
+ };
55
+ var generateMermaidConfig = (config, models) => {
56
+ if (config["themeVariables"]) {
57
+ const { edgeSpacing, nodeSpacing } = generateDiagramSpacing({
58
+ baseEdge: DEFAULT_BASE_EDGE_SPACING,
59
+ baseNode: DEFAULT_BASE_NODE_SPACING,
60
+ models
61
+ });
62
+ config["themeVariables"] = {
63
+ ...config["themeVariables"],
64
+ edgeSpacing,
65
+ nodeSpacing
66
+ };
67
+ }
68
+ const json = JSON.stringify(config, null, 2);
69
+ return `%%{init: ${json}}%%
70
+ `;
71
+ };
72
+
73
+ // src/lib/MermaidClass/utils.ts
74
+ var generateCardinality = ({
75
+ isList,
76
+ isRequired
77
+ }) => {
78
+ if (isList) return '"*"';
79
+ return isRequired ? '"1"' : '"0..1"';
80
+ };
81
+ var generateRelationships = ({
82
+ relationships
83
+ }) => {
84
+ const lines = [];
85
+ for (const relName in relationships) {
86
+ const sides = relationships[relName];
87
+ if (!sides) continue;
88
+ if (sides.length === 1) {
89
+ const a = sides[0];
90
+ if (!a) continue;
91
+ lines.push(
92
+ `${a.model} ${generateCardinality({
93
+ isList: a.isList,
94
+ isRequired: a.isRequired
95
+ })} --> "1" ${a.fieldType} : ${relName}`
96
+ );
97
+ } else if (sides.length === 2) {
98
+ const a = sides[0];
99
+ const b = sides[1];
100
+ if (!a || !b) continue;
101
+ lines.push(
102
+ `${a.model} ${generateCardinality({
103
+ isList: a.isList,
104
+ isRequired: a.isRequired
105
+ })} --> ${generateCardinality({
106
+ isList: b.isList,
107
+ isRequired: b.isRequired
108
+ })} ${b.model} : ${relName}`
109
+ );
110
+ } else {
111
+ for (let i = 1; i < sides.length; i++) {
112
+ const a = sides[0];
113
+ const b = sides[i];
114
+ if (!a || !b) continue;
115
+ lines.push(
116
+ `${a.model} ${generateCardinality({
117
+ isList: a.isList,
118
+ isRequired: a.isRequired
119
+ })} --> ${generateCardinality({
120
+ isList: b.isList,
121
+ isRequired: b.isRequired
122
+ })} ${b.model} : ${relName}`
123
+ );
124
+ }
125
+ }
126
+ }
127
+ return lines;
128
+ };
129
+
130
+ // src/lib/MermaidClass/prismaMermaidClass.ts
131
+ var { getDMMF } = pkg;
132
+ var generateDiagram = async ({
133
+ outputPath,
134
+ schemaPath,
135
+ generatorPrismaDocument
136
+ }) => {
137
+ const outputDir = outputPath ? path.resolve(outputPath) : path.join(`${process.cwd()}/src/generated/diagrams`);
138
+ try {
139
+ const prismaDocument = generatorPrismaDocument ?? await getDMMF({
140
+ datamodel: readFileSync(schemaPath, "utf-8")
141
+ });
142
+ const models = prismaDocument.datamodel.models;
143
+ const enums = prismaDocument.datamodel.enums;
144
+ const mermaidLines = [
145
+ "%% --------------------------------------------",
146
+ "%% Auto-generated Mermaid Class Diagram. Do Not Edit Directly.",
147
+ "%% --------------------------------------------\n",
148
+ generateMermaidConfig(mermaidClassDiagramConfig, models),
149
+ "classDiagram"
150
+ ];
151
+ const relationships = {};
152
+ models.forEach((model) => {
153
+ mermaidLines.push(`class ${model.name} {`);
154
+ model.fields.forEach((field) => {
155
+ mermaidLines.push(` ${field.type} ${field.name}`);
156
+ if (field.relationName) {
157
+ if (!relationships[field.relationName]) {
158
+ relationships[field.relationName] = [];
159
+ }
160
+ relationships[field.relationName].push({
161
+ model: model.name,
162
+ fieldType: field.type,
163
+ isList: field.isList ?? false,
164
+ isRequired: field.isRequired ?? false
165
+ });
166
+ }
167
+ });
168
+ mermaidLines.push("}");
169
+ });
170
+ enums.forEach((enumDef) => {
171
+ mermaidLines.push(`class ${enumDef.name} {`);
172
+ enumDef.values.forEach((val) => {
173
+ mermaidLines.push(` <<enumeration>> ${val.name}`);
174
+ });
175
+ mermaidLines.push("}");
176
+ });
177
+ const relationLines = generateRelationships({ relationships });
178
+ const output = mermaidLines.concat(relationLines).join("\n");
179
+ mkdirSync(outputDir, { recursive: true });
180
+ const outFile = path.join(outputDir, "mermaidClassDiagram.mmd");
181
+ writeFileSync(outFile, output, "utf-8");
182
+ console.log(`Mermaid Class Diagram written to: ${outFile}`);
183
+ return outFile;
184
+ } catch (e) {
185
+ console.error("Failed to generate Mermaid Class Diagram.", e);
186
+ return "";
187
+ }
188
+ };
189
+
190
+ // src/lib/MermaidERD/prismaMermaidErd.ts
191
+ import pkg2 from "@prisma/internals";
192
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
193
+ import { mkdirSync as mkdirSync2 } from "fs";
194
+ import path2 from "path";
195
+
196
+ // src/lib/MermaidERD/utils.ts
197
+ var generateCardinality2 = ({
198
+ isList,
199
+ isRequired
200
+ }) => {
201
+ if (isList) {
202
+ return "}|";
203
+ }
204
+ return isRequired ? "||" : "o|";
205
+ };
206
+ var getKeyConstraints = (isId, fieldName, foreignKeys, nativeTypes) => {
207
+ 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
+ if (!isId && foreignKeys.has(fieldName)) return "FK";
215
+ return "";
216
+ };
217
+ var getOptionalitySymbol = (isRequired) => {
218
+ return isRequired ? "" : `"?"`;
219
+ };
220
+ var generateRelationships2 = ({
221
+ relationships
222
+ }) => {
223
+ const relationLines = [];
224
+ for (const relName in relationships) {
225
+ const sides = relationships[relName];
226
+ if (!sides) continue;
227
+ if (sides.length === 1) {
228
+ const a = sides[0];
229
+ if (!a) continue;
230
+ relationLines.push(
231
+ ` ${a.model} ${generateCardinality2({
232
+ isList: a.isList,
233
+ isRequired: a.isRequired
234
+ })}--${generateCardinality2({ isList: false, isRequired: true })} ${a.fieldType} : ${relName}`
235
+ );
236
+ } else if (sides.length === 2) {
237
+ const a = sides[0];
238
+ const b = sides[1];
239
+ if (!a || !b) continue;
240
+ relationLines.push(
241
+ ` ${a.model} ${generateCardinality2({
242
+ isList: a.isList,
243
+ isRequired: a.isRequired
244
+ })}--${generateCardinality2({
245
+ isList: b.isList,
246
+ isRequired: b.isRequired
247
+ })} ${b.model} : ${relName}`
248
+ );
249
+ } else {
250
+ for (let i = 1; i < sides.length; i++) {
251
+ const a = sides[0];
252
+ const b = sides[i];
253
+ if (!a || !b) continue;
254
+ relationLines.push(
255
+ ` ${a.model} ${generateCardinality2({
256
+ isList: a.isList,
257
+ isRequired: a.isRequired
258
+ })}--${generateCardinality2({
259
+ isList: b.isList,
260
+ isRequired: b.isRequired
261
+ })} ${b.model} : ${relName}`
262
+ );
263
+ }
264
+ }
265
+ }
266
+ return relationLines;
267
+ };
268
+
269
+ // src/lib/MermaidERD/prismaMermaidErd.ts
270
+ var { getDMMF: getDMMF2 } = pkg2;
271
+ var generateDiagram2 = async ({
272
+ outputPath,
273
+ schemaPath,
274
+ generatorPrismaDocument
275
+ }) => {
276
+ const outputDir = outputPath ? path2.resolve(outputPath) : path2.join(`${process.cwd()}/src/generated/diagrams`);
277
+ try {
278
+ const prismaDocument = generatorPrismaDocument ?? await getDMMF2({
279
+ datamodel: readFileSync2(schemaPath, "utf-8")
280
+ });
281
+ const schemaModels = prismaDocument.datamodel.models;
282
+ const schemaEnums = prismaDocument.datamodel.enums;
283
+ console.dir(schemaEnums, { depth: null });
284
+ const mermaidLines = [
285
+ "%% --------------------------------------------",
286
+ "%% Auto-generated Mermaid ER Diagram. Do Not Edit Directly.",
287
+ "%% --------------------------------------------\n",
288
+ generateMermaidConfig(mermaidERDiagramConfig, schemaModels),
289
+ "erDiagram"
290
+ ];
291
+ const relationships = {};
292
+ schemaModels.forEach((model) => {
293
+ mermaidLines.push(` ${model.name} {`);
294
+ const foreignKeys = /* @__PURE__ */ new Set();
295
+ model.fields.forEach((field) => {
296
+ if (field.relationFromFields && field.relationFromFields.length > 0) {
297
+ field.relationFromFields.forEach((fk) => {
298
+ foreignKeys.add(fk);
299
+ });
300
+ }
301
+ mermaidLines.push(
302
+ ` ${field.type} ${field.name} ${getKeyConstraints(
303
+ field.isId,
304
+ field.name,
305
+ foreignKeys,
306
+ field.nativeType
307
+ )} ${getOptionalitySymbol(field.isRequired)}`
308
+ );
309
+ if (field.relationName) {
310
+ if (!relationships[field.relationName]) {
311
+ relationships[field.relationName] = [];
312
+ }
313
+ relationships[field.relationName].push({
314
+ model: model.name,
315
+ fieldType: field.type,
316
+ isList: field.isList ?? false,
317
+ isRequired: field.isRequired ?? false
318
+ });
319
+ }
320
+ });
321
+ mermaidLines.push(` }`);
322
+ });
323
+ schemaEnums.forEach((enumDef) => {
324
+ mermaidLines.push(` ${enumDef.name} {`);
325
+ enumDef.values.forEach((enumValue) => {
326
+ mermaidLines.push(` ${enumValue.name}`);
327
+ });
328
+ mermaidLines.push(` }`);
329
+ });
330
+ const relationLines = generateRelationships2({ relationships });
331
+ const output = mermaidLines.concat(relationLines);
332
+ mkdirSync2(outputDir, { recursive: true });
333
+ const outFile = path2.join(outputDir, "mermaidErdDiagram.mmd");
334
+ writeFileSync2(outFile, output.join("\n"));
335
+ console.log(`Mermaid ERD generated at: ${outFile}`);
336
+ return outFile;
337
+ } catch {
338
+ console.error("Failed to generate Mermaid ER Diagram.");
339
+ return "";
340
+ }
341
+ };
342
+ export {
343
+ generateDiagram as generateMermaidClass,
344
+ generateDiagram2 as generateMermaidERD
345
+ };