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.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: "",
@@ -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 = columns.length === model.columns.length ? model : { ...model, columns };
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, "&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
+
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).replace(/[[\]]/g, "\\$&");
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
- if (isTypeScriptConfig) {
1055
- let register;
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
- const detail = cause instanceof Error ? cause.message : String(cause);
1082
- throw new Error(
1083
- `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.
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
- { 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)."
1089
1227
  );
1090
1228
  }
1091
- throw cause;
1092
- }
1093
- if (mod.default === void 0) {
1094
- throw new Error("Config file must use a default export, e.g. `export default defineConfig({ ... })`.");
1095
- }
1096
- const config = mod.default;
1097
- if (typeof config === "function" || config instanceof Promise) {
1098
- throw new Error(
1099
- "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)."
1100
- );
1101
- }
1102
- if (typeof config !== "object" || config === null || Array.isArray(config)) {
1103
- throw new Error(
1104
- "Config file default export must be a configuration object, not a primitive value or array.\nExport a plain MikroORM options object instead."
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
- ).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);
1218
1353
  function isDirectCliExecution() {
1219
1354
  const entryPoint = process.argv[1];
1220
1355
  if (entryPoint === void 0) {