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 CHANGED
@@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.1.2] - 2026-07-06
9
+
10
+ ### Fixed
11
+
12
+ - Hidden STI parent entity names no longer leak into child entity captions when the parent is excluded with `@hidden`
13
+ - JSDoc extraction now includes getter accessors and constructor parameter properties, including rich inline JSDoc text such as `{@link ...}`
14
+ - Markdown escaping now treats underscore emphasis and link-like labels consistently while preserving identifier-style names such as `author_id`
15
+ - CLI TypeScript config loading now unregisters the temporary `tsx` loader when it is no longer needed
16
+
17
+ ### Changed
18
+
19
+ - Mermaid layout and theme CLI options now use Commander's built-in choice validation
20
+ - Diagram model construction was moved into the model layer so Mermaid rendering remains focused on output formatting
21
+
8
22
  ## [0.1.1] - 2026-07-03
9
23
 
10
24
  ### Fixed
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 = /* @__PURE__ */ new Map();
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/render/mermaid.ts
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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
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: "",
@@ -564,75 +595,6 @@ function buildEdge(fromEntity, prop) {
564
595
  }
565
596
  return null;
566
597
  }
567
- function normalizeType(type) {
568
- const t = type.toLowerCase().trim();
569
- if (t === "uuid" || t === "text" || t === "string" || t.startsWith("varchar")) {
570
- return "string";
571
- }
572
- if (t === "timestamptz" || t === "timestamp" || t === "datetime") {
573
- return "datetime";
574
- }
575
- if (t === "integer" || t === "int" || t === "bigint" || t === "smallint") {
576
- return "integer";
577
- }
578
- if (t === "doubletype" || t === "double precision" || t === "double" || t === "float" || t === "decimal") {
579
- return "float";
580
- }
581
- if (t === "boolean" || t === "bool") {
582
- return "boolean";
583
- }
584
- if (t === "jsonb") {
585
- return "json";
586
- }
587
- return type;
588
- }
589
- function renderErDiagram(model, mermaid) {
590
- const lines = [];
591
- if (mermaid?.layout !== void 0 || mermaid?.theme !== void 0) {
592
- lines.push("---", "config:");
593
- if (mermaid.layout !== void 0) {
594
- lines.push(` layout: ${mermaid.layout}`);
595
- }
596
- if (mermaid.theme !== void 0) {
597
- lines.push(` theme: ${mermaid.theme}`);
598
- }
599
- lines.push("---");
600
- }
601
- lines.push("erDiagram");
602
- for (const entity of model.entities) {
603
- lines.push(` ${toMermaidIdentifier(entity.className)} {`);
604
- for (const col of entity.columns) {
605
- lines.push(` ${renderColumnLine(col)}`);
606
- }
607
- lines.push(" }");
608
- }
609
- for (const rel of model.relations) {
610
- lines.push(
611
- ` ${toMermaidIdentifier(rel.fromEntity)} ${rel.fromCardinality}--${rel.toCardinality} ${toMermaidIdentifier(rel.toEntity)} : "${escapeMermaidQuotedText(rel.label)}"`
612
- );
613
- }
614
- return lines.join("\n");
615
- }
616
- function renderColumnLine(col) {
617
- let qualifier = "";
618
- if (col.isPrimary) {
619
- qualifier = " PK";
620
- } else if (col.isUnique) {
621
- qualifier = " UK";
622
- }
623
- let comment;
624
- if (col.formula !== void 0) {
625
- comment = col.formula ? `formula: ${col.formula}` : "formula";
626
- } else if (col.isDiscriminator) {
627
- comment = "discriminator";
628
- } else if (col.embeddedIn !== void 0) {
629
- comment = `[${col.embeddedIn}]`;
630
- } else if (col.isSelfReference) {
631
- comment = "self-ref";
632
- }
633
- const commentStr = comment !== void 0 ? ` "${escapeMermaidQuotedText(comment)}"` : "";
634
- return `${toMermaidIdentifier(normalizeType(col.type))} ${toMermaidIdentifier(col.fieldName)}${qualifier}${commentStr}`;
635
- }
636
598
 
637
599
  // src/model/build.ts
638
600
  var FROM_ONE_OR_MORE = "}|";
@@ -655,7 +617,10 @@ function buildDocumentModel(metas, jsDocResult, title, description, onWarn) {
655
617
  const columns = model.columns.filter(
656
618
  (col) => !(col.isForeignKey && col.referencedEntity !== void 0 && hiddenClasses.has(col.referencedEntity))
657
619
  );
658
- const visibleModel = columns.length === model.columns.length ? model : { ...model, columns };
620
+ const visibleModel = removeHiddenEntityReferences(
621
+ columns.length === model.columns.length ? model : { ...model, columns },
622
+ hiddenClasses
623
+ );
659
624
  const ownPropDocs = jsDocResult.props.get(model.className) ?? /* @__PURE__ */ new Map();
660
625
  const propDocs = withEmbeddedPropDocs(ownPropDocs, visibleModel.columns, jsDocResult.props);
661
626
  enrichedByClass.set(model.className, { model: visibleModel, jsDoc, propDocs });
@@ -706,6 +671,14 @@ function buildDocumentModel(metas, jsDocResult, title, description, onWarn) {
706
671
  });
707
672
  return { title, groups, ...description !== void 0 && { description } };
708
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
+ }
709
682
  function withEmbeddedPropDocs(ownPropDocs, columns, allPropDocs) {
710
683
  const merged = new Map(ownPropDocs);
711
684
  for (const col of columns) {
@@ -817,6 +790,144 @@ async function withTsMorphMetadataProvider(options, onWarn) {
817
790
  }
818
791
  }
819
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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
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
+
820
931
  // src/render/markdown.ts
821
932
  function renderMarkdown(docModel, mermaid) {
822
933
  const sections = [`# ${escapeMarkdownInline(docModel.title)}`];
@@ -840,7 +951,7 @@ function renderBulletSection(header, items, renderItem) {
840
951
  }
841
952
  function renderTableOfContents(groups, anchors) {
842
953
  return renderBulletSection("## Contents", groups, (group) => {
843
- const label = escapeMarkdownInline(group.name).replace(/[[\]]/g, "\\$&");
954
+ const label = escapeMarkdownInline(group.name);
844
955
  return `- [${label}](#${anchors.get(group) ?? toMarkdownAnchor(group.name)})`;
845
956
  });
846
957
  }
@@ -925,7 +1036,7 @@ function resolveColumnKey(col) {
925
1036
  return "PK";
926
1037
  }
927
1038
  if (col.isForeignKey) {
928
- return fkKey;
1039
+ return col.isUnique ? `${fkKey}, UK` : fkKey;
929
1040
  }
930
1041
  if (col.isUnique) {
931
1042
  return "UK";
@@ -1038,6 +1149,12 @@ async function generateMarkdown(options) {
1038
1149
  }
1039
1150
 
1040
1151
  // src/cli.ts
1152
+ var activeTsxUnregister;
1153
+ async function unregisterActiveTsxLoader() {
1154
+ const unregister = activeTsxUnregister;
1155
+ activeTsxUnregister = void 0;
1156
+ await unregister?.();
1157
+ }
1041
1158
  function toConfigImportSpecifier(configPath) {
1042
1159
  return (0, import_node_url.pathToFileURL)(path2.resolve(configPath)).href;
1043
1160
  }
@@ -1055,64 +1172,78 @@ function findNearestTsconfig(fromPath) {
1055
1172
  dir = parent;
1056
1173
  }
1057
1174
  }
1058
- async function loadOrmOptions(configPath, tsconfigPath) {
1175
+ async function loadOrmOptions(configPath, tsconfigPath, loadOptions = {}) {
1059
1176
  const isTypeScriptConfig = configPath.endsWith(".ts");
1060
- if (isTypeScriptConfig) {
1061
- let register;
1062
- try {
1063
- ({ register } = await import("tsx/esm/api"));
1064
- } catch {
1065
- throw new Error('TypeScript config files require the "tsx" package.\nInstall it with: npm install -D tsx');
1066
- }
1067
- let tsconfig;
1068
- if (tsconfigPath !== void 0) {
1069
- tsconfig = path2.resolve(tsconfigPath);
1070
- if (!syncFs.existsSync(tsconfig)) {
1071
- throw new Error(`--tsconfig file not found: ${tsconfig}`);
1072
- }
1073
- } else {
1074
- tsconfig = findNearestTsconfig(configPath);
1075
- }
1076
- register(tsconfig !== void 0 ? { tsconfig } : {});
1077
- }
1078
- const configUrl = toConfigImportSpecifier(configPath);
1079
- let mod;
1177
+ let unregisterTsx;
1178
+ let shouldKeepTsxRegistered = false;
1080
1179
  try {
1081
- mod = await import(
1082
- /* @vite-ignore */
1083
- configUrl
1084
- );
1085
- } catch (cause) {
1086
1180
  if (isTypeScriptConfig) {
1087
- const detail = cause instanceof Error ? cause.message : String(cause);
1088
- throw new Error(
1089
- `Failed to load TypeScript config.
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.
1090
1211
  ${detail}
1091
1212
 
1092
1213
  If this looks like a decorator/metadata error, the tsconfig applied to your entity files is likely missing "experimentalDecorators" / "emitDecoratorMetadata".
1093
1214
  Make sure a tsconfig.json with those options sits next to your config file, or pass one explicitly with --tsconfig <path>.`,
1094
- { cause }
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)."
1095
1227
  );
1096
1228
  }
1097
- throw cause;
1098
- }
1099
- if (mod.default === void 0) {
1100
- throw new Error("Config file must use a default export, e.g. `export default defineConfig({ ... })`.");
1101
- }
1102
- const config = mod.default;
1103
- if (typeof config === "function" || config instanceof Promise) {
1104
- throw new Error(
1105
- "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)."
1106
- );
1107
- }
1108
- if (typeof config !== "object" || config === null || Array.isArray(config)) {
1109
- throw new Error(
1110
- "Config file default export must be a configuration object, not a primitive value or array.\nExport a plain MikroORM options object instead."
1111
- );
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
+ }
1112
1246
  }
1113
- const options = config;
1114
- const withPreferTs = isTypeScriptConfig && options.preferTs === void 0 ? { ...options, preferTs: true } : options;
1115
- return withPreferTs;
1116
1247
  }
1117
1248
  function formatErrorChain(err) {
1118
1249
  const lines = [];
@@ -1151,20 +1282,6 @@ ${formatFileSystemError(cause)}`, { cause });
1151
1282
  }
1152
1283
  function parseMermaidOptions(opts) {
1153
1284
  const { mermaidLayout, mermaidTheme } = opts;
1154
- if (mermaidLayout !== void 0 && !MERMAID_LAYOUTS.includes(mermaidLayout)) {
1155
- process.stderr.write(
1156
- `Error: Invalid --mermaid-layout "${mermaidLayout}". Allowed values: ${MERMAID_LAYOUTS.join(", ")}
1157
- `
1158
- );
1159
- process.exit(1);
1160
- }
1161
- if (mermaidTheme !== void 0 && !MERMAID_THEMES.includes(mermaidTheme)) {
1162
- process.stderr.write(
1163
- `Error: Invalid --mermaid-theme "${mermaidTheme}". Allowed values: ${MERMAID_THEMES.join(", ")}
1164
- `
1165
- );
1166
- process.exit(1);
1167
- }
1168
1285
  if (mermaidLayout === void 0 && mermaidTheme === void 0) {
1169
1286
  return void 0;
1170
1287
  }
@@ -1179,7 +1296,7 @@ async function run(opts) {
1179
1296
  const mermaid = parseMermaidOptions(opts);
1180
1297
  let ormOptions;
1181
1298
  try {
1182
- ormOptions = await loadOrmOptions(configPath, opts.tsconfig);
1299
+ ormOptions = await loadOrmOptions(configPath, opts.tsconfig, { keepTsxRegistered: true });
1183
1300
  } catch (err) {
1184
1301
  process.stderr.write(
1185
1302
  `Error: Cannot load config: ${configPath}
@@ -1200,10 +1317,12 @@ ${err instanceof Error ? err.message : String(err)}
1200
1317
  `)
1201
1318
  });
1202
1319
  } catch (err) {
1320
+ await unregisterActiveTsxLoader();
1203
1321
  process.stderr.write(`Error: ${formatDiscoveryError(err)}
1204
1322
  `);
1205
1323
  process.exit(1);
1206
1324
  }
1325
+ await unregisterActiveTsxLoader();
1207
1326
  try {
1208
1327
  await writeMarkdownFile(outPath, markdown);
1209
1328
  } catch (err) {
@@ -1220,7 +1339,17 @@ var program = new import_commander.Command().name("mikro-orm-markdown").descript
1220
1339
  ).option(
1221
1340
  "--src <paths...>",
1222
1341
  "Source .ts file globs to read JSDoc from when entities run from compiled .js (comments are stripped at build time)"
1223
- ).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);
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);
1224
1353
  function isDirectCliExecution() {
1225
1354
  const entryPoint = process.argv[1];
1226
1355
  if (entryPoint === void 0) {