mikro-orm-markdown 0.1.0-alpha.4 → 0.1.0
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 +18 -0
- package/README.ko.md +88 -1
- package/README.md +91 -1
- package/dist/cli.cjs +88 -18
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +88 -18
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +59 -17
- 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 +59 -17
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
package/dist/cli.js
CHANGED
|
@@ -9,7 +9,7 @@ import { Command } from "commander";
|
|
|
9
9
|
|
|
10
10
|
// src/docs/jsdoc.ts
|
|
11
11
|
import { Project } from "ts-morph";
|
|
12
|
-
function loadJsDoc(filePaths) {
|
|
12
|
+
function loadJsDoc(filePaths, onWarn) {
|
|
13
13
|
const entities = /* @__PURE__ */ new Map();
|
|
14
14
|
const props = /* @__PURE__ */ new Map();
|
|
15
15
|
const classNames = /* @__PURE__ */ new Set();
|
|
@@ -26,8 +26,12 @@ function loadJsDoc(filePaths) {
|
|
|
26
26
|
});
|
|
27
27
|
for (const filePath of filePaths) {
|
|
28
28
|
try {
|
|
29
|
-
project.addSourceFilesAtPaths(filePath);
|
|
30
|
-
|
|
29
|
+
const sourceFiles2 = project.addSourceFilesAtPaths(filePath);
|
|
30
|
+
if (sourceFiles2.length === 0 && !hasGlobPattern(filePath)) {
|
|
31
|
+
onWarn?.(`No JSDoc source file matched path: ${filePath}`);
|
|
32
|
+
}
|
|
33
|
+
} catch (err) {
|
|
34
|
+
onWarn?.(`Could not load JSDoc source path "${filePath}": ${formatUnknownError(err)}`);
|
|
31
35
|
}
|
|
32
36
|
}
|
|
33
37
|
const sourceFiles = project.getSourceFiles();
|
|
@@ -58,11 +62,18 @@ function loadJsDoc(filePaths) {
|
|
|
58
62
|
props.set(className, propMap);
|
|
59
63
|
}
|
|
60
64
|
}
|
|
61
|
-
} catch {
|
|
65
|
+
} catch (err) {
|
|
66
|
+
onWarn?.(`Could not parse JSDoc source file "${sourceFile.getFilePath()}": ${formatUnknownError(err)}`);
|
|
62
67
|
}
|
|
63
68
|
}
|
|
64
69
|
return { entities, props, sourceFileCount: sourceFiles.length, classNames };
|
|
65
70
|
}
|
|
71
|
+
function formatUnknownError(err) {
|
|
72
|
+
return err instanceof Error ? err.message : String(err);
|
|
73
|
+
}
|
|
74
|
+
function hasGlobPattern(filePath) {
|
|
75
|
+
return /[*?[\]{}]/.test(filePath);
|
|
76
|
+
}
|
|
66
77
|
function parseEntityJsDoc(jsDocs) {
|
|
67
78
|
const namespaces = [];
|
|
68
79
|
const erdNamespaces = [];
|
|
@@ -237,6 +248,8 @@ function toMarkdownAnchor(value) {
|
|
|
237
248
|
}
|
|
238
249
|
|
|
239
250
|
// src/render/mermaid.ts
|
|
251
|
+
var MERMAID_LAYOUTS = ["dagre", "elk", "elk.stress"];
|
|
252
|
+
var MERMAID_THEMES = ["default", "neutral", "dark", "forest", "base"];
|
|
240
253
|
var FORMULA_DUMMY_TABLE = {
|
|
241
254
|
alias: "e0",
|
|
242
255
|
name: "",
|
|
@@ -523,8 +536,19 @@ function normalizeType(type) {
|
|
|
523
536
|
}
|
|
524
537
|
return type;
|
|
525
538
|
}
|
|
526
|
-
function renderErDiagram(model) {
|
|
527
|
-
const lines = [
|
|
539
|
+
function renderErDiagram(model, mermaid) {
|
|
540
|
+
const lines = [];
|
|
541
|
+
if (mermaid?.layout !== void 0 || mermaid?.theme !== void 0) {
|
|
542
|
+
lines.push("---", "config:");
|
|
543
|
+
if (mermaid.layout !== void 0) {
|
|
544
|
+
lines.push(` layout: ${mermaid.layout}`);
|
|
545
|
+
}
|
|
546
|
+
if (mermaid.theme !== void 0) {
|
|
547
|
+
lines.push(` theme: ${mermaid.theme}`);
|
|
548
|
+
}
|
|
549
|
+
lines.push("---");
|
|
550
|
+
}
|
|
551
|
+
lines.push("erDiagram");
|
|
528
552
|
for (const entity of model.entities) {
|
|
529
553
|
lines.push(` ${toMermaidIdentifier(entity.className)} {`);
|
|
530
554
|
for (const col of entity.columns) {
|
|
@@ -719,7 +743,8 @@ function isCrossNamespaceInGroup(jsDoc, groupName, isDefault) {
|
|
|
719
743
|
if (isDefault || !jsDoc) {
|
|
720
744
|
return false;
|
|
721
745
|
}
|
|
722
|
-
|
|
746
|
+
const hasHomeNamespace = jsDoc.namespaces.length > 0 || jsDoc.describeNamespaces.length > 0;
|
|
747
|
+
return hasHomeNamespace && jsDoc.erdNamespaces.includes(groupName) && !jsDoc.namespaces.includes(groupName) && !jsDoc.describeNamespaces.includes(groupName);
|
|
723
748
|
}
|
|
724
749
|
|
|
725
750
|
// src/provider.ts
|
|
@@ -743,16 +768,16 @@ async function withTsMorphMetadataProvider(options, onWarn) {
|
|
|
743
768
|
}
|
|
744
769
|
|
|
745
770
|
// src/render/markdown.ts
|
|
746
|
-
function renderMarkdown(docModel) {
|
|
771
|
+
function renderMarkdown(docModel, mermaid) {
|
|
747
772
|
const sections = [`# ${escapeMarkdownInline(docModel.title)}`];
|
|
748
773
|
if (docModel.description) {
|
|
749
774
|
sections.push(escapeMarkdownParagraph(docModel.description));
|
|
750
775
|
}
|
|
751
776
|
if (docModel.groups.length > 1) {
|
|
752
|
-
sections.push(renderTableOfContents(docModel.groups));
|
|
777
|
+
sections.push(renderTableOfContents(docModel.groups, resolveGroupAnchors(docModel)));
|
|
753
778
|
}
|
|
754
779
|
for (const group of docModel.groups) {
|
|
755
|
-
sections.push(renderGroupSection(group));
|
|
780
|
+
sections.push(renderGroupSection(group, mermaid));
|
|
756
781
|
}
|
|
757
782
|
return sections.join("\n\n");
|
|
758
783
|
}
|
|
@@ -763,20 +788,39 @@ function renderBulletSection(header, items, renderItem) {
|
|
|
763
788
|
}
|
|
764
789
|
return lines.join("\n");
|
|
765
790
|
}
|
|
766
|
-
function renderTableOfContents(groups) {
|
|
791
|
+
function renderTableOfContents(groups, anchors) {
|
|
767
792
|
return renderBulletSection("## Contents", groups, (group) => {
|
|
768
793
|
const label = escapeMarkdownInline(group.name).replace(/[[\]]/g, "\\$&");
|
|
769
|
-
return `- [${label}](#${toMarkdownAnchor(group.name)})`;
|
|
794
|
+
return `- [${label}](#${anchors.get(group) ?? toMarkdownAnchor(group.name)})`;
|
|
770
795
|
});
|
|
771
796
|
}
|
|
772
|
-
function
|
|
797
|
+
function resolveGroupAnchors(docModel) {
|
|
798
|
+
const seenAnchors = /* @__PURE__ */ new Map();
|
|
799
|
+
const groupAnchors = /* @__PURE__ */ new Map();
|
|
800
|
+
resolveHeadingAnchor(docModel.title, seenAnchors);
|
|
801
|
+
resolveHeadingAnchor("Contents", seenAnchors);
|
|
802
|
+
for (const group of docModel.groups) {
|
|
803
|
+
groupAnchors.set(group, resolveHeadingAnchor(group.name, seenAnchors));
|
|
804
|
+
for (const entity of group.textEntities) {
|
|
805
|
+
resolveHeadingAnchor(entity.model.className, seenAnchors);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
return groupAnchors;
|
|
809
|
+
}
|
|
810
|
+
function resolveHeadingAnchor(value, seenAnchors) {
|
|
811
|
+
const baseAnchor = toMarkdownAnchor(value);
|
|
812
|
+
const previousCount = seenAnchors.get(baseAnchor) ?? 0;
|
|
813
|
+
seenAnchors.set(baseAnchor, previousCount + 1);
|
|
814
|
+
return previousCount === 0 ? baseAnchor : `${baseAnchor}-${previousCount}`;
|
|
815
|
+
}
|
|
816
|
+
function renderGroupSection(group, mermaid) {
|
|
773
817
|
const parts = [`## ${escapeMarkdownInline(group.name)}`];
|
|
774
818
|
if (group.erdEntities.length > 0) {
|
|
775
819
|
const diagramModel = {
|
|
776
820
|
entities: group.erdEntities.map((e) => e.model),
|
|
777
821
|
relations: group.erdRelations
|
|
778
822
|
};
|
|
779
|
-
parts.push("```mermaid\n" + renderErDiagram(diagramModel) + "\n```");
|
|
823
|
+
parts.push("```mermaid\n" + renderErDiagram(diagramModel, mermaid) + "\n```");
|
|
780
824
|
}
|
|
781
825
|
for (const entity of group.textEntities) {
|
|
782
826
|
parts.push(renderEntitySection(entity));
|
|
@@ -932,15 +976,15 @@ async function loadEntityMetadataWithTsMorphFallback(originalOrm, effectiveOrm)
|
|
|
932
976
|
}
|
|
933
977
|
}
|
|
934
978
|
async function generateMarkdown(options) {
|
|
935
|
-
const { orm, title = "Database Schema", description, src, onWarn } = options;
|
|
979
|
+
const { orm, title = "Database Schema", description, src, onWarn, mermaid } = options;
|
|
936
980
|
const effectiveOrm = await withTsMorphMetadataProvider(orm, onWarn);
|
|
937
981
|
const { metas, sourcePaths } = await loadEntityMetadataWithTsMorphFallback(orm, effectiveOrm);
|
|
938
|
-
const jsDocResult = loadJsDoc(resolveJsDocSources(sourcePaths, src, onWarn));
|
|
982
|
+
const jsDocResult = loadJsDoc(resolveJsDocSources(sourcePaths, src, onWarn), onWarn);
|
|
939
983
|
if (src !== void 0 && src.length > 0) {
|
|
940
984
|
assertExplicitJsDocSourceCoverage(metas, jsDocResult, src, onWarn);
|
|
941
985
|
}
|
|
942
986
|
const docModel = buildDocumentModel(metas, jsDocResult, title, description, onWarn);
|
|
943
|
-
return renderMarkdown(docModel);
|
|
987
|
+
return renderMarkdown(docModel, mermaid);
|
|
944
988
|
}
|
|
945
989
|
|
|
946
990
|
// src/cli.ts
|
|
@@ -1055,9 +1099,34 @@ async function writeMarkdownFile(outPath, markdown) {
|
|
|
1055
1099
|
${formatFileSystemError(cause)}`, { cause });
|
|
1056
1100
|
}
|
|
1057
1101
|
}
|
|
1102
|
+
function parseMermaidOptions(opts) {
|
|
1103
|
+
const { mermaidLayout, mermaidTheme } = opts;
|
|
1104
|
+
if (mermaidLayout !== void 0 && !MERMAID_LAYOUTS.includes(mermaidLayout)) {
|
|
1105
|
+
process.stderr.write(
|
|
1106
|
+
`Error: Invalid --mermaid-layout "${mermaidLayout}". Allowed values: ${MERMAID_LAYOUTS.join(", ")}
|
|
1107
|
+
`
|
|
1108
|
+
);
|
|
1109
|
+
process.exit(1);
|
|
1110
|
+
}
|
|
1111
|
+
if (mermaidTheme !== void 0 && !MERMAID_THEMES.includes(mermaidTheme)) {
|
|
1112
|
+
process.stderr.write(
|
|
1113
|
+
`Error: Invalid --mermaid-theme "${mermaidTheme}". Allowed values: ${MERMAID_THEMES.join(", ")}
|
|
1114
|
+
`
|
|
1115
|
+
);
|
|
1116
|
+
process.exit(1);
|
|
1117
|
+
}
|
|
1118
|
+
if (mermaidLayout === void 0 && mermaidTheme === void 0) {
|
|
1119
|
+
return void 0;
|
|
1120
|
+
}
|
|
1121
|
+
return {
|
|
1122
|
+
...mermaidLayout !== void 0 && { layout: mermaidLayout },
|
|
1123
|
+
...mermaidTheme !== void 0 && { theme: mermaidTheme }
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1058
1126
|
async function run(opts) {
|
|
1059
1127
|
const configPath = path2.resolve(opts.config);
|
|
1060
1128
|
const outPath = path2.resolve(opts.out);
|
|
1129
|
+
const mermaid = parseMermaidOptions(opts);
|
|
1061
1130
|
let ormOptions;
|
|
1062
1131
|
try {
|
|
1063
1132
|
ormOptions = await loadOrmOptions(configPath, opts.tsconfig);
|
|
@@ -1076,6 +1145,7 @@ ${err instanceof Error ? err.message : String(err)}
|
|
|
1076
1145
|
title: opts.title,
|
|
1077
1146
|
...opts.description !== void 0 && { description: opts.description },
|
|
1078
1147
|
...opts.src !== void 0 && { src: opts.src },
|
|
1148
|
+
...mermaid !== void 0 && { mermaid },
|
|
1079
1149
|
onWarn: (message) => void process.stderr.write(`Warning: ${message}
|
|
1080
1150
|
`)
|
|
1081
1151
|
});
|
|
@@ -1100,7 +1170,7 @@ var program = new Command().name("mikro-orm-markdown").description("Generate Mer
|
|
|
1100
1170
|
).option(
|
|
1101
1171
|
"--src <paths...>",
|
|
1102
1172
|
"Source .ts file globs to read JSDoc from when entities run from compiled .js (comments are stripped at build time)"
|
|
1103
|
-
).action(run);
|
|
1173
|
+
).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);
|
|
1104
1174
|
function isDirectCliExecution() {
|
|
1105
1175
|
const entryPoint = process.argv[1];
|
|
1106
1176
|
if (entryPoint === void 0) {
|