From f160d0f9ab982ebeaf9f1a184a96033e785d3fdf Mon Sep 17 00:00:00 2001 From: darshanr0107 Date: Tue, 13 Jan 2026 17:21:33 +0530 Subject: [PATCH 1/2] fix: improve anchor node handling for clusters with external connections on-behalf-of: @Mermaid-Chart --- .../dagre/mermaid-graphlib.js | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/packages/mermaid/src/rendering-util/layout-algorithms/dagre/mermaid-graphlib.js b/packages/mermaid/src/rendering-util/layout-algorithms/dagre/mermaid-graphlib.js index f0e5cd5ed9b..e9ce3c14615 100644 --- a/packages/mermaid/src/rendering-util/layout-algorithms/dagre/mermaid-graphlib.js +++ b/packages/mermaid/src/rendering-util/layout-algorithms/dagre/mermaid-graphlib.js @@ -247,6 +247,15 @@ export const adjustClustersAndEdges = (graph, depth) => { if (parent !== id && clusterDb.has(parent) && !clusterDb.get(parent).externalConnections) { clusterDb.get(id).id = parent; } + const hasDirectOutgoingEdge = graph.edges().some((edge) => edge.v === id); + if (nonClusterChild && clusterDb.get(id)?.externalConnections && hasDirectOutgoingEdge) { + // Check if the current anchor is invalid + isNodeInExtractableCluster(graph, nonClusterChild, id); + const safeAnchor = findSafeAnchorNode(graph, id, graph.parent(nonClusterChild)); + if (safeAnchor) { + clusterDb.get(id).id = safeAnchor; + } + } } graph.edges().forEach(function (e) { @@ -411,3 +420,40 @@ const sorter = (graph, nodes) => { }; export const sortNodesByHierarchy = (graph) => sorter(graph, graph.children()); + +/** Checks if a node is inside a cluster that will be extracted (has no external connections). */ +const isNodeInExtractableCluster = (graph, node, rootId) => { + let parent = graph.parent(node); + + while (parent && parent !== rootId) { + const cluster = clusterDb.get(parent); + if (cluster && !cluster.externalConnections) { + return true; + } + parent = graph.parent(parent); + } + + return false; +}; + +/** Finds an alternative anchor node for a cluster that is not inside an extractable cluster. */ +const findSafeAnchorNode = (graph, clusterId, excludedCluster) => { + const children = graph.children(clusterId) ?? []; + + for (const child of children) { + if (child === excludedCluster || isDescendant(child, excludedCluster)) { + continue; + } + + const candidate = findNonClusterChild(child, graph, clusterId); + if (!candidate || candidate === child) { + continue; + } + + if (!isNodeInExtractableCluster(graph, candidate, clusterId)) { + return candidate; + } + } + + return null; +}; From 4e4e6c4a108d834dd0f643b08deb89159e0eca94 Mon Sep 17 00:00:00 2001 From: darshanr0107 Date: Tue, 13 Jan 2026 18:25:04 +0530 Subject: [PATCH 2/2] fix: add test for rendering nested subgraphs with edges from clusters on-behalf-of: @Mermaid-Chart --- .changeset/brave-radios-live.md | 5 +++ .../rendering/flowchart-v2.spec.js | 31 +++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 .changeset/brave-radios-live.md diff --git a/.changeset/brave-radios-live.md b/.changeset/brave-radios-live.md new file mode 100644 index 00000000000..8f24a2324b1 --- /dev/null +++ b/.changeset/brave-radios-live.md @@ -0,0 +1,5 @@ +--- +'mermaid': patch +--- + +fix: Prevent crash when flowchart node shape is undefined diff --git a/cypress/integration/rendering/flowchart-v2.spec.js b/cypress/integration/rendering/flowchart-v2.spec.js index cd3676fbf3d..dbc6657a0b0 100644 --- a/cypress/integration/rendering/flowchart-v2.spec.js +++ b/cypress/integration/rendering/flowchart-v2.spec.js @@ -1211,4 +1211,35 @@ class link myClass ` ); }); + + it('V2 - 18: should render nested subgraphs with edge from cluster containing extractable subgraph', () => { + imgSnapshotTest( + `flowchart TB + subgraph asub + aa + end + a + subgraph bsub + subgraph csub + subgraph dsub + da + end + end + subgraph esub + subgraph fsub + %%comment out subgraph below + subgraph gsub + ga + abc + abcd + end + end + end + end + bsub-->a + da-->a + `, + { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' } + ); + }); });