mirror of
https://github.com/amitwh/markdown-converter.git
synced 2026-10-01 09:19:34 +05:30
Each pure module's UMD wrapper assigned window.FlowchartXxx only in the else branch — when 'module' was undefined. But the renderer runs with nodeIntegration:true, so 'module' is always truthy and the else branch never ran. The standalone Flowchart Generator window then aborted with 'Flowchart pure modules not loaded'. Fix: append 'if (typeof window !== undefined) window.FlowchartXxx = exported;' after the CommonJS branch in all 4 pure modules (store, shapes, mermaid, canvas). Both branches can run now; the legacy sidebar panel still loads them via require() and the renderer unconditionally exposes the global. Regression guard: 4 new source-grep tests in tests/flowchart-controller.test.js assert each module's source contains the 'window.FlowchartXxx = exported' assignment. Attribution: Amit Haridas
112 lines
3.3 KiB
JavaScript
112 lines
3.3 KiB
JavaScript
/**
|
|
* Pure translator: graph → Mermaid `flowchart TD` source.
|
|
*
|
|
* Output format:
|
|
* flowchart TD
|
|
* A[Step 1]
|
|
* B{Valid?}
|
|
* C([Start])
|
|
* D[[Do thing]]
|
|
* E[/Report/]
|
|
* A --> B
|
|
* B -->|yes| C
|
|
*
|
|
* IDs are 1-3 chars (A..Z, AA..ZZ, …). Labels are escaped: double quotes
|
|
* become `#quot;`, newlines become literal `\n` (Mermaid's escape).
|
|
*
|
|
* @module flowchart-mermaid
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
const SHAPE_SYNTAX = {
|
|
process: (id, label) => `${id}[${label}]`,
|
|
decision: (id, label) => `${id}{${label}}`,
|
|
terminator: (id, label) => `${id}([${label}])`,
|
|
subroutine: (id, label) => `${id}[[${label}]]`,
|
|
document: (id, label) => `${id}[/${label}/]`,
|
|
};
|
|
|
|
const EDGE_ARROW = {
|
|
solid: '-->',
|
|
dotted: '-.->',
|
|
thick: '==>',
|
|
};
|
|
|
|
function escapeLabel(label) {
|
|
return String(label || '')
|
|
.replace(/"/g, '#quot;')
|
|
.replace(/\r?\n/g, '\\n');
|
|
}
|
|
|
|
function assignIds(nodes) {
|
|
// A, B, C, …, Z, AA, AB, … (matches Mermaid's preferred short ids).
|
|
const map = new Map();
|
|
let n = 0;
|
|
for (const node of nodes) {
|
|
let id;
|
|
if (n < 26) id = String.fromCharCode(65 + n);
|
|
else {
|
|
const first = Math.floor(n / 26) - 1;
|
|
const second = n % 26;
|
|
id = String.fromCharCode(65 + first) + String.fromCharCode(65 + second);
|
|
}
|
|
map.set(node.id, id);
|
|
n += 1;
|
|
}
|
|
return map;
|
|
}
|
|
|
|
function nodeDeclaration(node) {
|
|
const fn = SHAPE_SYNTAX[node.kind];
|
|
if (!fn) throw new Error(`flowchart-mermaid: unknown node kind "${node.kind}"`);
|
|
return fn(node.id, escapeLabel(node.label));
|
|
}
|
|
|
|
function edgeDeclaration(edge, fromId, toId) {
|
|
const arrow = EDGE_ARROW[edge.kind];
|
|
if (!arrow) throw new Error(`flowchart-mermaid: unknown edge kind "${edge.kind}"`);
|
|
if (edge.label && edge.label.length > 0) {
|
|
return `${fromId} ${arrow}|${escapeLabel(edge.label)}| ${toId}`;
|
|
}
|
|
return `${fromId} ${arrow} ${toId}`;
|
|
}
|
|
|
|
function toMermaid(graph) {
|
|
if (!graph || !Array.isArray(graph.nodes) || !Array.isArray(graph.edges)) {
|
|
throw new Error('flowchart-mermaid: graph must have nodes[] and edges[]');
|
|
}
|
|
const ids = assignIds(graph.nodes);
|
|
const lines = ['flowchart TD'];
|
|
for (const node of graph.nodes) {
|
|
lines.push(nodeDeclaration({ ...node, id: ids.get(node.id) }));
|
|
}
|
|
for (const edge of graph.edges) {
|
|
const fromId = ids.get(edge.fromNodeId);
|
|
const toId = ids.get(edge.toNodeId);
|
|
if (!fromId || !toId) continue; // skip dangling edges (defensive)
|
|
lines.push(edgeDeclaration(edge, fromId, toId));
|
|
}
|
|
return lines.join('\n');
|
|
}
|
|
|
|
// v4.9.6 UMD wrapper — same CommonJS export shape + browser global
|
|
// (window.FlowchartMermaid) so the standalone window's controller can load
|
|
// this module via <script> tag without nodeIntegration.
|
|
(function (root, factory) {
|
|
const exported = factory();
|
|
if (typeof module === 'object' && module.exports) {
|
|
module.exports = exported;
|
|
} else {
|
|
root.FlowchartMermaid = exported;
|
|
}
|
|
// v4.9.7 — also expose as window global when running in Electron renderer
|
|
// (nodeIntegration:true makes `module` truthy so the else branch above never
|
|
// runs; the controller still expects window.FlowchartMermaid).
|
|
if (typeof window !== 'undefined') {
|
|
window.FlowchartMermaid = exported;
|
|
}
|
|
})(typeof window !== 'undefined' ? window : globalThis, function () {
|
|
return { toMermaid, escapeLabel, nodeDeclaration, edgeDeclaration };
|
|
});
|