mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-24 17:59:39 +02:00
Compare commits
4 Commits
subgraph-t
...
fix/er-dia
Author | SHA1 | Date | |
---|---|---|---|
![]() |
09b74f1c29 | ||
![]() |
880da21908 | ||
![]() |
38191243be | ||
![]() |
4c1e170f4a |
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
'mermaid': patch
|
|
||||||
---
|
|
||||||
|
|
||||||
fix: Resolved parsing error where direction TD was not recognized within subgraphs
|
|
5
.changeset/sweet-games-build.md
Normal file
5
.changeset/sweet-games-build.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'mermaid': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix(er-diagram): prevent syntax error when using 'u', numbers, and decimals in node names
|
@@ -369,4 +369,92 @@ ORDER ||--|{ LINE-ITEM : contains
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Special characters and numbers syntax', () => {
|
||||||
|
it('should render ER diagram with numeric entity names', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`
|
||||||
|
erDiagram
|
||||||
|
1 ||--|| ORDER : places
|
||||||
|
ORDER ||--|{ 2 : contains
|
||||||
|
2 ||--o{ 3.5 : references
|
||||||
|
`,
|
||||||
|
{ logLevel: 1 }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render ER diagram with "u" character in entity names and cardinality', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`
|
||||||
|
erDiagram
|
||||||
|
CUSTOMER ||--|| u : has
|
||||||
|
u ||--|| ORDER : places
|
||||||
|
PROJECT u--o{ TEAM_MEMBER : "parent"
|
||||||
|
`,
|
||||||
|
{ logLevel: 1 }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render ER diagram with decimal numbers in relationships', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`
|
||||||
|
erDiagram
|
||||||
|
2.5 ||--|| 1.5 : has
|
||||||
|
CUSTOMER ||--o{ 3.14 : references
|
||||||
|
1.0 ||--|{ ORDER : contains
|
||||||
|
`,
|
||||||
|
{ logLevel: 1 }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render ER diagram with numeric entity names and attributes', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`
|
||||||
|
erDiagram
|
||||||
|
1 {
|
||||||
|
string name
|
||||||
|
int value
|
||||||
|
}
|
||||||
|
1 ||--|| ORDER : places
|
||||||
|
ORDER {
|
||||||
|
float price
|
||||||
|
string description
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
{ logLevel: 1 }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render complex ER diagram with mixed special entity names', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`
|
||||||
|
erDiagram
|
||||||
|
CUSTOMER ||--o{ 1 : places
|
||||||
|
1 ||--|{ u : contains
|
||||||
|
1.5
|
||||||
|
u ||--|| 2.5 : processes
|
||||||
|
2.5 {
|
||||||
|
string id
|
||||||
|
float value
|
||||||
|
}
|
||||||
|
u {
|
||||||
|
varchar(50) name
|
||||||
|
int count
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
{ logLevel: 1 }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('should render ER diagram with numeric entity names and attributes', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`erDiagram
|
||||||
|
PRODUCT ||--o{ ORDER-ITEM : has
|
||||||
|
1.5
|
||||||
|
u
|
||||||
|
1
|
||||||
|
`,
|
||||||
|
{ logLevel: 1 }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -973,19 +973,4 @@ graph TD
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('70: should render a subgraph with direction TD', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
flowchart LR
|
|
||||||
subgraph A
|
|
||||||
direction TD
|
|
||||||
a --> b
|
|
||||||
end
|
|
||||||
`,
|
|
||||||
{
|
|
||||||
fontFamily: 'courier',
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@@ -66,12 +66,15 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
|
|||||||
\}\| return 'ONE_OR_MORE';
|
\}\| return 'ONE_OR_MORE';
|
||||||
"one" return 'ONLY_ONE';
|
"one" return 'ONLY_ONE';
|
||||||
"only one" return 'ONLY_ONE';
|
"only one" return 'ONLY_ONE';
|
||||||
"1" return 'ONLY_ONE';
|
[0-9]+\.[0-9]+ return 'DECIMAL_NUM';
|
||||||
|
"1"(?=\s+[A-Za-z_"']) return 'ONLY_ONE';
|
||||||
|
"1" return 'ENTITY_ONE';
|
||||||
|
[0-9]+ return 'NUM';
|
||||||
\|\| return 'ONLY_ONE';
|
\|\| return 'ONLY_ONE';
|
||||||
o\| return 'ZERO_OR_ONE';
|
o\| return 'ZERO_OR_ONE';
|
||||||
o\{ return 'ZERO_OR_MORE';
|
o\{ return 'ZERO_OR_MORE';
|
||||||
\|\{ return 'ONE_OR_MORE';
|
\|\{ return 'ONE_OR_MORE';
|
||||||
\s*u return 'MD_PARENT';
|
u(?=[\.\-\|]) return 'MD_PARENT';
|
||||||
\.\. return 'NON_IDENTIFYING';
|
\.\. return 'NON_IDENTIFYING';
|
||||||
\-\- return 'IDENTIFYING';
|
\-\- return 'IDENTIFYING';
|
||||||
"to" return 'IDENTIFYING';
|
"to" return 'IDENTIFYING';
|
||||||
@@ -80,13 +83,15 @@ o\{ return 'ZERO_OR_MORE';
|
|||||||
\-\. return 'NON_IDENTIFYING';
|
\-\. return 'NON_IDENTIFYING';
|
||||||
<style>([^\x00-\x7F]|\w|\-|\*)+ return 'STYLE_TEXT';
|
<style>([^\x00-\x7F]|\w|\-|\*)+ return 'STYLE_TEXT';
|
||||||
<style>';' return 'SEMI';
|
<style>';' return 'SEMI';
|
||||||
([^\x00-\x7F]|\w|\-|\*)+ return 'UNICODE_TEXT';
|
([^\x00-\x7F]|\w|\-|\*|\.)+ return 'UNICODE_TEXT';
|
||||||
[0-9] return 'NUM';
|
|
||||||
. return yytext[0];
|
. return yytext[0];
|
||||||
<<EOF>> return 'EOF';
|
<<EOF>> return 'EOF';
|
||||||
|
|
||||||
/lex
|
/lex
|
||||||
|
|
||||||
|
%left 'ONLY_ONE'
|
||||||
|
%left 'ZERO_OR_ONE' 'ZERO_OR_MORE' 'ONE_OR_MORE' 'MD_PARENT'
|
||||||
|
|
||||||
%start start
|
%start start
|
||||||
%% /* language grammar */
|
%% /* language grammar */
|
||||||
|
|
||||||
@@ -228,6 +233,9 @@ styleComponent: STYLE_TEXT | NUM | COLON | BRKT;
|
|||||||
entityName
|
entityName
|
||||||
: 'ENTITY_NAME' { $$ = $1.replace(/"/g, ''); }
|
: 'ENTITY_NAME' { $$ = $1.replace(/"/g, ''); }
|
||||||
| 'UNICODE_TEXT' { $$ = $1; }
|
| 'UNICODE_TEXT' { $$ = $1; }
|
||||||
|
| 'NUM' { $$ = $1; }
|
||||||
|
| 'DECIMAL_NUM' { $$ = $1; }
|
||||||
|
| 'ENTITY_ONE' { $$ = $1; }
|
||||||
;
|
;
|
||||||
|
|
||||||
attributes
|
attributes
|
||||||
|
@@ -1001,4 +1001,90 @@ describe('when parsing ER diagram it...', function () {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('syntax fixes for special characters and numbers', function () {
|
||||||
|
describe('standalone entity names', function () {
|
||||||
|
it('should allow number "1" as standalone entity', function () {
|
||||||
|
erDiagram.parser.parse(`erDiagram\nCUSTOMER }|..|{ DELIVERY-ADDRESS : has\n1`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow character "u" as standalone entity', function () {
|
||||||
|
erDiagram.parser.parse(`erDiagram\nCUSTOMER }|..|{ DELIVERY-ADDRESS : has\nu`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow decimal numbers as standalone entities', function () {
|
||||||
|
erDiagram.parser.parse(`erDiagram\nCUSTOMER }|..|{ DELIVERY-ADDRESS : has\n2.5`);
|
||||||
|
erDiagram.parser.parse(`erDiagram\nCUSTOMER }|..|{ DELIVERY-ADDRESS : has\n1.5`);
|
||||||
|
erDiagram.parser.parse(`erDiagram\nCUSTOMER }|..|{ DELIVERY-ADDRESS : has\n0.1`);
|
||||||
|
erDiagram.parser.parse(`erDiagram\nCUSTOMER }|..|{ DELIVERY-ADDRESS : has\n99.99`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('entity names with attributes', function () {
|
||||||
|
it('should allow "u" as entity name with attributes', function () {
|
||||||
|
erDiagram.parser.parse(`erDiagram\nu {\nstring name\nint id\n}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow number "1" as entity name with attributes', function () {
|
||||||
|
erDiagram.parser.parse(`erDiagram\n1 {\nstring name\nint id\n}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow decimal numbers as entity names with attributes', function () {
|
||||||
|
erDiagram.parser.parse(`erDiagram\n2.5 {\nstring name\nint id\n}`);
|
||||||
|
erDiagram.parser.parse(`erDiagram\n1.5 {\nstring value\n}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('entity names in relationships', function () {
|
||||||
|
it('should allow "u" in relationships', function () {
|
||||||
|
erDiagram.parser.parse(`erDiagram\nCUSTOMER ||--|| u : has`);
|
||||||
|
erDiagram.parser.parse(`erDiagram\nu ||--|| ORDER : places`);
|
||||||
|
erDiagram.parser.parse(`erDiagram\nu ||--|| v : connects`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow numbers in relationships', function () {
|
||||||
|
erDiagram.parser.parse(`erDiagram\nCUSTOMER ||--|| 1 : has`);
|
||||||
|
erDiagram.parser.parse(`erDiagram\n1 ||--|| ORDER : places`);
|
||||||
|
erDiagram.parser.parse(`erDiagram\n1 ||--|| 2 : connects`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow decimal numbers in relationships', function () {
|
||||||
|
erDiagram.parser.parse(`erDiagram\nCUSTOMER ||--|| 2.5 : has`);
|
||||||
|
erDiagram.parser.parse(`erDiagram\n1.5 ||--|| ORDER : places`);
|
||||||
|
erDiagram.parser.parse(`erDiagram\n2.5 ||--|| 5.5 : connects`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('mixed scenarios', function () {
|
||||||
|
it('should handle complex diagram with special entity names', function () {
|
||||||
|
erDiagram.parser.parse(
|
||||||
|
`erDiagram
|
||||||
|
CUSTOMER ||--o{ 1 : places
|
||||||
|
1 ||--|{ u : contains
|
||||||
|
u {
|
||||||
|
string name
|
||||||
|
int quantity
|
||||||
|
}
|
||||||
|
"2.5" ||--|| ORDER : processes
|
||||||
|
ORDER {
|
||||||
|
int id
|
||||||
|
date created
|
||||||
|
}
|
||||||
|
`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle attributes with numbers in names (but not starting)', function () {
|
||||||
|
erDiagram.parser.parse(
|
||||||
|
`erDiagram
|
||||||
|
ENTITY {
|
||||||
|
string name1
|
||||||
|
int value2
|
||||||
|
float point3_5
|
||||||
|
}
|
||||||
|
`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -140,7 +140,6 @@ that id.
|
|||||||
.*direction\s+BT[^\n]* return 'direction_bt';
|
.*direction\s+BT[^\n]* return 'direction_bt';
|
||||||
.*direction\s+RL[^\n]* return 'direction_rl';
|
.*direction\s+RL[^\n]* return 'direction_rl';
|
||||||
.*direction\s+LR[^\n]* return 'direction_lr';
|
.*direction\s+LR[^\n]* return 'direction_lr';
|
||||||
.*direction\s+TD[^\n]* return 'direction_td';
|
|
||||||
|
|
||||||
[^\s\"]+\@(?=[^\{\"]) { return 'LINK_ID'; }
|
[^\s\"]+\@(?=[^\{\"]) { return 'LINK_ID'; }
|
||||||
[0-9]+ return 'NUM';
|
[0-9]+ return 'NUM';
|
||||||
@@ -627,8 +626,6 @@ direction
|
|||||||
{ $$={stmt:'dir', value:'RL'};}
|
{ $$={stmt:'dir', value:'RL'};}
|
||||||
| direction_lr
|
| direction_lr
|
||||||
{ $$={stmt:'dir', value:'LR'};}
|
{ $$={stmt:'dir', value:'LR'};}
|
||||||
| direction_td
|
|
||||||
{ $$={stmt:'dir', value:'TD'};}
|
|
||||||
;
|
;
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
@@ -309,21 +309,4 @@ describe('when parsing subgraphs', function () {
|
|||||||
expect(subgraphA.nodes).toContain('a');
|
expect(subgraphA.nodes).toContain('a');
|
||||||
expect(subgraphA.nodes).not.toContain('c');
|
expect(subgraphA.nodes).not.toContain('c');
|
||||||
});
|
});
|
||||||
it('should correctly parse direction TD inside a subgraph', function () {
|
|
||||||
const res = flow.parser.parse(`
|
|
||||||
graph LR
|
|
||||||
subgraph WithTD
|
|
||||||
direction TD
|
|
||||||
A1 --> A2
|
|
||||||
end
|
|
||||||
`);
|
|
||||||
|
|
||||||
const subgraphs = flow.parser.yy.getSubGraphs();
|
|
||||||
expect(subgraphs.length).toBe(1);
|
|
||||||
const subgraph = subgraphs[0];
|
|
||||||
|
|
||||||
expect(subgraph.dir).toBe('TD');
|
|
||||||
expect(subgraph.nodes).toContain('A1');
|
|
||||||
expect(subgraph.nodes).toContain('A2');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user