From 48d267c6dced72e16ddd225ecfcbffcb0219f025 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Thu, 30 Mar 2023 22:08:50 +0530 Subject: [PATCH] fix(#4137): Cleanup comments before parsing --- docs/config/setup/modules/mermaidAPI.md | 2 +- packages/mermaid/src/Diagram.ts | 4 +- .../mermaid/src/diagram-api/comments.spec.ts | 70 +++++++++++++++++++ packages/mermaid/src/diagram-api/comments.ts | 8 +++ .../src/diagrams/er/parser/erDiagram.jison | 4 -- .../flowchart/parser/flow-comments.spec.js | 21 +++--- .../src/diagrams/flowchart/parser/flow.jison | 2 - .../diagrams/flowchart/parser/flow.spec.js | 3 +- packages/mermaid/src/mermaidAPI.ts | 3 - 9 files changed, 96 insertions(+), 21 deletions(-) create mode 100644 packages/mermaid/src/diagram-api/comments.spec.ts create mode 100644 packages/mermaid/src/diagram-api/comments.ts diff --git a/docs/config/setup/modules/mermaidAPI.md b/docs/config/setup/modules/mermaidAPI.md index b94fc8b94..4b8f57bd7 100644 --- a/docs/config/setup/modules/mermaidAPI.md +++ b/docs/config/setup/modules/mermaidAPI.md @@ -95,7 +95,7 @@ mermaid.initialize(config); #### Defined in -[mermaidAPI.ts:662](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L662) +[mermaidAPI.ts:659](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L659) ## Functions diff --git a/packages/mermaid/src/Diagram.ts b/packages/mermaid/src/Diagram.ts index 3010ce130..dd26d8de8 100644 --- a/packages/mermaid/src/Diagram.ts +++ b/packages/mermaid/src/Diagram.ts @@ -5,6 +5,7 @@ import { detectType, getDiagramLoader } from './diagram-api/detectType'; import { extractFrontMatter } from './diagram-api/frontmatter'; import { UnknownDiagramError } from './errors'; import { DetailedError } from './utils'; +import { cleanupComments } from './diagram-api/comments'; export type ParseErrorFunction = (err: string | DetailedError | unknown, hash?: any) => void; @@ -43,7 +44,8 @@ export class Diagram { // Similarly, we can't do this in getDiagramFromText() because some code // calls diagram.db.clear(), which would reset anything set by // extractFrontMatter(). - this.parser.parse = (text: string) => originalParse(extractFrontMatter(text, this.db)); + this.parser.parse = (text: string) => + originalParse(cleanupComments(extractFrontMatter(text, this.db))); this.parser.parser.yy = this.db; if (diagram.init) { diagram.init(cnf); diff --git a/packages/mermaid/src/diagram-api/comments.spec.ts b/packages/mermaid/src/diagram-api/comments.spec.ts new file mode 100644 index 000000000..2435db0a0 --- /dev/null +++ b/packages/mermaid/src/diagram-api/comments.spec.ts @@ -0,0 +1,70 @@ +// tests to check that comments are removed + +import { cleanupComments } from './comments'; +import { describe, it, expect } from 'vitest'; + +describe('comments', () => { + it('should remove comments', () => { + const text = ` + +%% This is a comment +%% This is another comment +graph TD + A-->B +%% This is a comment +`; + expect(cleanupComments(text)).toMatchInlineSnapshot(` + " + + graph TD + A-->B + + " + `); + }); + + it('should keep init statements when removing comments', () => { + const text = ` +%% This is a comment + +%% This is another comment +%%{init: {'theme': 'forest'}}%% +%%{init: {'theme': 'space after ending'}}%% +graph TD + A-->B + + B-->C +%% This is a comment +`; + expect(cleanupComments(text)).toMatchInlineSnapshot(` + " + + %%{init: {'theme': 'forest'}}%% + %%{init: {'theme': 'space after ending'}}%% + graph TD + A-->B + + B-->C + + " + `); + }); + + it('should remove indented comments', () => { + const text = ` +%% This is a comment +graph TD + A-->B + %% This is a comment + C-->D +`; + expect(cleanupComments(text)).toMatchInlineSnapshot(` + " + graph TD + A-->B + + C-->D + " + `); + }); +}); diff --git a/packages/mermaid/src/diagram-api/comments.ts b/packages/mermaid/src/diagram-api/comments.ts new file mode 100644 index 000000000..19fafe15c --- /dev/null +++ b/packages/mermaid/src/diagram-api/comments.ts @@ -0,0 +1,8 @@ +/** + * Remove all lines starting with `%%` from the text that don't contain a `%%{` + * @param text - The text to remove comments from + * @returns cleaned text + */ +export const cleanupComments = (text: string): string => { + return text.replace(/^\s*%%(?!{)[^\n]+/gm, ''); +}; diff --git a/packages/mermaid/src/diagrams/er/parser/erDiagram.jison b/packages/mermaid/src/diagrams/er/parser/erDiagram.jison index d9f03c387..8ffa87c63 100644 --- a/packages/mermaid/src/diagrams/er/parser/erDiagram.jison +++ b/packages/mermaid/src/diagrams/er/parser/erDiagram.jison @@ -19,8 +19,6 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili ":" { this.popState(); this.begin('arg_directive'); return ':'; } \}\%\% { this.popState(); this.popState(); return 'close_directive'; } ((?:(?!\}\%\%).|\n)*) return 'arg_directive'; -\%%(?!\{)[^\n]* /* skip comments */ -[^\}]\%\%[^\n]* /* skip comments */ [\n]+ return 'NEWLINE'; \s+ /* skip whitespace */ [\s]+ return 'SPACE'; @@ -35,8 +33,6 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili [A-Za-z_][A-Za-z0-9\-_\[\]\(\)]* return 'ATTRIBUTE_WORD' \"[^"]*\" return 'COMMENT'; [\n]+ /* nothing */ -\%%(?!\{)[^\n]* /* skip comments in attribute block */ -[^\}]\%\%[^\n]* /* skip comments in attribute block */ "}" { this.popState(); return 'BLOCK_STOP'; } . return yytext[0]; diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow-comments.spec.js b/packages/mermaid/src/diagrams/flowchart/parser/flow-comments.spec.js index 7aeed304c..cb1e4a923 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow-comments.spec.js +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow-comments.spec.js @@ -1,6 +1,7 @@ import flowDb from '../flowDb'; import flow from './flow'; import { setConfig } from '../../../config'; +import { cleanupComments } from '../../../diagram-api/comments'; setConfig({ securityLevel: 'strict', @@ -13,7 +14,7 @@ describe('[Comments] when parsing', () => { }); it('should handle comments', function () { - const res = flow.parser.parse('graph TD;\n%% Comment\n A-->B;'); + const res = flow.parser.parse(cleanupComments('graph TD;\n%% Comment\n A-->B;')); const vert = flow.parser.yy.getVertices(); const edges = flow.parser.yy.getEdges(); @@ -28,7 +29,7 @@ describe('[Comments] when parsing', () => { }); it('should handle comments at the start', function () { - const res = flow.parser.parse('%% Comment\ngraph TD;\n A-->B;'); + const res = flow.parser.parse(cleanupComments('%% Comment\ngraph TD;\n A-->B;')); const vert = flow.parser.yy.getVertices(); const edges = flow.parser.yy.getEdges(); @@ -43,7 +44,7 @@ describe('[Comments] when parsing', () => { }); it('should handle comments at the end', function () { - const res = flow.parser.parse('graph TD;\n A-->B\n %% Comment at the end\n'); + const res = flow.parser.parse(cleanupComments('graph TD;\n A-->B\n %% Comment at the end\n')); const vert = flow.parser.yy.getVertices(); const edges = flow.parser.yy.getEdges(); @@ -58,7 +59,7 @@ describe('[Comments] when parsing', () => { }); it('should handle comments at the end no trailing newline', function () { - const res = flow.parser.parse('graph TD;\n A-->B\n%% Comment'); + const res = flow.parser.parse(cleanupComments('graph TD;\n A-->B\n%% Comment')); const vert = flow.parser.yy.getVertices(); const edges = flow.parser.yy.getEdges(); @@ -73,7 +74,7 @@ describe('[Comments] when parsing', () => { }); it('should handle comments at the end many trailing newlines', function () { - const res = flow.parser.parse('graph TD;\n A-->B\n%% Comment\n\n\n'); + const res = flow.parser.parse(cleanupComments('graph TD;\n A-->B\n%% Comment\n\n\n')); const vert = flow.parser.yy.getVertices(); const edges = flow.parser.yy.getEdges(); @@ -88,7 +89,7 @@ describe('[Comments] when parsing', () => { }); it('should handle no trailing newlines', function () { - const res = flow.parser.parse('graph TD;\n A-->B'); + const res = flow.parser.parse(cleanupComments('graph TD;\n A-->B')); const vert = flow.parser.yy.getVertices(); const edges = flow.parser.yy.getEdges(); @@ -103,7 +104,7 @@ describe('[Comments] when parsing', () => { }); it('should handle many trailing newlines', function () { - const res = flow.parser.parse('graph TD;\n A-->B\n\n'); + const res = flow.parser.parse(cleanupComments('graph TD;\n A-->B\n\n')); const vert = flow.parser.yy.getVertices(); const edges = flow.parser.yy.getEdges(); @@ -118,7 +119,7 @@ describe('[Comments] when parsing', () => { }); it('should handle a comment with blank rows in-between', function () { - const res = flow.parser.parse('graph TD;\n\n\n %% Comment\n A-->B;'); + const res = flow.parser.parse(cleanupComments('graph TD;\n\n\n %% Comment\n A-->B;')); const vert = flow.parser.yy.getVertices(); const edges = flow.parser.yy.getEdges(); @@ -134,7 +135,9 @@ describe('[Comments] when parsing', () => { it('should handle a comment with mermaid flowchart code in them', function () { const res = flow.parser.parse( - 'graph TD;\n\n\n %% Test od>Odd shape]-->|Two line
edge comment|ro;\n A-->B;' + cleanupComments( + 'graph TD;\n\n\n %% Test od>Odd shape]-->|Two line
edge comment|ro;\n A-->B;' + ) ); const vert = flow.parser.yy.getVertices(); diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison index e2dad5881..0aeb30062 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison @@ -27,8 +27,6 @@ ":" { this.popState(); this.begin('arg_directive'); return ':'; } \}\%\% { this.popState(); this.popState(); return 'close_directive'; } ((?:(?!\}\%\%).|\n)*) return 'arg_directive'; -\%\%(?!\{)[^\n]* /* skip comments */ -[^\}]\%\%[^\n]* /* skip comments */ accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; } (?!\n|;|#)*[^\n]* { this.popState(); return "acc_title_value"; } accDescr\s*":"\s* { this.begin("acc_descr");return 'acc_descr'; } diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.spec.js b/packages/mermaid/src/diagrams/flowchart/parser/flow.spec.js index 6b440da79..692e72f37 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.spec.js +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.spec.js @@ -1,6 +1,7 @@ import flowDb from '../flowDb'; import flow from './flow'; import { setConfig } from '../../../config'; +import { cleanupComments } from '../../../diagram-api/comments'; setConfig({ securityLevel: 'strict', @@ -13,7 +14,7 @@ describe('parsing a flow chart', function () { }); it('should handle a trailing whitespaces after statements', function () { - const res = flow.parser.parse('graph TD;\n\n\n %% Comment\n A-->B; \n B-->C;'); + const res = flow.parser.parse(cleanupComments('graph TD;\n\n\n %% Comment\n A-->B; \n B-->C;')); const vert = flow.parser.yy.getVertices(); const edges = flow.parser.yy.getEdges(); diff --git a/packages/mermaid/src/mermaidAPI.ts b/packages/mermaid/src/mermaidAPI.ts index dba629477..1b440672a 100644 --- a/packages/mermaid/src/mermaidAPI.ts +++ b/packages/mermaid/src/mermaidAPI.ts @@ -399,9 +399,6 @@ const render = async function ( // clean up text CRLFs text = text.replace(/\r\n?/g, '\n'); // parser problems on CRLF ignore all CR and leave LF;; - // eslint-disable-next-line unicorn/better-regex - text = text.replace(/\s*%%[^{\ninit].*\n/gm, '\n'); // remove comments from text to avoid issues with parser - const idSelector = '#' + id; const iFrameID = 'i' + id; const iFrameID_selector = '#' + iFrameID;