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/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 = /* @__PURE__ */ new Map();
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/render/mermaid.ts
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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
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: "",
@@ -309,6 +340,9 @@ function buildColumns(prop, metaByClass, owningMeta) {
309
340
  return [];
310
341
  }
311
342
  const formulaExpr = prop.formula !== void 0 ? resolveFormulaExpr(prop.formula) : void 0;
343
+ if (prop.persist === false && formulaExpr === void 0) {
344
+ return [];
345
+ }
312
346
  let embeddedIn;
313
347
  let embeddedPropName;
314
348
  if (prop.embedded !== void 0) {
@@ -495,6 +529,9 @@ function buildEdge(fromEntity, prop) {
495
529
  };
496
530
  }
497
531
  if (prop.kind === ReferenceKind.ONE_TO_ONE && prop.owner === true) {
532
+ if (prop.type === fromEntity) {
533
+ return null;
534
+ }
498
535
  return {
499
536
  fromEntity,
500
537
  toEntity: prop.type,
@@ -514,75 +551,6 @@ function buildEdge(fromEntity, prop) {
514
551
  }
515
552
  return null;
516
553
  }
517
- function normalizeType(type) {
518
- const t = type.toLowerCase().trim();
519
- if (t === "uuid" || t === "text" || t === "string" || t.startsWith("varchar")) {
520
- return "string";
521
- }
522
- if (t === "timestamptz" || t === "timestamp" || t === "datetime") {
523
- return "datetime";
524
- }
525
- if (t === "integer" || t === "int" || t === "bigint" || t === "smallint") {
526
- return "integer";
527
- }
528
- if (t === "doubletype" || t === "double precision" || t === "double" || t === "float" || t === "decimal") {
529
- return "float";
530
- }
531
- if (t === "boolean" || t === "bool") {
532
- return "boolean";
533
- }
534
- if (t === "jsonb") {
535
- return "json";
536
- }
537
- return type;
538
- }
539
- function renderErDiagram(model, mermaid) {
540
- const lines = [];
541
- if (mermaid?.layout !== void 0 || mermaid?.theme !== void 0) {
542
- lines.push("---", "config:");
543
- if (mermaid.layout !== void 0) {
544
- lines.push(` layout: ${mermaid.layout}`);
545
- }
546
- if (mermaid.theme !== void 0) {
547
- lines.push(` theme: ${mermaid.theme}`);
548
- }
549
- lines.push("---");
550
- }
551
- lines.push("erDiagram");
552
- for (const entity of model.entities) {
553
- lines.push(` ${toMermaidIdentifier(entity.className)} {`);
554
- for (const col of entity.columns) {
555
- lines.push(` ${renderColumnLine(col)}`);
556
- }
557
- lines.push(" }");
558
- }
559
- for (const rel of model.relations) {
560
- lines.push(
561
- ` ${toMermaidIdentifier(rel.fromEntity)} ${rel.fromCardinality}--${rel.toCardinality} ${toMermaidIdentifier(rel.toEntity)} : "${escapeMermaidQuotedText(rel.label)}"`
562
- );
563
- }
564
- return lines.join("\n");
565
- }
566
- function renderColumnLine(col) {
567
- let qualifier = "";
568
- if (col.isPrimary) {
569
- qualifier = " PK";
570
- } else if (col.isUnique) {
571
- qualifier = " UK";
572
- }
573
- let comment;
574
- if (col.formula !== void 0) {
575
- comment = col.formula ? `formula: ${col.formula}` : "formula";
576
- } else if (col.isDiscriminator) {
577
- comment = "discriminator";
578
- } else if (col.embeddedIn !== void 0) {
579
- comment = `[${col.embeddedIn}]`;
580
- } else if (col.isSelfReference) {
581
- comment = "self-ref";
582
- }
583
- const commentStr = comment !== void 0 ? ` "${escapeMermaidQuotedText(comment)}"` : "";
584
- return `${toMermaidIdentifier(normalizeType(col.type))} ${toMermaidIdentifier(col.fieldName)}${qualifier}${commentStr}`;
585
- }
586
554
 
587
555
  // src/model/build.ts
588
556
  var FROM_ONE_OR_MORE = "}|";
@@ -605,7 +573,10 @@ function buildDocumentModel(metas, jsDocResult, title, description, onWarn) {
605
573
  const columns = model.columns.filter(
606
574
  (col) => !(col.isForeignKey && col.referencedEntity !== void 0 && hiddenClasses.has(col.referencedEntity))
607
575
  );
608
- const visibleModel = columns.length === model.columns.length ? model : { ...model, columns };
576
+ const visibleModel = removeHiddenEntityReferences(
577
+ columns.length === model.columns.length ? model : { ...model, columns },
578
+ hiddenClasses
579
+ );
609
580
  const ownPropDocs = jsDocResult.props.get(model.className) ?? /* @__PURE__ */ new Map();
610
581
  const propDocs = withEmbeddedPropDocs(ownPropDocs, visibleModel.columns, jsDocResult.props);
611
582
  enrichedByClass.set(model.className, { model: visibleModel, jsDoc, propDocs });
@@ -656,6 +627,14 @@ function buildDocumentModel(metas, jsDocResult, title, description, onWarn) {
656
627
  });
657
628
  return { title, groups, ...description !== void 0 && { description } };
658
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
+ }
659
638
  function withEmbeddedPropDocs(ownPropDocs, columns, allPropDocs) {
660
639
  const merged = new Map(ownPropDocs);
661
640
  for (const col of columns) {
@@ -767,6 +746,144 @@ async function withTsMorphMetadataProvider(options, onWarn) {
767
746
  }
768
747
  }
769
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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
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
+
770
887
  // src/render/markdown.ts
771
888
  function renderMarkdown(docModel, mermaid) {
772
889
  const sections = [`# ${escapeMarkdownInline(docModel.title)}`];
@@ -790,7 +907,7 @@ function renderBulletSection(header, items, renderItem) {
790
907
  }
791
908
  function renderTableOfContents(groups, anchors) {
792
909
  return renderBulletSection("## Contents", groups, (group) => {
793
- const label = escapeMarkdownInline(group.name).replace(/[[\]]/g, "\\$&");
910
+ const label = escapeMarkdownInline(group.name);
794
911
  return `- [${label}](#${anchors.get(group) ?? toMarkdownAnchor(group.name)})`;
795
912
  });
796
913
  }
@@ -875,7 +992,7 @@ function resolveColumnKey(col) {
875
992
  return "PK";
876
993
  }
877
994
  if (col.isForeignKey) {
878
- return fkKey;
995
+ return col.isUnique ? `${fkKey}, UK` : fkKey;
879
996
  }
880
997
  if (col.isUnique) {
881
998
  return "UK";
@@ -988,6 +1105,12 @@ async function generateMarkdown(options) {
988
1105
  }
989
1106
 
990
1107
  // src/cli.ts
1108
+ var activeTsxUnregister;
1109
+ async function unregisterActiveTsxLoader() {
1110
+ const unregister = activeTsxUnregister;
1111
+ activeTsxUnregister = void 0;
1112
+ await unregister?.();
1113
+ }
991
1114
  function toConfigImportSpecifier(configPath) {
992
1115
  return pathToFileURL(path2.resolve(configPath)).href;
993
1116
  }
@@ -1005,64 +1128,78 @@ function findNearestTsconfig(fromPath) {
1005
1128
  dir = parent;
1006
1129
  }
1007
1130
  }
1008
- async function loadOrmOptions(configPath, tsconfigPath) {
1131
+ async function loadOrmOptions(configPath, tsconfigPath, loadOptions = {}) {
1009
1132
  const isTypeScriptConfig = configPath.endsWith(".ts");
1010
- if (isTypeScriptConfig) {
1011
- let register;
1012
- try {
1013
- ({ register } = await import("tsx/esm/api"));
1014
- } catch {
1015
- throw new Error('TypeScript config files require the "tsx" package.\nInstall it with: npm install -D tsx');
1016
- }
1017
- let tsconfig;
1018
- if (tsconfigPath !== void 0) {
1019
- tsconfig = path2.resolve(tsconfigPath);
1020
- if (!syncFs.existsSync(tsconfig)) {
1021
- throw new Error(`--tsconfig file not found: ${tsconfig}`);
1022
- }
1023
- } else {
1024
- tsconfig = findNearestTsconfig(configPath);
1025
- }
1026
- register(tsconfig !== void 0 ? { tsconfig } : {});
1027
- }
1028
- const configUrl = toConfigImportSpecifier(configPath);
1029
- let mod;
1133
+ let unregisterTsx;
1134
+ let shouldKeepTsxRegistered = false;
1030
1135
  try {
1031
- mod = await import(
1032
- /* @vite-ignore */
1033
- configUrl
1034
- );
1035
- } catch (cause) {
1036
1136
  if (isTypeScriptConfig) {
1037
- const detail = cause instanceof Error ? cause.message : String(cause);
1038
- throw new Error(
1039
- `Failed to load TypeScript config.
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.
1040
1167
  ${detail}
1041
1168
 
1042
1169
  If this looks like a decorator/metadata error, the tsconfig applied to your entity files is likely missing "experimentalDecorators" / "emitDecoratorMetadata".
1043
1170
  Make sure a tsconfig.json with those options sits next to your config file, or pass one explicitly with --tsconfig <path>.`,
1044
- { cause }
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)."
1045
1183
  );
1046
1184
  }
1047
- throw cause;
1048
- }
1049
- if (mod.default === void 0) {
1050
- throw new Error("Config file must use a default export, e.g. `export default defineConfig({ ... })`.");
1051
- }
1052
- const config = mod.default;
1053
- if (typeof config === "function" || config instanceof Promise) {
1054
- throw new Error(
1055
- "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)."
1056
- );
1057
- }
1058
- if (typeof config !== "object" || config === null || Array.isArray(config)) {
1059
- throw new Error(
1060
- "Config file default export must be a configuration object, not a primitive value or array.\nExport a plain MikroORM options object instead."
1061
- );
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
+ }
1062
1202
  }
1063
- const options = config;
1064
- const withPreferTs = isTypeScriptConfig && options.preferTs === void 0 ? { ...options, preferTs: true } : options;
1065
- return withPreferTs;
1066
1203
  }
1067
1204
  function formatErrorChain(err) {
1068
1205
  const lines = [];
@@ -1101,20 +1238,6 @@ ${formatFileSystemError(cause)}`, { cause });
1101
1238
  }
1102
1239
  function parseMermaidOptions(opts) {
1103
1240
  const { mermaidLayout, mermaidTheme } = opts;
1104
- if (mermaidLayout !== void 0 && !MERMAID_LAYOUTS.includes(mermaidLayout)) {
1105
- process.stderr.write(
1106
- `Error: Invalid --mermaid-layout "${mermaidLayout}". Allowed values: ${MERMAID_LAYOUTS.join(", ")}
1107
- `
1108
- );
1109
- process.exit(1);
1110
- }
1111
- if (mermaidTheme !== void 0 && !MERMAID_THEMES.includes(mermaidTheme)) {
1112
- process.stderr.write(
1113
- `Error: Invalid --mermaid-theme "${mermaidTheme}". Allowed values: ${MERMAID_THEMES.join(", ")}
1114
- `
1115
- );
1116
- process.exit(1);
1117
- }
1118
1241
  if (mermaidLayout === void 0 && mermaidTheme === void 0) {
1119
1242
  return void 0;
1120
1243
  }
@@ -1129,7 +1252,7 @@ async function run(opts) {
1129
1252
  const mermaid = parseMermaidOptions(opts);
1130
1253
  let ormOptions;
1131
1254
  try {
1132
- ormOptions = await loadOrmOptions(configPath, opts.tsconfig);
1255
+ ormOptions = await loadOrmOptions(configPath, opts.tsconfig, { keepTsxRegistered: true });
1133
1256
  } catch (err) {
1134
1257
  process.stderr.write(
1135
1258
  `Error: Cannot load config: ${configPath}
@@ -1150,10 +1273,12 @@ ${err instanceof Error ? err.message : String(err)}
1150
1273
  `)
1151
1274
  });
1152
1275
  } catch (err) {
1276
+ await unregisterActiveTsxLoader();
1153
1277
  process.stderr.write(`Error: ${formatDiscoveryError(err)}
1154
1278
  `);
1155
1279
  process.exit(1);
1156
1280
  }
1281
+ await unregisterActiveTsxLoader();
1157
1282
  try {
1158
1283
  await writeMarkdownFile(outPath, markdown);
1159
1284
  } catch (err) {
@@ -1170,7 +1295,17 @@ var program = new Command().name("mikro-orm-markdown").description("Generate Mer
1170
1295
  ).option(
1171
1296
  "--src <paths...>",
1172
1297
  "Source .ts file globs to read JSDoc from when entities run from compiled .js (comments are stripped at build time)"
1173
- ).option("--mermaid-layout <layout>", `Mermaid layout engine injected as frontmatter (${MERMAID_LAYOUTS.join("|")})`).option("--mermaid-theme <theme>", `Mermaid theme injected as frontmatter (${MERMAID_THEMES.join("|")})`).action(run);
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);
1174
1309
  function isDirectCliExecution() {
1175
1310
  const entryPoint = process.argv[1];
1176
1311
  if (entryPoint === void 0) {