mikro-orm-markdown 0.1.0-alpha.3 → 0.1.0-alpha.5
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 +22 -0
- package/README.ko.md +53 -0
- package/README.md +53 -0
- package/dist/cli.cjs +111 -15
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +111 -15
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +82 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.js +82 -14
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/cli.js
CHANGED
|
@@ -237,6 +237,8 @@ function toMarkdownAnchor(value) {
|
|
|
237
237
|
}
|
|
238
238
|
|
|
239
239
|
// src/render/mermaid.ts
|
|
240
|
+
var MERMAID_LAYOUTS = ["dagre", "elk", "elk.stress"];
|
|
241
|
+
var MERMAID_THEMES = ["default", "neutral", "dark", "forest", "base"];
|
|
240
242
|
var FORMULA_DUMMY_TABLE = {
|
|
241
243
|
alias: "e0",
|
|
242
244
|
name: "",
|
|
@@ -375,9 +377,31 @@ function resolveFkTypes(prop, metaByClass, fieldNameCount) {
|
|
|
375
377
|
return Array.from({ length: fieldNameCount }, (_value, index) => {
|
|
376
378
|
const referencedColumnName = referencedColumnNames[index];
|
|
377
379
|
const pkProp = referencedColumnName !== void 0 ? primaryProps.find((candidate) => candidate.fieldNames.includes(referencedColumnName)) : void 0;
|
|
378
|
-
|
|
380
|
+
const resolvedProp = pkProp ?? primaryProps[index] ?? primaryProps[0];
|
|
381
|
+
const rawType = resolvedProp?.type ?? "integer";
|
|
382
|
+
const pkIndex = resolvedProp !== void 0 ? primaryProps.indexOf(resolvedProp) : 0;
|
|
383
|
+
return resolveScalarType(rawType, metaByClass, pkIndex);
|
|
379
384
|
});
|
|
380
385
|
}
|
|
386
|
+
function resolveScalarType(type, metaByClass, pkIndex = 0, depth = 0) {
|
|
387
|
+
if (depth >= 5) {
|
|
388
|
+
return "integer";
|
|
389
|
+
}
|
|
390
|
+
const refMeta = metaByClass.get(type);
|
|
391
|
+
if (!refMeta) {
|
|
392
|
+
return type;
|
|
393
|
+
}
|
|
394
|
+
const primaryProps = getPrimaryProps(refMeta);
|
|
395
|
+
if (primaryProps.length === 0) {
|
|
396
|
+
return type;
|
|
397
|
+
}
|
|
398
|
+
const targetProp = primaryProps[pkIndex] ?? primaryProps[0];
|
|
399
|
+
const nextType = targetProp?.type ?? type;
|
|
400
|
+
if (nextType === type) {
|
|
401
|
+
return type;
|
|
402
|
+
}
|
|
403
|
+
return resolveScalarType(nextType, metaByClass, pkIndex, depth + 1);
|
|
404
|
+
}
|
|
381
405
|
function getPrimaryProps(meta) {
|
|
382
406
|
const primaryKeys = meta.primaryKeys ?? [];
|
|
383
407
|
const orderedPrimaryProps = primaryKeys.map((key) => meta.properties[String(key)]).filter((prop) => prop !== void 0);
|
|
@@ -479,8 +503,41 @@ function buildEdge(fromEntity, prop) {
|
|
|
479
503
|
}
|
|
480
504
|
return null;
|
|
481
505
|
}
|
|
482
|
-
function
|
|
483
|
-
const
|
|
506
|
+
function normalizeType(type) {
|
|
507
|
+
const t = type.toLowerCase().trim();
|
|
508
|
+
if (t === "uuid" || t === "text" || t === "string" || t.startsWith("varchar")) {
|
|
509
|
+
return "string";
|
|
510
|
+
}
|
|
511
|
+
if (t === "timestamptz" || t === "timestamp" || t === "datetime") {
|
|
512
|
+
return "datetime";
|
|
513
|
+
}
|
|
514
|
+
if (t === "integer" || t === "int" || t === "bigint" || t === "smallint") {
|
|
515
|
+
return "integer";
|
|
516
|
+
}
|
|
517
|
+
if (t === "doubletype" || t === "double precision" || t === "double" || t === "float" || t === "decimal") {
|
|
518
|
+
return "float";
|
|
519
|
+
}
|
|
520
|
+
if (t === "boolean" || t === "bool") {
|
|
521
|
+
return "boolean";
|
|
522
|
+
}
|
|
523
|
+
if (t === "jsonb") {
|
|
524
|
+
return "json";
|
|
525
|
+
}
|
|
526
|
+
return type;
|
|
527
|
+
}
|
|
528
|
+
function renderErDiagram(model, mermaid) {
|
|
529
|
+
const lines = [];
|
|
530
|
+
if (mermaid?.layout !== void 0 || mermaid?.theme !== void 0) {
|
|
531
|
+
lines.push("---", "config:");
|
|
532
|
+
if (mermaid.layout !== void 0) {
|
|
533
|
+
lines.push(` layout: ${mermaid.layout}`);
|
|
534
|
+
}
|
|
535
|
+
if (mermaid.theme !== void 0) {
|
|
536
|
+
lines.push(` theme: ${mermaid.theme}`);
|
|
537
|
+
}
|
|
538
|
+
lines.push("---");
|
|
539
|
+
}
|
|
540
|
+
lines.push("erDiagram");
|
|
484
541
|
for (const entity of model.entities) {
|
|
485
542
|
lines.push(` ${toMermaidIdentifier(entity.className)} {`);
|
|
486
543
|
for (const col of entity.columns) {
|
|
@@ -513,7 +570,7 @@ function renderColumnLine(col) {
|
|
|
513
570
|
comment = "self-ref";
|
|
514
571
|
}
|
|
515
572
|
const commentStr = comment !== void 0 ? ` "${escapeMermaidQuotedText(comment)}"` : "";
|
|
516
|
-
return `${toMermaidIdentifier(col.type)} ${toMermaidIdentifier(col.fieldName)}${qualifier}${commentStr}`;
|
|
573
|
+
return `${toMermaidIdentifier(normalizeType(col.type))} ${toMermaidIdentifier(col.fieldName)}${qualifier}${commentStr}`;
|
|
517
574
|
}
|
|
518
575
|
|
|
519
576
|
// src/model/build.ts
|
|
@@ -560,9 +617,16 @@ function buildDocumentModel(metas, jsDocResult, title, description, onWarn) {
|
|
|
560
617
|
const groups = [];
|
|
561
618
|
for (const groupName of groupNames) {
|
|
562
619
|
const isDefault = groupName === "default";
|
|
563
|
-
const erdEntities = [...enrichedByClass.values()].filter(
|
|
564
|
-
|
|
565
|
-
|
|
620
|
+
const erdEntities = [...enrichedByClass.values()].filter(({ jsDoc }) => belongsToGroupForErd(jsDoc, groupName, isDefault)).map((entity) => {
|
|
621
|
+
if (isCrossNamespaceInGroup(entity.jsDoc, groupName, isDefault)) {
|
|
622
|
+
const pkColumns = entity.model.columns.filter((col) => col.isPrimary);
|
|
623
|
+
if (pkColumns.length === 0) {
|
|
624
|
+
return null;
|
|
625
|
+
}
|
|
626
|
+
return { ...entity, model: { ...entity.model, columns: pkColumns } };
|
|
627
|
+
}
|
|
628
|
+
return entity;
|
|
629
|
+
}).filter((entity) => entity !== null);
|
|
566
630
|
const textEntities = [...enrichedByClass.values()].filter(
|
|
567
631
|
({ jsDoc }) => belongsToGroupForText(jsDoc, groupName, isDefault)
|
|
568
632
|
);
|
|
@@ -664,6 +728,12 @@ function belongsToGroupForText(jsDoc, groupName, isDefault) {
|
|
|
664
728
|
}
|
|
665
729
|
return jsDoc.namespaces.includes(groupName) || jsDoc.describeNamespaces.includes(groupName);
|
|
666
730
|
}
|
|
731
|
+
function isCrossNamespaceInGroup(jsDoc, groupName, isDefault) {
|
|
732
|
+
if (isDefault || !jsDoc) {
|
|
733
|
+
return false;
|
|
734
|
+
}
|
|
735
|
+
return jsDoc.erdNamespaces.includes(groupName) && !jsDoc.namespaces.includes(groupName) && !jsDoc.describeNamespaces.includes(groupName);
|
|
736
|
+
}
|
|
667
737
|
|
|
668
738
|
// src/provider.ts
|
|
669
739
|
async function withTsMorphMetadataProvider(options, onWarn) {
|
|
@@ -686,7 +756,7 @@ async function withTsMorphMetadataProvider(options, onWarn) {
|
|
|
686
756
|
}
|
|
687
757
|
|
|
688
758
|
// src/render/markdown.ts
|
|
689
|
-
function renderMarkdown(docModel) {
|
|
759
|
+
function renderMarkdown(docModel, mermaid) {
|
|
690
760
|
const sections = [`# ${escapeMarkdownInline(docModel.title)}`];
|
|
691
761
|
if (docModel.description) {
|
|
692
762
|
sections.push(escapeMarkdownParagraph(docModel.description));
|
|
@@ -695,7 +765,7 @@ function renderMarkdown(docModel) {
|
|
|
695
765
|
sections.push(renderTableOfContents(docModel.groups));
|
|
696
766
|
}
|
|
697
767
|
for (const group of docModel.groups) {
|
|
698
|
-
sections.push(renderGroupSection(group));
|
|
768
|
+
sections.push(renderGroupSection(group, mermaid));
|
|
699
769
|
}
|
|
700
770
|
return sections.join("\n\n");
|
|
701
771
|
}
|
|
@@ -712,14 +782,14 @@ function renderTableOfContents(groups) {
|
|
|
712
782
|
return `- [${label}](#${toMarkdownAnchor(group.name)})`;
|
|
713
783
|
});
|
|
714
784
|
}
|
|
715
|
-
function renderGroupSection(group) {
|
|
785
|
+
function renderGroupSection(group, mermaid) {
|
|
716
786
|
const parts = [`## ${escapeMarkdownInline(group.name)}`];
|
|
717
787
|
if (group.erdEntities.length > 0) {
|
|
718
788
|
const diagramModel = {
|
|
719
789
|
entities: group.erdEntities.map((e) => e.model),
|
|
720
790
|
relations: group.erdRelations
|
|
721
791
|
};
|
|
722
|
-
parts.push("```mermaid\n" + renderErDiagram(diagramModel) + "\n```");
|
|
792
|
+
parts.push("```mermaid\n" + renderErDiagram(diagramModel, mermaid) + "\n```");
|
|
723
793
|
}
|
|
724
794
|
for (const entity of group.textEntities) {
|
|
725
795
|
parts.push(renderEntitySection(entity));
|
|
@@ -761,7 +831,7 @@ function renderColumnTable(entity) {
|
|
|
761
831
|
const docDesc = entity.propDocs.get(col.propName)?.description ?? col.comment ?? "";
|
|
762
832
|
const enumDesc = col.enumItems !== void 0 ? `One of: ${col.enumItems.join(", ")}` : "";
|
|
763
833
|
const desc = [docDesc, enumDesc].filter((part) => part !== "").join("\n");
|
|
764
|
-
return `| ${escapeMarkdownTableCell(col.fieldName)} | ${escapeMarkdownTableCell(col.type)} | ${escapeMarkdownTableCell(key)} | ${nullable} | ${escapeMarkdownTableCell(desc)} |`;
|
|
834
|
+
return `| ${escapeMarkdownTableCell(col.fieldName)} | ${escapeMarkdownTableCell(normalizeType(col.type))} | ${escapeMarkdownTableCell(key)} | ${nullable} | ${escapeMarkdownTableCell(desc)} |`;
|
|
765
835
|
});
|
|
766
836
|
return [header, sep, ...rows].join("\n");
|
|
767
837
|
}
|
|
@@ -875,7 +945,7 @@ async function loadEntityMetadataWithTsMorphFallback(originalOrm, effectiveOrm)
|
|
|
875
945
|
}
|
|
876
946
|
}
|
|
877
947
|
async function generateMarkdown(options) {
|
|
878
|
-
const { orm, title = "Database Schema", description, src, onWarn } = options;
|
|
948
|
+
const { orm, title = "Database Schema", description, src, onWarn, mermaid } = options;
|
|
879
949
|
const effectiveOrm = await withTsMorphMetadataProvider(orm, onWarn);
|
|
880
950
|
const { metas, sourcePaths } = await loadEntityMetadataWithTsMorphFallback(orm, effectiveOrm);
|
|
881
951
|
const jsDocResult = loadJsDoc(resolveJsDocSources(sourcePaths, src, onWarn));
|
|
@@ -883,7 +953,7 @@ async function generateMarkdown(options) {
|
|
|
883
953
|
assertExplicitJsDocSourceCoverage(metas, jsDocResult, src, onWarn);
|
|
884
954
|
}
|
|
885
955
|
const docModel = buildDocumentModel(metas, jsDocResult, title, description, onWarn);
|
|
886
|
-
return renderMarkdown(docModel);
|
|
956
|
+
return renderMarkdown(docModel, mermaid);
|
|
887
957
|
}
|
|
888
958
|
|
|
889
959
|
// src/cli.ts
|
|
@@ -998,9 +1068,34 @@ async function writeMarkdownFile(outPath, markdown) {
|
|
|
998
1068
|
${formatFileSystemError(cause)}`, { cause });
|
|
999
1069
|
}
|
|
1000
1070
|
}
|
|
1071
|
+
function parseMermaidOptions(opts) {
|
|
1072
|
+
const { mermaidLayout, mermaidTheme } = opts;
|
|
1073
|
+
if (mermaidLayout !== void 0 && !MERMAID_LAYOUTS.includes(mermaidLayout)) {
|
|
1074
|
+
process.stderr.write(
|
|
1075
|
+
`Error: Invalid --mermaid-layout "${mermaidLayout}". Allowed values: ${MERMAID_LAYOUTS.join(", ")}
|
|
1076
|
+
`
|
|
1077
|
+
);
|
|
1078
|
+
process.exit(1);
|
|
1079
|
+
}
|
|
1080
|
+
if (mermaidTheme !== void 0 && !MERMAID_THEMES.includes(mermaidTheme)) {
|
|
1081
|
+
process.stderr.write(
|
|
1082
|
+
`Error: Invalid --mermaid-theme "${mermaidTheme}". Allowed values: ${MERMAID_THEMES.join(", ")}
|
|
1083
|
+
`
|
|
1084
|
+
);
|
|
1085
|
+
process.exit(1);
|
|
1086
|
+
}
|
|
1087
|
+
if (mermaidLayout === void 0 && mermaidTheme === void 0) {
|
|
1088
|
+
return void 0;
|
|
1089
|
+
}
|
|
1090
|
+
return {
|
|
1091
|
+
...mermaidLayout !== void 0 && { layout: mermaidLayout },
|
|
1092
|
+
...mermaidTheme !== void 0 && { theme: mermaidTheme }
|
|
1093
|
+
};
|
|
1094
|
+
}
|
|
1001
1095
|
async function run(opts) {
|
|
1002
1096
|
const configPath = path2.resolve(opts.config);
|
|
1003
1097
|
const outPath = path2.resolve(opts.out);
|
|
1098
|
+
const mermaid = parseMermaidOptions(opts);
|
|
1004
1099
|
let ormOptions;
|
|
1005
1100
|
try {
|
|
1006
1101
|
ormOptions = await loadOrmOptions(configPath, opts.tsconfig);
|
|
@@ -1019,6 +1114,7 @@ ${err instanceof Error ? err.message : String(err)}
|
|
|
1019
1114
|
title: opts.title,
|
|
1020
1115
|
...opts.description !== void 0 && { description: opts.description },
|
|
1021
1116
|
...opts.src !== void 0 && { src: opts.src },
|
|
1117
|
+
...mermaid !== void 0 && { mermaid },
|
|
1022
1118
|
onWarn: (message) => void process.stderr.write(`Warning: ${message}
|
|
1023
1119
|
`)
|
|
1024
1120
|
});
|
|
@@ -1043,7 +1139,7 @@ var program = new Command().name("mikro-orm-markdown").description("Generate Mer
|
|
|
1043
1139
|
).option(
|
|
1044
1140
|
"--src <paths...>",
|
|
1045
1141
|
"Source .ts file globs to read JSDoc from when entities run from compiled .js (comments are stripped at build time)"
|
|
1046
|
-
).action(run);
|
|
1142
|
+
).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);
|
|
1047
1143
|
function isDirectCliExecution() {
|
|
1048
1144
|
const entryPoint = process.argv[1];
|
|
1049
1145
|
if (entryPoint === void 0) {
|