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/index.cjs CHANGED
@@ -76,17 +76,7 @@ function loadJsDoc(filePaths, onWarn) {
76
76
  if (classDocs.length > 0) {
77
77
  entities.set(className, parseEntityJsDoc(classDocs));
78
78
  }
79
- const propMap = /* @__PURE__ */ new Map();
80
- for (const prop of cls.getProperties()) {
81
- const propDocs = prop.getJsDocs();
82
- if (propDocs.length === 0) {
83
- continue;
84
- }
85
- const info = parsePropJsDoc(propDocs);
86
- if (info.description !== void 0 || info.atLeastOne) {
87
- propMap.set(prop.getName(), info);
88
- }
89
- }
79
+ const propMap = collectPropJsDocs(cls);
90
80
  if (propMap.size > 0) {
91
81
  props.set(className, propMap);
92
82
  }
@@ -103,6 +93,28 @@ function formatUnknownError(err) {
103
93
  function hasGlobPattern(filePath) {
104
94
  return /[*?[\]{}]/.test(filePath);
105
95
  }
96
+ function collectPropJsDocs(cls) {
97
+ const propMap = /* @__PURE__ */ new Map();
98
+ for (const prop of [...cls.getProperties(), ...cls.getGetAccessors()]) {
99
+ const info = parsePropJsDoc(prop.getJsDocs());
100
+ addPropInfo(propMap, prop.getName(), info);
101
+ }
102
+ for (const prop of getConstructorParameterProperties(cls)) {
103
+ const info = parseCompilerPropJsDoc(import_ts_morph.ts.getJSDocCommentsAndTags(prop.compilerNode));
104
+ addPropInfo(propMap, prop.getName(), info);
105
+ }
106
+ return propMap;
107
+ }
108
+ function getConstructorParameterProperties(cls) {
109
+ return cls.getConstructors().flatMap(
110
+ (constructorDeclaration) => constructorDeclaration.getParameters().filter((param) => param.isParameterProperty())
111
+ );
112
+ }
113
+ function addPropInfo(propMap, propName, info) {
114
+ if (info.description !== void 0 || info.atLeastOne) {
115
+ propMap.set(propName, info);
116
+ }
117
+ }
106
118
  function parseEntityJsDoc(jsDocs) {
107
119
  const namespaces = [];
108
120
  const erdNamespaces = [];
@@ -152,6 +164,35 @@ function parsePropJsDoc(jsDocs) {
152
164
  }
153
165
  return { ...description !== void 0 && { description }, atLeastOne };
154
166
  }
167
+ function parseCompilerPropJsDoc(jsDocs) {
168
+ let description;
169
+ let atLeastOne = false;
170
+ for (const doc of jsDocs) {
171
+ if (import_ts_morph.ts.isJSDoc(doc)) {
172
+ const desc = formatCompilerJsDocComment(doc.comment);
173
+ if (desc && description === void 0) {
174
+ description = desc;
175
+ }
176
+ for (const tag of doc.tags ?? []) {
177
+ if (tag.tagName.getText() === "atLeastOne") {
178
+ atLeastOne = true;
179
+ }
180
+ }
181
+ continue;
182
+ }
183
+ if (doc.tagName.getText() === "atLeastOne") {
184
+ atLeastOne = true;
185
+ }
186
+ }
187
+ return { ...description !== void 0 && { description }, atLeastOne };
188
+ }
189
+ function formatCompilerJsDocComment(comment) {
190
+ if (comment === void 0) {
191
+ return void 0;
192
+ }
193
+ const trimmed = import_ts_morph.ts.getTextOfJSDocComment(comment)?.trim();
194
+ return trimmed === void 0 || trimmed === "" ? void 0 : trimmed;
195
+ }
155
196
 
156
197
  // src/metadata/load.ts
157
198
  var path = __toESM(require("path"), 1);
@@ -191,6 +232,44 @@ function assertNoEntitySchemaEntities(options) {
191
232
  Use decorator-based @Entity() classes instead.`
192
233
  );
193
234
  }
235
+ function hasDecoratorMarker(target) {
236
+ const pathSymbol = import_core.MetadataStorage.PATH_SYMBOL;
237
+ if (typeof pathSymbol === "symbol" && pathSymbol in target) {
238
+ return true;
239
+ }
240
+ return "__path" in target;
241
+ }
242
+ function isRenderableMeta(meta) {
243
+ return !meta.pivotTable && !meta.embeddable;
244
+ }
245
+ function assertDiscoveredEntitiesAreSupported(metas) {
246
+ const confirmed = [];
247
+ const unconfirmed = [];
248
+ for (const meta of metas) {
249
+ if (!isRenderableMeta(meta)) {
250
+ continue;
251
+ }
252
+ if (import_core.EntitySchema.REGISTRY.has(meta.class)) {
253
+ confirmed.push(meta.className);
254
+ } else if (!hasDecoratorMarker(meta.class)) {
255
+ unconfirmed.push(meta.className);
256
+ }
257
+ }
258
+ if (confirmed.length === 0 && unconfirmed.length === 0) {
259
+ return;
260
+ }
261
+ const lines = [];
262
+ if (confirmed.length > 0) {
263
+ lines.push(`EntitySchema-defined entities are not currently supported: ${confirmed.join(", ")}.`);
264
+ }
265
+ if (unconfirmed.length > 0) {
266
+ lines.push(
267
+ `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`
268
+ );
269
+ }
270
+ lines.push("Use decorator-based @Entity() classes instead.");
271
+ throw new MetadataLoadError(lines.join("\n"));
272
+ }
194
273
  async function loadEntityMetadata(options) {
195
274
  assertNoEntitySchemaEntities(options);
196
275
  let orm;
@@ -217,6 +296,7 @@ async function loadEntityMetadata(options) {
217
296
  "No entities were discovered. Check that your config specifies at least one entity path or class."
218
297
  );
219
298
  }
299
+ assertDiscoveredEntitiesAreSupported(all);
220
300
  const baseDir = orm.config.get("baseDir");
221
301
  const sourcePaths = [...new Set(all.filter((m) => m.path).map((m) => path.resolve(baseDir, m.path)))];
222
302
  return { metas: all, sourcePaths };
@@ -228,55 +308,8 @@ async function loadEntityMetadata(options) {
228
308
  // src/model/build.ts
229
309
  var import_core3 = require("@mikro-orm/core");
230
310
 
231
- // src/render/mermaid.ts
311
+ // src/model/diagram.ts
232
312
  var import_core2 = require("@mikro-orm/core");
233
-
234
- // src/render/escape.ts
235
- var MARKDOWN_INLINE_SPECIAL_CHARS = /[\\`|*#]/g;
236
- var MERMAID_IDENTIFIER_INVALID_CHARS = /[^a-zA-Z0-9_]/g;
237
- function normalizeInlineText(value) {
238
- return value.replace(/\r\n?/g, "\n").replace(/[\n\t]+/g, " ").replace(/\s{2,}/g, " ").trim();
239
- }
240
- function splitNormalizedLines(value) {
241
- return value.replace(/\r\n?/g, "\n").split("\n");
242
- }
243
- function escapeHtmlText(value) {
244
- return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
245
- }
246
- function escapeMarkdownInline(value) {
247
- return escapeHtmlText(normalizeInlineText(value)).replace(MARKDOWN_INLINE_SPECIAL_CHARS, "\\$&");
248
- }
249
- function escapeMarkdownTableCell(value) {
250
- return splitNormalizedLines(value).map((line) => escapeMarkdownInline(line)).join("<br>");
251
- }
252
- function escapeMarkdownParagraph(value) {
253
- return splitNormalizedLines(value).map((line) => escapeMarkdownInline(line)).join(" \n");
254
- }
255
- function renderMarkdownBlockQuote(value) {
256
- return splitNormalizedLines(value).map((line) => `> ${escapeMarkdownInline(line)}`).join("\n");
257
- }
258
- function renderMarkdownInlineCode(value) {
259
- const normalized = normalizeInlineText(value);
260
- const backtickRuns = normalized.match(/`+/g) ?? [];
261
- const longestRun = Math.max(0, ...backtickRuns.map((run) => run.length));
262
- const fence = "`".repeat(longestRun + 1);
263
- const needsPadding = normalized.startsWith("`") || normalized.endsWith("`");
264
- const content = needsPadding ? ` ${normalized} ` : normalized;
265
- return `${fence}${content}${fence}`;
266
- }
267
- function toMermaidIdentifier(value) {
268
- const normalized = normalizeInlineText(value).replace(MERMAID_IDENTIFIER_INVALID_CHARS, "_").replace(/_+/g, "_");
269
- const identifier = normalized === "" ? "_" : normalized;
270
- return /^[a-zA-Z_]/.test(identifier) ? identifier : `_${identifier}`;
271
- }
272
- function escapeMermaidQuotedText(value) {
273
- return normalizeInlineText(value).replace(/\\/g, "\\\\").replace(/"/g, '\\"');
274
- }
275
- function toMarkdownAnchor(value) {
276
- return normalizeInlineText(value).toLowerCase().replace(/[^\p{L}\p{N}\s_-]/gu, "").replace(/\s+/g, "-");
277
- }
278
-
279
- // src/render/mermaid.ts
280
313
  var FORMULA_DUMMY_TABLE = {
281
314
  alias: "e0",
282
315
  name: "",
@@ -547,75 +580,6 @@ function buildEdge(fromEntity, prop) {
547
580
  }
548
581
  return null;
549
582
  }
550
- function normalizeType(type) {
551
- const t = type.toLowerCase().trim();
552
- if (t === "uuid" || t === "text" || t === "string" || t.startsWith("varchar")) {
553
- return "string";
554
- }
555
- if (t === "timestamptz" || t === "timestamp" || t === "datetime") {
556
- return "datetime";
557
- }
558
- if (t === "integer" || t === "int" || t === "bigint" || t === "smallint") {
559
- return "integer";
560
- }
561
- if (t === "doubletype" || t === "double precision" || t === "double" || t === "float" || t === "decimal") {
562
- return "float";
563
- }
564
- if (t === "boolean" || t === "bool") {
565
- return "boolean";
566
- }
567
- if (t === "jsonb") {
568
- return "json";
569
- }
570
- return type;
571
- }
572
- function renderErDiagram(model, mermaid) {
573
- const lines = [];
574
- if (mermaid?.layout !== void 0 || mermaid?.theme !== void 0) {
575
- lines.push("---", "config:");
576
- if (mermaid.layout !== void 0) {
577
- lines.push(` layout: ${mermaid.layout}`);
578
- }
579
- if (mermaid.theme !== void 0) {
580
- lines.push(` theme: ${mermaid.theme}`);
581
- }
582
- lines.push("---");
583
- }
584
- lines.push("erDiagram");
585
- for (const entity of model.entities) {
586
- lines.push(` ${toMermaidIdentifier(entity.className)} {`);
587
- for (const col of entity.columns) {
588
- lines.push(` ${renderColumnLine(col)}`);
589
- }
590
- lines.push(" }");
591
- }
592
- for (const rel of model.relations) {
593
- lines.push(
594
- ` ${toMermaidIdentifier(rel.fromEntity)} ${rel.fromCardinality}--${rel.toCardinality} ${toMermaidIdentifier(rel.toEntity)} : "${escapeMermaidQuotedText(rel.label)}"`
595
- );
596
- }
597
- return lines.join("\n");
598
- }
599
- function renderColumnLine(col) {
600
- let qualifier = "";
601
- if (col.isPrimary) {
602
- qualifier = " PK";
603
- } else if (col.isUnique) {
604
- qualifier = " UK";
605
- }
606
- let comment;
607
- if (col.formula !== void 0) {
608
- comment = col.formula ? `formula: ${col.formula}` : "formula";
609
- } else if (col.isDiscriminator) {
610
- comment = "discriminator";
611
- } else if (col.embeddedIn !== void 0) {
612
- comment = `[${col.embeddedIn}]`;
613
- } else if (col.isSelfReference) {
614
- comment = "self-ref";
615
- }
616
- const commentStr = comment !== void 0 ? ` "${escapeMermaidQuotedText(comment)}"` : "";
617
- return `${toMermaidIdentifier(normalizeType(col.type))} ${toMermaidIdentifier(col.fieldName)}${qualifier}${commentStr}`;
618
- }
619
583
 
620
584
  // src/model/build.ts
621
585
  var FROM_ONE_OR_MORE = "}|";
@@ -638,7 +602,10 @@ function buildDocumentModel(metas, jsDocResult, title, description, onWarn) {
638
602
  const columns = model.columns.filter(
639
603
  (col) => !(col.isForeignKey && col.referencedEntity !== void 0 && hiddenClasses.has(col.referencedEntity))
640
604
  );
641
- const visibleModel = columns.length === model.columns.length ? model : { ...model, columns };
605
+ const visibleModel = removeHiddenEntityReferences(
606
+ columns.length === model.columns.length ? model : { ...model, columns },
607
+ hiddenClasses
608
+ );
642
609
  const ownPropDocs = jsDocResult.props.get(model.className) ?? /* @__PURE__ */ new Map();
643
610
  const propDocs = withEmbeddedPropDocs(ownPropDocs, visibleModel.columns, jsDocResult.props);
644
611
  enrichedByClass.set(model.className, { model: visibleModel, jsDoc, propDocs });
@@ -689,6 +656,14 @@ function buildDocumentModel(metas, jsDocResult, title, description, onWarn) {
689
656
  });
690
657
  return { title, groups, ...description !== void 0 && { description } };
691
658
  }
659
+ function removeHiddenEntityReferences(model, hiddenClasses) {
660
+ if (model.extendsEntity === void 0 || !hiddenClasses.has(model.extendsEntity)) {
661
+ return model;
662
+ }
663
+ const visibleModel = { ...model };
664
+ delete visibleModel.extendsEntity;
665
+ return visibleModel;
666
+ }
692
667
  function withEmbeddedPropDocs(ownPropDocs, columns, allPropDocs) {
693
668
  const merged = new Map(ownPropDocs);
694
669
  for (const col of columns) {
@@ -800,6 +775,142 @@ async function withTsMorphMetadataProvider(options, onWarn) {
800
775
  }
801
776
  }
802
777
 
778
+ // src/render/escape.ts
779
+ var MARKDOWN_INLINE_SPECIAL_CHARS = /[\\`|*#\[\]]/g;
780
+ var MARKDOWN_EMPHASIS_UNDERSCORE = /(?<![a-zA-Z0-9])_|_(?![a-zA-Z0-9])/g;
781
+ var MERMAID_IDENTIFIER_INVALID_CHARS = /[^a-zA-Z0-9_]/g;
782
+ function normalizeInlineText(value) {
783
+ return value.replace(/\r\n?/g, "\n").replace(/[\n\t]+/g, " ").replace(/\s{2,}/g, " ").trim();
784
+ }
785
+ function splitNormalizedLines(value) {
786
+ return value.replace(/\r\n?/g, "\n").split("\n");
787
+ }
788
+ function escapeHtmlText(value) {
789
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
790
+ }
791
+ function escapeMarkdownInline(value) {
792
+ return escapeHtmlText(normalizeInlineText(value)).replace(MARKDOWN_INLINE_SPECIAL_CHARS, "\\$&").replace(MARKDOWN_EMPHASIS_UNDERSCORE, "\\_");
793
+ }
794
+ function escapeMarkdownTableCell(value) {
795
+ return splitNormalizedLines(value).map((line) => escapeMarkdownInline(line)).join("<br>");
796
+ }
797
+ function escapeMarkdownParagraph(value) {
798
+ return splitNormalizedLines(value).map((line) => escapeMarkdownInline(line)).join(" \n");
799
+ }
800
+ function renderMarkdownBlockQuote(value) {
801
+ return splitNormalizedLines(value).map((line) => `> ${escapeMarkdownInline(line)}`).join("\n");
802
+ }
803
+ function renderMarkdownInlineCode(value) {
804
+ const normalized = normalizeInlineText(value);
805
+ const backtickRuns = normalized.match(/`+/g) ?? [];
806
+ const longestRun = Math.max(0, ...backtickRuns.map((run) => run.length));
807
+ const fence = "`".repeat(longestRun + 1);
808
+ const needsPadding = normalized.startsWith("`") || normalized.endsWith("`");
809
+ const content = needsPadding ? ` ${normalized} ` : normalized;
810
+ return `${fence}${content}${fence}`;
811
+ }
812
+ function toMermaidIdentifier(value) {
813
+ const normalized = normalizeInlineText(value).replace(MERMAID_IDENTIFIER_INVALID_CHARS, "_").replace(/_+/g, "_");
814
+ const identifier = normalized === "" ? "_" : normalized;
815
+ return /^[a-zA-Z_]/.test(identifier) ? identifier : `_${identifier}`;
816
+ }
817
+ function escapeMermaidQuotedText(value) {
818
+ return normalizeInlineText(value).replace(/\\/g, "\\\\").replace(/"/g, '\\"');
819
+ }
820
+ function toMarkdownAnchor(value) {
821
+ return normalizeInlineText(value).toLowerCase().replace(/[^\p{L}\p{N}\s_-]/gu, "").replace(/\s+/g, "-");
822
+ }
823
+
824
+ // src/render/mermaid.ts
825
+ var GENERIC_TYPE_BY_BASE_NAME = /* @__PURE__ */ new Map([
826
+ ["uuid", "string"],
827
+ ["text", "string"],
828
+ ["string", "string"],
829
+ ["varchar", "string"],
830
+ ["character varying", "string"],
831
+ ["character", "string"],
832
+ ["char", "string"],
833
+ ["tinytext", "string"],
834
+ ["mediumtext", "string"],
835
+ ["longtext", "string"],
836
+ ["timestamptz", "datetime"],
837
+ ["timestamp", "datetime"],
838
+ ["datetime", "datetime"],
839
+ ["integer", "integer"],
840
+ ["int", "integer"],
841
+ ["bigint", "integer"],
842
+ ["smallint", "integer"],
843
+ ["tinyint", "integer"],
844
+ ["mediumint", "integer"],
845
+ ["serial", "integer"],
846
+ ["bigserial", "integer"],
847
+ ["doubletype", "float"],
848
+ ["double precision", "float"],
849
+ ["double", "float"],
850
+ ["float", "float"],
851
+ ["decimal", "float"],
852
+ ["numeric", "float"],
853
+ ["real", "float"],
854
+ ["boolean", "boolean"],
855
+ ["bool", "boolean"],
856
+ ["jsonb", "json"]
857
+ ]);
858
+ function normalizeType(type) {
859
+ const t = type.toLowerCase().replace(/\s+/g, " ").trim();
860
+ if (/^tinyint\s*\(\s*1\s*\)$/.test(t)) {
861
+ return "boolean";
862
+ }
863
+ const baseName = t.replace(/\(.*\)/, "").replace(/\b(un)?signed\b/, "").trim();
864
+ return GENERIC_TYPE_BY_BASE_NAME.get(baseName) ?? type;
865
+ }
866
+ function renderErDiagram(model, mermaid) {
867
+ const lines = [];
868
+ if (mermaid?.layout !== void 0 || mermaid?.theme !== void 0) {
869
+ lines.push("---", "config:");
870
+ if (mermaid.layout !== void 0) {
871
+ lines.push(` layout: ${mermaid.layout}`);
872
+ }
873
+ if (mermaid.theme !== void 0) {
874
+ lines.push(` theme: ${mermaid.theme}`);
875
+ }
876
+ lines.push("---");
877
+ }
878
+ lines.push("erDiagram");
879
+ for (const entity of model.entities) {
880
+ lines.push(` ${toMermaidIdentifier(entity.className)} {`);
881
+ for (const col of entity.columns) {
882
+ lines.push(` ${renderColumnLine(col)}`);
883
+ }
884
+ lines.push(" }");
885
+ }
886
+ for (const rel of model.relations) {
887
+ lines.push(
888
+ ` ${toMermaidIdentifier(rel.fromEntity)} ${rel.fromCardinality}--${rel.toCardinality} ${toMermaidIdentifier(rel.toEntity)} : "${escapeMermaidQuotedText(rel.label)}"`
889
+ );
890
+ }
891
+ return lines.join("\n");
892
+ }
893
+ function renderColumnLine(col) {
894
+ let qualifier = "";
895
+ if (col.isPrimary) {
896
+ qualifier = " PK";
897
+ } else if (col.isUnique) {
898
+ qualifier = " UK";
899
+ }
900
+ let comment;
901
+ if (col.formula !== void 0) {
902
+ comment = col.formula ? `formula: ${col.formula}` : "formula";
903
+ } else if (col.isDiscriminator) {
904
+ comment = "discriminator";
905
+ } else if (col.embeddedIn !== void 0) {
906
+ comment = `[${col.embeddedIn}]`;
907
+ } else if (col.isSelfReference) {
908
+ comment = "self-ref";
909
+ }
910
+ const commentStr = comment !== void 0 ? ` "${escapeMermaidQuotedText(comment)}"` : "";
911
+ return `${toMermaidIdentifier(normalizeType(col.type))} ${toMermaidIdentifier(col.fieldName)}${qualifier}${commentStr}`;
912
+ }
913
+
803
914
  // src/render/markdown.ts
804
915
  function renderMarkdown(docModel, mermaid) {
805
916
  const sections = [`# ${escapeMarkdownInline(docModel.title)}`];
@@ -823,7 +934,7 @@ function renderBulletSection(header, items, renderItem) {
823
934
  }
824
935
  function renderTableOfContents(groups, anchors) {
825
936
  return renderBulletSection("## Contents", groups, (group) => {
826
- const label = escapeMarkdownInline(group.name).replace(/[[\]]/g, "\\$&");
937
+ const label = escapeMarkdownInline(group.name);
827
938
  return `- [${label}](#${anchors.get(group) ?? toMarkdownAnchor(group.name)})`;
828
939
  });
829
940
  }
@@ -908,7 +1019,7 @@ function resolveColumnKey(col) {
908
1019
  return "PK";
909
1020
  }
910
1021
  if (col.isForeignKey) {
911
- return fkKey;
1022
+ return col.isUnique ? `${fkKey}, UK` : fkKey;
912
1023
  }
913
1024
  if (col.isUnique) {
914
1025
  return "UK";