mikro-orm-markdown 0.1.1 → 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 +14 -0
- package/dist/cli.cjs +326 -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 +329 -200
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +242 -131
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +244 -133
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -5,10 +5,10 @@ import * as syncFs from "fs";
|
|
|
5
5
|
import * as fs from "fs/promises";
|
|
6
6
|
import * as path2 from "path";
|
|
7
7
|
import { fileURLToPath, pathToFileURL } from "url";
|
|
8
|
-
import { Command } from "commander";
|
|
8
|
+
import { Command, Option } from "commander";
|
|
9
9
|
|
|
10
10
|
// src/docs/jsdoc.ts
|
|
11
|
-
import { Project } from "ts-morph";
|
|
11
|
+
import { Project, ts } from "ts-morph";
|
|
12
12
|
function loadJsDoc(filePaths, onWarn) {
|
|
13
13
|
const entities = /* @__PURE__ */ new Map();
|
|
14
14
|
const props = /* @__PURE__ */ new Map();
|
|
@@ -47,17 +47,7 @@ function loadJsDoc(filePaths, onWarn) {
|
|
|
47
47
|
if (classDocs.length > 0) {
|
|
48
48
|
entities.set(className, parseEntityJsDoc(classDocs));
|
|
49
49
|
}
|
|
50
|
-
const propMap =
|
|
51
|
-
for (const prop of cls.getProperties()) {
|
|
52
|
-
const propDocs = prop.getJsDocs();
|
|
53
|
-
if (propDocs.length === 0) {
|
|
54
|
-
continue;
|
|
55
|
-
}
|
|
56
|
-
const info = parsePropJsDoc(propDocs);
|
|
57
|
-
if (info.description !== void 0 || info.atLeastOne) {
|
|
58
|
-
propMap.set(prop.getName(), info);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
50
|
+
const propMap = collectPropJsDocs(cls);
|
|
61
51
|
if (propMap.size > 0) {
|
|
62
52
|
props.set(className, propMap);
|
|
63
53
|
}
|
|
@@ -74,6 +64,28 @@ function formatUnknownError(err) {
|
|
|
74
64
|
function hasGlobPattern(filePath) {
|
|
75
65
|
return /[*?[\]{}]/.test(filePath);
|
|
76
66
|
}
|
|
67
|
+
function collectPropJsDocs(cls) {
|
|
68
|
+
const propMap = /* @__PURE__ */ new Map();
|
|
69
|
+
for (const prop of [...cls.getProperties(), ...cls.getGetAccessors()]) {
|
|
70
|
+
const info = parsePropJsDoc(prop.getJsDocs());
|
|
71
|
+
addPropInfo(propMap, prop.getName(), info);
|
|
72
|
+
}
|
|
73
|
+
for (const prop of getConstructorParameterProperties(cls)) {
|
|
74
|
+
const info = parseCompilerPropJsDoc(ts.getJSDocCommentsAndTags(prop.compilerNode));
|
|
75
|
+
addPropInfo(propMap, prop.getName(), info);
|
|
76
|
+
}
|
|
77
|
+
return propMap;
|
|
78
|
+
}
|
|
79
|
+
function getConstructorParameterProperties(cls) {
|
|
80
|
+
return cls.getConstructors().flatMap(
|
|
81
|
+
(constructorDeclaration) => constructorDeclaration.getParameters().filter((param) => param.isParameterProperty())
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
function addPropInfo(propMap, propName, info) {
|
|
85
|
+
if (info.description !== void 0 || info.atLeastOne) {
|
|
86
|
+
propMap.set(propName, info);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
77
89
|
function parseEntityJsDoc(jsDocs) {
|
|
78
90
|
const namespaces = [];
|
|
79
91
|
const erdNamespaces = [];
|
|
@@ -123,10 +135,39 @@ function parsePropJsDoc(jsDocs) {
|
|
|
123
135
|
}
|
|
124
136
|
return { ...description !== void 0 && { description }, atLeastOne };
|
|
125
137
|
}
|
|
138
|
+
function parseCompilerPropJsDoc(jsDocs) {
|
|
139
|
+
let description;
|
|
140
|
+
let atLeastOne = false;
|
|
141
|
+
for (const doc of jsDocs) {
|
|
142
|
+
if (ts.isJSDoc(doc)) {
|
|
143
|
+
const desc = formatCompilerJsDocComment(doc.comment);
|
|
144
|
+
if (desc && description === void 0) {
|
|
145
|
+
description = desc;
|
|
146
|
+
}
|
|
147
|
+
for (const tag of doc.tags ?? []) {
|
|
148
|
+
if (tag.tagName.getText() === "atLeastOne") {
|
|
149
|
+
atLeastOne = true;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
if (doc.tagName.getText() === "atLeastOne") {
|
|
155
|
+
atLeastOne = true;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return { ...description !== void 0 && { description }, atLeastOne };
|
|
159
|
+
}
|
|
160
|
+
function formatCompilerJsDocComment(comment) {
|
|
161
|
+
if (comment === void 0) {
|
|
162
|
+
return void 0;
|
|
163
|
+
}
|
|
164
|
+
const trimmed = ts.getTextOfJSDocComment(comment)?.trim();
|
|
165
|
+
return trimmed === void 0 || trimmed === "" ? void 0 : trimmed;
|
|
166
|
+
}
|
|
126
167
|
|
|
127
168
|
// src/metadata/load.ts
|
|
128
169
|
import * as path from "path";
|
|
129
|
-
import { EntitySchema, MikroORM } from "@mikro-orm/core";
|
|
170
|
+
import { EntitySchema, MetadataStorage, MikroORM } from "@mikro-orm/core";
|
|
130
171
|
var MetadataLoadError = class extends Error {
|
|
131
172
|
constructor(message, cause) {
|
|
132
173
|
super(message);
|
|
@@ -162,6 +203,44 @@ function assertNoEntitySchemaEntities(options) {
|
|
|
162
203
|
Use decorator-based @Entity() classes instead.`
|
|
163
204
|
);
|
|
164
205
|
}
|
|
206
|
+
function hasDecoratorMarker(target) {
|
|
207
|
+
const pathSymbol = MetadataStorage.PATH_SYMBOL;
|
|
208
|
+
if (typeof pathSymbol === "symbol" && pathSymbol in target) {
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
211
|
+
return "__path" in target;
|
|
212
|
+
}
|
|
213
|
+
function isRenderableMeta(meta) {
|
|
214
|
+
return !meta.pivotTable && !meta.embeddable;
|
|
215
|
+
}
|
|
216
|
+
function assertDiscoveredEntitiesAreSupported(metas) {
|
|
217
|
+
const confirmed = [];
|
|
218
|
+
const unconfirmed = [];
|
|
219
|
+
for (const meta of metas) {
|
|
220
|
+
if (!isRenderableMeta(meta)) {
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
if (EntitySchema.REGISTRY.has(meta.class)) {
|
|
224
|
+
confirmed.push(meta.className);
|
|
225
|
+
} else if (!hasDecoratorMarker(meta.class)) {
|
|
226
|
+
unconfirmed.push(meta.className);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
if (confirmed.length === 0 && unconfirmed.length === 0) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
const lines = [];
|
|
233
|
+
if (confirmed.length > 0) {
|
|
234
|
+
lines.push(`EntitySchema-defined entities are not currently supported: ${confirmed.join(", ")}.`);
|
|
235
|
+
}
|
|
236
|
+
if (unconfirmed.length > 0) {
|
|
237
|
+
lines.push(
|
|
238
|
+
`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`
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
lines.push("Use decorator-based @Entity() classes instead.");
|
|
242
|
+
throw new MetadataLoadError(lines.join("\n"));
|
|
243
|
+
}
|
|
165
244
|
async function loadEntityMetadata(options) {
|
|
166
245
|
assertNoEntitySchemaEntities(options);
|
|
167
246
|
let orm;
|
|
@@ -188,6 +267,7 @@ async function loadEntityMetadata(options) {
|
|
|
188
267
|
"No entities were discovered. Check that your config specifies at least one entity path or class."
|
|
189
268
|
);
|
|
190
269
|
}
|
|
270
|
+
assertDiscoveredEntitiesAreSupported(all);
|
|
191
271
|
const baseDir = orm.config.get("baseDir");
|
|
192
272
|
const sourcePaths = [...new Set(all.filter((m) => m.path).map((m) => path.resolve(baseDir, m.path)))];
|
|
193
273
|
return { metas: all, sourcePaths };
|
|
@@ -199,57 +279,8 @@ async function loadEntityMetadata(options) {
|
|
|
199
279
|
// src/model/build.ts
|
|
200
280
|
import { ReferenceKind as ReferenceKind2 } from "@mikro-orm/core";
|
|
201
281
|
|
|
202
|
-
// src/
|
|
282
|
+
// src/model/diagram.ts
|
|
203
283
|
import { ReferenceKind } from "@mikro-orm/core";
|
|
204
|
-
|
|
205
|
-
// src/render/escape.ts
|
|
206
|
-
var MARKDOWN_INLINE_SPECIAL_CHARS = /[\\`|*#]/g;
|
|
207
|
-
var MERMAID_IDENTIFIER_INVALID_CHARS = /[^a-zA-Z0-9_]/g;
|
|
208
|
-
function normalizeInlineText(value) {
|
|
209
|
-
return value.replace(/\r\n?/g, "\n").replace(/[\n\t]+/g, " ").replace(/\s{2,}/g, " ").trim();
|
|
210
|
-
}
|
|
211
|
-
function splitNormalizedLines(value) {
|
|
212
|
-
return value.replace(/\r\n?/g, "\n").split("\n");
|
|
213
|
-
}
|
|
214
|
-
function escapeHtmlText(value) {
|
|
215
|
-
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
216
|
-
}
|
|
217
|
-
function escapeMarkdownInline(value) {
|
|
218
|
-
return escapeHtmlText(normalizeInlineText(value)).replace(MARKDOWN_INLINE_SPECIAL_CHARS, "\\$&");
|
|
219
|
-
}
|
|
220
|
-
function escapeMarkdownTableCell(value) {
|
|
221
|
-
return splitNormalizedLines(value).map((line) => escapeMarkdownInline(line)).join("<br>");
|
|
222
|
-
}
|
|
223
|
-
function escapeMarkdownParagraph(value) {
|
|
224
|
-
return splitNormalizedLines(value).map((line) => escapeMarkdownInline(line)).join(" \n");
|
|
225
|
-
}
|
|
226
|
-
function renderMarkdownBlockQuote(value) {
|
|
227
|
-
return splitNormalizedLines(value).map((line) => `> ${escapeMarkdownInline(line)}`).join("\n");
|
|
228
|
-
}
|
|
229
|
-
function renderMarkdownInlineCode(value) {
|
|
230
|
-
const normalized = normalizeInlineText(value);
|
|
231
|
-
const backtickRuns = normalized.match(/`+/g) ?? [];
|
|
232
|
-
const longestRun = Math.max(0, ...backtickRuns.map((run2) => run2.length));
|
|
233
|
-
const fence = "`".repeat(longestRun + 1);
|
|
234
|
-
const needsPadding = normalized.startsWith("`") || normalized.endsWith("`");
|
|
235
|
-
const content = needsPadding ? ` ${normalized} ` : normalized;
|
|
236
|
-
return `${fence}${content}${fence}`;
|
|
237
|
-
}
|
|
238
|
-
function toMermaidIdentifier(value) {
|
|
239
|
-
const normalized = normalizeInlineText(value).replace(MERMAID_IDENTIFIER_INVALID_CHARS, "_").replace(/_+/g, "_");
|
|
240
|
-
const identifier = normalized === "" ? "_" : normalized;
|
|
241
|
-
return /^[a-zA-Z_]/.test(identifier) ? identifier : `_${identifier}`;
|
|
242
|
-
}
|
|
243
|
-
function escapeMermaidQuotedText(value) {
|
|
244
|
-
return normalizeInlineText(value).replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
245
|
-
}
|
|
246
|
-
function toMarkdownAnchor(value) {
|
|
247
|
-
return normalizeInlineText(value).toLowerCase().replace(/[^\p{L}\p{N}\s_-]/gu, "").replace(/\s+/g, "-");
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// src/render/mermaid.ts
|
|
251
|
-
var MERMAID_LAYOUTS = ["dagre", "elk", "elk.stress"];
|
|
252
|
-
var MERMAID_THEMES = ["default", "neutral", "dark", "forest", "base"];
|
|
253
284
|
var FORMULA_DUMMY_TABLE = {
|
|
254
285
|
alias: "e0",
|
|
255
286
|
name: "",
|
|
@@ -520,75 +551,6 @@ function buildEdge(fromEntity, prop) {
|
|
|
520
551
|
}
|
|
521
552
|
return null;
|
|
522
553
|
}
|
|
523
|
-
function normalizeType(type) {
|
|
524
|
-
const t = type.toLowerCase().trim();
|
|
525
|
-
if (t === "uuid" || t === "text" || t === "string" || t.startsWith("varchar")) {
|
|
526
|
-
return "string";
|
|
527
|
-
}
|
|
528
|
-
if (t === "timestamptz" || t === "timestamp" || t === "datetime") {
|
|
529
|
-
return "datetime";
|
|
530
|
-
}
|
|
531
|
-
if (t === "integer" || t === "int" || t === "bigint" || t === "smallint") {
|
|
532
|
-
return "integer";
|
|
533
|
-
}
|
|
534
|
-
if (t === "doubletype" || t === "double precision" || t === "double" || t === "float" || t === "decimal") {
|
|
535
|
-
return "float";
|
|
536
|
-
}
|
|
537
|
-
if (t === "boolean" || t === "bool") {
|
|
538
|
-
return "boolean";
|
|
539
|
-
}
|
|
540
|
-
if (t === "jsonb") {
|
|
541
|
-
return "json";
|
|
542
|
-
}
|
|
543
|
-
return type;
|
|
544
|
-
}
|
|
545
|
-
function renderErDiagram(model, mermaid) {
|
|
546
|
-
const lines = [];
|
|
547
|
-
if (mermaid?.layout !== void 0 || mermaid?.theme !== void 0) {
|
|
548
|
-
lines.push("---", "config:");
|
|
549
|
-
if (mermaid.layout !== void 0) {
|
|
550
|
-
lines.push(` layout: ${mermaid.layout}`);
|
|
551
|
-
}
|
|
552
|
-
if (mermaid.theme !== void 0) {
|
|
553
|
-
lines.push(` theme: ${mermaid.theme}`);
|
|
554
|
-
}
|
|
555
|
-
lines.push("---");
|
|
556
|
-
}
|
|
557
|
-
lines.push("erDiagram");
|
|
558
|
-
for (const entity of model.entities) {
|
|
559
|
-
lines.push(` ${toMermaidIdentifier(entity.className)} {`);
|
|
560
|
-
for (const col of entity.columns) {
|
|
561
|
-
lines.push(` ${renderColumnLine(col)}`);
|
|
562
|
-
}
|
|
563
|
-
lines.push(" }");
|
|
564
|
-
}
|
|
565
|
-
for (const rel of model.relations) {
|
|
566
|
-
lines.push(
|
|
567
|
-
` ${toMermaidIdentifier(rel.fromEntity)} ${rel.fromCardinality}--${rel.toCardinality} ${toMermaidIdentifier(rel.toEntity)} : "${escapeMermaidQuotedText(rel.label)}"`
|
|
568
|
-
);
|
|
569
|
-
}
|
|
570
|
-
return lines.join("\n");
|
|
571
|
-
}
|
|
572
|
-
function renderColumnLine(col) {
|
|
573
|
-
let qualifier = "";
|
|
574
|
-
if (col.isPrimary) {
|
|
575
|
-
qualifier = " PK";
|
|
576
|
-
} else if (col.isUnique) {
|
|
577
|
-
qualifier = " UK";
|
|
578
|
-
}
|
|
579
|
-
let comment;
|
|
580
|
-
if (col.formula !== void 0) {
|
|
581
|
-
comment = col.formula ? `formula: ${col.formula}` : "formula";
|
|
582
|
-
} else if (col.isDiscriminator) {
|
|
583
|
-
comment = "discriminator";
|
|
584
|
-
} else if (col.embeddedIn !== void 0) {
|
|
585
|
-
comment = `[${col.embeddedIn}]`;
|
|
586
|
-
} else if (col.isSelfReference) {
|
|
587
|
-
comment = "self-ref";
|
|
588
|
-
}
|
|
589
|
-
const commentStr = comment !== void 0 ? ` "${escapeMermaidQuotedText(comment)}"` : "";
|
|
590
|
-
return `${toMermaidIdentifier(normalizeType(col.type))} ${toMermaidIdentifier(col.fieldName)}${qualifier}${commentStr}`;
|
|
591
|
-
}
|
|
592
554
|
|
|
593
555
|
// src/model/build.ts
|
|
594
556
|
var FROM_ONE_OR_MORE = "}|";
|
|
@@ -611,7 +573,10 @@ function buildDocumentModel(metas, jsDocResult, title, description, onWarn) {
|
|
|
611
573
|
const columns = model.columns.filter(
|
|
612
574
|
(col) => !(col.isForeignKey && col.referencedEntity !== void 0 && hiddenClasses.has(col.referencedEntity))
|
|
613
575
|
);
|
|
614
|
-
const visibleModel =
|
|
576
|
+
const visibleModel = removeHiddenEntityReferences(
|
|
577
|
+
columns.length === model.columns.length ? model : { ...model, columns },
|
|
578
|
+
hiddenClasses
|
|
579
|
+
);
|
|
615
580
|
const ownPropDocs = jsDocResult.props.get(model.className) ?? /* @__PURE__ */ new Map();
|
|
616
581
|
const propDocs = withEmbeddedPropDocs(ownPropDocs, visibleModel.columns, jsDocResult.props);
|
|
617
582
|
enrichedByClass.set(model.className, { model: visibleModel, jsDoc, propDocs });
|
|
@@ -662,6 +627,14 @@ function buildDocumentModel(metas, jsDocResult, title, description, onWarn) {
|
|
|
662
627
|
});
|
|
663
628
|
return { title, groups, ...description !== void 0 && { description } };
|
|
664
629
|
}
|
|
630
|
+
function removeHiddenEntityReferences(model, hiddenClasses) {
|
|
631
|
+
if (model.extendsEntity === void 0 || !hiddenClasses.has(model.extendsEntity)) {
|
|
632
|
+
return model;
|
|
633
|
+
}
|
|
634
|
+
const visibleModel = { ...model };
|
|
635
|
+
delete visibleModel.extendsEntity;
|
|
636
|
+
return visibleModel;
|
|
637
|
+
}
|
|
665
638
|
function withEmbeddedPropDocs(ownPropDocs, columns, allPropDocs) {
|
|
666
639
|
const merged = new Map(ownPropDocs);
|
|
667
640
|
for (const col of columns) {
|
|
@@ -773,6 +746,144 @@ async function withTsMorphMetadataProvider(options, onWarn) {
|
|
|
773
746
|
}
|
|
774
747
|
}
|
|
775
748
|
|
|
749
|
+
// src/render/escape.ts
|
|
750
|
+
var MARKDOWN_INLINE_SPECIAL_CHARS = /[\\`|*#\[\]]/g;
|
|
751
|
+
var MARKDOWN_EMPHASIS_UNDERSCORE = /(?<![a-zA-Z0-9])_|_(?![a-zA-Z0-9])/g;
|
|
752
|
+
var MERMAID_IDENTIFIER_INVALID_CHARS = /[^a-zA-Z0-9_]/g;
|
|
753
|
+
function normalizeInlineText(value) {
|
|
754
|
+
return value.replace(/\r\n?/g, "\n").replace(/[\n\t]+/g, " ").replace(/\s{2,}/g, " ").trim();
|
|
755
|
+
}
|
|
756
|
+
function splitNormalizedLines(value) {
|
|
757
|
+
return value.replace(/\r\n?/g, "\n").split("\n");
|
|
758
|
+
}
|
|
759
|
+
function escapeHtmlText(value) {
|
|
760
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
761
|
+
}
|
|
762
|
+
function escapeMarkdownInline(value) {
|
|
763
|
+
return escapeHtmlText(normalizeInlineText(value)).replace(MARKDOWN_INLINE_SPECIAL_CHARS, "\\$&").replace(MARKDOWN_EMPHASIS_UNDERSCORE, "\\_");
|
|
764
|
+
}
|
|
765
|
+
function escapeMarkdownTableCell(value) {
|
|
766
|
+
return splitNormalizedLines(value).map((line) => escapeMarkdownInline(line)).join("<br>");
|
|
767
|
+
}
|
|
768
|
+
function escapeMarkdownParagraph(value) {
|
|
769
|
+
return splitNormalizedLines(value).map((line) => escapeMarkdownInline(line)).join(" \n");
|
|
770
|
+
}
|
|
771
|
+
function renderMarkdownBlockQuote(value) {
|
|
772
|
+
return splitNormalizedLines(value).map((line) => `> ${escapeMarkdownInline(line)}`).join("\n");
|
|
773
|
+
}
|
|
774
|
+
function renderMarkdownInlineCode(value) {
|
|
775
|
+
const normalized = normalizeInlineText(value);
|
|
776
|
+
const backtickRuns = normalized.match(/`+/g) ?? [];
|
|
777
|
+
const longestRun = Math.max(0, ...backtickRuns.map((run2) => run2.length));
|
|
778
|
+
const fence = "`".repeat(longestRun + 1);
|
|
779
|
+
const needsPadding = normalized.startsWith("`") || normalized.endsWith("`");
|
|
780
|
+
const content = needsPadding ? ` ${normalized} ` : normalized;
|
|
781
|
+
return `${fence}${content}${fence}`;
|
|
782
|
+
}
|
|
783
|
+
function toMermaidIdentifier(value) {
|
|
784
|
+
const normalized = normalizeInlineText(value).replace(MERMAID_IDENTIFIER_INVALID_CHARS, "_").replace(/_+/g, "_");
|
|
785
|
+
const identifier = normalized === "" ? "_" : normalized;
|
|
786
|
+
return /^[a-zA-Z_]/.test(identifier) ? identifier : `_${identifier}`;
|
|
787
|
+
}
|
|
788
|
+
function escapeMermaidQuotedText(value) {
|
|
789
|
+
return normalizeInlineText(value).replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
790
|
+
}
|
|
791
|
+
function toMarkdownAnchor(value) {
|
|
792
|
+
return normalizeInlineText(value).toLowerCase().replace(/[^\p{L}\p{N}\s_-]/gu, "").replace(/\s+/g, "-");
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
// src/render/mermaid.ts
|
|
796
|
+
var MERMAID_LAYOUTS = ["dagre", "elk", "elk.stress"];
|
|
797
|
+
var MERMAID_THEMES = ["default", "neutral", "dark", "forest", "base"];
|
|
798
|
+
var GENERIC_TYPE_BY_BASE_NAME = /* @__PURE__ */ new Map([
|
|
799
|
+
["uuid", "string"],
|
|
800
|
+
["text", "string"],
|
|
801
|
+
["string", "string"],
|
|
802
|
+
["varchar", "string"],
|
|
803
|
+
["character varying", "string"],
|
|
804
|
+
["character", "string"],
|
|
805
|
+
["char", "string"],
|
|
806
|
+
["tinytext", "string"],
|
|
807
|
+
["mediumtext", "string"],
|
|
808
|
+
["longtext", "string"],
|
|
809
|
+
["timestamptz", "datetime"],
|
|
810
|
+
["timestamp", "datetime"],
|
|
811
|
+
["datetime", "datetime"],
|
|
812
|
+
["integer", "integer"],
|
|
813
|
+
["int", "integer"],
|
|
814
|
+
["bigint", "integer"],
|
|
815
|
+
["smallint", "integer"],
|
|
816
|
+
["tinyint", "integer"],
|
|
817
|
+
["mediumint", "integer"],
|
|
818
|
+
["serial", "integer"],
|
|
819
|
+
["bigserial", "integer"],
|
|
820
|
+
["doubletype", "float"],
|
|
821
|
+
["double precision", "float"],
|
|
822
|
+
["double", "float"],
|
|
823
|
+
["float", "float"],
|
|
824
|
+
["decimal", "float"],
|
|
825
|
+
["numeric", "float"],
|
|
826
|
+
["real", "float"],
|
|
827
|
+
["boolean", "boolean"],
|
|
828
|
+
["bool", "boolean"],
|
|
829
|
+
["jsonb", "json"]
|
|
830
|
+
]);
|
|
831
|
+
function normalizeType(type) {
|
|
832
|
+
const t = type.toLowerCase().replace(/\s+/g, " ").trim();
|
|
833
|
+
if (/^tinyint\s*\(\s*1\s*\)$/.test(t)) {
|
|
834
|
+
return "boolean";
|
|
835
|
+
}
|
|
836
|
+
const baseName = t.replace(/\(.*\)/, "").replace(/\b(un)?signed\b/, "").trim();
|
|
837
|
+
return GENERIC_TYPE_BY_BASE_NAME.get(baseName) ?? type;
|
|
838
|
+
}
|
|
839
|
+
function renderErDiagram(model, mermaid) {
|
|
840
|
+
const lines = [];
|
|
841
|
+
if (mermaid?.layout !== void 0 || mermaid?.theme !== void 0) {
|
|
842
|
+
lines.push("---", "config:");
|
|
843
|
+
if (mermaid.layout !== void 0) {
|
|
844
|
+
lines.push(` layout: ${mermaid.layout}`);
|
|
845
|
+
}
|
|
846
|
+
if (mermaid.theme !== void 0) {
|
|
847
|
+
lines.push(` theme: ${mermaid.theme}`);
|
|
848
|
+
}
|
|
849
|
+
lines.push("---");
|
|
850
|
+
}
|
|
851
|
+
lines.push("erDiagram");
|
|
852
|
+
for (const entity of model.entities) {
|
|
853
|
+
lines.push(` ${toMermaidIdentifier(entity.className)} {`);
|
|
854
|
+
for (const col of entity.columns) {
|
|
855
|
+
lines.push(` ${renderColumnLine(col)}`);
|
|
856
|
+
}
|
|
857
|
+
lines.push(" }");
|
|
858
|
+
}
|
|
859
|
+
for (const rel of model.relations) {
|
|
860
|
+
lines.push(
|
|
861
|
+
` ${toMermaidIdentifier(rel.fromEntity)} ${rel.fromCardinality}--${rel.toCardinality} ${toMermaidIdentifier(rel.toEntity)} : "${escapeMermaidQuotedText(rel.label)}"`
|
|
862
|
+
);
|
|
863
|
+
}
|
|
864
|
+
return lines.join("\n");
|
|
865
|
+
}
|
|
866
|
+
function renderColumnLine(col) {
|
|
867
|
+
let qualifier = "";
|
|
868
|
+
if (col.isPrimary) {
|
|
869
|
+
qualifier = " PK";
|
|
870
|
+
} else if (col.isUnique) {
|
|
871
|
+
qualifier = " UK";
|
|
872
|
+
}
|
|
873
|
+
let comment;
|
|
874
|
+
if (col.formula !== void 0) {
|
|
875
|
+
comment = col.formula ? `formula: ${col.formula}` : "formula";
|
|
876
|
+
} else if (col.isDiscriminator) {
|
|
877
|
+
comment = "discriminator";
|
|
878
|
+
} else if (col.embeddedIn !== void 0) {
|
|
879
|
+
comment = `[${col.embeddedIn}]`;
|
|
880
|
+
} else if (col.isSelfReference) {
|
|
881
|
+
comment = "self-ref";
|
|
882
|
+
}
|
|
883
|
+
const commentStr = comment !== void 0 ? ` "${escapeMermaidQuotedText(comment)}"` : "";
|
|
884
|
+
return `${toMermaidIdentifier(normalizeType(col.type))} ${toMermaidIdentifier(col.fieldName)}${qualifier}${commentStr}`;
|
|
885
|
+
}
|
|
886
|
+
|
|
776
887
|
// src/render/markdown.ts
|
|
777
888
|
function renderMarkdown(docModel, mermaid) {
|
|
778
889
|
const sections = [`# ${escapeMarkdownInline(docModel.title)}`];
|
|
@@ -796,7 +907,7 @@ function renderBulletSection(header, items, renderItem) {
|
|
|
796
907
|
}
|
|
797
908
|
function renderTableOfContents(groups, anchors) {
|
|
798
909
|
return renderBulletSection("## Contents", groups, (group) => {
|
|
799
|
-
const label = escapeMarkdownInline(group.name)
|
|
910
|
+
const label = escapeMarkdownInline(group.name);
|
|
800
911
|
return `- [${label}](#${anchors.get(group) ?? toMarkdownAnchor(group.name)})`;
|
|
801
912
|
});
|
|
802
913
|
}
|
|
@@ -881,7 +992,7 @@ function resolveColumnKey(col) {
|
|
|
881
992
|
return "PK";
|
|
882
993
|
}
|
|
883
994
|
if (col.isForeignKey) {
|
|
884
|
-
return fkKey;
|
|
995
|
+
return col.isUnique ? `${fkKey}, UK` : fkKey;
|
|
885
996
|
}
|
|
886
997
|
if (col.isUnique) {
|
|
887
998
|
return "UK";
|
|
@@ -994,6 +1105,12 @@ async function generateMarkdown(options) {
|
|
|
994
1105
|
}
|
|
995
1106
|
|
|
996
1107
|
// src/cli.ts
|
|
1108
|
+
var activeTsxUnregister;
|
|
1109
|
+
async function unregisterActiveTsxLoader() {
|
|
1110
|
+
const unregister = activeTsxUnregister;
|
|
1111
|
+
activeTsxUnregister = void 0;
|
|
1112
|
+
await unregister?.();
|
|
1113
|
+
}
|
|
997
1114
|
function toConfigImportSpecifier(configPath) {
|
|
998
1115
|
return pathToFileURL(path2.resolve(configPath)).href;
|
|
999
1116
|
}
|
|
@@ -1011,64 +1128,78 @@ function findNearestTsconfig(fromPath) {
|
|
|
1011
1128
|
dir = parent;
|
|
1012
1129
|
}
|
|
1013
1130
|
}
|
|
1014
|
-
async function loadOrmOptions(configPath, tsconfigPath) {
|
|
1131
|
+
async function loadOrmOptions(configPath, tsconfigPath, loadOptions = {}) {
|
|
1015
1132
|
const isTypeScriptConfig = configPath.endsWith(".ts");
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
try {
|
|
1019
|
-
({ register } = await import("tsx/esm/api"));
|
|
1020
|
-
} catch {
|
|
1021
|
-
throw new Error('TypeScript config files require the "tsx" package.\nInstall it with: npm install -D tsx');
|
|
1022
|
-
}
|
|
1023
|
-
let tsconfig;
|
|
1024
|
-
if (tsconfigPath !== void 0) {
|
|
1025
|
-
tsconfig = path2.resolve(tsconfigPath);
|
|
1026
|
-
if (!syncFs.existsSync(tsconfig)) {
|
|
1027
|
-
throw new Error(`--tsconfig file not found: ${tsconfig}`);
|
|
1028
|
-
}
|
|
1029
|
-
} else {
|
|
1030
|
-
tsconfig = findNearestTsconfig(configPath);
|
|
1031
|
-
}
|
|
1032
|
-
register(tsconfig !== void 0 ? { tsconfig } : {});
|
|
1033
|
-
}
|
|
1034
|
-
const configUrl = toConfigImportSpecifier(configPath);
|
|
1035
|
-
let mod;
|
|
1133
|
+
let unregisterTsx;
|
|
1134
|
+
let shouldKeepTsxRegistered = false;
|
|
1036
1135
|
try {
|
|
1037
|
-
mod = await import(
|
|
1038
|
-
/* @vite-ignore */
|
|
1039
|
-
configUrl
|
|
1040
|
-
);
|
|
1041
|
-
} catch (cause) {
|
|
1042
1136
|
if (isTypeScriptConfig) {
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1137
|
+
await unregisterActiveTsxLoader();
|
|
1138
|
+
let register;
|
|
1139
|
+
try {
|
|
1140
|
+
({ register } = await import("tsx/esm/api"));
|
|
1141
|
+
} catch {
|
|
1142
|
+
throw new Error('TypeScript config files require the "tsx" package.\nInstall it with: npm install -D tsx');
|
|
1143
|
+
}
|
|
1144
|
+
let tsconfig;
|
|
1145
|
+
if (tsconfigPath !== void 0) {
|
|
1146
|
+
tsconfig = path2.resolve(tsconfigPath);
|
|
1147
|
+
if (!syncFs.existsSync(tsconfig)) {
|
|
1148
|
+
throw new Error(`--tsconfig file not found: ${tsconfig}`);
|
|
1149
|
+
}
|
|
1150
|
+
} else {
|
|
1151
|
+
tsconfig = findNearestTsconfig(configPath);
|
|
1152
|
+
}
|
|
1153
|
+
unregisterTsx = register(tsconfig !== void 0 ? { tsconfig } : {});
|
|
1154
|
+
}
|
|
1155
|
+
const configUrl = toConfigImportSpecifier(configPath);
|
|
1156
|
+
let mod;
|
|
1157
|
+
try {
|
|
1158
|
+
mod = await import(
|
|
1159
|
+
/* @vite-ignore */
|
|
1160
|
+
configUrl
|
|
1161
|
+
);
|
|
1162
|
+
} catch (cause) {
|
|
1163
|
+
if (isTypeScriptConfig) {
|
|
1164
|
+
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
1165
|
+
throw new Error(
|
|
1166
|
+
`Failed to load TypeScript config.
|
|
1046
1167
|
${detail}
|
|
1047
1168
|
|
|
1048
1169
|
If this looks like a decorator/metadata error, the tsconfig applied to your entity files is likely missing "experimentalDecorators" / "emitDecoratorMetadata".
|
|
1049
1170
|
Make sure a tsconfig.json with those options sits next to your config file, or pass one explicitly with --tsconfig <path>.`,
|
|
1050
|
-
|
|
1171
|
+
{ cause }
|
|
1172
|
+
);
|
|
1173
|
+
}
|
|
1174
|
+
throw cause;
|
|
1175
|
+
}
|
|
1176
|
+
if (mod.default === void 0) {
|
|
1177
|
+
throw new Error("Config file must use a default export, e.g. `export default defineConfig({ ... })`.");
|
|
1178
|
+
}
|
|
1179
|
+
const config = mod.default;
|
|
1180
|
+
if (typeof config === "function" || config instanceof Promise) {
|
|
1181
|
+
throw new Error(
|
|
1182
|
+
"Config file default export must be a configuration object, not a function or Promise.\nResolve it first, or use the programmatic API instead (see README)."
|
|
1051
1183
|
);
|
|
1052
1184
|
}
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1185
|
+
if (typeof config !== "object" || config === null || Array.isArray(config)) {
|
|
1186
|
+
throw new Error(
|
|
1187
|
+
"Config file default export must be a configuration object, not a primitive value or array.\nExport a plain MikroORM options object instead."
|
|
1188
|
+
);
|
|
1189
|
+
}
|
|
1190
|
+
const options = config;
|
|
1191
|
+
const withPreferTs = isTypeScriptConfig && options.preferTs === void 0 ? { ...options, preferTs: true } : options;
|
|
1192
|
+
shouldKeepTsxRegistered = loadOptions.keepTsxRegistered === true;
|
|
1193
|
+
return withPreferTs;
|
|
1194
|
+
} finally {
|
|
1195
|
+
if (unregisterTsx !== void 0) {
|
|
1196
|
+
if (shouldKeepTsxRegistered) {
|
|
1197
|
+
activeTsxUnregister = unregisterTsx;
|
|
1198
|
+
} else {
|
|
1199
|
+
await unregisterTsx();
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1068
1202
|
}
|
|
1069
|
-
const options = config;
|
|
1070
|
-
const withPreferTs = isTypeScriptConfig && options.preferTs === void 0 ? { ...options, preferTs: true } : options;
|
|
1071
|
-
return withPreferTs;
|
|
1072
1203
|
}
|
|
1073
1204
|
function formatErrorChain(err) {
|
|
1074
1205
|
const lines = [];
|
|
@@ -1107,20 +1238,6 @@ ${formatFileSystemError(cause)}`, { cause });
|
|
|
1107
1238
|
}
|
|
1108
1239
|
function parseMermaidOptions(opts) {
|
|
1109
1240
|
const { mermaidLayout, mermaidTheme } = opts;
|
|
1110
|
-
if (mermaidLayout !== void 0 && !MERMAID_LAYOUTS.includes(mermaidLayout)) {
|
|
1111
|
-
process.stderr.write(
|
|
1112
|
-
`Error: Invalid --mermaid-layout "${mermaidLayout}". Allowed values: ${MERMAID_LAYOUTS.join(", ")}
|
|
1113
|
-
`
|
|
1114
|
-
);
|
|
1115
|
-
process.exit(1);
|
|
1116
|
-
}
|
|
1117
|
-
if (mermaidTheme !== void 0 && !MERMAID_THEMES.includes(mermaidTheme)) {
|
|
1118
|
-
process.stderr.write(
|
|
1119
|
-
`Error: Invalid --mermaid-theme "${mermaidTheme}". Allowed values: ${MERMAID_THEMES.join(", ")}
|
|
1120
|
-
`
|
|
1121
|
-
);
|
|
1122
|
-
process.exit(1);
|
|
1123
|
-
}
|
|
1124
1241
|
if (mermaidLayout === void 0 && mermaidTheme === void 0) {
|
|
1125
1242
|
return void 0;
|
|
1126
1243
|
}
|
|
@@ -1135,7 +1252,7 @@ async function run(opts) {
|
|
|
1135
1252
|
const mermaid = parseMermaidOptions(opts);
|
|
1136
1253
|
let ormOptions;
|
|
1137
1254
|
try {
|
|
1138
|
-
ormOptions = await loadOrmOptions(configPath, opts.tsconfig);
|
|
1255
|
+
ormOptions = await loadOrmOptions(configPath, opts.tsconfig, { keepTsxRegistered: true });
|
|
1139
1256
|
} catch (err) {
|
|
1140
1257
|
process.stderr.write(
|
|
1141
1258
|
`Error: Cannot load config: ${configPath}
|
|
@@ -1156,10 +1273,12 @@ ${err instanceof Error ? err.message : String(err)}
|
|
|
1156
1273
|
`)
|
|
1157
1274
|
});
|
|
1158
1275
|
} catch (err) {
|
|
1276
|
+
await unregisterActiveTsxLoader();
|
|
1159
1277
|
process.stderr.write(`Error: ${formatDiscoveryError(err)}
|
|
1160
1278
|
`);
|
|
1161
1279
|
process.exit(1);
|
|
1162
1280
|
}
|
|
1281
|
+
await unregisterActiveTsxLoader();
|
|
1163
1282
|
try {
|
|
1164
1283
|
await writeMarkdownFile(outPath, markdown);
|
|
1165
1284
|
} catch (err) {
|
|
@@ -1176,7 +1295,17 @@ var program = new Command().name("mikro-orm-markdown").description("Generate Mer
|
|
|
1176
1295
|
).option(
|
|
1177
1296
|
"--src <paths...>",
|
|
1178
1297
|
"Source .ts file globs to read JSDoc from when entities run from compiled .js (comments are stripped at build time)"
|
|
1179
|
-
).
|
|
1298
|
+
).addOption(
|
|
1299
|
+
new Option(
|
|
1300
|
+
"--mermaid-layout <layout>",
|
|
1301
|
+
`Mermaid layout engine injected as frontmatter (${MERMAID_LAYOUTS.join("|")})`
|
|
1302
|
+
).choices(MERMAID_LAYOUTS)
|
|
1303
|
+
).addOption(
|
|
1304
|
+
new Option(
|
|
1305
|
+
"--mermaid-theme <theme>",
|
|
1306
|
+
`Mermaid theme injected as frontmatter (${MERMAID_THEMES.join("|")})`
|
|
1307
|
+
).choices(MERMAID_THEMES)
|
|
1308
|
+
).action(run);
|
|
1180
1309
|
function isDirectCliExecution() {
|
|
1181
1310
|
const entryPoint = process.argv[1];
|
|
1182
1311
|
if (entryPoint === void 0) {
|