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/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: "",
@@ -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 = columns.length === model.columns.length ? model : { ...model, columns };
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, "&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
+
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).replace(/[[\]]/g, "\\$&");
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
- if (isTypeScriptConfig) {
1017
- let register;
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
- const detail = cause instanceof Error ? cause.message : String(cause);
1044
- throw new Error(
1045
- `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.
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
- { 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)."
1051
1183
  );
1052
1184
  }
1053
- throw cause;
1054
- }
1055
- if (mod.default === void 0) {
1056
- throw new Error("Config file must use a default export, e.g. `export default defineConfig({ ... })`.");
1057
- }
1058
- const config = mod.default;
1059
- if (typeof config === "function" || config instanceof Promise) {
1060
- throw new Error(
1061
- "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)."
1062
- );
1063
- }
1064
- if (typeof config !== "object" || config === null || Array.isArray(config)) {
1065
- throw new Error(
1066
- "Config file default export must be a configuration object, not a primitive value or array.\nExport a plain MikroORM options object instead."
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
- ).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);
1180
1309
  function isDirectCliExecution() {
1181
1310
  const entryPoint = process.argv[1];
1182
1311
  if (entryPoint === void 0) {