Skip to content

Commit b96f1e5

Browse files
committed
fix(mermaid): escape special characters in node labels
Fixes issue #21: parentheses and other special characters in node names now break Mermaid rendering on GitHub. Added escapeMermaidLabel() helper that wraps labels in double quotes when they contain Mermaid shape delimiters or special characters: ( ) { } [ ] / \ This allows nodes like "Firebase Hosting (Tenant UI)" to render correctly in all generated Mermaid diagrams. The quoted labels are properly interpreted by Mermaid parsers as literal text. Fixes #21
1 parent bc1c8cd commit b96f1e5

1 file changed

Lines changed: 24 additions & 4 deletions

File tree

src/operations/graph-shared.ts

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,25 @@ export function mermaidShapeForNode(node: Node): MermaidShape {
145145
return NODE_TYPE_SHAPES[node.type] ?? "rectangle";
146146
}
147147

148+
/**
149+
* Escape a Mermaid label by wrapping in quotes if it contains special characters.
150+
* Mermaid shape delimiters and other special chars need escaping: ( ) { } [ ] / \
151+
* Wrapping in double quotes allows these characters to be rendered as-is.
152+
* @param label - The label text to escape
153+
* @returns The label, quoted if it contains special characters
154+
* @example
155+
* escapeMermaidLabel("Firebase (Tenant)") // returns '"Firebase (Tenant)"'
156+
*/
157+
function escapeMermaidLabel(label: string): string {
158+
// Check if label contains any Mermaid special characters that need escaping
159+
const specialChars = ["(", ")", "{", "}", "[", "]", "/", "\\"];
160+
if (specialChars.some((char) => label.includes(char))) {
161+
// Wrap in double quotes to escape
162+
return `"${label}"`;
163+
}
164+
return label;
165+
}
166+
148167
/**
149168
* Render a Mermaid node definition for a node id/name/shape.
150169
* @param id - Node id
@@ -161,16 +180,17 @@ export function renderMermaidNode(
161180
): string {
162181
const safeId = sanitiseMermaidId(id);
163182
const label = mode === "compact" ? id : `${id}: ${name}`;
183+
const escapedLabel = escapeMermaidLabel(label);
164184
switch (shape) {
165185
case "rounded":
166-
return `${safeId}([${label}])`;
186+
return `${safeId}([${escapedLabel}])`;
167187
case "rhombus":
168-
return `${safeId}{{${label}}}`;
188+
return `${safeId}{{${escapedLabel}}}`;
169189
case "parallelogram":
170-
return `${safeId}[/${label}/]`;
190+
return `${safeId}[/${escapedLabel}/]`;
171191
case "rectangle":
172192
default:
173-
return `${safeId}[${label}]`;
193+
return `${safeId}[${escapedLabel}]`;
174194
}
175195
}
176196

0 commit comments

Comments
 (0)