added mermaid and a lot of cmap changes

This commit is contained in:
2026-08-27 13:13:39 +02:00
parent 20c1584016
commit 2215d1d04a
35 changed files with 6442 additions and 350 deletions
+145
View File
@@ -0,0 +1,145 @@
"use strict";
const test = require("node:test");
const assert = require("node:assert/strict");
const { generateMarkdown, derivedDocument, relationLines } = require("../static/js/cmap-export.js");
function map(slug, title, document) {
return { slug, title, document };
}
const root = map("root", "Root map", {
schemaVersion: 2,
metadata: {
tags: ["architecture", "report"],
summary: "Summary of the root map.",
explanationPageSlug: "cmap:root-explanation"
},
concepts: [
{
id: "concept-a", label: "Decision", synopsis: "Choose the approach.",
aspects: ["governance"], pageSlug: "decisions:approach",
descriptionPageSlug: "cmap:decision",
cmapSlug: "child",
externalUrl: "https://example.com/decision",
tags: [{ type: "person", value: "Alex Morgan" }, { type: "person", value: "Sam de Vries" }]
},
{ id: "concept-b", label: "Delivery" }
],
items: [
{ id: 1, conceptId: "concept-a", kind: "concept" },
{ id: 2, kind: "phrase", label: "enables" },
{ id: 3, conceptId: "concept-b", kind: "concept" }
],
connectors: [
{ id: 1, sourceId: 1, targetId: 2, hasArrow: false },
{ id: 2, sourceId: 2, targetId: 3, hasArrow: true }
]
});
const child = map("child", "Child map", {
schemaVersion: 2,
metadata: { tags: ["detail"], summary: "Child summary." },
concepts: [{ id: "concept-c", label: "Child concept", cmapSlug: "root" }],
items: [{ id: 1, conceptId: "concept-c", kind: "concept" }],
connectors: []
});
const pages = new Map([
["cmap:root-explanation", { slug: "cmap:root-explanation", title: "Root explanation", tags: ["explanation"], markdown: "# Why\n\nBecause." }],
["decisions:approach", { slug: "decisions:approach", title: "Approach", tags: ["decision"], markdown: "# Decision\n\nUse the simple option." }],
["cmap:decision", { slug: "cmap:decision", title: "Decision explanation", tags: [], markdown: "Details." }]
]);
test("exports metadata, person tags, relations and linked maps to the selected depth", async () => {
const markdown = await generateMarkdown({
rootMap: root,
maxDepth: 1,
includeWikiPages: false,
language: "nl",
loadConceptMap: async (slug) => ({ root, child }[slug]),
loadWikiPage: async (slug) => pages.get(slug)
});
assert.match(markdown, /CMap: Root map \(niveau 0\)/);
assert.match(markdown, /CMap: Child map \(niveau 1\)/);
assert.match(markdown, /`architecture`, `report`/);
assert.match(markdown, /Personen \(verantwoordelijkheid\/actie\):\*\* Alex Morgan, Sam de Vries/);
assert.match(markdown, /Decision — \*\*enables\*\* → Delivery/);
assert.match(markdown, /### CMap-uitleg/);
assert.match(markdown, /Webpagina:\*\* https:\/\/example\.com\/decision/);
assert.doesNotMatch(markdown, /## Gekoppelde wikipagina's/);
assert.equal((markdown.match(/## CMap: Root map/g) || []).length, 1, "cycles are exported once");
});
test("depth zero exports only the selected map", async () => {
const markdown = await generateMarkdown({
rootMap: root,
maxDepth: 0,
includeWikiPages: false,
loadConceptMap: async (slug) => ({ root, child }[slug]),
loadWikiPage: async (slug) => pages.get(slug)
});
assert.match(markdown, /Root map/);
assert.doesNotMatch(markdown, /## CMap: Child map/);
});
test("optionally appends linked wiki pages and concept explanations", async () => {
const markdown = await generateMarkdown({
rootMap: root,
maxDepth: 0,
includeWikiPages: true,
language: "en",
loadConceptMap: async (slug) => ({ root, child }[slug]),
loadWikiPage: async (slug) => pages.get(slug)
});
assert.match(markdown, /## Linked wiki pages/);
assert.match(markdown, /### Approach/);
assert.match(markdown, /### Decision explanation/);
assert.equal((markdown.match(/Root explanation/g) || []).length, 0,
"the explanation page is embedded under the CMap rather than duplicated as an appendix heading");
assert.match(markdown, /Because\./);
});
test("derived views contain their root subtree and inherit useful root metadata", () => {
const source = {
schemaVersion: 2,
concepts: [
{ id: "root-concept", label: "Team", synopsis: "Team scope", aspects: ["people"], descriptionPageSlug: "cmap:team" },
{ id: "child-concept", label: "Work" },
{ id: "outside-concept", label: "Outside" }
],
items: [
{ id: 10, conceptId: "root-concept", kind: "submap" },
{ id: 11, conceptId: "child-concept", kind: "concept", parentSubmapId: 10 },
{ id: 12, conceptId: "outside-concept", kind: "concept" }
],
connectors: [
{ id: 1, sourceId: 10, targetId: 11 },
{ id: 2, sourceId: 11, targetId: 12 }
]
};
const result = derivedDocument(source, {
derivedView: { sourceCmapSlug: "source", rootItemId: 10 }
});
assert.deepEqual(result.items.map((item) => item.id), [10, 11]);
assert.deepEqual(result.connectors.map((connector) => connector.id), [1]);
assert.deepEqual(result.metadata.tags, ["people"]);
assert.equal(result.metadata.summary, "Team scope");
assert.equal(result.metadata.explanationPageSlug, "cmap:team");
});
test("direct and phrase relations receive readable Markdown", () => {
const documentValue = {
items: [
{ id: 1, label: "A" }, { id: 2, kind: "phrase", label: "supports" },
{ id: 3, label: "B" }, { id: 4, label: "C" }
],
connectors: [
{ sourceId: 1, targetId: 2, hasArrow: false },
{ sourceId: 2, targetId: 3, hasArrow: true },
{ sourceId: 3, targetId: 4, hasArrow: true }
]
};
assert.deepEqual(relationLines(documentValue), ["A — **supports** → B", "B → C"]);
});