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/cli.cjs
CHANGED
|
@@ -91,17 +91,7 @@ function loadJsDoc(filePaths, onWarn) {
|
|
|
91
91
|
if (classDocs.length > 0) {
|
|
92
92
|
entities.set(className, parseEntityJsDoc(classDocs));
|
|
93
93
|
}
|
|
94
|
-
const propMap =
|
|
95
|
-
for (const prop of cls.getProperties()) {
|
|
96
|
-
const propDocs = prop.getJsDocs();
|
|
97
|
-
if (propDocs.length === 0) {
|
|
98
|
-
continue;
|
|
99
|
-
}
|
|
100
|
-
const info = parsePropJsDoc(propDocs);
|
|
101
|
-
if (info.description !== void 0 || info.atLeastOne) {
|
|
102
|
-
propMap.set(prop.getName(), info);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
94
|
+
const propMap = collectPropJsDocs(cls);
|
|
105
95
|
if (propMap.size > 0) {
|
|
106
96
|
props.set(className, propMap);
|
|
107
97
|
}
|
|
@@ -118,6 +108,28 @@ function formatUnknownError(err) {
|
|
|
118
108
|
function hasGlobPattern(filePath) {
|
|
119
109
|
return /[*?[\]{}]/.test(filePath);
|
|
120
110
|
}
|
|
111
|
+
function collectPropJsDocs(cls) {
|
|
112
|
+
const propMap = /* @__PURE__ */ new Map();
|
|
113
|
+
for (const prop of [...cls.getProperties(), ...cls.getGetAccessors()]) {
|
|
114
|
+
const info = parsePropJsDoc(prop.getJsDocs());
|
|
115
|
+
addPropInfo(propMap, prop.getName(), info);
|
|
116
|
+
}
|
|
117
|
+
for (const prop of getConstructorParameterProperties(cls)) {
|
|
118
|
+
const info = parseCompilerPropJsDoc(import_ts_morph.ts.getJSDocCommentsAndTags(prop.compilerNode));
|
|
119
|
+
addPropInfo(propMap, prop.getName(), info);
|
|
120
|
+
}
|
|
121
|
+
return propMap;
|
|
122
|
+
}
|
|
123
|
+
function getConstructorParameterProperties(cls) {
|
|
124
|
+
return cls.getConstructors().flatMap(
|
|
125
|
+
(constructorDeclaration) => constructorDeclaration.getParameters().filter((param) => param.isParameterProperty())
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
function addPropInfo(propMap, propName, info) {
|
|
129
|
+
if (info.description !== void 0 || info.atLeastOne) {
|
|
130
|
+
propMap.set(propName, info);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
121
133
|
function parseEntityJsDoc(jsDocs) {
|
|
122
134
|
const namespaces = [];
|
|
123
135
|
const erdNamespaces = [];
|
|
@@ -167,6 +179,35 @@ function parsePropJsDoc(jsDocs) {
|
|
|
167
179
|
}
|
|
168
180
|
return { ...description !== void 0 && { description }, atLeastOne };
|
|
169
181
|
}
|
|
182
|
+
function parseCompilerPropJsDoc(jsDocs) {
|
|
183
|
+
let description;
|
|
184
|
+
let atLeastOne = false;
|
|
185
|
+
for (const doc of jsDocs) {
|
|
186
|
+
if (import_ts_morph.ts.isJSDoc(doc)) {
|
|
187
|
+
const desc = formatCompilerJsDocComment(doc.comment);
|
|
188
|
+
if (desc && description === void 0) {
|
|
189
|
+
description = desc;
|
|
190
|
+
}
|
|
191
|
+
for (const tag of doc.tags ?? []) {
|
|
192
|
+
if (tag.tagName.getText() === "atLeastOne") {
|
|
193
|
+
atLeastOne = true;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
if (doc.tagName.getText() === "atLeastOne") {
|
|
199
|
+
atLeastOne = true;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return { ...description !== void 0 && { description }, atLeastOne };
|
|
203
|
+
}
|
|
204
|
+
function formatCompilerJsDocComment(comment) {
|
|
205
|
+
if (comment === void 0) {
|
|
206
|
+
return void 0;
|
|
207
|
+
}
|
|
208
|
+
const trimmed = import_ts_morph.ts.getTextOfJSDocComment(comment)?.trim();
|
|
209
|
+
return trimmed === void 0 || trimmed === "" ? void 0 : trimmed;
|
|
210
|
+
}
|
|
170
211
|
|
|
171
212
|
// src/metadata/load.ts
|
|
172
213
|
var path = __toESM(require("path"), 1);
|
|
@@ -206,6 +247,44 @@ function assertNoEntitySchemaEntities(options) {
|
|
|
206
247
|
Use decorator-based @Entity() classes instead.`
|
|
207
248
|
);
|
|
208
249
|
}
|
|
250
|
+
function hasDecoratorMarker(target) {
|
|
251
|
+
const pathSymbol = import_core.MetadataStorage.PATH_SYMBOL;
|
|
252
|
+
if (typeof pathSymbol === "symbol" && pathSymbol in target) {
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
return "__path" in target;
|
|
256
|
+
}
|
|
257
|
+
function isRenderableMeta(meta) {
|
|
258
|
+
return !meta.pivotTable && !meta.embeddable;
|
|
259
|
+
}
|
|
260
|
+
function assertDiscoveredEntitiesAreSupported(metas) {
|
|
261
|
+
const confirmed = [];
|
|
262
|
+
const unconfirmed = [];
|
|
263
|
+
for (const meta of metas) {
|
|
264
|
+
if (!isRenderableMeta(meta)) {
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
if (import_core.EntitySchema.REGISTRY.has(meta.class)) {
|
|
268
|
+
confirmed.push(meta.className);
|
|
269
|
+
} else if (!hasDecoratorMarker(meta.class)) {
|
|
270
|
+
unconfirmed.push(meta.className);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
if (confirmed.length === 0 && unconfirmed.length === 0) {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
const lines = [];
|
|
277
|
+
if (confirmed.length > 0) {
|
|
278
|
+
lines.push(`EntitySchema-defined entities are not currently supported: ${confirmed.join(", ")}.`);
|
|
279
|
+
}
|
|
280
|
+
if (unconfirmed.length > 0) {
|
|
281
|
+
lines.push(
|
|
282
|
+
`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`
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
lines.push("Use decorator-based @Entity() classes instead.");
|
|
286
|
+
throw new MetadataLoadError(lines.join("\n"));
|
|
287
|
+
}
|
|
209
288
|
async function loadEntityMetadata(options) {
|
|
210
289
|
assertNoEntitySchemaEntities(options);
|
|
211
290
|
let orm;
|
|
@@ -232,6 +311,7 @@ async function loadEntityMetadata(options) {
|
|
|
232
311
|
"No entities were discovered. Check that your config specifies at least one entity path or class."
|
|
233
312
|
);
|
|
234
313
|
}
|
|
314
|
+
assertDiscoveredEntitiesAreSupported(all);
|
|
235
315
|
const baseDir = orm.config.get("baseDir");
|
|
236
316
|
const sourcePaths = [...new Set(all.filter((m) => m.path).map((m) => path.resolve(baseDir, m.path)))];
|
|
237
317
|
return { metas: all, sourcePaths };
|
|
@@ -243,57 +323,8 @@ async function loadEntityMetadata(options) {
|
|
|
243
323
|
// src/model/build.ts
|
|
244
324
|
var import_core3 = require("@mikro-orm/core");
|
|
245
325
|
|
|
246
|
-
// src/
|
|
326
|
+
// src/model/diagram.ts
|
|
247
327
|
var import_core2 = require("@mikro-orm/core");
|
|
248
|
-
|
|
249
|
-
// src/render/escape.ts
|
|
250
|
-
var MARKDOWN_INLINE_SPECIAL_CHARS = /[\\`|*#]/g;
|
|
251
|
-
var MERMAID_IDENTIFIER_INVALID_CHARS = /[^a-zA-Z0-9_]/g;
|
|
252
|
-
function normalizeInlineText(value) {
|
|
253
|
-
return value.replace(/\r\n?/g, "\n").replace(/[\n\t]+/g, " ").replace(/\s{2,}/g, " ").trim();
|
|
254
|
-
}
|
|
255
|
-
function splitNormalizedLines(value) {
|
|
256
|
-
return value.replace(/\r\n?/g, "\n").split("\n");
|
|
257
|
-
}
|
|
258
|
-
function escapeHtmlText(value) {
|
|
259
|
-
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
260
|
-
}
|
|
261
|
-
function escapeMarkdownInline(value) {
|
|
262
|
-
return escapeHtmlText(normalizeInlineText(value)).replace(MARKDOWN_INLINE_SPECIAL_CHARS, "\\$&");
|
|
263
|
-
}
|
|
264
|
-
function escapeMarkdownTableCell(value) {
|
|
265
|
-
return splitNormalizedLines(value).map((line) => escapeMarkdownInline(line)).join("<br>");
|
|
266
|
-
}
|
|
267
|
-
function escapeMarkdownParagraph(value) {
|
|
268
|
-
return splitNormalizedLines(value).map((line) => escapeMarkdownInline(line)).join(" \n");
|
|
269
|
-
}
|
|
270
|
-
function renderMarkdownBlockQuote(value) {
|
|
271
|
-
return splitNormalizedLines(value).map((line) => `> ${escapeMarkdownInline(line)}`).join("\n");
|
|
272
|
-
}
|
|
273
|
-
function renderMarkdownInlineCode(value) {
|
|
274
|
-
const normalized = normalizeInlineText(value);
|
|
275
|
-
const backtickRuns = normalized.match(/`+/g) ?? [];
|
|
276
|
-
const longestRun = Math.max(0, ...backtickRuns.map((run2) => run2.length));
|
|
277
|
-
const fence = "`".repeat(longestRun + 1);
|
|
278
|
-
const needsPadding = normalized.startsWith("`") || normalized.endsWith("`");
|
|
279
|
-
const content = needsPadding ? ` ${normalized} ` : normalized;
|
|
280
|
-
return `${fence}${content}${fence}`;
|
|
281
|
-
}
|
|
282
|
-
function toMermaidIdentifier(value) {
|
|
283
|
-
const normalized = normalizeInlineText(value).replace(MERMAID_IDENTIFIER_INVALID_CHARS, "_").replace(/_+/g, "_");
|
|
284
|
-
const identifier = normalized === "" ? "_" : normalized;
|
|
285
|
-
return /^[a-zA-Z_]/.test(identifier) ? identifier : `_${identifier}`;
|
|
286
|
-
}
|
|
287
|
-
function escapeMermaidQuotedText(value) {
|
|
288
|
-
return normalizeInlineText(value).replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
289
|
-
}
|
|
290
|
-
function toMarkdownAnchor(value) {
|
|
291
|
-
return normalizeInlineText(value).toLowerCase().replace(/[^\p{L}\p{N}\s_-]/gu, "").replace(/\s+/g, "-");
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
// src/render/mermaid.ts
|
|
295
|
-
var MERMAID_LAYOUTS = ["dagre", "elk", "elk.stress"];
|
|
296
|
-
var MERMAID_THEMES = ["default", "neutral", "dark", "forest", "base"];
|
|
297
328
|
var FORMULA_DUMMY_TABLE = {
|
|
298
329
|
alias: "e0",
|
|
299
330
|
name: "",
|
|
@@ -353,6 +384,9 @@ function buildColumns(prop, metaByClass, owningMeta) {
|
|
|
353
384
|
return [];
|
|
354
385
|
}
|
|
355
386
|
const formulaExpr = prop.formula !== void 0 ? resolveFormulaExpr(prop.formula) : void 0;
|
|
387
|
+
if (prop.persist === false && formulaExpr === void 0) {
|
|
388
|
+
return [];
|
|
389
|
+
}
|
|
356
390
|
let embeddedIn;
|
|
357
391
|
let embeddedPropName;
|
|
358
392
|
if (prop.embedded !== void 0) {
|
|
@@ -539,6 +573,9 @@ function buildEdge(fromEntity, prop) {
|
|
|
539
573
|
};
|
|
540
574
|
}
|
|
541
575
|
if (prop.kind === import_core2.ReferenceKind.ONE_TO_ONE && prop.owner === true) {
|
|
576
|
+
if (prop.type === fromEntity) {
|
|
577
|
+
return null;
|
|
578
|
+
}
|
|
542
579
|
return {
|
|
543
580
|
fromEntity,
|
|
544
581
|
toEntity: prop.type,
|
|
@@ -558,75 +595,6 @@ function buildEdge(fromEntity, prop) {
|
|
|
558
595
|
}
|
|
559
596
|
return null;
|
|
560
597
|
}
|
|
561
|
-
function normalizeType(type) {
|
|
562
|
-
const t = type.toLowerCase().trim();
|
|
563
|
-
if (t === "uuid" || t === "text" || t === "string" || t.startsWith("varchar")) {
|
|
564
|
-
return "string";
|
|
565
|
-
}
|
|
566
|
-
if (t === "timestamptz" || t === "timestamp" || t === "datetime") {
|
|
567
|
-
return "datetime";
|
|
568
|
-
}
|
|
569
|
-
if (t === "integer" || t === "int" || t === "bigint" || t === "smallint") {
|
|
570
|
-
return "integer";
|
|
571
|
-
}
|
|
572
|
-
if (t === "doubletype" || t === "double precision" || t === "double" || t === "float" || t === "decimal") {
|
|
573
|
-
return "float";
|
|
574
|
-
}
|
|
575
|
-
if (t === "boolean" || t === "bool") {
|
|
576
|
-
return "boolean";
|
|
577
|
-
}
|
|
578
|
-
if (t === "jsonb") {
|
|
579
|
-
return "json";
|
|
580
|
-
}
|
|
581
|
-
return type;
|
|
582
|
-
}
|
|
583
|
-
function renderErDiagram(model, mermaid) {
|
|
584
|
-
const lines = [];
|
|
585
|
-
if (mermaid?.layout !== void 0 || mermaid?.theme !== void 0) {
|
|
586
|
-
lines.push("---", "config:");
|
|
587
|
-
if (mermaid.layout !== void 0) {
|
|
588
|
-
lines.push(` layout: ${mermaid.layout}`);
|
|
589
|
-
}
|
|
590
|
-
if (mermaid.theme !== void 0) {
|
|
591
|
-
lines.push(` theme: ${mermaid.theme}`);
|
|
592
|
-
}
|
|
593
|
-
lines.push("---");
|
|
594
|
-
}
|
|
595
|
-
lines.push("erDiagram");
|
|
596
|
-
for (const entity of model.entities) {
|
|
597
|
-
lines.push(` ${toMermaidIdentifier(entity.className)} {`);
|
|
598
|
-
for (const col of entity.columns) {
|
|
599
|
-
lines.push(` ${renderColumnLine(col)}`);
|
|
600
|
-
}
|
|
601
|
-
lines.push(" }");
|
|
602
|
-
}
|
|
603
|
-
for (const rel of model.relations) {
|
|
604
|
-
lines.push(
|
|
605
|
-
` ${toMermaidIdentifier(rel.fromEntity)} ${rel.fromCardinality}--${rel.toCardinality} ${toMermaidIdentifier(rel.toEntity)} : "${escapeMermaidQuotedText(rel.label)}"`
|
|
606
|
-
);
|
|
607
|
-
}
|
|
608
|
-
return lines.join("\n");
|
|
609
|
-
}
|
|
610
|
-
function renderColumnLine(col) {
|
|
611
|
-
let qualifier = "";
|
|
612
|
-
if (col.isPrimary) {
|
|
613
|
-
qualifier = " PK";
|
|
614
|
-
} else if (col.isUnique) {
|
|
615
|
-
qualifier = " UK";
|
|
616
|
-
}
|
|
617
|
-
let comment;
|
|
618
|
-
if (col.formula !== void 0) {
|
|
619
|
-
comment = col.formula ? `formula: ${col.formula}` : "formula";
|
|
620
|
-
} else if (col.isDiscriminator) {
|
|
621
|
-
comment = "discriminator";
|
|
622
|
-
} else if (col.embeddedIn !== void 0) {
|
|
623
|
-
comment = `[${col.embeddedIn}]`;
|
|
624
|
-
} else if (col.isSelfReference) {
|
|
625
|
-
comment = "self-ref";
|
|
626
|
-
}
|
|
627
|
-
const commentStr = comment !== void 0 ? ` "${escapeMermaidQuotedText(comment)}"` : "";
|
|
628
|
-
return `${toMermaidIdentifier(normalizeType(col.type))} ${toMermaidIdentifier(col.fieldName)}${qualifier}${commentStr}`;
|
|
629
|
-
}
|
|
630
598
|
|
|
631
599
|
// src/model/build.ts
|
|
632
600
|
var FROM_ONE_OR_MORE = "}|";
|
|
@@ -649,7 +617,10 @@ function buildDocumentModel(metas, jsDocResult, title, description, onWarn) {
|
|
|
649
617
|
const columns = model.columns.filter(
|
|
650
618
|
(col) => !(col.isForeignKey && col.referencedEntity !== void 0 && hiddenClasses.has(col.referencedEntity))
|
|
651
619
|
);
|
|
652
|
-
const visibleModel =
|
|
620
|
+
const visibleModel = removeHiddenEntityReferences(
|
|
621
|
+
columns.length === model.columns.length ? model : { ...model, columns },
|
|
622
|
+
hiddenClasses
|
|
623
|
+
);
|
|
653
624
|
const ownPropDocs = jsDocResult.props.get(model.className) ?? /* @__PURE__ */ new Map();
|
|
654
625
|
const propDocs = withEmbeddedPropDocs(ownPropDocs, visibleModel.columns, jsDocResult.props);
|
|
655
626
|
enrichedByClass.set(model.className, { model: visibleModel, jsDoc, propDocs });
|
|
@@ -700,6 +671,14 @@ function buildDocumentModel(metas, jsDocResult, title, description, onWarn) {
|
|
|
700
671
|
});
|
|
701
672
|
return { title, groups, ...description !== void 0 && { description } };
|
|
702
673
|
}
|
|
674
|
+
function removeHiddenEntityReferences(model, hiddenClasses) {
|
|
675
|
+
if (model.extendsEntity === void 0 || !hiddenClasses.has(model.extendsEntity)) {
|
|
676
|
+
return model;
|
|
677
|
+
}
|
|
678
|
+
const visibleModel = { ...model };
|
|
679
|
+
delete visibleModel.extendsEntity;
|
|
680
|
+
return visibleModel;
|
|
681
|
+
}
|
|
703
682
|
function withEmbeddedPropDocs(ownPropDocs, columns, allPropDocs) {
|
|
704
683
|
const merged = new Map(ownPropDocs);
|
|
705
684
|
for (const col of columns) {
|
|
@@ -811,6 +790,144 @@ async function withTsMorphMetadataProvider(options, onWarn) {
|
|
|
811
790
|
}
|
|
812
791
|
}
|
|
813
792
|
|
|
793
|
+
// src/render/escape.ts
|
|
794
|
+
var MARKDOWN_INLINE_SPECIAL_CHARS = /[\\`|*#\[\]]/g;
|
|
795
|
+
var MARKDOWN_EMPHASIS_UNDERSCORE = /(?<![a-zA-Z0-9])_|_(?![a-zA-Z0-9])/g;
|
|
796
|
+
var MERMAID_IDENTIFIER_INVALID_CHARS = /[^a-zA-Z0-9_]/g;
|
|
797
|
+
function normalizeInlineText(value) {
|
|
798
|
+
return value.replace(/\r\n?/g, "\n").replace(/[\n\t]+/g, " ").replace(/\s{2,}/g, " ").trim();
|
|
799
|
+
}
|
|
800
|
+
function splitNormalizedLines(value) {
|
|
801
|
+
return value.replace(/\r\n?/g, "\n").split("\n");
|
|
802
|
+
}
|
|
803
|
+
function escapeHtmlText(value) {
|
|
804
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
805
|
+
}
|
|
806
|
+
function escapeMarkdownInline(value) {
|
|
807
|
+
return escapeHtmlText(normalizeInlineText(value)).replace(MARKDOWN_INLINE_SPECIAL_CHARS, "\\$&").replace(MARKDOWN_EMPHASIS_UNDERSCORE, "\\_");
|
|
808
|
+
}
|
|
809
|
+
function escapeMarkdownTableCell(value) {
|
|
810
|
+
return splitNormalizedLines(value).map((line) => escapeMarkdownInline(line)).join("<br>");
|
|
811
|
+
}
|
|
812
|
+
function escapeMarkdownParagraph(value) {
|
|
813
|
+
return splitNormalizedLines(value).map((line) => escapeMarkdownInline(line)).join(" \n");
|
|
814
|
+
}
|
|
815
|
+
function renderMarkdownBlockQuote(value) {
|
|
816
|
+
return splitNormalizedLines(value).map((line) => `> ${escapeMarkdownInline(line)}`).join("\n");
|
|
817
|
+
}
|
|
818
|
+
function renderMarkdownInlineCode(value) {
|
|
819
|
+
const normalized = normalizeInlineText(value);
|
|
820
|
+
const backtickRuns = normalized.match(/`+/g) ?? [];
|
|
821
|
+
const longestRun = Math.max(0, ...backtickRuns.map((run2) => run2.length));
|
|
822
|
+
const fence = "`".repeat(longestRun + 1);
|
|
823
|
+
const needsPadding = normalized.startsWith("`") || normalized.endsWith("`");
|
|
824
|
+
const content = needsPadding ? ` ${normalized} ` : normalized;
|
|
825
|
+
return `${fence}${content}${fence}`;
|
|
826
|
+
}
|
|
827
|
+
function toMermaidIdentifier(value) {
|
|
828
|
+
const normalized = normalizeInlineText(value).replace(MERMAID_IDENTIFIER_INVALID_CHARS, "_").replace(/_+/g, "_");
|
|
829
|
+
const identifier = normalized === "" ? "_" : normalized;
|
|
830
|
+
return /^[a-zA-Z_]/.test(identifier) ? identifier : `_${identifier}`;
|
|
831
|
+
}
|
|
832
|
+
function escapeMermaidQuotedText(value) {
|
|
833
|
+
return normalizeInlineText(value).replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
834
|
+
}
|
|
835
|
+
function toMarkdownAnchor(value) {
|
|
836
|
+
return normalizeInlineText(value).toLowerCase().replace(/[^\p{L}\p{N}\s_-]/gu, "").replace(/\s+/g, "-");
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
// src/render/mermaid.ts
|
|
840
|
+
var MERMAID_LAYOUTS = ["dagre", "elk", "elk.stress"];
|
|
841
|
+
var MERMAID_THEMES = ["default", "neutral", "dark", "forest", "base"];
|
|
842
|
+
var GENERIC_TYPE_BY_BASE_NAME = /* @__PURE__ */ new Map([
|
|
843
|
+
["uuid", "string"],
|
|
844
|
+
["text", "string"],
|
|
845
|
+
["string", "string"],
|
|
846
|
+
["varchar", "string"],
|
|
847
|
+
["character varying", "string"],
|
|
848
|
+
["character", "string"],
|
|
849
|
+
["char", "string"],
|
|
850
|
+
["tinytext", "string"],
|
|
851
|
+
["mediumtext", "string"],
|
|
852
|
+
["longtext", "string"],
|
|
853
|
+
["timestamptz", "datetime"],
|
|
854
|
+
["timestamp", "datetime"],
|
|
855
|
+
["datetime", "datetime"],
|
|
856
|
+
["integer", "integer"],
|
|
857
|
+
["int", "integer"],
|
|
858
|
+
["bigint", "integer"],
|
|
859
|
+
["smallint", "integer"],
|
|
860
|
+
["tinyint", "integer"],
|
|
861
|
+
["mediumint", "integer"],
|
|
862
|
+
["serial", "integer"],
|
|
863
|
+
["bigserial", "integer"],
|
|
864
|
+
["doubletype", "float"],
|
|
865
|
+
["double precision", "float"],
|
|
866
|
+
["double", "float"],
|
|
867
|
+
["float", "float"],
|
|
868
|
+
["decimal", "float"],
|
|
869
|
+
["numeric", "float"],
|
|
870
|
+
["real", "float"],
|
|
871
|
+
["boolean", "boolean"],
|
|
872
|
+
["bool", "boolean"],
|
|
873
|
+
["jsonb", "json"]
|
|
874
|
+
]);
|
|
875
|
+
function normalizeType(type) {
|
|
876
|
+
const t = type.toLowerCase().replace(/\s+/g, " ").trim();
|
|
877
|
+
if (/^tinyint\s*\(\s*1\s*\)$/.test(t)) {
|
|
878
|
+
return "boolean";
|
|
879
|
+
}
|
|
880
|
+
const baseName = t.replace(/\(.*\)/, "").replace(/\b(un)?signed\b/, "").trim();
|
|
881
|
+
return GENERIC_TYPE_BY_BASE_NAME.get(baseName) ?? type;
|
|
882
|
+
}
|
|
883
|
+
function renderErDiagram(model, mermaid) {
|
|
884
|
+
const lines = [];
|
|
885
|
+
if (mermaid?.layout !== void 0 || mermaid?.theme !== void 0) {
|
|
886
|
+
lines.push("---", "config:");
|
|
887
|
+
if (mermaid.layout !== void 0) {
|
|
888
|
+
lines.push(` layout: ${mermaid.layout}`);
|
|
889
|
+
}
|
|
890
|
+
if (mermaid.theme !== void 0) {
|
|
891
|
+
lines.push(` theme: ${mermaid.theme}`);
|
|
892
|
+
}
|
|
893
|
+
lines.push("---");
|
|
894
|
+
}
|
|
895
|
+
lines.push("erDiagram");
|
|
896
|
+
for (const entity of model.entities) {
|
|
897
|
+
lines.push(` ${toMermaidIdentifier(entity.className)} {`);
|
|
898
|
+
for (const col of entity.columns) {
|
|
899
|
+
lines.push(` ${renderColumnLine(col)}`);
|
|
900
|
+
}
|
|
901
|
+
lines.push(" }");
|
|
902
|
+
}
|
|
903
|
+
for (const rel of model.relations) {
|
|
904
|
+
lines.push(
|
|
905
|
+
` ${toMermaidIdentifier(rel.fromEntity)} ${rel.fromCardinality}--${rel.toCardinality} ${toMermaidIdentifier(rel.toEntity)} : "${escapeMermaidQuotedText(rel.label)}"`
|
|
906
|
+
);
|
|
907
|
+
}
|
|
908
|
+
return lines.join("\n");
|
|
909
|
+
}
|
|
910
|
+
function renderColumnLine(col) {
|
|
911
|
+
let qualifier = "";
|
|
912
|
+
if (col.isPrimary) {
|
|
913
|
+
qualifier = " PK";
|
|
914
|
+
} else if (col.isUnique) {
|
|
915
|
+
qualifier = " UK";
|
|
916
|
+
}
|
|
917
|
+
let comment;
|
|
918
|
+
if (col.formula !== void 0) {
|
|
919
|
+
comment = col.formula ? `formula: ${col.formula}` : "formula";
|
|
920
|
+
} else if (col.isDiscriminator) {
|
|
921
|
+
comment = "discriminator";
|
|
922
|
+
} else if (col.embeddedIn !== void 0) {
|
|
923
|
+
comment = `[${col.embeddedIn}]`;
|
|
924
|
+
} else if (col.isSelfReference) {
|
|
925
|
+
comment = "self-ref";
|
|
926
|
+
}
|
|
927
|
+
const commentStr = comment !== void 0 ? ` "${escapeMermaidQuotedText(comment)}"` : "";
|
|
928
|
+
return `${toMermaidIdentifier(normalizeType(col.type))} ${toMermaidIdentifier(col.fieldName)}${qualifier}${commentStr}`;
|
|
929
|
+
}
|
|
930
|
+
|
|
814
931
|
// src/render/markdown.ts
|
|
815
932
|
function renderMarkdown(docModel, mermaid) {
|
|
816
933
|
const sections = [`# ${escapeMarkdownInline(docModel.title)}`];
|
|
@@ -834,7 +951,7 @@ function renderBulletSection(header, items, renderItem) {
|
|
|
834
951
|
}
|
|
835
952
|
function renderTableOfContents(groups, anchors) {
|
|
836
953
|
return renderBulletSection("## Contents", groups, (group) => {
|
|
837
|
-
const label = escapeMarkdownInline(group.name)
|
|
954
|
+
const label = escapeMarkdownInline(group.name);
|
|
838
955
|
return `- [${label}](#${anchors.get(group) ?? toMarkdownAnchor(group.name)})`;
|
|
839
956
|
});
|
|
840
957
|
}
|
|
@@ -919,7 +1036,7 @@ function resolveColumnKey(col) {
|
|
|
919
1036
|
return "PK";
|
|
920
1037
|
}
|
|
921
1038
|
if (col.isForeignKey) {
|
|
922
|
-
return fkKey;
|
|
1039
|
+
return col.isUnique ? `${fkKey}, UK` : fkKey;
|
|
923
1040
|
}
|
|
924
1041
|
if (col.isUnique) {
|
|
925
1042
|
return "UK";
|
|
@@ -1032,6 +1149,12 @@ async function generateMarkdown(options) {
|
|
|
1032
1149
|
}
|
|
1033
1150
|
|
|
1034
1151
|
// src/cli.ts
|
|
1152
|
+
var activeTsxUnregister;
|
|
1153
|
+
async function unregisterActiveTsxLoader() {
|
|
1154
|
+
const unregister = activeTsxUnregister;
|
|
1155
|
+
activeTsxUnregister = void 0;
|
|
1156
|
+
await unregister?.();
|
|
1157
|
+
}
|
|
1035
1158
|
function toConfigImportSpecifier(configPath) {
|
|
1036
1159
|
return (0, import_node_url.pathToFileURL)(path2.resolve(configPath)).href;
|
|
1037
1160
|
}
|
|
@@ -1049,64 +1172,78 @@ function findNearestTsconfig(fromPath) {
|
|
|
1049
1172
|
dir = parent;
|
|
1050
1173
|
}
|
|
1051
1174
|
}
|
|
1052
|
-
async function loadOrmOptions(configPath, tsconfigPath) {
|
|
1175
|
+
async function loadOrmOptions(configPath, tsconfigPath, loadOptions = {}) {
|
|
1053
1176
|
const isTypeScriptConfig = configPath.endsWith(".ts");
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
try {
|
|
1057
|
-
({ register } = await import("tsx/esm/api"));
|
|
1058
|
-
} catch {
|
|
1059
|
-
throw new Error('TypeScript config files require the "tsx" package.\nInstall it with: npm install -D tsx');
|
|
1060
|
-
}
|
|
1061
|
-
let tsconfig;
|
|
1062
|
-
if (tsconfigPath !== void 0) {
|
|
1063
|
-
tsconfig = path2.resolve(tsconfigPath);
|
|
1064
|
-
if (!syncFs.existsSync(tsconfig)) {
|
|
1065
|
-
throw new Error(`--tsconfig file not found: ${tsconfig}`);
|
|
1066
|
-
}
|
|
1067
|
-
} else {
|
|
1068
|
-
tsconfig = findNearestTsconfig(configPath);
|
|
1069
|
-
}
|
|
1070
|
-
register(tsconfig !== void 0 ? { tsconfig } : {});
|
|
1071
|
-
}
|
|
1072
|
-
const configUrl = toConfigImportSpecifier(configPath);
|
|
1073
|
-
let mod;
|
|
1177
|
+
let unregisterTsx;
|
|
1178
|
+
let shouldKeepTsxRegistered = false;
|
|
1074
1179
|
try {
|
|
1075
|
-
mod = await import(
|
|
1076
|
-
/* @vite-ignore */
|
|
1077
|
-
configUrl
|
|
1078
|
-
);
|
|
1079
|
-
} catch (cause) {
|
|
1080
1180
|
if (isTypeScriptConfig) {
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1181
|
+
await unregisterActiveTsxLoader();
|
|
1182
|
+
let register;
|
|
1183
|
+
try {
|
|
1184
|
+
({ register } = await import("tsx/esm/api"));
|
|
1185
|
+
} catch {
|
|
1186
|
+
throw new Error('TypeScript config files require the "tsx" package.\nInstall it with: npm install -D tsx');
|
|
1187
|
+
}
|
|
1188
|
+
let tsconfig;
|
|
1189
|
+
if (tsconfigPath !== void 0) {
|
|
1190
|
+
tsconfig = path2.resolve(tsconfigPath);
|
|
1191
|
+
if (!syncFs.existsSync(tsconfig)) {
|
|
1192
|
+
throw new Error(`--tsconfig file not found: ${tsconfig}`);
|
|
1193
|
+
}
|
|
1194
|
+
} else {
|
|
1195
|
+
tsconfig = findNearestTsconfig(configPath);
|
|
1196
|
+
}
|
|
1197
|
+
unregisterTsx = register(tsconfig !== void 0 ? { tsconfig } : {});
|
|
1198
|
+
}
|
|
1199
|
+
const configUrl = toConfigImportSpecifier(configPath);
|
|
1200
|
+
let mod;
|
|
1201
|
+
try {
|
|
1202
|
+
mod = await import(
|
|
1203
|
+
/* @vite-ignore */
|
|
1204
|
+
configUrl
|
|
1205
|
+
);
|
|
1206
|
+
} catch (cause) {
|
|
1207
|
+
if (isTypeScriptConfig) {
|
|
1208
|
+
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
1209
|
+
throw new Error(
|
|
1210
|
+
`Failed to load TypeScript config.
|
|
1084
1211
|
${detail}
|
|
1085
1212
|
|
|
1086
1213
|
If this looks like a decorator/metadata error, the tsconfig applied to your entity files is likely missing "experimentalDecorators" / "emitDecoratorMetadata".
|
|
1087
1214
|
Make sure a tsconfig.json with those options sits next to your config file, or pass one explicitly with --tsconfig <path>.`,
|
|
1088
|
-
|
|
1215
|
+
{ cause }
|
|
1216
|
+
);
|
|
1217
|
+
}
|
|
1218
|
+
throw cause;
|
|
1219
|
+
}
|
|
1220
|
+
if (mod.default === void 0) {
|
|
1221
|
+
throw new Error("Config file must use a default export, e.g. `export default defineConfig({ ... })`.");
|
|
1222
|
+
}
|
|
1223
|
+
const config = mod.default;
|
|
1224
|
+
if (typeof config === "function" || config instanceof Promise) {
|
|
1225
|
+
throw new Error(
|
|
1226
|
+
"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)."
|
|
1089
1227
|
);
|
|
1090
1228
|
}
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1229
|
+
if (typeof config !== "object" || config === null || Array.isArray(config)) {
|
|
1230
|
+
throw new Error(
|
|
1231
|
+
"Config file default export must be a configuration object, not a primitive value or array.\nExport a plain MikroORM options object instead."
|
|
1232
|
+
);
|
|
1233
|
+
}
|
|
1234
|
+
const options = config;
|
|
1235
|
+
const withPreferTs = isTypeScriptConfig && options.preferTs === void 0 ? { ...options, preferTs: true } : options;
|
|
1236
|
+
shouldKeepTsxRegistered = loadOptions.keepTsxRegistered === true;
|
|
1237
|
+
return withPreferTs;
|
|
1238
|
+
} finally {
|
|
1239
|
+
if (unregisterTsx !== void 0) {
|
|
1240
|
+
if (shouldKeepTsxRegistered) {
|
|
1241
|
+
activeTsxUnregister = unregisterTsx;
|
|
1242
|
+
} else {
|
|
1243
|
+
await unregisterTsx();
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1106
1246
|
}
|
|
1107
|
-
const options = config;
|
|
1108
|
-
const withPreferTs = isTypeScriptConfig && options.preferTs === void 0 ? { ...options, preferTs: true } : options;
|
|
1109
|
-
return withPreferTs;
|
|
1110
1247
|
}
|
|
1111
1248
|
function formatErrorChain(err) {
|
|
1112
1249
|
const lines = [];
|
|
@@ -1145,20 +1282,6 @@ ${formatFileSystemError(cause)}`, { cause });
|
|
|
1145
1282
|
}
|
|
1146
1283
|
function parseMermaidOptions(opts) {
|
|
1147
1284
|
const { mermaidLayout, mermaidTheme } = opts;
|
|
1148
|
-
if (mermaidLayout !== void 0 && !MERMAID_LAYOUTS.includes(mermaidLayout)) {
|
|
1149
|
-
process.stderr.write(
|
|
1150
|
-
`Error: Invalid --mermaid-layout "${mermaidLayout}". Allowed values: ${MERMAID_LAYOUTS.join(", ")}
|
|
1151
|
-
`
|
|
1152
|
-
);
|
|
1153
|
-
process.exit(1);
|
|
1154
|
-
}
|
|
1155
|
-
if (mermaidTheme !== void 0 && !MERMAID_THEMES.includes(mermaidTheme)) {
|
|
1156
|
-
process.stderr.write(
|
|
1157
|
-
`Error: Invalid --mermaid-theme "${mermaidTheme}". Allowed values: ${MERMAID_THEMES.join(", ")}
|
|
1158
|
-
`
|
|
1159
|
-
);
|
|
1160
|
-
process.exit(1);
|
|
1161
|
-
}
|
|
1162
1285
|
if (mermaidLayout === void 0 && mermaidTheme === void 0) {
|
|
1163
1286
|
return void 0;
|
|
1164
1287
|
}
|
|
@@ -1173,7 +1296,7 @@ async function run(opts) {
|
|
|
1173
1296
|
const mermaid = parseMermaidOptions(opts);
|
|
1174
1297
|
let ormOptions;
|
|
1175
1298
|
try {
|
|
1176
|
-
ormOptions = await loadOrmOptions(configPath, opts.tsconfig);
|
|
1299
|
+
ormOptions = await loadOrmOptions(configPath, opts.tsconfig, { keepTsxRegistered: true });
|
|
1177
1300
|
} catch (err) {
|
|
1178
1301
|
process.stderr.write(
|
|
1179
1302
|
`Error: Cannot load config: ${configPath}
|
|
@@ -1194,10 +1317,12 @@ ${err instanceof Error ? err.message : String(err)}
|
|
|
1194
1317
|
`)
|
|
1195
1318
|
});
|
|
1196
1319
|
} catch (err) {
|
|
1320
|
+
await unregisterActiveTsxLoader();
|
|
1197
1321
|
process.stderr.write(`Error: ${formatDiscoveryError(err)}
|
|
1198
1322
|
`);
|
|
1199
1323
|
process.exit(1);
|
|
1200
1324
|
}
|
|
1325
|
+
await unregisterActiveTsxLoader();
|
|
1201
1326
|
try {
|
|
1202
1327
|
await writeMarkdownFile(outPath, markdown);
|
|
1203
1328
|
} catch (err) {
|
|
@@ -1214,7 +1339,17 @@ var program = new import_commander.Command().name("mikro-orm-markdown").descript
|
|
|
1214
1339
|
).option(
|
|
1215
1340
|
"--src <paths...>",
|
|
1216
1341
|
"Source .ts file globs to read JSDoc from when entities run from compiled .js (comments are stripped at build time)"
|
|
1217
|
-
).
|
|
1342
|
+
).addOption(
|
|
1343
|
+
new import_commander.Option(
|
|
1344
|
+
"--mermaid-layout <layout>",
|
|
1345
|
+
`Mermaid layout engine injected as frontmatter (${MERMAID_LAYOUTS.join("|")})`
|
|
1346
|
+
).choices(MERMAID_LAYOUTS)
|
|
1347
|
+
).addOption(
|
|
1348
|
+
new import_commander.Option(
|
|
1349
|
+
"--mermaid-theme <theme>",
|
|
1350
|
+
`Mermaid theme injected as frontmatter (${MERMAID_THEMES.join("|")})`
|
|
1351
|
+
).choices(MERMAID_THEMES)
|
|
1352
|
+
).action(run);
|
|
1218
1353
|
function isDirectCliExecution() {
|
|
1219
1354
|
const entryPoint = process.argv[1];
|
|
1220
1355
|
if (entryPoint === void 0) {
|