mikro-orm-markdown 0.1.0 → 0.1.2
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/CHANGELOG.md +21 -0
- package/README.ko.md +10 -1
- package/README.md +10 -1
- package/dist/cli.cjs +332 -197
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +5 -2
- package/dist/cli.d.ts +5 -2
- package/dist/cli.js +335 -200
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +248 -131
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +250 -133
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/docs/jsdoc.ts
|
|
2
|
-
import { Project } from "ts-morph";
|
|
2
|
+
import { Project, ts } from "ts-morph";
|
|
3
3
|
function loadJsDoc(filePaths, onWarn) {
|
|
4
4
|
const entities = /* @__PURE__ */ new Map();
|
|
5
5
|
const props = /* @__PURE__ */ new Map();
|
|
@@ -38,17 +38,7 @@ function loadJsDoc(filePaths, onWarn) {
|
|
|
38
38
|
if (classDocs.length > 0) {
|
|
39
39
|
entities.set(className, parseEntityJsDoc(classDocs));
|
|
40
40
|
}
|
|
41
|
-
const propMap =
|
|
42
|
-
for (const prop of cls.getProperties()) {
|
|
43
|
-
const propDocs = prop.getJsDocs();
|
|
44
|
-
if (propDocs.length === 0) {
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
const info = parsePropJsDoc(propDocs);
|
|
48
|
-
if (info.description !== void 0 || info.atLeastOne) {
|
|
49
|
-
propMap.set(prop.getName(), info);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
41
|
+
const propMap = collectPropJsDocs(cls);
|
|
52
42
|
if (propMap.size > 0) {
|
|
53
43
|
props.set(className, propMap);
|
|
54
44
|
}
|
|
@@ -65,6 +55,28 @@ function formatUnknownError(err) {
|
|
|
65
55
|
function hasGlobPattern(filePath) {
|
|
66
56
|
return /[*?[\]{}]/.test(filePath);
|
|
67
57
|
}
|
|
58
|
+
function collectPropJsDocs(cls) {
|
|
59
|
+
const propMap = /* @__PURE__ */ new Map();
|
|
60
|
+
for (const prop of [...cls.getProperties(), ...cls.getGetAccessors()]) {
|
|
61
|
+
const info = parsePropJsDoc(prop.getJsDocs());
|
|
62
|
+
addPropInfo(propMap, prop.getName(), info);
|
|
63
|
+
}
|
|
64
|
+
for (const prop of getConstructorParameterProperties(cls)) {
|
|
65
|
+
const info = parseCompilerPropJsDoc(ts.getJSDocCommentsAndTags(prop.compilerNode));
|
|
66
|
+
addPropInfo(propMap, prop.getName(), info);
|
|
67
|
+
}
|
|
68
|
+
return propMap;
|
|
69
|
+
}
|
|
70
|
+
function getConstructorParameterProperties(cls) {
|
|
71
|
+
return cls.getConstructors().flatMap(
|
|
72
|
+
(constructorDeclaration) => constructorDeclaration.getParameters().filter((param) => param.isParameterProperty())
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
function addPropInfo(propMap, propName, info) {
|
|
76
|
+
if (info.description !== void 0 || info.atLeastOne) {
|
|
77
|
+
propMap.set(propName, info);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
68
80
|
function parseEntityJsDoc(jsDocs) {
|
|
69
81
|
const namespaces = [];
|
|
70
82
|
const erdNamespaces = [];
|
|
@@ -114,10 +126,39 @@ function parsePropJsDoc(jsDocs) {
|
|
|
114
126
|
}
|
|
115
127
|
return { ...description !== void 0 && { description }, atLeastOne };
|
|
116
128
|
}
|
|
129
|
+
function parseCompilerPropJsDoc(jsDocs) {
|
|
130
|
+
let description;
|
|
131
|
+
let atLeastOne = false;
|
|
132
|
+
for (const doc of jsDocs) {
|
|
133
|
+
if (ts.isJSDoc(doc)) {
|
|
134
|
+
const desc = formatCompilerJsDocComment(doc.comment);
|
|
135
|
+
if (desc && description === void 0) {
|
|
136
|
+
description = desc;
|
|
137
|
+
}
|
|
138
|
+
for (const tag of doc.tags ?? []) {
|
|
139
|
+
if (tag.tagName.getText() === "atLeastOne") {
|
|
140
|
+
atLeastOne = true;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (doc.tagName.getText() === "atLeastOne") {
|
|
146
|
+
atLeastOne = true;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return { ...description !== void 0 && { description }, atLeastOne };
|
|
150
|
+
}
|
|
151
|
+
function formatCompilerJsDocComment(comment) {
|
|
152
|
+
if (comment === void 0) {
|
|
153
|
+
return void 0;
|
|
154
|
+
}
|
|
155
|
+
const trimmed = ts.getTextOfJSDocComment(comment)?.trim();
|
|
156
|
+
return trimmed === void 0 || trimmed === "" ? void 0 : trimmed;
|
|
157
|
+
}
|
|
117
158
|
|
|
118
159
|
// src/metadata/load.ts
|
|
119
160
|
import * as path from "path";
|
|
120
|
-
import { EntitySchema, MikroORM } from "@mikro-orm/core";
|
|
161
|
+
import { EntitySchema, MetadataStorage, MikroORM } from "@mikro-orm/core";
|
|
121
162
|
var MetadataLoadError = class extends Error {
|
|
122
163
|
constructor(message, cause) {
|
|
123
164
|
super(message);
|
|
@@ -153,6 +194,44 @@ function assertNoEntitySchemaEntities(options) {
|
|
|
153
194
|
Use decorator-based @Entity() classes instead.`
|
|
154
195
|
);
|
|
155
196
|
}
|
|
197
|
+
function hasDecoratorMarker(target) {
|
|
198
|
+
const pathSymbol = MetadataStorage.PATH_SYMBOL;
|
|
199
|
+
if (typeof pathSymbol === "symbol" && pathSymbol in target) {
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
return "__path" in target;
|
|
203
|
+
}
|
|
204
|
+
function isRenderableMeta(meta) {
|
|
205
|
+
return !meta.pivotTable && !meta.embeddable;
|
|
206
|
+
}
|
|
207
|
+
function assertDiscoveredEntitiesAreSupported(metas) {
|
|
208
|
+
const confirmed = [];
|
|
209
|
+
const unconfirmed = [];
|
|
210
|
+
for (const meta of metas) {
|
|
211
|
+
if (!isRenderableMeta(meta)) {
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
if (EntitySchema.REGISTRY.has(meta.class)) {
|
|
215
|
+
confirmed.push(meta.className);
|
|
216
|
+
} else if (!hasDecoratorMarker(meta.class)) {
|
|
217
|
+
unconfirmed.push(meta.className);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (confirmed.length === 0 && unconfirmed.length === 0) {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
const lines = [];
|
|
224
|
+
if (confirmed.length > 0) {
|
|
225
|
+
lines.push(`EntitySchema-defined entities are not currently supported: ${confirmed.join(", ")}.`);
|
|
226
|
+
}
|
|
227
|
+
if (unconfirmed.length > 0) {
|
|
228
|
+
lines.push(
|
|
229
|
+
`Could not confirm these entities are decorator-based @Entity() classes: ${unconfirmed.join(", ")}. This usually means they are EntitySchema-defined entities (also not currently supported). If you're certain these are valid @Entity() classes, this may be a detection false positive in mikro-orm-markdown \u2014 please open an issue: https://github.com/iamkanguk97/mikro-orm-markdown/issues`
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
lines.push("Use decorator-based @Entity() classes instead.");
|
|
233
|
+
throw new MetadataLoadError(lines.join("\n"));
|
|
234
|
+
}
|
|
156
235
|
async function loadEntityMetadata(options) {
|
|
157
236
|
assertNoEntitySchemaEntities(options);
|
|
158
237
|
let orm;
|
|
@@ -179,6 +258,7 @@ async function loadEntityMetadata(options) {
|
|
|
179
258
|
"No entities were discovered. Check that your config specifies at least one entity path or class."
|
|
180
259
|
);
|
|
181
260
|
}
|
|
261
|
+
assertDiscoveredEntitiesAreSupported(all);
|
|
182
262
|
const baseDir = orm.config.get("baseDir");
|
|
183
263
|
const sourcePaths = [...new Set(all.filter((m) => m.path).map((m) => path.resolve(baseDir, m.path)))];
|
|
184
264
|
return { metas: all, sourcePaths };
|
|
@@ -190,55 +270,8 @@ async function loadEntityMetadata(options) {
|
|
|
190
270
|
// src/model/build.ts
|
|
191
271
|
import { ReferenceKind as ReferenceKind2 } from "@mikro-orm/core";
|
|
192
272
|
|
|
193
|
-
// src/
|
|
273
|
+
// src/model/diagram.ts
|
|
194
274
|
import { ReferenceKind } from "@mikro-orm/core";
|
|
195
|
-
|
|
196
|
-
// src/render/escape.ts
|
|
197
|
-
var MARKDOWN_INLINE_SPECIAL_CHARS = /[\\`|*#]/g;
|
|
198
|
-
var MERMAID_IDENTIFIER_INVALID_CHARS = /[^a-zA-Z0-9_]/g;
|
|
199
|
-
function normalizeInlineText(value) {
|
|
200
|
-
return value.replace(/\r\n?/g, "\n").replace(/[\n\t]+/g, " ").replace(/\s{2,}/g, " ").trim();
|
|
201
|
-
}
|
|
202
|
-
function splitNormalizedLines(value) {
|
|
203
|
-
return value.replace(/\r\n?/g, "\n").split("\n");
|
|
204
|
-
}
|
|
205
|
-
function escapeHtmlText(value) {
|
|
206
|
-
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
207
|
-
}
|
|
208
|
-
function escapeMarkdownInline(value) {
|
|
209
|
-
return escapeHtmlText(normalizeInlineText(value)).replace(MARKDOWN_INLINE_SPECIAL_CHARS, "\\$&");
|
|
210
|
-
}
|
|
211
|
-
function escapeMarkdownTableCell(value) {
|
|
212
|
-
return splitNormalizedLines(value).map((line) => escapeMarkdownInline(line)).join("<br>");
|
|
213
|
-
}
|
|
214
|
-
function escapeMarkdownParagraph(value) {
|
|
215
|
-
return splitNormalizedLines(value).map((line) => escapeMarkdownInline(line)).join(" \n");
|
|
216
|
-
}
|
|
217
|
-
function renderMarkdownBlockQuote(value) {
|
|
218
|
-
return splitNormalizedLines(value).map((line) => `> ${escapeMarkdownInline(line)}`).join("\n");
|
|
219
|
-
}
|
|
220
|
-
function renderMarkdownInlineCode(value) {
|
|
221
|
-
const normalized = normalizeInlineText(value);
|
|
222
|
-
const backtickRuns = normalized.match(/`+/g) ?? [];
|
|
223
|
-
const longestRun = Math.max(0, ...backtickRuns.map((run) => run.length));
|
|
224
|
-
const fence = "`".repeat(longestRun + 1);
|
|
225
|
-
const needsPadding = normalized.startsWith("`") || normalized.endsWith("`");
|
|
226
|
-
const content = needsPadding ? ` ${normalized} ` : normalized;
|
|
227
|
-
return `${fence}${content}${fence}`;
|
|
228
|
-
}
|
|
229
|
-
function toMermaidIdentifier(value) {
|
|
230
|
-
const normalized = normalizeInlineText(value).replace(MERMAID_IDENTIFIER_INVALID_CHARS, "_").replace(/_+/g, "_");
|
|
231
|
-
const identifier = normalized === "" ? "_" : normalized;
|
|
232
|
-
return /^[a-zA-Z_]/.test(identifier) ? identifier : `_${identifier}`;
|
|
233
|
-
}
|
|
234
|
-
function escapeMermaidQuotedText(value) {
|
|
235
|
-
return normalizeInlineText(value).replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
236
|
-
}
|
|
237
|
-
function toMarkdownAnchor(value) {
|
|
238
|
-
return normalizeInlineText(value).toLowerCase().replace(/[^\p{L}\p{N}\s_-]/gu, "").replace(/\s+/g, "-");
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// src/render/mermaid.ts
|
|
242
275
|
var FORMULA_DUMMY_TABLE = {
|
|
243
276
|
alias: "e0",
|
|
244
277
|
name: "",
|
|
@@ -298,6 +331,9 @@ function buildColumns(prop, metaByClass, owningMeta) {
|
|
|
298
331
|
return [];
|
|
299
332
|
}
|
|
300
333
|
const formulaExpr = prop.formula !== void 0 ? resolveFormulaExpr(prop.formula) : void 0;
|
|
334
|
+
if (prop.persist === false && formulaExpr === void 0) {
|
|
335
|
+
return [];
|
|
336
|
+
}
|
|
301
337
|
let embeddedIn;
|
|
302
338
|
let embeddedPropName;
|
|
303
339
|
if (prop.embedded !== void 0) {
|
|
@@ -484,6 +520,9 @@ function buildEdge(fromEntity, prop) {
|
|
|
484
520
|
};
|
|
485
521
|
}
|
|
486
522
|
if (prop.kind === ReferenceKind.ONE_TO_ONE && prop.owner === true) {
|
|
523
|
+
if (prop.type === fromEntity) {
|
|
524
|
+
return null;
|
|
525
|
+
}
|
|
487
526
|
return {
|
|
488
527
|
fromEntity,
|
|
489
528
|
toEntity: prop.type,
|
|
@@ -503,75 +542,6 @@ function buildEdge(fromEntity, prop) {
|
|
|
503
542
|
}
|
|
504
543
|
return null;
|
|
505
544
|
}
|
|
506
|
-
function normalizeType(type) {
|
|
507
|
-
const t = type.toLowerCase().trim();
|
|
508
|
-
if (t === "uuid" || t === "text" || t === "string" || t.startsWith("varchar")) {
|
|
509
|
-
return "string";
|
|
510
|
-
}
|
|
511
|
-
if (t === "timestamptz" || t === "timestamp" || t === "datetime") {
|
|
512
|
-
return "datetime";
|
|
513
|
-
}
|
|
514
|
-
if (t === "integer" || t === "int" || t === "bigint" || t === "smallint") {
|
|
515
|
-
return "integer";
|
|
516
|
-
}
|
|
517
|
-
if (t === "doubletype" || t === "double precision" || t === "double" || t === "float" || t === "decimal") {
|
|
518
|
-
return "float";
|
|
519
|
-
}
|
|
520
|
-
if (t === "boolean" || t === "bool") {
|
|
521
|
-
return "boolean";
|
|
522
|
-
}
|
|
523
|
-
if (t === "jsonb") {
|
|
524
|
-
return "json";
|
|
525
|
-
}
|
|
526
|
-
return type;
|
|
527
|
-
}
|
|
528
|
-
function renderErDiagram(model, mermaid) {
|
|
529
|
-
const lines = [];
|
|
530
|
-
if (mermaid?.layout !== void 0 || mermaid?.theme !== void 0) {
|
|
531
|
-
lines.push("---", "config:");
|
|
532
|
-
if (mermaid.layout !== void 0) {
|
|
533
|
-
lines.push(` layout: ${mermaid.layout}`);
|
|
534
|
-
}
|
|
535
|
-
if (mermaid.theme !== void 0) {
|
|
536
|
-
lines.push(` theme: ${mermaid.theme}`);
|
|
537
|
-
}
|
|
538
|
-
lines.push("---");
|
|
539
|
-
}
|
|
540
|
-
lines.push("erDiagram");
|
|
541
|
-
for (const entity of model.entities) {
|
|
542
|
-
lines.push(` ${toMermaidIdentifier(entity.className)} {`);
|
|
543
|
-
for (const col of entity.columns) {
|
|
544
|
-
lines.push(` ${renderColumnLine(col)}`);
|
|
545
|
-
}
|
|
546
|
-
lines.push(" }");
|
|
547
|
-
}
|
|
548
|
-
for (const rel of model.relations) {
|
|
549
|
-
lines.push(
|
|
550
|
-
` ${toMermaidIdentifier(rel.fromEntity)} ${rel.fromCardinality}--${rel.toCardinality} ${toMermaidIdentifier(rel.toEntity)} : "${escapeMermaidQuotedText(rel.label)}"`
|
|
551
|
-
);
|
|
552
|
-
}
|
|
553
|
-
return lines.join("\n");
|
|
554
|
-
}
|
|
555
|
-
function renderColumnLine(col) {
|
|
556
|
-
let qualifier = "";
|
|
557
|
-
if (col.isPrimary) {
|
|
558
|
-
qualifier = " PK";
|
|
559
|
-
} else if (col.isUnique) {
|
|
560
|
-
qualifier = " UK";
|
|
561
|
-
}
|
|
562
|
-
let comment;
|
|
563
|
-
if (col.formula !== void 0) {
|
|
564
|
-
comment = col.formula ? `formula: ${col.formula}` : "formula";
|
|
565
|
-
} else if (col.isDiscriminator) {
|
|
566
|
-
comment = "discriminator";
|
|
567
|
-
} else if (col.embeddedIn !== void 0) {
|
|
568
|
-
comment = `[${col.embeddedIn}]`;
|
|
569
|
-
} else if (col.isSelfReference) {
|
|
570
|
-
comment = "self-ref";
|
|
571
|
-
}
|
|
572
|
-
const commentStr = comment !== void 0 ? ` "${escapeMermaidQuotedText(comment)}"` : "";
|
|
573
|
-
return `${toMermaidIdentifier(normalizeType(col.type))} ${toMermaidIdentifier(col.fieldName)}${qualifier}${commentStr}`;
|
|
574
|
-
}
|
|
575
545
|
|
|
576
546
|
// src/model/build.ts
|
|
577
547
|
var FROM_ONE_OR_MORE = "}|";
|
|
@@ -594,7 +564,10 @@ function buildDocumentModel(metas, jsDocResult, title, description, onWarn) {
|
|
|
594
564
|
const columns = model.columns.filter(
|
|
595
565
|
(col) => !(col.isForeignKey && col.referencedEntity !== void 0 && hiddenClasses.has(col.referencedEntity))
|
|
596
566
|
);
|
|
597
|
-
const visibleModel =
|
|
567
|
+
const visibleModel = removeHiddenEntityReferences(
|
|
568
|
+
columns.length === model.columns.length ? model : { ...model, columns },
|
|
569
|
+
hiddenClasses
|
|
570
|
+
);
|
|
598
571
|
const ownPropDocs = jsDocResult.props.get(model.className) ?? /* @__PURE__ */ new Map();
|
|
599
572
|
const propDocs = withEmbeddedPropDocs(ownPropDocs, visibleModel.columns, jsDocResult.props);
|
|
600
573
|
enrichedByClass.set(model.className, { model: visibleModel, jsDoc, propDocs });
|
|
@@ -645,6 +618,14 @@ function buildDocumentModel(metas, jsDocResult, title, description, onWarn) {
|
|
|
645
618
|
});
|
|
646
619
|
return { title, groups, ...description !== void 0 && { description } };
|
|
647
620
|
}
|
|
621
|
+
function removeHiddenEntityReferences(model, hiddenClasses) {
|
|
622
|
+
if (model.extendsEntity === void 0 || !hiddenClasses.has(model.extendsEntity)) {
|
|
623
|
+
return model;
|
|
624
|
+
}
|
|
625
|
+
const visibleModel = { ...model };
|
|
626
|
+
delete visibleModel.extendsEntity;
|
|
627
|
+
return visibleModel;
|
|
628
|
+
}
|
|
648
629
|
function withEmbeddedPropDocs(ownPropDocs, columns, allPropDocs) {
|
|
649
630
|
const merged = new Map(ownPropDocs);
|
|
650
631
|
for (const col of columns) {
|
|
@@ -756,6 +737,142 @@ async function withTsMorphMetadataProvider(options, onWarn) {
|
|
|
756
737
|
}
|
|
757
738
|
}
|
|
758
739
|
|
|
740
|
+
// src/render/escape.ts
|
|
741
|
+
var MARKDOWN_INLINE_SPECIAL_CHARS = /[\\`|*#\[\]]/g;
|
|
742
|
+
var MARKDOWN_EMPHASIS_UNDERSCORE = /(?<![a-zA-Z0-9])_|_(?![a-zA-Z0-9])/g;
|
|
743
|
+
var MERMAID_IDENTIFIER_INVALID_CHARS = /[^a-zA-Z0-9_]/g;
|
|
744
|
+
function normalizeInlineText(value) {
|
|
745
|
+
return value.replace(/\r\n?/g, "\n").replace(/[\n\t]+/g, " ").replace(/\s{2,}/g, " ").trim();
|
|
746
|
+
}
|
|
747
|
+
function splitNormalizedLines(value) {
|
|
748
|
+
return value.replace(/\r\n?/g, "\n").split("\n");
|
|
749
|
+
}
|
|
750
|
+
function escapeHtmlText(value) {
|
|
751
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
752
|
+
}
|
|
753
|
+
function escapeMarkdownInline(value) {
|
|
754
|
+
return escapeHtmlText(normalizeInlineText(value)).replace(MARKDOWN_INLINE_SPECIAL_CHARS, "\\$&").replace(MARKDOWN_EMPHASIS_UNDERSCORE, "\\_");
|
|
755
|
+
}
|
|
756
|
+
function escapeMarkdownTableCell(value) {
|
|
757
|
+
return splitNormalizedLines(value).map((line) => escapeMarkdownInline(line)).join("<br>");
|
|
758
|
+
}
|
|
759
|
+
function escapeMarkdownParagraph(value) {
|
|
760
|
+
return splitNormalizedLines(value).map((line) => escapeMarkdownInline(line)).join(" \n");
|
|
761
|
+
}
|
|
762
|
+
function renderMarkdownBlockQuote(value) {
|
|
763
|
+
return splitNormalizedLines(value).map((line) => `> ${escapeMarkdownInline(line)}`).join("\n");
|
|
764
|
+
}
|
|
765
|
+
function renderMarkdownInlineCode(value) {
|
|
766
|
+
const normalized = normalizeInlineText(value);
|
|
767
|
+
const backtickRuns = normalized.match(/`+/g) ?? [];
|
|
768
|
+
const longestRun = Math.max(0, ...backtickRuns.map((run) => run.length));
|
|
769
|
+
const fence = "`".repeat(longestRun + 1);
|
|
770
|
+
const needsPadding = normalized.startsWith("`") || normalized.endsWith("`");
|
|
771
|
+
const content = needsPadding ? ` ${normalized} ` : normalized;
|
|
772
|
+
return `${fence}${content}${fence}`;
|
|
773
|
+
}
|
|
774
|
+
function toMermaidIdentifier(value) {
|
|
775
|
+
const normalized = normalizeInlineText(value).replace(MERMAID_IDENTIFIER_INVALID_CHARS, "_").replace(/_+/g, "_");
|
|
776
|
+
const identifier = normalized === "" ? "_" : normalized;
|
|
777
|
+
return /^[a-zA-Z_]/.test(identifier) ? identifier : `_${identifier}`;
|
|
778
|
+
}
|
|
779
|
+
function escapeMermaidQuotedText(value) {
|
|
780
|
+
return normalizeInlineText(value).replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
781
|
+
}
|
|
782
|
+
function toMarkdownAnchor(value) {
|
|
783
|
+
return normalizeInlineText(value).toLowerCase().replace(/[^\p{L}\p{N}\s_-]/gu, "").replace(/\s+/g, "-");
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// src/render/mermaid.ts
|
|
787
|
+
var GENERIC_TYPE_BY_BASE_NAME = /* @__PURE__ */ new Map([
|
|
788
|
+
["uuid", "string"],
|
|
789
|
+
["text", "string"],
|
|
790
|
+
["string", "string"],
|
|
791
|
+
["varchar", "string"],
|
|
792
|
+
["character varying", "string"],
|
|
793
|
+
["character", "string"],
|
|
794
|
+
["char", "string"],
|
|
795
|
+
["tinytext", "string"],
|
|
796
|
+
["mediumtext", "string"],
|
|
797
|
+
["longtext", "string"],
|
|
798
|
+
["timestamptz", "datetime"],
|
|
799
|
+
["timestamp", "datetime"],
|
|
800
|
+
["datetime", "datetime"],
|
|
801
|
+
["integer", "integer"],
|
|
802
|
+
["int", "integer"],
|
|
803
|
+
["bigint", "integer"],
|
|
804
|
+
["smallint", "integer"],
|
|
805
|
+
["tinyint", "integer"],
|
|
806
|
+
["mediumint", "integer"],
|
|
807
|
+
["serial", "integer"],
|
|
808
|
+
["bigserial", "integer"],
|
|
809
|
+
["doubletype", "float"],
|
|
810
|
+
["double precision", "float"],
|
|
811
|
+
["double", "float"],
|
|
812
|
+
["float", "float"],
|
|
813
|
+
["decimal", "float"],
|
|
814
|
+
["numeric", "float"],
|
|
815
|
+
["real", "float"],
|
|
816
|
+
["boolean", "boolean"],
|
|
817
|
+
["bool", "boolean"],
|
|
818
|
+
["jsonb", "json"]
|
|
819
|
+
]);
|
|
820
|
+
function normalizeType(type) {
|
|
821
|
+
const t = type.toLowerCase().replace(/\s+/g, " ").trim();
|
|
822
|
+
if (/^tinyint\s*\(\s*1\s*\)$/.test(t)) {
|
|
823
|
+
return "boolean";
|
|
824
|
+
}
|
|
825
|
+
const baseName = t.replace(/\(.*\)/, "").replace(/\b(un)?signed\b/, "").trim();
|
|
826
|
+
return GENERIC_TYPE_BY_BASE_NAME.get(baseName) ?? type;
|
|
827
|
+
}
|
|
828
|
+
function renderErDiagram(model, mermaid) {
|
|
829
|
+
const lines = [];
|
|
830
|
+
if (mermaid?.layout !== void 0 || mermaid?.theme !== void 0) {
|
|
831
|
+
lines.push("---", "config:");
|
|
832
|
+
if (mermaid.layout !== void 0) {
|
|
833
|
+
lines.push(` layout: ${mermaid.layout}`);
|
|
834
|
+
}
|
|
835
|
+
if (mermaid.theme !== void 0) {
|
|
836
|
+
lines.push(` theme: ${mermaid.theme}`);
|
|
837
|
+
}
|
|
838
|
+
lines.push("---");
|
|
839
|
+
}
|
|
840
|
+
lines.push("erDiagram");
|
|
841
|
+
for (const entity of model.entities) {
|
|
842
|
+
lines.push(` ${toMermaidIdentifier(entity.className)} {`);
|
|
843
|
+
for (const col of entity.columns) {
|
|
844
|
+
lines.push(` ${renderColumnLine(col)}`);
|
|
845
|
+
}
|
|
846
|
+
lines.push(" }");
|
|
847
|
+
}
|
|
848
|
+
for (const rel of model.relations) {
|
|
849
|
+
lines.push(
|
|
850
|
+
` ${toMermaidIdentifier(rel.fromEntity)} ${rel.fromCardinality}--${rel.toCardinality} ${toMermaidIdentifier(rel.toEntity)} : "${escapeMermaidQuotedText(rel.label)}"`
|
|
851
|
+
);
|
|
852
|
+
}
|
|
853
|
+
return lines.join("\n");
|
|
854
|
+
}
|
|
855
|
+
function renderColumnLine(col) {
|
|
856
|
+
let qualifier = "";
|
|
857
|
+
if (col.isPrimary) {
|
|
858
|
+
qualifier = " PK";
|
|
859
|
+
} else if (col.isUnique) {
|
|
860
|
+
qualifier = " UK";
|
|
861
|
+
}
|
|
862
|
+
let comment;
|
|
863
|
+
if (col.formula !== void 0) {
|
|
864
|
+
comment = col.formula ? `formula: ${col.formula}` : "formula";
|
|
865
|
+
} else if (col.isDiscriminator) {
|
|
866
|
+
comment = "discriminator";
|
|
867
|
+
} else if (col.embeddedIn !== void 0) {
|
|
868
|
+
comment = `[${col.embeddedIn}]`;
|
|
869
|
+
} else if (col.isSelfReference) {
|
|
870
|
+
comment = "self-ref";
|
|
871
|
+
}
|
|
872
|
+
const commentStr = comment !== void 0 ? ` "${escapeMermaidQuotedText(comment)}"` : "";
|
|
873
|
+
return `${toMermaidIdentifier(normalizeType(col.type))} ${toMermaidIdentifier(col.fieldName)}${qualifier}${commentStr}`;
|
|
874
|
+
}
|
|
875
|
+
|
|
759
876
|
// src/render/markdown.ts
|
|
760
877
|
function renderMarkdown(docModel, mermaid) {
|
|
761
878
|
const sections = [`# ${escapeMarkdownInline(docModel.title)}`];
|
|
@@ -779,7 +896,7 @@ function renderBulletSection(header, items, renderItem) {
|
|
|
779
896
|
}
|
|
780
897
|
function renderTableOfContents(groups, anchors) {
|
|
781
898
|
return renderBulletSection("## Contents", groups, (group) => {
|
|
782
|
-
const label = escapeMarkdownInline(group.name)
|
|
899
|
+
const label = escapeMarkdownInline(group.name);
|
|
783
900
|
return `- [${label}](#${anchors.get(group) ?? toMarkdownAnchor(group.name)})`;
|
|
784
901
|
});
|
|
785
902
|
}
|
|
@@ -864,7 +981,7 @@ function resolveColumnKey(col) {
|
|
|
864
981
|
return "PK";
|
|
865
982
|
}
|
|
866
983
|
if (col.isForeignKey) {
|
|
867
|
-
return fkKey;
|
|
984
|
+
return col.isUnique ? `${fkKey}, UK` : fkKey;
|
|
868
985
|
}
|
|
869
986
|
if (col.isUnique) {
|
|
870
987
|
return "UK";
|