Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/mermaid/src/diagrams/block/parser/block.jison
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ space { yytext = '1'; yy.getLogger().debug('COLUMNS (LEX)', yyt
<STYLE_DEFINITION>[^\n]* { this.popState(); return 'STYLE_DEFINITION_DATA' }

accTitle\s*":"\s* { this.pushState("acc_title");return 'acc_title'; }
<acc_title>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_title_value"; }
<acc_title>(?!\n|;)*[^\n]* { this.popState(); return "acc_title_value"; }
accDescr\s*":"\s* { this.pushState("acc_descr");return 'acc_descr'; }
<acc_descr>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_descr_value"; }
<acc_descr>(?!\n|;)*[^\n]* { this.popState(); return "acc_descr_value"; }
accDescr\s*"{"\s* { this.pushState("acc_descr_multiline");}
<acc_descr_multiline>[\}] { this.popState(); }
<acc_descr_multiline>[^\}]* return "acc_descr_multiline_value";
Expand Down
8 changes: 4 additions & 4 deletions packages/mermaid/src/diagrams/c4/parser/c4Diagram.jison
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,12 @@
.*direction\s+LR[^\n]* return 'direction_lr';


"title"\s[^#\n;]+ return 'title';
"accDescription"\s[^#\n;]+ return 'accDescription';
"title"\s[^\n;]+ return 'title';
"accDescription"\s[^\n;]+ return 'accDescription';
accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; }
<acc_title>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_title_value"; }
<acc_title>(?!\n|;)*[^\n]* { this.popState(); return "acc_title_value"; }
accDescr\s*":"\s* { this.begin("acc_descr");return 'acc_descr'; }
<acc_descr>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_descr_value"; }
<acc_descr>(?!\n|;)*[^\n]* { this.popState(); return "acc_descr_value"; }
accDescr\s*"{"\s* { this.begin("acc_descr_multiline");}
<acc_descr_multiline>[\}] { this.popState(); }
<acc_descr_multiline>[^\}]* return "acc_descr_multiline_value";
Expand Down
4 changes: 2 additions & 2 deletions packages/mermaid/src/diagrams/class/parser/classDiagram.jison
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@
\%\%(?!\{)*[^\n]*(\r?\n?)+ /* skip comments */
\%\%[^\n]*(\r?\n)* /* skip comments */
accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; }
<acc_title>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_title_value"; }
<acc_title>(?!\n|;)*[^\n]* { this.popState(); return "acc_title_value"; }
accDescr\s*":"\s* { this.begin("acc_descr");return 'acc_descr'; }
<acc_descr>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_descr_value"; }
<acc_descr>(?!\n|;)*[^\n]* { this.popState(); return "acc_descr_value"; }
accDescr\s*"{"\s* { this.begin("acc_descr_multiline");}
<acc_descr_multiline>[\}] { this.popState(); }
<acc_descr_multiline>[^\}]* return "acc_descr_multiline_value";
Expand Down
86 changes: 86 additions & 0 deletions packages/mermaid/src/diagrams/common/entity-handling-fix.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/**
* Test cases for HTML entity handling fix
* Issue: https://github.com/mermaid-js/mermaid/issues/7105
*
* Tests that titles with HTML entities (both valid and invalid) are handled correctly
*/

import { describe, it, expect, beforeEach } from 'vitest';
import mermaid from '../../mermaid.js';

describe('HTML Entity Handling Fix - Issue #7105', () => {
beforeEach(() => {
mermaid.initialize({ startOnLoad: false });
});

describe('Journey diagrams', () => {
it('should handle titles with hash symbols and colons correctly', async () => {
const journeyWithHash = `
journey
title Book #2: sous-titre
section Chapter 1
Task: 5: Person1
`;

// Should not fail and should include the full title
const result = await mermaid.render('test', journeyWithHash);
expect(result.svg).toContain('Book #2: sous-titre');
});

it('should handle invalid HTML entities in titles', async () => {
const journeyWithInvalidEntity = `
journey
title Test #abc; invalid entity text
section Chapter 1
Task: 5: Person1
`;

// Should not truncate text after invalid entity - # gets HTML escaped to &amp;
const result = await mermaid.render('test', journeyWithInvalidEntity);
expect(result.svg).toContain('Test &amp;abc; invalid entity text');
});

it('should handle valid HTML entities correctly', async () => {
const journeyWithValidEntities = `
journey
title Text with #lt;brackets#gt; and symbols
section Chapter 1
Task: 5: Person1
`;

const result = await mermaid.render('test', journeyWithValidEntities);
expect(result.svg).toContain('Text with &lt;brackets&gt; and symbols');
});
});

// Sequence diagrams tests skipped due to test environment issues with getBBox
// The fix applies to sequence diagrams as well since we modified their parser

describe('Accessibility titles and descriptions', () => {
it('should handle acc titles with hash symbols', async () => {
const journeyWithAccTitle = `
journey
accTitle: Process #1: Main workflow
section Step
Do task: 3: User
`;

const result = await mermaid.render('test', journeyWithAccTitle);
expect(result.svg).toBeDefined();
// The acc title should be processed without error
});

it('should handle acc descriptions with hash symbols', async () => {
const journeyWithAccDescr = `
journey
accDescr: This shows process #2: backup workflow
section Step
Do task: 3: User
`;

const result = await mermaid.render('test', journeyWithAccDescr);
expect(result.svg).toBeDefined();
// The acc description should be processed without error
});
});
});
4 changes: 2 additions & 2 deletions packages/mermaid/src/diagrams/er/parser/erDiagram.jison
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@

%%
accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; }
<acc_title>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_title_value"; }
<acc_title>(?!\n|;)*[^\n]* { this.popState(); return "acc_title_value"; }
accDescr\s*":"\s* { this.begin("acc_descr");return 'acc_descr'; }
<acc_descr>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_descr_value"; }
<acc_descr>(?!\n|;)*[^\n]* { this.popState(); return "acc_descr_value"; }
accDescr\s*"{"\s* { this.begin("acc_descr_multiline");}
<acc_descr_multiline>[\}] { this.popState(); }
<acc_descr_multiline>[^\}]* return "acc_descr_multiline_value";
Expand Down
4 changes: 2 additions & 2 deletions packages/mermaid/src/diagrams/flowchart/parser/flow.jison
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@

%%
accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; }
<acc_title>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_title_value"; }
<acc_title>(?!\n|;)*[^\n]* { this.popState(); return "acc_title_value"; }
accDescr\s*":"\s* { this.begin("acc_descr");return 'acc_descr'; }
<acc_descr>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_descr_value"; }
<acc_descr>(?!\n|;)*[^\n]* { this.popState(); return "acc_descr_value"; }
accDescr\s*"{"\s* { this.begin("acc_descr_multiline");}
<acc_descr_multiline>[\}] { this.popState(); }
<acc_descr_multiline>[^\}]* return "acc_descr_multiline_value";
Expand Down
18 changes: 9 additions & 9 deletions packages/mermaid/src/diagrams/gantt/parser/gantt.jison
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
\%\%\{ { this.begin('open_directive'); return 'open_directive'; }

accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; }
<acc_title>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_title_value"; }
<acc_title>(?!\n|;)*[^\n]* { this.popState(); return "acc_title_value"; }
accDescr\s*":"\s* { this.begin("acc_descr");return 'acc_descr'; }
<acc_descr>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_descr_value"; }
<acc_descr>(?!\n|;)*[^\n]* { this.popState(); return "acc_descr_value"; }
accDescr\s*"{"\s* { this.begin("acc_descr_multiline");}
<acc_descr_multiline>[\}] { this.popState(); }
<acc_descr_multiline>[^\}]* return "acc_descr_multiline_value";
Expand Down Expand Up @@ -69,13 +69,13 @@ that id.
<click>[^\s\n]* return 'click';

"gantt" return 'gantt';
"dateFormat"\s[^#\n;]+ return 'dateFormat';
"dateFormat"\s[^\n;]+ return 'dateFormat';
"inclusiveEndDates" return 'inclusiveEndDates';
"topAxis" return 'topAxis';
"axisFormat"\s[^#\n;]+ return 'axisFormat';
"tickInterval"\s[^#\n;]+ return 'tickInterval';
"includes"\s[^#\n;]+ return 'includes';
"excludes"\s[^#\n;]+ return 'excludes';
"axisFormat"\s[^\n;]+ return 'axisFormat';
"tickInterval"\s[^\n;]+ return 'tickInterval';
"includes"\s[^\n;]+ return 'includes';
"excludes"\s[^\n;]+ return 'excludes';
"todayMarker"\s[^\n;]+ return 'todayMarker';
weekday\s+monday return 'weekday_monday'
weekday\s+tuesday return 'weekday_tuesday'
Expand All @@ -88,10 +88,10 @@ weekend\s+friday return 'weekend_friday'
weekend\s+saturday return 'weekend_saturday'
\d\d\d\d"-"\d\d"-"\d\d return 'date';
"title"\s[^\n]+ return 'title';
"accDescription"\s[^#\n;]+ return 'accDescription'
"accDescription"\s[^\n;]+ return 'accDescription'
"section"\s[^\n]+ return 'section';
[^:\n]+ return 'taskTxt';
":"[^#\n;]+ return 'taskData';
":"[^\n;]+ return 'taskData';
":" return ':';
<<EOF>> return 'EOF';
. return 'INVALID';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@
\%\%[^\n]* /* do nothing */

title { this.begin("title");return 'title'; }
<title>(?!\n|;|#)*[^\n]* { this.popState(); return "title_value"; }
<title>(?!\n|;)*[^\n]* { this.popState(); return "title_value"; }

accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; }
<acc_title>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_title_value"; }
<acc_title>(?!\n|;)*[^\n]* { this.popState(); return "acc_title_value"; }
accDescr\s*":"\s* { this.begin("acc_descr");return 'acc_descr'; }
<acc_descr>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_descr_value"; }
<acc_descr>(?!\n|;)*[^\n]* { this.popState(); return "acc_descr_value"; }
accDescr\s*"{"\s* { this.begin("acc_descr_multiline");}
<acc_descr_multiline>[\}] { this.popState(); }
<acc_descr_multiline>[^\}]* return "acc_descr_multiline_value";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
%x acc_descr_multiline
%%

"title"\s[^#\n;]+ return 'title';
"title"\s[^\n;]+ return 'title';
accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; }
<acc_title>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_title_value"; }
<acc_title>(?!\n|;)*[^\n]* { this.popState(); return "acc_title_value"; }
accDescr\s*":"\s* { this.begin("acc_descr");return 'acc_descr'; }
<acc_descr>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_descr_value"; }
<acc_descr>(?!\n|;)*[^\n]* { this.popState(); return "acc_descr_value"; }
accDescr\s*"{"\s* { this.begin("acc_descr_multiline");}
<acc_descr_multiline>[\}] { this.popState(); }
<acc_descr_multiline>[^\}]* return "acc_descr_multiline_value";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,12 @@
"note" return 'note';
"activate" { this.begin('ID'); return 'activate'; }
"deactivate" { this.begin('ID'); return 'deactivate'; }
"title"\s[^#\n;]+ return 'title';
"title:"\s[^#\n;]+ return 'legacy_title';
"title"\s[^\n;]+ return 'title';
"title:"\s[^\n;]+ return 'legacy_title';
accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; }
<acc_title>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_title_value"; }
<acc_title>(?!\n|;)*[^\n]* { this.popState(); return "acc_title_value"; }
accDescr\s*":"\s* { this.begin("acc_descr");return 'acc_descr'; }
<acc_descr>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_descr_value"; }
<acc_descr>(?!\n|;)*[^\n]* { this.popState(); return "acc_descr_value"; }
accDescr\s*"{"\s* { this.begin("acc_descr_multiline");}
<acc_descr_multiline>[\}] { this.popState(); }
<acc_descr_multiline>[^\}]* return "acc_descr_multiline_value";
Expand Down
4 changes: 2 additions & 2 deletions packages/mermaid/src/diagrams/state/parser/stateDiagram.jison
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@
<SCALE>\s+"width" { this.popState(); }

accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; }
<acc_title>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_title_value"; }
<acc_title>(?!\n|;)*[^\n]* { this.popState(); return "acc_title_value"; }
accDescr\s*":"\s* { this.begin("acc_descr");return 'acc_descr'; }
<acc_descr>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_descr_value"; }
<acc_descr>(?!\n|;)*[^\n]* { this.popState(); return "acc_descr_value"; }
accDescr\s*"{"\s* { this.begin("acc_descr_multiline"); }
<acc_descr_multiline>[\}] { this.popState(); }
<acc_descr_multiline>[^\}]* return "acc_descr_multiline_value";
Expand Down
4 changes: 2 additions & 2 deletions packages/mermaid/src/diagrams/timeline/parser/timeline.jison
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@
"timeline" return 'timeline';
"title"\s[^\n]+ return 'title';
accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; }
<acc_title>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_title_value"; }
<acc_title>(?!\n|;)*[^\n]* { this.popState(); return "acc_title_value"; }
accDescr\s*":"\s* { this.begin("acc_descr");return 'acc_descr'; }
<acc_descr>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_descr_value"; }
<acc_descr>(?!\n|;)*[^\n]* { this.popState(); return "acc_descr_value"; }
accDescr\s*"{"\s* { this.begin("acc_descr_multiline");}
<acc_descr_multiline>[\}] { this.popState(); }
<acc_descr_multiline>[^\}]* return "acc_descr_multiline_value";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,17 @@
\#[^\n]* /* skip comments */

"journey" return 'journey';
"title"\s[^#\n;]+ return 'title';
"title"\s[^\n;]+ return 'title';
accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; }
<acc_title>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_title_value"; }
<acc_title>(?!\n|;)*[^\n]* { this.popState(); return "acc_title_value"; }
accDescr\s*":"\s* { this.begin("acc_descr");return 'acc_descr'; }
<acc_descr>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_descr_value"; }
<acc_descr>(?!\n|;)*[^\n]* { this.popState(); return "acc_descr_value"; }
accDescr\s*"{"\s* { this.begin("acc_descr_multiline");}
<acc_descr_multiline>[\}] { this.popState(); }
<acc_descr_multiline>[^\}]* return "acc_descr_multiline_value";
"section"\s[^#:\n;]+ return 'section';
[^#:\n;]+ return 'taskName';
":"[^#\n;]+ return 'taskData';
":"[^\n;]+ return 'taskData';
":" return ':';
<<EOF>> return 'EOF';
. return 'INVALID';
Expand Down
4 changes: 2 additions & 2 deletions packages/mermaid/src/diagrams/xychart/parser/xychart.jison
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@
"title" { return 'title'; }

"accTitle"\s*":"\s* { this.pushState("acc_title");return 'acc_title'; }
<acc_title>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_title_value"; }
<acc_title>(?!\n|;)*[^\n]* { this.popState(); return "acc_title_value"; }
"accDescr"\s*":"\s* { this.pushState("acc_descr");return 'acc_descr'; }
<acc_descr>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_descr_value"; }
<acc_descr>(?!\n|;)*[^\n]* { this.popState(); return "acc_descr_value"; }
"accDescr"\s*"{"\s* { this.pushState("acc_descr_multiline");}
<acc_descr_multiline>"{" { this.popState(); }
<acc_descr_multiline>[^\}]* { return "acc_descr_multiline_value"; }
Expand Down
Loading