mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-11-30 01:24:24 +01:00
Compare commits
268 Commits
release/10
...
feature/fr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3944e9ac00 | ||
|
|
7e251ee8bc | ||
|
|
6e0f41180f | ||
|
|
52f05552a1 | ||
|
|
bb01b3972d | ||
|
|
1a50a326cb | ||
|
|
fd45dbfc14 | ||
|
|
5c9eafabae | ||
|
|
2b9dc0ea80 | ||
|
|
8ac7dc81e0 | ||
|
|
2967b3c1bb | ||
|
|
534fd85339 | ||
|
|
7298008374 | ||
|
|
844a175039 | ||
|
|
fd35a54735 | ||
|
|
a6e6c3fb18 | ||
|
|
31c0a0cbab | ||
|
|
767baa4ec6 | ||
|
|
f422a66dde | ||
|
|
fae976e994 | ||
|
|
f4c62436ea | ||
|
|
21d0998db8 | ||
|
|
0899f7918a | ||
|
|
7a547abd89 | ||
|
|
72fa3488b5 | ||
|
|
cfffba2817 | ||
|
|
e7ee3eb9ea | ||
|
|
b85c011cd1 | ||
|
|
aec97d68cc | ||
|
|
7b7e281ec7 | ||
|
|
35cc34d422 | ||
|
|
edb6ceae43 | ||
|
|
ef8b75a6da | ||
|
|
02daf5417b | ||
|
|
77d8e15dc4 | ||
|
|
62142089f1 | ||
|
|
defe40692a | ||
|
|
337ff3c32b | ||
|
|
744cc792f4 | ||
|
|
99978da55b | ||
|
|
50eb3cf1c9 | ||
|
|
c1b9c54fc9 | ||
|
|
0d179c501e | ||
|
|
085e8f78b3 | ||
|
|
f9d4a62609 | ||
|
|
2a8374312f | ||
|
|
b60410161d | ||
|
|
d219f92a19 | ||
|
|
3f3a7340e3 | ||
|
|
cb5f70c139 | ||
|
|
99c1758490 | ||
|
|
43217e1395 | ||
|
|
aa34b99203 | ||
|
|
541ee1eade | ||
|
|
f01ad644e3 | ||
|
|
9538233573 | ||
|
|
5a2ea7c297 | ||
|
|
22b172d873 | ||
|
|
92098e23eb | ||
|
|
591cb794eb | ||
|
|
f9d978859e | ||
|
|
6170538c47 | ||
|
|
c37c494a1e | ||
|
|
a0b80f5490 | ||
|
|
493023118f | ||
|
|
c5e7e6040c | ||
|
|
c938c7a438 | ||
|
|
c7bcf72c69 | ||
|
|
da602add5d | ||
|
|
9049a99c8d | ||
|
|
60957519c8 | ||
|
|
8a8e062342 | ||
|
|
1721282182 | ||
|
|
d8dd68cad2 | ||
|
|
9b9ad1da7d | ||
|
|
0d380bb2c7 | ||
|
|
120bdabee1 | ||
|
|
9ab048c7e9 | ||
|
|
a5a3ffc768 | ||
|
|
1078b1c8f6 | ||
|
|
dfeb25127b | ||
|
|
396bda8d95 | ||
|
|
cc70d37166 | ||
|
|
3f5da06bb0 | ||
|
|
be106befff | ||
|
|
980a5ac8b5 | ||
|
|
d5f11fc80a | ||
|
|
46de343785 | ||
|
|
c4113541e1 | ||
|
|
fb49f25eef | ||
|
|
a9681d1b1c | ||
|
|
545d361d3f | ||
|
|
66b579cd89 | ||
|
|
c55a0947be | ||
|
|
0b9f6d1ff9 | ||
|
|
7742a6c485 | ||
|
|
3e598f4e8e | ||
|
|
eb397fdb04 | ||
|
|
6c1afb7a8d | ||
|
|
fee6f459a8 | ||
|
|
e128a11f3b | ||
|
|
38dc17f426 | ||
|
|
453c67e5ea | ||
|
|
a69a97fdd9 | ||
|
|
820cc48c11 | ||
|
|
06e4a6398c | ||
|
|
85a988c31c | ||
|
|
776035553a | ||
|
|
ca1cdb1d94 | ||
|
|
5485517b27 | ||
|
|
96380600d9 | ||
|
|
da8112afa5 | ||
|
|
bae9a7c1b8 | ||
|
|
776b219c03 | ||
|
|
4471001ef2 | ||
|
|
bcb0817ecd | ||
|
|
646f7662d0 | ||
|
|
9563b22132 | ||
|
|
6df76d93e5 | ||
|
|
e6a18eea91 | ||
|
|
7da38b9afe | ||
|
|
71205f5bd6 | ||
|
|
1802f5d0c0 | ||
|
|
ef29388d68 | ||
|
|
e1379ee048 | ||
|
|
4845635f48 | ||
|
|
34bf618ecb | ||
|
|
c9db0ee749 | ||
|
|
850513fa3d | ||
|
|
aaf333676c | ||
|
|
5a165e4c1d | ||
|
|
ee6fa94aa2 | ||
|
|
23b6d53f80 | ||
|
|
f4671e4e3a | ||
|
|
971215e353 | ||
|
|
199fdce982 | ||
|
|
90c8dd1dab | ||
|
|
f8ebfeefec | ||
|
|
8db2fb8f90 | ||
|
|
e2fac72166 | ||
|
|
c954e0eb1d | ||
|
|
de37efefd7 | ||
|
|
bdfd8974d4 | ||
|
|
95efc1786f | ||
|
|
0c55e4c2ad | ||
|
|
10d5b5d1d6 | ||
|
|
23fea41fb4 | ||
|
|
a4f778f4de | ||
|
|
bb6664a2c6 | ||
|
|
939d082a8e | ||
|
|
b26449f338 | ||
|
|
b99eb8b624 | ||
|
|
ffdfe11f10 | ||
|
|
7465384e1d | ||
|
|
aecf451ed1 | ||
|
|
7d69ad2d5b | ||
|
|
74fa9956a3 | ||
|
|
e33340331a | ||
|
|
bdd9394d0b | ||
|
|
7ebfc272e4 | ||
|
|
b87f1f2098 | ||
|
|
c9c2f6c121 | ||
|
|
585834c561 | ||
|
|
dc57fcf7e7 | ||
|
|
8b79060829 | ||
|
|
daf43f8d34 | ||
|
|
4cdb43f791 | ||
|
|
660162686b | ||
|
|
6e2bf00a43 | ||
|
|
cc01ff9129 | ||
|
|
7dc985712a | ||
|
|
e3c5e6f095 | ||
|
|
ef4f22841f | ||
|
|
c292c897a1 | ||
|
|
cc7fd337ac | ||
|
|
6b41420ddd | ||
|
|
351b229c37 | ||
|
|
266a5e8133 | ||
|
|
55963107d0 | ||
|
|
a7e37d0c8b | ||
|
|
ead48600be | ||
|
|
834c67ecaa | ||
|
|
1a99825ab2 | ||
|
|
b5456813e8 | ||
|
|
ed4feaebf2 | ||
|
|
844f9d96e7 | ||
|
|
bed05ce061 | ||
|
|
f269f8cc5b | ||
|
|
e25763d645 | ||
|
|
6c12bbb80f | ||
|
|
d8897426cd | ||
|
|
9eed2e278b | ||
|
|
1df68f9a5f | ||
|
|
1397a873c2 | ||
|
|
1dfb400a46 | ||
|
|
35299978a4 | ||
|
|
165e0b6c68 | ||
|
|
304d8844f0 | ||
|
|
9655ba9e63 | ||
|
|
fd461b7860 | ||
|
|
30a9b5574d | ||
|
|
edf94790f2 | ||
|
|
20cd685ae3 | ||
|
|
47c100809b | ||
|
|
651274bc6f | ||
|
|
4cfbd0d380 | ||
|
|
45d92769aa | ||
|
|
474e0b9c82 | ||
|
|
087738df78 | ||
|
|
fa8e02721a | ||
|
|
4b9773a272 | ||
|
|
fd88b424b4 | ||
|
|
3ab0e9998d | ||
|
|
f48a9c8696 | ||
|
|
eea0ea5e07 | ||
|
|
20011c6882 | ||
|
|
0cc8f89115 | ||
|
|
8ff06e88ce | ||
|
|
3fa3ed7b18 | ||
|
|
0a4e5f5f6b | ||
|
|
0d7cc748b8 | ||
|
|
69c91ae5ed | ||
|
|
7adb1bccb3 | ||
|
|
5b987dee93 | ||
|
|
3496f275bc | ||
|
|
b46c28425e | ||
|
|
38ba45f735 | ||
|
|
925e76e283 | ||
|
|
760548335c | ||
|
|
0a6fc1b582 | ||
|
|
30a66533bc | ||
|
|
41c5152015 | ||
|
|
b0ae0708e1 | ||
|
|
796a761a7d | ||
|
|
09c4a26509 | ||
|
|
e5907ec5ea | ||
|
|
daa98a2f54 | ||
|
|
99da3d7cd5 | ||
|
|
ce9d0e2e6a | ||
|
|
ae8860eec3 | ||
|
|
67d287f85e | ||
|
|
9c2b95fc3c | ||
|
|
3a22d4a501 | ||
|
|
cecf759b0b | ||
|
|
35c6b671de | ||
|
|
c894c1f5b5 | ||
|
|
a92571d588 | ||
|
|
34a47706fd | ||
|
|
906d909d87 | ||
|
|
1d0aa763de | ||
|
|
f6dc089ddf | ||
|
|
95e01b4935 | ||
|
|
9145a9e69e | ||
|
|
6941814729 | ||
|
|
32d3001e2a | ||
|
|
452e543e77 | ||
|
|
23a5832fc9 | ||
|
|
8794fa0b38 | ||
|
|
f2338f5b66 | ||
|
|
5aba2fed8b | ||
|
|
bd6795032f | ||
|
|
c17b723295 | ||
|
|
231a9630df | ||
|
|
bdb967e0a8 | ||
|
|
ea3fbbd58d | ||
|
|
afea3e8f37 | ||
|
|
4e7dbf76cc | ||
|
|
a3901f691a |
44
.github/lychee.toml
vendored
Normal file
44
.github/lychee.toml
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
############################# Display #############################
|
||||
|
||||
# Verbose program output
|
||||
# Accepts log level: "error", "warn", "info", "debug", "trace"
|
||||
verbose = "debug"
|
||||
|
||||
# Don't show interactive progress bar while checking links.
|
||||
no_progress = true
|
||||
|
||||
############################# Cache ###############################
|
||||
|
||||
# Enable link caching. This can be helpful to avoid checking the same links on
|
||||
# multiple runs.
|
||||
cache = true
|
||||
|
||||
# Discard all cached requests older than this duration.
|
||||
max_cache_age = "1d"
|
||||
|
||||
############################# Requests ############################
|
||||
|
||||
# Comma-separated list of accepted status codes for valid links.
|
||||
accept = [200, 429]
|
||||
|
||||
############################# Exclusions ##########################
|
||||
|
||||
# Exclude URLs and mail addresses from checking (supports regex).
|
||||
exclude = [
|
||||
# Network error: Forbidden
|
||||
"https://codepen.io",
|
||||
|
||||
# Timeout error, maybe Twitter has anti-bot defenses against GitHub's CI servers?
|
||||
"https://twitter.com/mermaidjs_",
|
||||
|
||||
# Don't check files that are generated during the build via `pnpm docs:code`
|
||||
'packages/mermaid/src/docs/config/setup/*',
|
||||
|
||||
# Ignore slack invite
|
||||
"https://join.slack.com/"
|
||||
]
|
||||
|
||||
# Exclude all private IPs from checking.
|
||||
# Equivalent to setting `exclude_private`, `exclude_link_local`, and
|
||||
# `exclude_loopback` to true.
|
||||
exclude_all_private = true
|
||||
15
.github/release-drafter.yml
vendored
15
.github/release-drafter.yml
vendored
@@ -1,14 +1,27 @@
|
||||
name-template: '$NEXT_PATCH_VERSION'
|
||||
tag-template: '$NEXT_PATCH_VERSION'
|
||||
categories:
|
||||
- title: '🚨 **Breaking Changes**'
|
||||
labels:
|
||||
- 'Breaking Change'
|
||||
- title: '🚀 Features'
|
||||
labels:
|
||||
- 'Type: Enhancement'
|
||||
- 'feature' # deprecated, new PRs shouldn't have this
|
||||
- title: '🐛 Bug Fixes'
|
||||
labels:
|
||||
- 'Type: Bug / Error'
|
||||
- 'fix' # deprecated, new PRs shouldn't have this
|
||||
- title: '🧰 Maintenance'
|
||||
label: 'Type: Other'
|
||||
labels:
|
||||
- 'Type: Other'
|
||||
- 'chore' # deprecated, new PRs shouldn't have this
|
||||
- title: '⚡️ Performance'
|
||||
labels:
|
||||
- 'Type: Performance'
|
||||
- title: '📚 Documentation'
|
||||
labels:
|
||||
- 'Area: Documentation'
|
||||
change-template: '- $TITLE (#$NUMBER) @$AUTHOR'
|
||||
sort-by: title
|
||||
sort-direction: ascending
|
||||
|
||||
7
.github/workflows/link-checker.yml
vendored
7
.github/workflows/link-checker.yml
vendored
@@ -20,7 +20,7 @@ on:
|
||||
- cron: '30 8 * * *'
|
||||
|
||||
jobs:
|
||||
linkChecker:
|
||||
link-checker:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
# lychee only uses the GITHUB_TOKEN to avoid rate-limiting
|
||||
@@ -39,10 +39,7 @@ jobs:
|
||||
uses: lycheeverse/lychee-action@v1.8.0
|
||||
with:
|
||||
args: >-
|
||||
--verbose
|
||||
--no-progress
|
||||
--cache
|
||||
--max-cache-age 1d
|
||||
--config .github/lychee.toml
|
||||
packages/mermaid/src/docs/**/*.md
|
||||
README.md
|
||||
README.zh-CN.md
|
||||
|
||||
18
.github/workflows/lint.yml
vendored
18
.github/workflows/lint.yml
vendored
@@ -62,8 +62,22 @@ jobs:
|
||||
ERROR_MESSAGE+=' `pnpm run --filter mermaid types:build-config`'
|
||||
ERROR_MESSAGE+=' on your local machine.'
|
||||
echo "::error title=Lint failure::${ERROR_MESSAGE}"
|
||||
# make sure to return an error exitcode so that GitHub actions shows a red-cross
|
||||
exit 1
|
||||
# make sure to return an error exitcode so that GitHub actions shows a red-cross
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Verify no circular dependencies
|
||||
working-directory: ./packages/mermaid
|
||||
shell: bash
|
||||
run: |
|
||||
if ! pnpm run --filter mermaid checkCircle; then
|
||||
ERROR_MESSAGE='Circular dependency detected.'
|
||||
ERROR_MESSAGE+=' This should be fixed by removing the circular dependency.'
|
||||
ERROR_MESSAGE+=' Run `pnpm run --filter mermaid checkCircle` on your local machine'
|
||||
ERROR_MESSAGE+=' to see the circular dependency.'
|
||||
echo "::error title=Lint failure::${ERROR_MESSAGE}"
|
||||
# make sure to return an error exitcode so that GitHub actions shows a red-cross
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Verify Docs
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -43,3 +43,6 @@ stats/
|
||||
**/contributor-names.json
|
||||
.pnpm-store
|
||||
.nyc_output
|
||||
|
||||
demos/dev/**
|
||||
!/demos/dev/example.html
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
# These links are ignored by our link checker https://github.com/lycheeverse/lychee
|
||||
# The file allows you to list multiple regular expressions for exclusion (one pattern per line).
|
||||
|
||||
# Network error: Forbidden
|
||||
https://codepen.io
|
||||
|
||||
# Timeout error, maybe Twitter has anti-bot defenses against GitHub's CI servers?
|
||||
https://twitter.com/mermaidjs_
|
||||
|
||||
# Don't check files that are generated during the build via `pnpm docs:code`
|
||||
packages/mermaid/src/docs/config/setup/*
|
||||
|
||||
# Ignore localhost
|
||||
http://localhost:3333/
|
||||
|
||||
# Ignore slack invite
|
||||
https://join.slack.com/
|
||||
@@ -26,9 +26,14 @@ Install required packages:
|
||||
```bash
|
||||
# npx is required for first install as volta support for pnpm is not added yet.
|
||||
npx pnpm install
|
||||
pnpm test
|
||||
pnpm test # run unit tests
|
||||
pnpm dev # starts a dev server
|
||||
```
|
||||
|
||||
Open <http://localhost:9000> in your browser after starting the dev server.
|
||||
You can also duplicate the `example.html` file in `demos/dev`, rename it and add your own mermaid code to it.
|
||||
That will be served at <http://localhost:9000/dev/your-file-name.html>.
|
||||
|
||||
### Docker
|
||||
|
||||
If you are using docker and docker-compose, you have self-documented `run` bash script, which is a convenient alias for docker-compose commands:
|
||||
@@ -64,7 +69,7 @@ eg: `feature/2945_state-diagram-new-arrow-florbs`, `bug/1123_fix_random_ugly_red
|
||||
|
||||
Documentation is necessary for all non bugfix/refactoring changes.
|
||||
|
||||
Only make changes to files are in [`/packages/mermaid/src/docs`](packages/mermaid/src/docs)
|
||||
Only make changes to files that are in [`/packages/mermaid/src/docs`](packages/mermaid/src/docs)
|
||||
|
||||
**_DO NOT CHANGE FILES IN `/docs`_**
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
/**
|
||||
* Mocked pie (picChart) diagram renderer
|
||||
*/
|
||||
|
||||
import { vi } from 'vitest';
|
||||
|
||||
export const draw = vi.fn().mockImplementation(() => {
|
||||
return '';
|
||||
});
|
||||
|
||||
export default {
|
||||
draw,
|
||||
};
|
||||
8
__mocks__/pieRenderer.ts
Normal file
8
__mocks__/pieRenderer.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Mocked pie (picChart) diagram renderer
|
||||
*/
|
||||
import { vi } from 'vitest';
|
||||
|
||||
const draw = vi.fn().mockImplementation(() => '');
|
||||
|
||||
export const renderer = { draw };
|
||||
@@ -44,6 +44,7 @@
|
||||
"faber",
|
||||
"flatmap",
|
||||
"foswiki",
|
||||
"frontmatter",
|
||||
"ftplugin",
|
||||
"gantt",
|
||||
"gitea",
|
||||
@@ -106,6 +107,7 @@
|
||||
"rects",
|
||||
"reda",
|
||||
"redmine",
|
||||
"regexes",
|
||||
"rehype",
|
||||
"roledescription",
|
||||
"rozhkov",
|
||||
|
||||
@@ -14,7 +14,6 @@ describe('Configuration and directives - nodes should be light blue', () => {
|
||||
`,
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('Settings from initialize - nodes should be green', () => {
|
||||
imgSnapshotTest(
|
||||
@@ -28,7 +27,6 @@ graph TD
|
||||
end `,
|
||||
{ theme: 'forest' }
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('Settings from initialize overriding themeVariable - nodes should be red', () => {
|
||||
imgSnapshotTest(
|
||||
@@ -46,7 +44,6 @@ graph TD
|
||||
`,
|
||||
{ theme: 'base', themeVariables: { primaryColor: '#ff0000' }, logLevel: 0 }
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('Settings from directive - nodes should be grey', () => {
|
||||
imgSnapshotTest(
|
||||
@@ -62,7 +59,24 @@ graph TD
|
||||
`,
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('Settings from frontmatter - nodes should be grey', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
---
|
||||
config:
|
||||
theme: neutral
|
||||
---
|
||||
graph TD
|
||||
A(Start) --> B[/Another/]
|
||||
A[/Another/] --> C[End]
|
||||
subgraph section
|
||||
B
|
||||
C
|
||||
end
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('Settings from directive overriding theme variable - nodes should be red', () => {
|
||||
@@ -79,7 +93,6 @@ graph TD
|
||||
`,
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('Settings from initialize and directive - nodes should be grey', () => {
|
||||
imgSnapshotTest(
|
||||
@@ -95,7 +108,6 @@ graph TD
|
||||
`,
|
||||
{ theme: 'forest' }
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('Theme from initialize, directive overriding theme variable - nodes should be red', () => {
|
||||
imgSnapshotTest(
|
||||
@@ -111,8 +123,71 @@ graph TD
|
||||
`,
|
||||
{ theme: 'base' }
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('Theme from initialize, frontmatter overriding theme variable - nodes should be red', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
---
|
||||
config:
|
||||
theme: base
|
||||
themeVariables:
|
||||
primaryColor: '#ff0000'
|
||||
---
|
||||
graph TD
|
||||
A(Start) --> B[/Another/]
|
||||
A[/Another/] --> C[End]
|
||||
subgraph section
|
||||
B
|
||||
C
|
||||
end
|
||||
`,
|
||||
{ theme: 'forest' }
|
||||
);
|
||||
});
|
||||
it('Theme from initialize, frontmatter overriding theme variable, directive overriding primaryColor - nodes should be red', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
---
|
||||
config:
|
||||
theme: base
|
||||
themeVariables:
|
||||
primaryColor: '#00ff00'
|
||||
---
|
||||
%%{init: {'theme': 'base', 'themeVariables':{ 'primaryColor': '#ff0000'}}}%%
|
||||
graph TD
|
||||
A(Start) --> B[/Another/]
|
||||
A[/Another/] --> C[End]
|
||||
subgraph section
|
||||
B
|
||||
C
|
||||
end
|
||||
`,
|
||||
{ theme: 'forest' }
|
||||
);
|
||||
});
|
||||
|
||||
it('should render if values are not quoted properly', () => {
|
||||
// #ff0000 is not quoted properly, and will evaluate to null.
|
||||
// This test ensures that the rendering still works.
|
||||
imgSnapshotTest(
|
||||
`---
|
||||
config:
|
||||
theme: base
|
||||
themeVariables:
|
||||
primaryColor: #ff0000
|
||||
---
|
||||
graph TD
|
||||
A(Start) --> B[/Another/]
|
||||
A[/Another/] --> C[End]
|
||||
subgraph section
|
||||
B
|
||||
C
|
||||
end
|
||||
`,
|
||||
{ theme: 'forest' }
|
||||
);
|
||||
});
|
||||
|
||||
it('Theme variable from initialize, theme from directive - nodes should be red', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
@@ -127,13 +202,11 @@ graph TD
|
||||
`,
|
||||
{ themeVariables: { primaryColor: '#ff0000' } }
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
describe('when rendering several diagrams', () => {
|
||||
it('diagrams should not taint later diagrams', () => {
|
||||
const url = 'http://localhost:9000/theme-directives.html';
|
||||
cy.visit(url);
|
||||
cy.get('svg');
|
||||
cy.matchImageSnapshot('conf-and-directives.spec-when-rendering-several-diagrams-diagram-1');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -449,7 +449,7 @@ flowchart TD
|
||||
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
|
||||
);
|
||||
});
|
||||
it('65: text-color from classes', () => {
|
||||
it('65-1: text-color from classes', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
flowchart LR
|
||||
@@ -460,6 +460,31 @@ flowchart TD
|
||||
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
|
||||
);
|
||||
});
|
||||
it('65-2: bold text from classes', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
flowchart
|
||||
classDef cat fill:#f9d5e5, stroke:#233d4d,stroke-width:2px, font-weight:bold;
|
||||
CS(A long bold text to be viewed):::cat
|
||||
`,
|
||||
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
|
||||
);
|
||||
});
|
||||
it('65-3: bigger font from classes', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
flowchart
|
||||
Node1:::class1 --> Node2:::class2
|
||||
Node1:::class1 --> Node3:::class2
|
||||
Node3 --> Node4((I am a circle)):::larger
|
||||
|
||||
classDef class1 fill:lightblue
|
||||
classDef class2 fill:pink
|
||||
classDef larger font-size:30px,fill:yellow
|
||||
`,
|
||||
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
|
||||
);
|
||||
});
|
||||
it('66: More nested subgraph cases (TB)', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
|
||||
@@ -891,4 +891,27 @@ graph TD
|
||||
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
|
||||
);
|
||||
});
|
||||
it('66: apply class called default on node called default', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
graph TD
|
||||
classDef default fill:#a34,stroke:#000,stroke-width:4px,color:#fff
|
||||
hello --> default
|
||||
`,
|
||||
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
|
||||
);
|
||||
});
|
||||
|
||||
it('67: should be able to style default node independently', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
flowchart TD
|
||||
classDef default fill:#a34
|
||||
hello --> default
|
||||
|
||||
style default stroke:#000,stroke-width:4px
|
||||
`,
|
||||
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,89 +1,85 @@
|
||||
import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts';
|
||||
|
||||
describe('Pie Chart', () => {
|
||||
describe('pie chart', () => {
|
||||
it('should render a simple pie diagram', () => {
|
||||
imgSnapshotTest(
|
||||
`pie title Sports in Sweden
|
||||
"Bandy": 40
|
||||
"Ice-Hockey": 80
|
||||
"Football": 90
|
||||
`
|
||||
pie title Sports in Sweden
|
||||
"Bandy" : 40
|
||||
"Ice-Hockey" : 80
|
||||
"Football" : 90
|
||||
`,
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
|
||||
it('should render a simple pie diagram with long labels', () => {
|
||||
imgSnapshotTest(
|
||||
`pie title NETFLIX
|
||||
"Time spent looking for movie": 90
|
||||
"Time spent watching it": 10
|
||||
`
|
||||
pie title NETFLIX
|
||||
"Time spent looking for movie" : 90
|
||||
"Time spent watching it" : 10
|
||||
`,
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
|
||||
it('should render a simple pie diagram with capital letters for labels', () => {
|
||||
imgSnapshotTest(
|
||||
`pie title What Voldemort doesn't have?
|
||||
"FRIENDS": 2
|
||||
"FAMILY": 3
|
||||
"NOSE": 45
|
||||
`
|
||||
pie title What Voldemort doesn't have?
|
||||
"FRIENDS" : 2
|
||||
"FAMILY" : 3
|
||||
"NOSE" : 45
|
||||
`,
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
|
||||
it('should render a pie diagram when useMaxWidth is true (default)', () => {
|
||||
renderGraph(
|
||||
`
|
||||
pie title Sports in Sweden
|
||||
"Bandy" : 40
|
||||
"Ice-Hockey" : 80
|
||||
"Football" : 90
|
||||
`pie title Sports in Sweden
|
||||
"Bandy": 40
|
||||
"Ice-Hockey": 80
|
||||
"Football": 90
|
||||
`,
|
||||
{ pie: { useMaxWidth: true } }
|
||||
);
|
||||
cy.get('svg').should((svg) => {
|
||||
expect(svg).to.have.attr('width', '100%');
|
||||
// expect(svg).to.have.attr('height');
|
||||
// const height = parseFloat(svg.attr('height'));
|
||||
// expect(height).to.eq(450);
|
||||
const style = svg.attr('style');
|
||||
expect(style).to.match(/^max-width: [\d.]+px;$/);
|
||||
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
|
||||
expect(maxWidthValue).to.eq(984);
|
||||
});
|
||||
});
|
||||
|
||||
it('should render a pie diagram when useMaxWidth is false', () => {
|
||||
renderGraph(
|
||||
`
|
||||
pie title Sports in Sweden
|
||||
"Bandy" : 40
|
||||
"Ice-Hockey" : 80
|
||||
"Football" : 90
|
||||
`pie title Sports in Sweden
|
||||
"Bandy": 40
|
||||
"Ice-Hockey": 80
|
||||
"Football": 90
|
||||
`,
|
||||
{ pie: { useMaxWidth: false } }
|
||||
);
|
||||
cy.get('svg').should((svg) => {
|
||||
// const height = parseFloat(svg.attr('height'));
|
||||
const width = parseFloat(svg.attr('width'));
|
||||
// expect(height).to.eq(450);
|
||||
expect(width).to.eq(984);
|
||||
expect(svg).to.not.have.attr('style');
|
||||
});
|
||||
});
|
||||
it('should render a pie diagram when textPosition is set', () => {
|
||||
|
||||
it('should render a pie diagram when textPosition is setted', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
pie
|
||||
"Dogs": 50
|
||||
"Cats": 25
|
||||
`,
|
||||
`pie
|
||||
"Dogs": 50
|
||||
"Cats": 25
|
||||
`,
|
||||
{ logLevel: 1, pie: { textPosition: 0.9 } }
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
|
||||
it('should render a pie diagram with showData', () => {
|
||||
imgSnapshotTest(
|
||||
`pie showData
|
||||
"Dogs": 50
|
||||
"Cats": 25
|
||||
`
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -58,12 +58,21 @@
|
||||
</head>
|
||||
<body>
|
||||
<pre id="diagram" class="mermaid">
|
||||
---
|
||||
title: Simple flowchart with invisible edges
|
||||
---
|
||||
flowchart TD
|
||||
A ~~~ B
|
||||
</pre
|
||||
flowchart
|
||||
classDef mainCategories fill:#f9d5e5, stroke:#233d4d,stroke-width:2px, font-weight:bold;
|
||||
CS(Customer Awareness Journey):::mainCategories
|
||||
</pre
|
||||
>
|
||||
<pre id="diagram" class="mermaid">
|
||||
flowchart
|
||||
Node1:::class1 --> Node2:::class2
|
||||
Node1:::class1 --> Node3:::class2
|
||||
Node3 --> Node4((I am a circle)):::larger
|
||||
|
||||
classDef class1 fill:lightblue
|
||||
classDef class2 fill:pink
|
||||
classDef larger font-size:30px,fill:yellow
|
||||
</pre
|
||||
>
|
||||
<pre id="diagram" class="mermaid2">
|
||||
stateDiagram-v2
|
||||
|
||||
34
demos/dev/example.html
Normal file
34
demos/dev/example.html
Normal file
@@ -0,0 +1,34 @@
|
||||
<!--Do not edit this file-->
|
||||
<!--Duplicate this file to any name you like, run `pnpm dev`, open http://localhost:9000/dev/name.html to view-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Mermaid development page</title>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="diagram" class="mermaid">
|
||||
graph TB
|
||||
a --> b
|
||||
a --> c
|
||||
b --> d
|
||||
c --> d
|
||||
</pre>
|
||||
|
||||
<div id="dynamicDiagram"></div>
|
||||
|
||||
<script type="module">
|
||||
import mermaid from '/mermaid.esm.mjs';
|
||||
mermaid.parseError = function (err, hash) {
|
||||
console.error('Mermaid error: ', err);
|
||||
};
|
||||
mermaid.initialize({
|
||||
startOnLoad: true,
|
||||
logLevel: 0,
|
||||
});
|
||||
const value = `graph TD\nHello --> World`;
|
||||
const el = document.getElementById('dynamicDiagram');
|
||||
const { svg } = await mermaid.render('dd', value);
|
||||
console.log(svg);
|
||||
el.innerHTML = svg;
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -21,6 +21,8 @@
|
||||
<pre class="mermaid">
|
||||
---
|
||||
title: This is a title
|
||||
config:
|
||||
theme: forest
|
||||
---
|
||||
erDiagram
|
||||
%% title This is a title
|
||||
|
||||
@@ -123,6 +123,13 @@
|
||||
|
||||
<h3>flowchart</h3>
|
||||
<pre class="mermaid">
|
||||
---
|
||||
title: This is another complicated flow
|
||||
config:
|
||||
theme: base
|
||||
flowchart:
|
||||
curve: cardinal
|
||||
---
|
||||
flowchart LR
|
||||
sid-B3655226-6C29-4D00-B685-3D5C734DC7E1["
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo=" />
|
||||
<style>
|
||||
div.mermaid {
|
||||
/* font-family: 'trebuchet ms', verdana, arial; */
|
||||
font-family: 'Courier New', Courier, monospace !important;
|
||||
}
|
||||
</style>
|
||||
@@ -17,37 +16,32 @@
|
||||
<h1>Pie chart demos</h1>
|
||||
<pre class="mermaid">
|
||||
pie title Pets adopted by volunteers
|
||||
accTitle: simple pie char demo
|
||||
accDescr: pie chart with 3 sections: dogs, cats, rats. Most are dogs.
|
||||
"Dogs" : 386
|
||||
"Cats" : 85
|
||||
"Rats" : 15
|
||||
accTitle: simple pie char demo
|
||||
accDescr: pie chart with 3 sections: dogs, cats, rats. Most are dogs.
|
||||
"Dogs": 386
|
||||
"Cats": 85
|
||||
"Rats": 15
|
||||
</pre>
|
||||
|
||||
<hr />
|
||||
<pre class="mermaid">
|
||||
%%{init: {"pie": {"textPosition": 0.9}, "themeVariables": {"pieOuterStrokeWidth": "5px"}} }%%
|
||||
pie
|
||||
title Key elements in Product X
|
||||
%%{init: {"pie": {"textPosition": 0.9}, "themeVariables": {"pieOuterStrokeWidth": "5px"}}}%%
|
||||
pie
|
||||
title Key elements in Product X
|
||||
accTitle: Key elements in Product X
|
||||
accDescr: This is a pie chart showing the key elements in Product X.
|
||||
"Calcium" : 42.96
|
||||
"Potassium" : 50.05
|
||||
"Magnesium" : 10.01
|
||||
"Iron" : 5
|
||||
accDescr: This is a pie chart showing the key elements in Product X.
|
||||
"Calcium": 42.96
|
||||
"Potassium": 50.05
|
||||
"Magnesium": 10.01
|
||||
"Iron": 5
|
||||
</pre>
|
||||
|
||||
<script type="module">
|
||||
import mermaid from './mermaid.esm.mjs';
|
||||
mermaid.initialize({
|
||||
theme: 'forest',
|
||||
// themeCSS: '.node rect { fill: red; }',
|
||||
logLevel: 3,
|
||||
securityLevel: 'loose',
|
||||
// flowchart: { curve: 'basis' },
|
||||
// gantt: { axisFormat: '%m/%d/%Y' },
|
||||
sequence: { actorMargin: 50 },
|
||||
// sequenceDiagram: { actorMargin: 300 } // deprecated
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
version: '3.9'
|
||||
services:
|
||||
mermaid:
|
||||
image: node:18.17.0-alpine3.18
|
||||
image: node:18.17.1-alpine3.18
|
||||
stdin_open: true
|
||||
tty: true
|
||||
working_dir: /mermaid
|
||||
@@ -17,7 +17,7 @@ services:
|
||||
- 9000:9000
|
||||
- 3333:3333
|
||||
cypress:
|
||||
image: cypress/included:12.17.2
|
||||
image: cypress/included:12.17.4
|
||||
stdin_open: true
|
||||
tty: true
|
||||
working_dir: /mermaid
|
||||
|
||||
@@ -70,7 +70,21 @@ pnpm test
|
||||
|
||||
The `test` script and others are in the top-level `package.json` file.
|
||||
|
||||
All tests should run successfully without any errors or failures. (You might see _lint_ or _formatting_ warnings; those are ok during this step.)
|
||||
All tests should run successfully without any errors or failures. (You might see _lint_ or _formatting_ "warnings"; those are ok during this step.)
|
||||
|
||||
#### 4. Make your changes
|
||||
|
||||
Now you are ready to make your changes!
|
||||
Edit whichever files in `src` as required.
|
||||
|
||||
#### 5. See your changes
|
||||
|
||||
Open <http://localhost:9000> in your browser, after starting the dev server.
|
||||
There is a list of demos that can be used to see and test your changes.
|
||||
|
||||
If you need a specific diagram, you can duplicate the `example.html` file in `/demos/dev` and add your own mermaid code to your copy.
|
||||
That will be served at <http://localhost:9000/dev/your-file-name.html>.
|
||||
After making code changes, the dev server will rebuild the mermaid library. You will need to reload the browser page yourself to see the changes. (PRs for auto reload are welcome!)
|
||||
|
||||
### Docker
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
#### Grammar
|
||||
|
||||
This would be to define a jison grammar for the new diagram type. That should start with a way to identify that the text in the mermaid tag is a diagram of that type. Create a new folder under diagrams for your new diagram type and a parser folder in it. This leads us to step 2.
|
||||
This would be to define a JISON grammar for the new diagram type. That should start with a way to identify that the text in the mermaid tag is a diagram of that type. Create a new folder under diagrams for your new diagram type and a parser folder in it. This leads us to step 2.
|
||||
|
||||
For instance:
|
||||
|
||||
@@ -60,7 +60,7 @@ Place the renderer in the diagram folder.
|
||||
|
||||
### Step 3: Detection of the new diagram type
|
||||
|
||||
The second thing to do is to add the capability to detect the new diagram to type to the detectType in utils.js. The detection should return a key for the new diagram type.
|
||||
The second thing to do is to add the capability to detect the new diagram to type to the detectType in `diagram-api/detectType.ts`. The detection should return a key for the new diagram type.
|
||||
[This key will be used to as the aria roledescription](#aria-roledescription), so it should be a word that clearly describes the diagram type.
|
||||
For example, if your new diagram use a UML deployment diagram, a good key would be "UMLDeploymentDiagram" because assistive technologies such as a screen reader
|
||||
would voice that as "U-M-L Deployment diagram." Another good key would be "deploymentDiagram" because that would be voiced as "Deployment Diagram." A bad key would be "deployment" because that would not sufficiently describe the diagram.
|
||||
@@ -124,53 +124,6 @@ There are a few features that are common between the different types of diagrams
|
||||
|
||||
Here some pointers on how to handle these different areas.
|
||||
|
||||
#### [Directives](../config/directives.md)
|
||||
|
||||
Here is example handling from flowcharts:
|
||||
Jison:
|
||||
|
||||
```jison
|
||||
/* lexical grammar */
|
||||
%lex
|
||||
%x open_directive
|
||||
%x type_directive
|
||||
%x arg_directive
|
||||
%x close_directive
|
||||
|
||||
\%\%\{ { this.begin('open_directive'); return 'open_directive'; }
|
||||
<open_directive>((?:(?!\}\%\%)[^:.])*) { this.begin('type_directive'); return 'type_directive'; }
|
||||
<type_directive>":" { this.popState(); this.begin('arg_directive'); return ':'; }
|
||||
<type_directive,arg_directive>\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
|
||||
<arg_directive>((?:(?!\}\%\%).|\n)*) return 'arg_directive';
|
||||
|
||||
/* language grammar */
|
||||
|
||||
/* ... */
|
||||
|
||||
directive
|
||||
: openDirective typeDirective closeDirective separator
|
||||
| openDirective typeDirective ':' argDirective closeDirective separator
|
||||
;
|
||||
|
||||
openDirective
|
||||
: open_directive { yy.parseDirective('%%{', 'open_directive'); }
|
||||
;
|
||||
|
||||
typeDirective
|
||||
: type_directive { yy.parseDirective($1, 'type_directive'); }
|
||||
;
|
||||
|
||||
argDirective
|
||||
: arg_directive { $1 = $1.trim().replace(/'/g, '"'); yy.parseDirective($1, 'arg_directive'); }
|
||||
;
|
||||
|
||||
closeDirective
|
||||
: close_directive { yy.parseDirective('}%%', 'close_directive', 'flowchart'); }
|
||||
;
|
||||
```
|
||||
|
||||
It is probably a good idea to keep the handling similar to this in your new diagram. The parseDirective function is provided by the mermaidAPI.
|
||||
|
||||
## Accessibility
|
||||
|
||||
Mermaid automatically adds the following accessibility information for the diagram SVG HTML element:
|
||||
@@ -189,7 +142,7 @@ See [the definition of aria-roledescription](https://www.w3.org/TR/wai-aria-1.1/
|
||||
|
||||
The syntax for accessible titles and descriptions is described in [the Accessibility documenation section.](../config/accessibility.md)
|
||||
|
||||
In a similar way to the directives, the jison syntax are quite similar between the diagrams.
|
||||
As a design goal, the jison syntax should be similar between the diagrams.
|
||||
|
||||
```jison
|
||||
|
||||
|
||||
@@ -10,10 +10,41 @@ When mermaid starts, configuration is extracted to determine a configuration to
|
||||
|
||||
- The default configuration
|
||||
- Overrides at the site level are set by the initialize call, and will be applied to all diagrams in the site/app. The term for this is the **siteConfig**.
|
||||
- Directives - diagram authors can update select configuration parameters directly in the diagram code via directives. These are applied to the render config.
|
||||
- Frontmatter (v\<MERMAID_RELEASE_VERSION>+) - diagram authors can update select configuration parameters in the frontmatter of the diagram. These are applied to the render config.
|
||||
- Directives (Deprecated by Frontmatter) - diagram authors can update select configuration parameters directly in the diagram code via directives. These are applied to the render config.
|
||||
|
||||
**The render config** is configuration that is used when rendering by applying these configurations.
|
||||
|
||||
## Frontmatter config
|
||||
|
||||
The entire mermaid configuration (except the secure configs) can be overridden by the diagram author in the frontmatter of the diagram. The frontmatter is a YAML block at the top of the diagram.
|
||||
|
||||
```mermaid-example
|
||||
---
|
||||
title: Hello Title
|
||||
config:
|
||||
theme: base
|
||||
themeVariables:
|
||||
primaryColor: "#00ff00"
|
||||
---
|
||||
flowchart
|
||||
Hello --> World
|
||||
|
||||
```
|
||||
|
||||
```mermaid
|
||||
---
|
||||
title: Hello Title
|
||||
config:
|
||||
theme: base
|
||||
themeVariables:
|
||||
primaryColor: "#00ff00"
|
||||
---
|
||||
flowchart
|
||||
Hello --> World
|
||||
|
||||
```
|
||||
|
||||
## Theme configuration
|
||||
|
||||
## Starting mermaid
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
|
||||
# Directives
|
||||
|
||||
> **Warning**
|
||||
> Directives are deprecated from v\<MERMAID_RELEASE_VERSION>. Please use the `config` key in frontmatter to pass configuration. See [Configuration](./configuration.md) for more details.
|
||||
|
||||
## Directives
|
||||
|
||||
Directives give a diagram author the capability to alter the appearance of a diagram before rendering by changing the applied configuration.
|
||||
|
||||
@@ -16,4 +16,4 @@
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:77](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L77)
|
||||
[mermaidAPI.ts:78](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L78)
|
||||
|
||||
@@ -39,7 +39,7 @@ bindFunctions?.(div); // To call bindFunctions only if it's present.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:97](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L97)
|
||||
[mermaidAPI.ts:98](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L98)
|
||||
|
||||
---
|
||||
|
||||
@@ -51,4 +51,4 @@ The svg code for the rendered graph.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:87](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L87)
|
||||
[mermaidAPI.ts:88](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L88)
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
#### Defined in
|
||||
|
||||
[config.ts:7](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L7)
|
||||
[config.ts:8](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L8)
|
||||
|
||||
## Functions
|
||||
|
||||
@@ -26,9 +26,9 @@ Pushes in a directive to the configuration
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :---------- | :---- | :----------------------- |
|
||||
| `directive` | `any` | The directive to push in |
|
||||
| Name | Type | Description |
|
||||
| :---------- | :-------------- | :----------------------- |
|
||||
| `directive` | `MermaidConfig` | The directive to push in |
|
||||
|
||||
#### Returns
|
||||
|
||||
@@ -36,7 +36,7 @@ Pushes in a directive to the configuration
|
||||
|
||||
#### Defined in
|
||||
|
||||
[config.ts:191](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L191)
|
||||
[config.ts:188](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L188)
|
||||
|
||||
---
|
||||
|
||||
@@ -60,7 +60,7 @@ The currentConfig
|
||||
|
||||
#### Defined in
|
||||
|
||||
[config.ts:137](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L137)
|
||||
[config.ts:131](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L131)
|
||||
|
||||
---
|
||||
|
||||
@@ -118,7 +118,7 @@ The siteConfig
|
||||
|
||||
#### Defined in
|
||||
|
||||
[config.ts:223](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L223)
|
||||
[config.ts:218](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L218)
|
||||
|
||||
---
|
||||
|
||||
@@ -147,7 +147,7 @@ options in-place
|
||||
|
||||
#### Defined in
|
||||
|
||||
[config.ts:152](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L152)
|
||||
[config.ts:146](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L146)
|
||||
|
||||
---
|
||||
|
||||
@@ -242,10 +242,10 @@ The new siteConfig
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type |
|
||||
| :------------ | :-------------- |
|
||||
| `siteCfg` | `MermaidConfig` |
|
||||
| `_directives` | `any`\[] |
|
||||
| Name | Type |
|
||||
| :------------ | :----------------- |
|
||||
| `siteCfg` | `MermaidConfig` |
|
||||
| `_directives` | `MermaidConfig`\[] |
|
||||
|
||||
#### Returns
|
||||
|
||||
@@ -253,7 +253,7 @@ The new siteConfig
|
||||
|
||||
#### Defined in
|
||||
|
||||
[config.ts:14](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L14)
|
||||
[config.ts:15](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L15)
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -10,17 +10,17 @@
|
||||
|
||||
### configKeys
|
||||
|
||||
• `Const` **configKeys**: `string`\[]
|
||||
• `Const` **configKeys**: `Set`<`string`>
|
||||
|
||||
#### Defined in
|
||||
|
||||
[defaultConfig.ts:266](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L266)
|
||||
[defaultConfig.ts:268](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L268)
|
||||
|
||||
---
|
||||
|
||||
### default
|
||||
|
||||
• `Const` **default**: `Partial`<`MermaidConfig`>
|
||||
• `Const` **default**: `RequiredDeep`<`MermaidConfig`>
|
||||
|
||||
Default mermaid configuration options.
|
||||
|
||||
@@ -30,4 +30,4 @@ Non-JSON JS default values are listed in this file, e.g. functions, or
|
||||
|
||||
#### Defined in
|
||||
|
||||
[defaultConfig.ts:16](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L16)
|
||||
[defaultConfig.ts:18](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L18)
|
||||
|
||||
@@ -25,7 +25,7 @@ Renames and re-exports [mermaidAPI](mermaidAPI.md#mermaidapi)
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:81](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L81)
|
||||
[mermaidAPI.ts:82](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L82)
|
||||
|
||||
## Variables
|
||||
|
||||
@@ -96,7 +96,7 @@ mermaid.initialize(config);
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:667](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L667)
|
||||
[mermaidAPI.ts:673](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L673)
|
||||
|
||||
## Functions
|
||||
|
||||
@@ -127,7 +127,7 @@ Return the last node appended
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:308](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L308)
|
||||
[mermaidAPI.ts:310](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L310)
|
||||
|
||||
---
|
||||
|
||||
@@ -320,4 +320,4 @@ Remove any existing elements from the given document
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:358](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L358)
|
||||
[mermaidAPI.ts:360](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L360)
|
||||
|
||||
@@ -14,8 +14,12 @@ It is a JavaScript based diagramming and charting tool that renders Markdown-ins
|
||||
|
||||
<img src="/header.png" alt="" />
|
||||
|
||||
<div class='badges'>
|
||||
|
||||
[](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [](https://www.npmjs.com/package/mermaid) [](https://bundlephobia.com/package/mermaid) [](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [](https://www.jsdelivr.com/package/npm/mermaid) [](https://www.npmjs.com/package/mermaid) [](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [](https://twitter.com/mermaidjs_)
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Mermaid book banner -->
|
||||
|
||||
[](https://mermaid-js.github.io/mermaid/landing/)
|
||||
@@ -387,7 +391,13 @@ The above command generates files into the `dist` folder and publishes them to \
|
||||
- [Live Editor](https://github.com/mermaid-js/mermaid-live-editor)
|
||||
- [HTTP Server](https://github.com/TomWright/mermaid-server)
|
||||
|
||||
## Contributors [](https://github.com/mermaid-js/mermaid/issues?q=is%3Aissue+is%3Aopen+label%3A%22Good+first+issue%21%22) [](https://github.com/mermaid-js/mermaid/graphs/contributors) [](https://github.com/mermaid-js/mermaid/graphs/contributors)
|
||||
## Contributors
|
||||
|
||||
<div class='badges'>
|
||||
|
||||
[](https://github.com/mermaid-js/mermaid/issues?q=is%3Aissue+is%3Aopen+label%3A%22Good+first+issue%21%22) [](https://github.com/mermaid-js/mermaid/graphs/contributors) [](https://github.com/mermaid-js/mermaid/graphs/contributors)
|
||||
|
||||
</div>
|
||||
|
||||
Mermaid is a growing community and is always accepting new contributors. There's a lot of different ways to help out and we're always looking for extra hands! Look at [this issue](https://github.com/mermaid-js/mermaid/issues/866) if you want to know where to start helping out.
|
||||
|
||||
@@ -422,20 +432,14 @@ A quick note from Knut Sveidqvist:
|
||||
_Mermaid was created by Knut Sveidqvist for easier documentation._
|
||||
|
||||
<style scoped>
|
||||
#contributors + p,
|
||||
#about-mermaid + p + p + blockquote + img + p
|
||||
{
|
||||
display: flex
|
||||
.badges > p {
|
||||
display: flex;
|
||||
}
|
||||
.badges > p > a {
|
||||
margin: 0 0.5rem;
|
||||
}
|
||||
|
||||
#contributors + p a,
|
||||
#about-mermaid + p + p + blockquote + img + p a
|
||||
{
|
||||
margin: 0 0.5rem
|
||||
}
|
||||
|
||||
.dark #VPContent > div > div > div.content > div > main > div > div > img
|
||||
{
|
||||
.dark #VPContent > div > div > div.content > div > main > div > div > img {
|
||||
filter: invert(1) hue-rotate(217deg) contrast(0.72);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
|
||||
# Announcements
|
||||
|
||||
## [Mermaid Chart Announces Visual Studio Code Plugin to Simplify Development Workflows](https://www.mermaidchart.com/blog/posts/mermaid-chart-announces-visual-studio-code-plugin)
|
||||
## [From Chaos to Clarity: Exploring Mind Maps with MermaidJS](https://www.mermaidchart.com/blog/posts/from-chaos-to-clarity-exploring-mind-maps-with-mermaidjs)
|
||||
|
||||
17 July 2023 · 3 mins
|
||||
24 July 2023 · 4 mins
|
||||
|
||||
New Integration Enhances Workflows By Enabling Developers To Reference And Edit Diagrams Within Visual Studio Code.
|
||||
Introducing the concept of mind mapping as a tool for organizing complex information, and highlights Mermaid as a user-friendly software that simplifies the creation and editing of mind maps for applications in IT solution design, business decision-making, and knowledge organization.
|
||||
|
||||
@@ -6,6 +6,12 @@
|
||||
|
||||
# Blog
|
||||
|
||||
## [From Chaos to Clarity: Exploring Mind Maps with MermaidJS](https://www.mermaidchart.com/blog/posts/from-chaos-to-clarity-exploring-mind-maps-with-mermaidjs)
|
||||
|
||||
24 July 2023 · 4 mins
|
||||
|
||||
Introducing the concept of mind mapping as a tool for organizing complex information, and highlights Mermaid as a user-friendly software that simplifies the creation and editing of mind maps for applications in IT solution design, business decision-making, and knowledge organization.
|
||||
|
||||
## [Mermaid Chart Announces Visual Studio Code Plugin to Simplify Development Workflows](https://www.mermaidchart.com/blog/posts/mermaid-chart-announces-visual-studio-code-plugin)
|
||||
|
||||
17 July 2023 · 3 mins
|
||||
|
||||
@@ -608,12 +608,12 @@ It is possible to escape characters using the syntax exemplified here.
|
||||
|
||||
```mermaid-example
|
||||
flowchart LR
|
||||
A["A double quote:#quot;"] -->B["A dec char:#9829;"]
|
||||
A["A double quote:#quot;"] --> B["A dec char:#9829;"]
|
||||
```
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["A double quote:#quot;"] -->B["A dec char:#9829;"]
|
||||
A["A double quote:#quot;"] --> B["A dec char:#9829;"]
|
||||
```
|
||||
|
||||
Numbers given are base 10, so `#` can be encoded as `#35;`. It is also supported to use HTML character names.
|
||||
@@ -748,6 +748,48 @@ flowchart LR
|
||||
B1 --> B2
|
||||
```
|
||||
|
||||
#### Limitation
|
||||
|
||||
If any of a subgraph's nodes are linked to the outside, subgraph direction will be ignored. Instead the subgraph will inherit the direction of the parent graph:
|
||||
|
||||
```mermaid-example
|
||||
flowchart LR
|
||||
subgraph subgraph1
|
||||
direction TB
|
||||
top1[top] --> bottom1[bottom]
|
||||
end
|
||||
subgraph subgraph2
|
||||
direction TB
|
||||
top2[top] --> bottom2[bottom]
|
||||
end
|
||||
%% ^ These subgraphs are identical, except for the links to them:
|
||||
|
||||
%% Link *to* subgraph1: subgraph1 direction is mantained
|
||||
outside --> subgraph1
|
||||
%% Link *within* subgraph2:
|
||||
%% subgraph2 inherits the direction of the top-level graph (LR)
|
||||
outside ---> top2
|
||||
```
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph subgraph1
|
||||
direction TB
|
||||
top1[top] --> bottom1[bottom]
|
||||
end
|
||||
subgraph subgraph2
|
||||
direction TB
|
||||
top2[top] --> bottom2[bottom]
|
||||
end
|
||||
%% ^ These subgraphs are identical, except for the links to them:
|
||||
|
||||
%% Link *to* subgraph1: subgraph1 direction is mantained
|
||||
outside --> subgraph1
|
||||
%% Link *within* subgraph2:
|
||||
%% subgraph2 inherits the direction of the top-level graph (LR)
|
||||
outside ---> top2
|
||||
```
|
||||
|
||||
## Markdown Strings
|
||||
|
||||
The "Markdown Strings" feature enhances flowcharts and mind maps by offering a more versatile string type, which supports text formatting options such as bold and italics, and automatically wraps text within labels.
|
||||
|
||||
16
package.json
16
package.json
@@ -4,7 +4,7 @@
|
||||
"version": "10.2.4",
|
||||
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
||||
"type": "module",
|
||||
"packageManager": "pnpm@8.6.10",
|
||||
"packageManager": "pnpm@8.6.12",
|
||||
"keywords": [
|
||||
"diagram",
|
||||
"markdown",
|
||||
@@ -78,15 +78,15 @@
|
||||
"@types/rollup-plugin-visualizer": "^4.2.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.59.0",
|
||||
"@typescript-eslint/parser": "^5.59.0",
|
||||
"@vitest/coverage-v8": "^0.33.0",
|
||||
"@vitest/spy": "^0.33.0",
|
||||
"@vitest/ui": "^0.33.0",
|
||||
"@vitest/coverage-v8": "^0.34.0",
|
||||
"@vitest/spy": "^0.34.0",
|
||||
"@vitest/ui": "^0.34.0",
|
||||
"ajv": "^8.12.0",
|
||||
"concurrently": "^8.0.1",
|
||||
"cors": "^2.8.5",
|
||||
"cypress": "^12.10.0",
|
||||
"cypress-image-snapshot": "^4.0.1",
|
||||
"esbuild": "^0.18.0",
|
||||
"esbuild": "^0.19.0",
|
||||
"eslint": "^8.39.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-plugin-cypress": "^2.13.2",
|
||||
@@ -109,7 +109,7 @@
|
||||
"lint-staged": "^13.2.1",
|
||||
"nyc": "^15.1.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
"pnpm": "^8.3.1",
|
||||
"pnpm": "^8.6.8",
|
||||
"prettier": "^2.8.8",
|
||||
"prettier-plugin-jsdoc": "^0.4.2",
|
||||
"rimraf": "^5.0.0",
|
||||
@@ -119,10 +119,10 @@
|
||||
"typescript": "^5.1.3",
|
||||
"vite": "^4.3.9",
|
||||
"vite-plugin-istanbul": "^4.1.0",
|
||||
"vitest": "^0.33.0"
|
||||
"vitest": "^0.34.0"
|
||||
},
|
||||
"volta": {
|
||||
"node": "18.17.0"
|
||||
"node": "18.17.1"
|
||||
},
|
||||
"nyc": {
|
||||
"report-dir": "coverage/cypress"
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@braintree/sanitize-url": "^6.0.0",
|
||||
"@braintree/sanitize-url": "^6.0.1",
|
||||
"cytoscape": "^3.23.0",
|
||||
"cytoscape-cose-bilkent": "^4.1.0",
|
||||
"cytoscape-fcose": "^2.1.0",
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@zenuml/core": "^3.0.3"
|
||||
"@zenuml/core": "^3.0.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mermaid": "workspace:^"
|
||||
|
||||
22
packages/mermaid/.madgerc
Normal file
22
packages/mermaid/.madgerc
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"detectiveOptions": {
|
||||
"ts": {
|
||||
"skipTypeImports": true
|
||||
},
|
||||
"es6": {
|
||||
"skipTypeImports": true
|
||||
}
|
||||
},
|
||||
"fileExtensions": [
|
||||
"js",
|
||||
"ts"
|
||||
],
|
||||
"excludeRegExp": [
|
||||
"node_modules",
|
||||
"docs",
|
||||
"vitepress",
|
||||
"detector",
|
||||
"Detector"
|
||||
],
|
||||
"tsConfig": "./tsconfig.json"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mermaid",
|
||||
"version": "10.3.0",
|
||||
"version": "10.3.1",
|
||||
"description": "Markdown-ish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
||||
"type": "module",
|
||||
"module": "./dist/mermaid.core.mjs",
|
||||
@@ -24,6 +24,7 @@
|
||||
],
|
||||
"scripts": {
|
||||
"clean": "rimraf dist",
|
||||
"dev": "pnpm -w dev",
|
||||
"docs:code": "typedoc src/defaultConfig.ts src/config.ts src/mermaidAPI.ts && prettier --write ./src/docs/config/setup",
|
||||
"docs:build": "rimraf ../../docs && pnpm docs:spellcheck && pnpm docs:code && ts-node-esm scripts/docs.cli.mts",
|
||||
"docs:verify": "pnpm docs:spellcheck && pnpm docs:code && ts-node-esm scripts/docs.cli.mts --verify",
|
||||
@@ -37,6 +38,7 @@
|
||||
"docs:verify-version": "ts-node-esm scripts/update-release-version.mts --verify",
|
||||
"types:build-config": "ts-node-esm --transpileOnly scripts/create-types-from-json-schema.mts",
|
||||
"types:verify-config": "ts-node-esm scripts/create-types-from-json-schema.mts --verify",
|
||||
"checkCircle": "npx madge --circular ./src",
|
||||
"release": "pnpm build",
|
||||
"prepublishOnly": "cpy '../../README.*' ./ --cwd=. && pnpm -w run build"
|
||||
},
|
||||
@@ -57,7 +59,7 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@braintree/sanitize-url": "^6.0.2",
|
||||
"@braintree/sanitize-url": "^6.0.1",
|
||||
"@types/d3-scale": "^4.0.3",
|
||||
"@types/d3-scale-chromatic": "^3.0.0",
|
||||
"cytoscape": "^3.23.0",
|
||||
@@ -67,7 +69,7 @@
|
||||
"d3-sankey": "^0.12.3",
|
||||
"dagre-d3-es": "7.0.10",
|
||||
"dayjs": "^1.11.7",
|
||||
"dompurify": "3.0.5",
|
||||
"dompurify": "^3.0.5",
|
||||
"elkjs": "^0.8.2",
|
||||
"khroma": "^2.0.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
@@ -83,7 +85,9 @@
|
||||
"@types/cytoscape": "^3.19.9",
|
||||
"@types/d3": "^7.4.0",
|
||||
"@types/d3-sankey": "^0.12.1",
|
||||
"@types/d3-scale": "^4.0.3",
|
||||
"@types/d3-selection": "^3.0.5",
|
||||
"@types/d3-shape": "^3.1.1",
|
||||
"@types/dompurify": "^3.0.2",
|
||||
"@types/jsdom": "^21.1.1",
|
||||
"@types/lodash-es": "^4.17.7",
|
||||
@@ -112,6 +116,7 @@
|
||||
"remark-gfm": "^3.0.1",
|
||||
"rimraf": "^5.0.0",
|
||||
"start-server-and-test": "^2.0.0",
|
||||
"type-fest": "^4.1.0",
|
||||
"typedoc": "^0.24.5",
|
||||
"typedoc-plugin-markdown": "^3.15.2",
|
||||
"typescript": "^5.0.4",
|
||||
|
||||
@@ -18,6 +18,7 @@ import { promisify } from 'node:util';
|
||||
|
||||
import { load, JSON_SCHEMA } from 'js-yaml';
|
||||
import { compile, type JSONSchema } from 'json-schema-to-typescript';
|
||||
import prettier from 'prettier';
|
||||
|
||||
import _Ajv2019, { type JSONSchemaType } from 'ajv/dist/2019.js';
|
||||
|
||||
@@ -207,6 +208,7 @@ async function generateTypescript(mermaidConfigSchema: JSONSchemaType<MermaidCon
|
||||
{
|
||||
additionalProperties: false, // in JSON Schema 2019-09, these are called `unevaluatedProperties`
|
||||
unreachableDefinitions: true, // definition for FontConfig is unreachable
|
||||
style: (await prettier.resolveConfig('.')) ?? {},
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -4,8 +4,9 @@ import { getDiagram, registerDiagram } from './diagram-api/diagramAPI.js';
|
||||
import { detectType, getDiagramLoader } from './diagram-api/detectType.js';
|
||||
import { extractFrontMatter } from './diagram-api/frontmatter.js';
|
||||
import { UnknownDiagramError } from './errors.js';
|
||||
import { DetailedError } from './utils.js';
|
||||
import { cleanupComments } from './diagram-api/comments.js';
|
||||
import type { DetailedError } from './utils.js';
|
||||
import type { DiagramDefinition } from './diagram-api/types.js';
|
||||
|
||||
export type ParseErrorFunction = (err: string | DetailedError | unknown, hash?: any) => void;
|
||||
|
||||
@@ -15,9 +16,11 @@ export type ParseErrorFunction = (err: string | DetailedError | unknown, hash?:
|
||||
*/
|
||||
export class Diagram {
|
||||
type = 'graph';
|
||||
parser;
|
||||
renderer;
|
||||
db;
|
||||
parser: DiagramDefinition['parser'];
|
||||
renderer: DiagramDefinition['renderer'];
|
||||
db: DiagramDefinition['db'];
|
||||
private init?: DiagramDefinition['init'];
|
||||
|
||||
private detectError?: UnknownDiagramError;
|
||||
constructor(public text: string) {
|
||||
this.text += '\n';
|
||||
@@ -32,7 +35,6 @@ export class Diagram {
|
||||
log.debug('Type ' + this.type);
|
||||
// Setup diagram
|
||||
this.db = diagram.db;
|
||||
this.db.clear?.();
|
||||
this.renderer = diagram.renderer;
|
||||
this.parser = diagram.parser;
|
||||
const originalParse = this.parser.parse.bind(this.parser);
|
||||
@@ -46,13 +48,10 @@ export class Diagram {
|
||||
// extractFrontMatter().
|
||||
|
||||
this.parser.parse = (text: string) =>
|
||||
originalParse(cleanupComments(extractFrontMatter(text, this.db)));
|
||||
originalParse(cleanupComments(extractFrontMatter(text, this.db, configApi.addDirective)));
|
||||
|
||||
this.parser.parser.yy = this.db;
|
||||
if (diagram.init) {
|
||||
diagram.init(cnf);
|
||||
log.info('Initialized diagram ' + this.type, cnf);
|
||||
}
|
||||
this.init = diagram.init;
|
||||
this.parse();
|
||||
}
|
||||
|
||||
@@ -61,6 +60,7 @@ export class Diagram {
|
||||
throw this.detectError;
|
||||
}
|
||||
this.db.clear?.();
|
||||
this.init?.(configApi.getConfig());
|
||||
this.parser.parse(this.text);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,32 +1,36 @@
|
||||
'use strict';
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
/**
|
||||
* @function assignWithDepth Extends the functionality of {@link ObjectConstructor.assign} with the
|
||||
* assignWithDepth Extends the functionality of {@link Object.assign} with the
|
||||
* ability to merge arbitrary-depth objects For each key in src with path `k` (recursively)
|
||||
* performs an Object.assign(dst[`k`], src[`k`]) with a slight change from the typical handling of
|
||||
* undefined for dst[`k`]: instead of raising an error, dst[`k`] is auto-initialized to {} and
|
||||
* undefined for dst[`k`]: instead of raising an error, dst[`k`] is auto-initialized to `{}` and
|
||||
* effectively merged with src[`k`]<p> Additionally, dissimilar types will not clobber unless the
|
||||
* config.clobber parameter === true. Example:
|
||||
*
|
||||
* ```js
|
||||
* let config_0 = { foo: { bar: 'bar' }, bar: 'foo' };
|
||||
* let config_1 = { foo: 'foo', bar: 'bar' };
|
||||
* let result = assignWithDepth(config_0, config_1);
|
||||
* console.log(result);
|
||||
* //-> result: { foo: { bar: 'bar' }, bar: 'bar' }
|
||||
* ```
|
||||
* ```
|
||||
* const config_0 = { foo: { bar: 'bar' }, bar: 'foo' };
|
||||
* const config_1 = { foo: 'foo', bar: 'bar' };
|
||||
* const result = assignWithDepth(config_0, config_1);
|
||||
* console.log(result);
|
||||
* //-> result: { foo: { bar: 'bar' }, bar: 'bar' }
|
||||
* ```
|
||||
*
|
||||
* Traditional Object.assign would have clobbered foo in config_0 with foo in config_1. If src is a
|
||||
* destructured array of objects and dst is not an array, assignWithDepth will apply each element
|
||||
* of src to dst in order.
|
||||
* @param {any} dst - The destination of the merge
|
||||
* @param {any} src - The source object(s) to merge into destination
|
||||
* @param {{ depth: number; clobber: boolean }} [config] - Depth: depth
|
||||
* to traverse within src and dst for merging - clobber: should dissimilar types clobber (default:
|
||||
* { depth: 2, clobber: false }). Default is `{ depth: 2, clobber: false }`
|
||||
* @returns {any}
|
||||
* @param dst - The destination of the merge
|
||||
* @param src - The source object(s) to merge into destination
|
||||
* @param config -
|
||||
* * depth: depth to traverse within src and dst for merging
|
||||
* * clobber: should dissimilar types clobber
|
||||
*/
|
||||
const assignWithDepth = function (dst, src, config) {
|
||||
const { depth, clobber } = Object.assign({ depth: 2, clobber: false }, config);
|
||||
const assignWithDepth = (
|
||||
dst: any,
|
||||
src: any,
|
||||
{ depth = 2, clobber = false }: { depth?: number; clobber?: boolean } = {}
|
||||
): any => {
|
||||
const config: { depth: number; clobber: boolean } = { depth, clobber };
|
||||
if (Array.isArray(src) && !Array.isArray(dst)) {
|
||||
src.forEach((s) => assignWithDepth(dst, s, config));
|
||||
return dst;
|
||||
@@ -1,11 +1,13 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import * as configApi from './config.js';
|
||||
import type { MermaidConfig } from './config.type.js';
|
||||
|
||||
describe('when working with site config', function () {
|
||||
describe('when working with site config', () => {
|
||||
beforeEach(() => {
|
||||
// Resets the site config to default config
|
||||
configApi.setSiteConfig({});
|
||||
});
|
||||
it('should set site config and config properly', function () {
|
||||
it('should set site config and config properly', () => {
|
||||
const config_0 = { fontFamily: 'foo-font', fontSize: 150 };
|
||||
configApi.setSiteConfig(config_0);
|
||||
const config_1 = configApi.getSiteConfig();
|
||||
@@ -14,19 +16,26 @@ describe('when working with site config', function () {
|
||||
expect(config_1.fontSize).toEqual(config_0.fontSize);
|
||||
expect(config_1).toEqual(config_2);
|
||||
});
|
||||
it('should respect secure keys when applying directives', function () {
|
||||
const config_0 = {
|
||||
it('should respect secure keys when applying directives', () => {
|
||||
const config_0: MermaidConfig = {
|
||||
fontFamily: 'foo-font',
|
||||
securityLevel: 'strict', // can't be changed
|
||||
fontSize: 12345, // can't be changed
|
||||
secure: [...configApi.defaultConfig.secure!, 'fontSize'],
|
||||
};
|
||||
configApi.setSiteConfig(config_0);
|
||||
const directive = { fontFamily: 'baf', fontSize: 54321 /* fontSize shouldn't be changed */ };
|
||||
const cfg = configApi.updateCurrentConfig(config_0, [directive]);
|
||||
const directive: MermaidConfig = {
|
||||
fontFamily: 'baf',
|
||||
// fontSize and securityLevel shouldn't be changed
|
||||
fontSize: 54321,
|
||||
securityLevel: 'loose',
|
||||
};
|
||||
const cfg: MermaidConfig = configApi.updateCurrentConfig(config_0, [directive]);
|
||||
expect(cfg.fontFamily).toEqual(directive.fontFamily);
|
||||
expect(cfg.fontSize).toBe(config_0.fontSize);
|
||||
expect(cfg.securityLevel).toBe(config_0.securityLevel);
|
||||
});
|
||||
it('should allow setting partial options', function () {
|
||||
it('should allow setting partial options', () => {
|
||||
const defaultConfig = configApi.getConfig();
|
||||
|
||||
configApi.setConfig({
|
||||
@@ -42,7 +51,7 @@ describe('when working with site config', function () {
|
||||
updatedConfig.quadrantChart!.chartWidth
|
||||
);
|
||||
});
|
||||
it('should set reset config properly', function () {
|
||||
it('should set reset config properly', () => {
|
||||
const config_0 = { fontFamily: 'foo-font', fontSize: 150 };
|
||||
configApi.setSiteConfig(config_0);
|
||||
const config_1 = { fontFamily: 'baf' };
|
||||
@@ -55,7 +64,7 @@ describe('when working with site config', function () {
|
||||
const config_4 = configApi.getSiteConfig();
|
||||
expect(config_4.fontFamily).toEqual(config_0.fontFamily);
|
||||
});
|
||||
it('should set global reset config properly', function () {
|
||||
it('should set global reset config properly', () => {
|
||||
const config_0 = { fontFamily: 'foo-font', fontSize: 150 };
|
||||
configApi.setSiteConfig(config_0);
|
||||
const config_1 = configApi.getSiteConfig();
|
||||
|
||||
@@ -3,15 +3,16 @@ import { log } from './logger.js';
|
||||
import theme from './themes/index.js';
|
||||
import config from './defaultConfig.js';
|
||||
import type { MermaidConfig } from './config.type.js';
|
||||
import { sanitizeDirective } from './utils.js';
|
||||
|
||||
export const defaultConfig: MermaidConfig = Object.freeze(config);
|
||||
|
||||
let siteConfig: MermaidConfig = assignWithDepth({}, defaultConfig);
|
||||
let configFromInitialize: MermaidConfig;
|
||||
let directives: any[] = [];
|
||||
let directives: MermaidConfig[] = [];
|
||||
let currentConfig: MermaidConfig = assignWithDepth({}, defaultConfig);
|
||||
|
||||
export const updateCurrentConfig = (siteCfg: MermaidConfig, _directives: any[]) => {
|
||||
export const updateCurrentConfig = (siteCfg: MermaidConfig, _directives: MermaidConfig[]) => {
|
||||
// start with config being the siteConfig
|
||||
let cfg: MermaidConfig = assignWithDepth({}, siteCfg);
|
||||
// let sCfg = assignWithDepth(defaultConfig, siteConfigDelta);
|
||||
@@ -20,7 +21,6 @@ export const updateCurrentConfig = (siteCfg: MermaidConfig, _directives: any[])
|
||||
let sumOfDirectives: MermaidConfig = {};
|
||||
for (const d of _directives) {
|
||||
sanitize(d);
|
||||
|
||||
// Apply the data from the directive where the the overrides the themeVariables
|
||||
sumOfDirectives = assignWithDepth(sumOfDirectives, d);
|
||||
}
|
||||
@@ -111,12 +111,6 @@ export const getSiteConfig = (): MermaidConfig => {
|
||||
* @returns The currentConfig merged with the sanitized conf
|
||||
*/
|
||||
export const setConfig = (conf: MermaidConfig): MermaidConfig => {
|
||||
// sanitize(conf);
|
||||
// Object.keys(conf).forEach(key => {
|
||||
// const manipulator = manipulators[key];
|
||||
// conf[key] = manipulator ? manipulator(conf[key]) : conf[key];
|
||||
// });
|
||||
|
||||
checkConfig(conf);
|
||||
assignWithDepth(currentConfig, conf);
|
||||
|
||||
@@ -150,9 +144,12 @@ export const getConfig = (): MermaidConfig => {
|
||||
* @param options - The potential setConfig parameter
|
||||
*/
|
||||
export const sanitize = (options: any) => {
|
||||
if (!options) {
|
||||
return;
|
||||
}
|
||||
// Checking that options are not in the list of excluded options
|
||||
['secure', ...(siteConfig.secure ?? [])].forEach((key) => {
|
||||
if (options[key] !== undefined) {
|
||||
if (Object.hasOwn(options, key)) {
|
||||
// DO NOT attempt to print options[key] within `${}` as a malicious script
|
||||
// can exploit the logger's attempt to stringify the value and execute arbitrary code
|
||||
log.debug(`Denied attempt to modify a secure key ${key}`, options[key]);
|
||||
@@ -162,7 +159,7 @@ export const sanitize = (options: any) => {
|
||||
|
||||
// Check that there no attempts of prototype pollution
|
||||
Object.keys(options).forEach((key) => {
|
||||
if (key.indexOf('__') === 0) {
|
||||
if (key.startsWith('__')) {
|
||||
delete options[key];
|
||||
}
|
||||
});
|
||||
@@ -188,16 +185,14 @@ export const sanitize = (options: any) => {
|
||||
*
|
||||
* @param directive - The directive to push in
|
||||
*/
|
||||
export const addDirective = (directive: any) => {
|
||||
if (directive.fontFamily) {
|
||||
if (!directive.themeVariables) {
|
||||
directive.themeVariables = { fontFamily: directive.fontFamily };
|
||||
} else {
|
||||
if (!directive.themeVariables.fontFamily) {
|
||||
directive.themeVariables = { fontFamily: directive.fontFamily };
|
||||
}
|
||||
}
|
||||
export const addDirective = (directive: MermaidConfig) => {
|
||||
sanitizeDirective(directive);
|
||||
|
||||
// If the directive has a fontFamily, but no themeVariables, add the fontFamily to the themeVariables
|
||||
if (directive.fontFamily && (!directive.themeVariables || !directive.themeVariables.fontFamily)) {
|
||||
directive.themeVariables = { fontFamily: directive.fontFamily };
|
||||
}
|
||||
|
||||
directives.push(directive);
|
||||
updateCurrentConfig(siteConfig, directives);
|
||||
};
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
/**
|
||||
* Configuration options to pass to the `dompurify` library.
|
||||
*/
|
||||
export type DOMPurifyConfiguration = import("dompurify").Config;
|
||||
export type DOMPurifyConfiguration = import('dompurify').Config;
|
||||
/**
|
||||
* JavaScript function that returns a `FontConfig`.
|
||||
*
|
||||
@@ -39,7 +39,7 @@ export type FontCalculator = () => Partial<FontConfig>;
|
||||
* This interface was referenced by `MermaidConfig`'s JSON-Schema
|
||||
* via the `definition` "SankeyLinkColor".
|
||||
*/
|
||||
export type SankeyLinkColor = "source" | "target" | "gradient";
|
||||
export type SankeyLinkColor = 'source' | 'target' | 'gradient';
|
||||
/**
|
||||
* Controls the alignment of the Sankey diagrams.
|
||||
*
|
||||
@@ -49,7 +49,7 @@ export type SankeyLinkColor = "source" | "target" | "gradient";
|
||||
* This interface was referenced by `MermaidConfig`'s JSON-Schema
|
||||
* via the `definition` "SankeyNodeAlignment".
|
||||
*/
|
||||
export type SankeyNodeAlignment = "left" | "right" | "center" | "justify";
|
||||
export type SankeyNodeAlignment = 'left' | 'right' | 'center' | 'justify';
|
||||
/**
|
||||
* The font size to use
|
||||
*/
|
||||
@@ -61,7 +61,7 @@ export interface MermaidConfig {
|
||||
* You may also use `themeCSS` to override this value.
|
||||
*
|
||||
*/
|
||||
theme?: string | "default" | "forest" | "dark" | "neutral" | "null";
|
||||
theme?: string | 'default' | 'forest' | 'dark' | 'neutral' | 'null';
|
||||
themeVariables?: any;
|
||||
themeCSS?: string;
|
||||
/**
|
||||
@@ -88,12 +88,12 @@ export interface MermaidConfig {
|
||||
| 0
|
||||
| 2
|
||||
| 1
|
||||
| "trace"
|
||||
| "debug"
|
||||
| "info"
|
||||
| "warn"
|
||||
| "error"
|
||||
| "fatal"
|
||||
| 'trace'
|
||||
| 'debug'
|
||||
| 'info'
|
||||
| 'warn'
|
||||
| 'error'
|
||||
| 'fatal'
|
||||
| 3
|
||||
| 4
|
||||
| 5
|
||||
@@ -101,7 +101,7 @@ export interface MermaidConfig {
|
||||
/**
|
||||
* Level of trust for parsed diagram
|
||||
*/
|
||||
securityLevel?: string | "strict" | "loose" | "antiscript" | "sandbox" | undefined;
|
||||
securityLevel?: string | 'strict' | 'loose' | 'antiscript' | 'sandbox' | undefined;
|
||||
/**
|
||||
* Dictates whether mermaid starts on Page load
|
||||
*/
|
||||
@@ -689,11 +689,11 @@ export interface QuadrantChartConfig extends BaseDiagramConfig {
|
||||
/**
|
||||
* position of x-axis labels
|
||||
*/
|
||||
xAxisPosition?: "top" | "bottom";
|
||||
xAxisPosition?: 'top' | 'bottom';
|
||||
/**
|
||||
* position of y-axis labels
|
||||
*/
|
||||
yAxisPosition?: "left" | "right";
|
||||
yAxisPosition?: 'left' | 'right';
|
||||
/**
|
||||
* stroke width of edges of the box that are inside the quadrant
|
||||
*/
|
||||
@@ -723,7 +723,7 @@ export interface ErDiagramConfig extends BaseDiagramConfig {
|
||||
/**
|
||||
* Directional bias for layout of entities
|
||||
*/
|
||||
layoutDirection?: string | "TB" | "BT" | "LR" | "RL";
|
||||
layoutDirection?: string | 'TB' | 'BT' | 'LR' | 'RL';
|
||||
/**
|
||||
* The minimum width of an entity box. Expressed in pixels.
|
||||
*/
|
||||
@@ -788,7 +788,7 @@ export interface StateDiagramConfig extends BaseDiagramConfig {
|
||||
* Decides which rendering engine that is to be used for the rendering.
|
||||
*
|
||||
*/
|
||||
defaultRenderer?: string | "dagre-d3" | "dagre-wrapper" | "elk";
|
||||
defaultRenderer?: string | 'dagre-d3' | 'dagre-wrapper' | 'elk';
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `MermaidConfig`'s JSON-Schema
|
||||
@@ -812,7 +812,7 @@ export interface ClassDiagramConfig extends BaseDiagramConfig {
|
||||
* Decides which rendering engine that is to be used for the rendering.
|
||||
*
|
||||
*/
|
||||
defaultRenderer?: string | "dagre-d3" | "dagre-wrapper" | "elk";
|
||||
defaultRenderer?: string | 'dagre-d3' | 'dagre-wrapper' | 'elk';
|
||||
nodeSpacing?: number;
|
||||
rankSpacing?: number;
|
||||
/**
|
||||
@@ -872,7 +872,7 @@ export interface JourneyDiagramConfig extends BaseDiagramConfig {
|
||||
/**
|
||||
* Multiline message alignment
|
||||
*/
|
||||
messageAlign?: string | "left" | "center" | "right";
|
||||
messageAlign?: string | 'left' | 'center' | 'right';
|
||||
/**
|
||||
* Prolongs the edge of the diagram downwards.
|
||||
*
|
||||
@@ -951,7 +951,7 @@ export interface TimelineDiagramConfig extends BaseDiagramConfig {
|
||||
/**
|
||||
* Multiline message alignment
|
||||
*/
|
||||
messageAlign?: string | "left" | "center" | "right";
|
||||
messageAlign?: string | 'left' | 'center' | 'right';
|
||||
/**
|
||||
* Prolongs the edge of the diagram downwards.
|
||||
*
|
||||
@@ -1062,12 +1062,12 @@ export interface GanttDiagramConfig extends BaseDiagramConfig {
|
||||
* Controls the display mode.
|
||||
*
|
||||
*/
|
||||
displayMode?: string | "compact";
|
||||
displayMode?: string | 'compact';
|
||||
/**
|
||||
* On which day a week-based interval should start
|
||||
*
|
||||
*/
|
||||
weekday?: "monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday";
|
||||
weekday?: 'monday' | 'tuesday' | 'wednesday' | 'thursday' | 'friday' | 'saturday' | 'sunday';
|
||||
}
|
||||
/**
|
||||
* The object containing configurations specific for sequence diagrams
|
||||
@@ -1121,7 +1121,7 @@ export interface SequenceDiagramConfig extends BaseDiagramConfig {
|
||||
/**
|
||||
* Multiline message alignment
|
||||
*/
|
||||
messageAlign?: string | "left" | "center" | "right";
|
||||
messageAlign?: string | 'left' | 'center' | 'right';
|
||||
/**
|
||||
* Mirror actors under diagram
|
||||
*
|
||||
@@ -1178,7 +1178,7 @@ export interface SequenceDiagramConfig extends BaseDiagramConfig {
|
||||
/**
|
||||
* This sets the text alignment of actor-attached notes
|
||||
*/
|
||||
noteAlign?: string | "left" | "center" | "right";
|
||||
noteAlign?: string | 'left' | 'center' | 'right';
|
||||
/**
|
||||
* This sets the font size of actor messages
|
||||
*/
|
||||
@@ -1254,7 +1254,7 @@ export interface FlowchartDiagramConfig extends BaseDiagramConfig {
|
||||
* Defines how mermaid renders curves for flowcharts.
|
||||
*
|
||||
*/
|
||||
curve?: string | "basis" | "linear" | "cardinal";
|
||||
curve?: string | 'basis' | 'linear' | 'cardinal';
|
||||
/**
|
||||
* Represents the padding between the labels and the shape
|
||||
*
|
||||
@@ -1266,7 +1266,7 @@ export interface FlowchartDiagramConfig extends BaseDiagramConfig {
|
||||
* Decides which rendering engine that is to be used for the rendering.
|
||||
*
|
||||
*/
|
||||
defaultRenderer?: string | "dagre-d3" | "dagre-wrapper" | "elk";
|
||||
defaultRenderer?: string | 'dagre-d3' | 'dagre-wrapper' | 'elk';
|
||||
/**
|
||||
* Width of nodes where text is wrapped.
|
||||
*
|
||||
@@ -1296,7 +1296,7 @@ export interface SankeyDiagramConfig extends BaseDiagramConfig {
|
||||
* See <https://github.com/d3/d3-sankey#alignments>.
|
||||
*
|
||||
*/
|
||||
nodeAlignment?: "left" | "right" | "center" | "justify";
|
||||
nodeAlignment?: 'left' | 'right' | 'center' | 'justify';
|
||||
useMaxWidth?: boolean;
|
||||
}
|
||||
/**
|
||||
|
||||
@@ -8,8 +8,25 @@ import note from './shapes/note.js';
|
||||
import { parseMember } from '../diagrams/class/svgDraw.js';
|
||||
import { evaluate } from '../diagrams/common/common.js';
|
||||
|
||||
const formatClass = (str) => {
|
||||
if (str) {
|
||||
return ' ' + str;
|
||||
}
|
||||
return '';
|
||||
};
|
||||
const getClassesFromNode = (node, otherClasses) => {
|
||||
return `${otherClasses ? otherClasses : 'node default'}${formatClass(node.classes)} ${formatClass(
|
||||
node.class
|
||||
)}`;
|
||||
};
|
||||
|
||||
const question = async (parent, node) => {
|
||||
const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
|
||||
const { shapeSvg, bbox } = await labelHelper(
|
||||
parent,
|
||||
node,
|
||||
getClassesFromNode(node, undefined),
|
||||
true
|
||||
);
|
||||
|
||||
const w = bbox.width + node.padding;
|
||||
const h = bbox.height + node.padding;
|
||||
@@ -70,7 +87,12 @@ const choice = (parent, node) => {
|
||||
};
|
||||
|
||||
const hexagon = async (parent, node) => {
|
||||
const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
|
||||
const { shapeSvg, bbox } = await labelHelper(
|
||||
parent,
|
||||
node,
|
||||
getClassesFromNode(node, undefined),
|
||||
true
|
||||
);
|
||||
|
||||
const f = 4;
|
||||
const h = bbox.height + node.padding;
|
||||
@@ -97,7 +119,12 @@ const hexagon = async (parent, node) => {
|
||||
};
|
||||
|
||||
const rect_left_inv_arrow = async (parent, node) => {
|
||||
const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
|
||||
const { shapeSvg, bbox } = await labelHelper(
|
||||
parent,
|
||||
node,
|
||||
getClassesFromNode(node, undefined),
|
||||
true
|
||||
);
|
||||
|
||||
const w = bbox.width + node.padding;
|
||||
const h = bbox.height + node.padding;
|
||||
@@ -123,7 +150,7 @@ const rect_left_inv_arrow = async (parent, node) => {
|
||||
};
|
||||
|
||||
const lean_right = async (parent, node) => {
|
||||
const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
|
||||
const { shapeSvg, bbox } = await labelHelper(parent, node, getClassesFromNode(node), true);
|
||||
|
||||
const w = bbox.width + node.padding;
|
||||
const h = bbox.height + node.padding;
|
||||
@@ -146,7 +173,12 @@ const lean_right = async (parent, node) => {
|
||||
};
|
||||
|
||||
const lean_left = async (parent, node) => {
|
||||
const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
|
||||
const { shapeSvg, bbox } = await labelHelper(
|
||||
parent,
|
||||
node,
|
||||
getClassesFromNode(node, undefined),
|
||||
true
|
||||
);
|
||||
|
||||
const w = bbox.width + node.padding;
|
||||
const h = bbox.height + node.padding;
|
||||
@@ -169,7 +201,12 @@ const lean_left = async (parent, node) => {
|
||||
};
|
||||
|
||||
const trapezoid = async (parent, node) => {
|
||||
const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
|
||||
const { shapeSvg, bbox } = await labelHelper(
|
||||
parent,
|
||||
node,
|
||||
getClassesFromNode(node, undefined),
|
||||
true
|
||||
);
|
||||
|
||||
const w = bbox.width + node.padding;
|
||||
const h = bbox.height + node.padding;
|
||||
@@ -192,7 +229,12 @@ const trapezoid = async (parent, node) => {
|
||||
};
|
||||
|
||||
const inv_trapezoid = async (parent, node) => {
|
||||
const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
|
||||
const { shapeSvg, bbox } = await labelHelper(
|
||||
parent,
|
||||
node,
|
||||
getClassesFromNode(node, undefined),
|
||||
true
|
||||
);
|
||||
|
||||
const w = bbox.width + node.padding;
|
||||
const h = bbox.height + node.padding;
|
||||
@@ -215,7 +257,12 @@ const inv_trapezoid = async (parent, node) => {
|
||||
};
|
||||
|
||||
const rect_right_inv_arrow = async (parent, node) => {
|
||||
const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
|
||||
const { shapeSvg, bbox } = await labelHelper(
|
||||
parent,
|
||||
node,
|
||||
getClassesFromNode(node, undefined),
|
||||
true
|
||||
);
|
||||
|
||||
const w = bbox.width + node.padding;
|
||||
const h = bbox.height + node.padding;
|
||||
@@ -239,7 +286,12 @@ const rect_right_inv_arrow = async (parent, node) => {
|
||||
};
|
||||
|
||||
const cylinder = async (parent, node) => {
|
||||
const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
|
||||
const { shapeSvg, bbox } = await labelHelper(
|
||||
parent,
|
||||
node,
|
||||
getClassesFromNode(node, undefined),
|
||||
true
|
||||
);
|
||||
|
||||
const w = bbox.width + node.padding;
|
||||
const rx = w / 2;
|
||||
@@ -314,7 +366,7 @@ const rect = async (parent, node) => {
|
||||
const { shapeSvg, bbox, halfPadding } = await labelHelper(
|
||||
parent,
|
||||
node,
|
||||
'node ' + node.classes,
|
||||
'node ' + node.classes + ' ' + node.class,
|
||||
true
|
||||
);
|
||||
|
||||
@@ -360,7 +412,7 @@ const rect = async (parent, node) => {
|
||||
const labelRect = async (parent, node) => {
|
||||
const { shapeSvg } = await labelHelper(parent, node, 'label', true);
|
||||
|
||||
log.trace('Classes = ', node.classes);
|
||||
log.trace('Classes = ', node.class);
|
||||
// add the rect
|
||||
const rect = shapeSvg.insert('rect', ':first-child');
|
||||
|
||||
@@ -545,7 +597,12 @@ const rectWithTitle = (parent, node) => {
|
||||
};
|
||||
|
||||
const stadium = async (parent, node) => {
|
||||
const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
|
||||
const { shapeSvg, bbox } = await labelHelper(
|
||||
parent,
|
||||
node,
|
||||
getClassesFromNode(node, undefined),
|
||||
true
|
||||
);
|
||||
|
||||
const h = bbox.height + node.padding;
|
||||
const w = bbox.width + h / 4 + node.padding;
|
||||
@@ -571,7 +628,12 @@ const stadium = async (parent, node) => {
|
||||
};
|
||||
|
||||
const circle = async (parent, node) => {
|
||||
const { shapeSvg, bbox, halfPadding } = await labelHelper(parent, node, undefined, true);
|
||||
const { shapeSvg, bbox, halfPadding } = await labelHelper(
|
||||
parent,
|
||||
node,
|
||||
getClassesFromNode(node, undefined),
|
||||
true
|
||||
);
|
||||
const circle = shapeSvg.insert('circle', ':first-child');
|
||||
|
||||
// center the circle around its coordinate
|
||||
@@ -596,7 +658,12 @@ const circle = async (parent, node) => {
|
||||
};
|
||||
|
||||
const doublecircle = async (parent, node) => {
|
||||
const { shapeSvg, bbox, halfPadding } = await labelHelper(parent, node, undefined, true);
|
||||
const { shapeSvg, bbox, halfPadding } = await labelHelper(
|
||||
parent,
|
||||
node,
|
||||
getClassesFromNode(node, undefined),
|
||||
true
|
||||
);
|
||||
const gap = 5;
|
||||
const circleGroup = shapeSvg.insert('g', ':first-child');
|
||||
const outerCircle = circleGroup.insert('circle');
|
||||
@@ -634,7 +701,12 @@ const doublecircle = async (parent, node) => {
|
||||
};
|
||||
|
||||
const subroutine = async (parent, node) => {
|
||||
const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
|
||||
const { shapeSvg, bbox } = await labelHelper(
|
||||
parent,
|
||||
node,
|
||||
getClassesFromNode(node, undefined),
|
||||
true
|
||||
);
|
||||
|
||||
const w = bbox.width + node.padding;
|
||||
const h = bbox.height + node.padding;
|
||||
|
||||
@@ -13,6 +13,7 @@ export const labelHelper = async (parent, node, _classes, isNode) => {
|
||||
} else {
|
||||
classes = _classes;
|
||||
}
|
||||
|
||||
// Add outer g element
|
||||
const shapeSvg = parent
|
||||
.insert('g')
|
||||
@@ -49,7 +50,6 @@ export const labelHelper = async (parent, node, _classes, isNode) => {
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Get the size of the label
|
||||
let bbox = text.getBBox();
|
||||
const halfPadding = node.padding / 2;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import type { RequiredDeep } from 'type-fest';
|
||||
|
||||
import theme from './themes/index.js';
|
||||
import { type MermaidConfig } from './config.type.js';
|
||||
import type { MermaidConfig } from './config.type.js';
|
||||
|
||||
// Uses our custom Vite jsonSchemaPlugin to load only the default values from
|
||||
// our JSON Schema
|
||||
@@ -13,7 +15,7 @@ import defaultConfigJson from './schemas/config.schema.yaml?only-defaults=true';
|
||||
* Non-JSON JS default values are listed in this file, e.g. functions, or
|
||||
* `undefined` (explicitly set so that `configKeys` finds them).
|
||||
*/
|
||||
const config: Partial<MermaidConfig> = {
|
||||
const config: RequiredDeep<MermaidConfig> = {
|
||||
...defaultConfigJson,
|
||||
// Set, even though they're `undefined` so that `configKeys` finds these keys
|
||||
// TODO: Should we replace these with `null` so that they can go in the JSON Schema?
|
||||
@@ -232,7 +234,7 @@ const config: Partial<MermaidConfig> = {
|
||||
},
|
||||
pie: {
|
||||
...defaultConfigJson.pie,
|
||||
useWidth: undefined,
|
||||
useWidth: 984,
|
||||
},
|
||||
requirement: {
|
||||
...defaultConfigJson.requirement,
|
||||
@@ -263,5 +265,5 @@ const keyify = (obj: any, prefix = ''): string[] =>
|
||||
return [...res, prefix + el];
|
||||
}, []);
|
||||
|
||||
export const configKeys: string[] = keyify(config, '');
|
||||
export const configKeys: Set<string> = new Set(keyify(config, ''));
|
||||
export default config;
|
||||
|
||||
@@ -6,14 +6,10 @@ import type {
|
||||
DiagramLoader,
|
||||
ExternalDiagramDefinition,
|
||||
} from './types.js';
|
||||
import { frontMatterRegex } from './frontmatter.js';
|
||||
import { getDiagram, registerDiagram } from './diagramAPI.js';
|
||||
import { anyCommentRegex, directiveRegex, frontMatterRegex } from './regexes.js';
|
||||
import { UnknownDiagramError } from '../errors.js';
|
||||
|
||||
const directive = /%{2}{\s*(?:(\w+)\s*:|(\w+))\s*(?:(\w+)|((?:(?!}%{2}).|\r?\n)*))?\s*(?:}%{2})?/gi;
|
||||
const anyComment = /\s*%%.*\n/gm;
|
||||
|
||||
const detectors: Record<string, DetectorRecord> = {};
|
||||
export const detectors: Record<string, DetectorRecord> = {};
|
||||
|
||||
/**
|
||||
* Detects the type of the graph text.
|
||||
@@ -38,7 +34,10 @@ const detectors: Record<string, DetectorRecord> = {};
|
||||
* @returns A graph definition key
|
||||
*/
|
||||
export const detectType = function (text: string, config?: MermaidConfig): string {
|
||||
text = text.replace(frontMatterRegex, '').replace(directive, '').replace(anyComment, '\n');
|
||||
text = text
|
||||
.replace(frontMatterRegex, '')
|
||||
.replace(directiveRegex, '')
|
||||
.replace(anyCommentRegex, '\n');
|
||||
for (const [key, { detector }] of Object.entries(detectors)) {
|
||||
const diagram = detector(text, config);
|
||||
if (diagram) {
|
||||
@@ -70,39 +69,6 @@ export const registerLazyLoadedDiagrams = (...diagrams: ExternalDiagramDefinitio
|
||||
}
|
||||
};
|
||||
|
||||
export const loadRegisteredDiagrams = async () => {
|
||||
log.debug(`Loading registered diagrams`);
|
||||
// Load all lazy loaded diagrams in parallel
|
||||
const results = await Promise.allSettled(
|
||||
Object.entries(detectors).map(async ([key, { detector, loader }]) => {
|
||||
if (loader) {
|
||||
try {
|
||||
getDiagram(key);
|
||||
} catch (error) {
|
||||
try {
|
||||
// Register diagram if it is not already registered
|
||||
const { diagram, id } = await loader();
|
||||
registerDiagram(id, diagram, detector);
|
||||
} catch (err) {
|
||||
// Remove failed diagram from detectors
|
||||
log.error(`Failed to load external diagram with key ${key}. Removing from detectors.`);
|
||||
delete detectors[key];
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
const failed = results.filter((result) => result.status === 'rejected');
|
||||
if (failed.length > 0) {
|
||||
log.error(`Failed to load ${failed.length} external diagrams`);
|
||||
for (const res of failed) {
|
||||
log.error(res);
|
||||
}
|
||||
throw new Error(`Failed to load ${failed.length} external diagrams`);
|
||||
}
|
||||
};
|
||||
|
||||
export const addDetector = (key: string, detector: DiagramDetector, loader?: DiagramLoader) => {
|
||||
if (detectors[key]) {
|
||||
log.error(`Detector with key ${key} already exists`);
|
||||
|
||||
@@ -5,7 +5,7 @@ import er from '../diagrams/er/erDetector.js';
|
||||
import git from '../diagrams/git/gitGraphDetector.js';
|
||||
import gantt from '../diagrams/gantt/ganttDetector.js';
|
||||
import { info } from '../diagrams/info/infoDetector.js';
|
||||
import pie from '../diagrams/pie/pieDetector.js';
|
||||
import { pie } from '../diagrams/pie/pieDetector.js';
|
||||
import quadrantChart from '../diagrams/quadrant-chart/quadrantDetector.js';
|
||||
import requirement from '../diagrams/requirement/requirementDetector.js';
|
||||
import sequence from '../diagrams/sequence/sequenceDetector.js';
|
||||
|
||||
@@ -35,7 +35,12 @@ describe('DiagramAPI', () => {
|
||||
'loki',
|
||||
{
|
||||
db: {},
|
||||
parser: {},
|
||||
parser: {
|
||||
parse: (_text) => {
|
||||
return;
|
||||
},
|
||||
parser: { yy: {} },
|
||||
},
|
||||
renderer: {},
|
||||
styles: {},
|
||||
},
|
||||
|
||||
@@ -4,7 +4,7 @@ import { getConfig as _getConfig } from '../config.js';
|
||||
import { sanitizeText as _sanitizeText } from '../diagrams/common/common.js';
|
||||
import { setupGraphViewbox as _setupGraphViewbox } from '../setupGraphViewbox.js';
|
||||
import { addStylesForDiagram } from '../styles.js';
|
||||
import { DiagramDefinition, DiagramDetector } from './types.js';
|
||||
import type { DiagramDefinition, DiagramDetector } from './types.js';
|
||||
import * as _commonDb from '../commonDb.js';
|
||||
import { parseDirective as _parseDirective } from '../directiveUtils.js';
|
||||
|
||||
@@ -69,11 +69,11 @@ export const getDiagram = (name: string): DiagramDefinition => {
|
||||
if (name in diagrams) {
|
||||
return diagrams[name];
|
||||
}
|
||||
throw new Error(`Diagram ${name} not found.`);
|
||||
throw new DiagramNotFoundError(name);
|
||||
};
|
||||
|
||||
export class DiagramNotFoundError extends Error {
|
||||
constructor(message: string) {
|
||||
super(`Diagram ${message} not found.`);
|
||||
constructor(name: string) {
|
||||
super(`Diagram ${name} not found.`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,13 @@ import { vi } from 'vitest';
|
||||
import { extractFrontMatter } from './frontmatter.js';
|
||||
|
||||
const dbMock = () => ({ setDiagramTitle: vi.fn() });
|
||||
const setConfigMock = vi.fn();
|
||||
|
||||
describe('extractFrontmatter', () => {
|
||||
beforeEach(() => {
|
||||
setConfigMock.mockClear();
|
||||
});
|
||||
|
||||
it('returns text unchanged if no frontmatter', () => {
|
||||
expect(extractFrontMatter('diagram', dbMock())).toEqual('diagram');
|
||||
});
|
||||
@@ -75,4 +80,21 @@ describe('extractFrontmatter', () => {
|
||||
'tag suffix cannot contain exclamation marks'
|
||||
);
|
||||
});
|
||||
|
||||
it('handles frontmatter with config', () => {
|
||||
const text = `---
|
||||
title: hello
|
||||
config:
|
||||
graph:
|
||||
string: hello
|
||||
number: 14
|
||||
boolean: false
|
||||
array: [1, 2, 3]
|
||||
---
|
||||
diagram`;
|
||||
expect(extractFrontMatter(text, {}, setConfigMock)).toEqual('diagram');
|
||||
expect(setConfigMock).toHaveBeenCalledWith({
|
||||
graph: { string: 'hello', number: 14, boolean: false, array: [1, 2, 3] },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,46 +1,53 @@
|
||||
import { DiagramDB } from './types.js';
|
||||
import type { MermaidConfig } from '../config.type.js';
|
||||
import { frontMatterRegex } from './regexes.js';
|
||||
import type { DiagramDB } from './types.js';
|
||||
// The "* as yaml" part is necessary for tree-shaking
|
||||
import * as yaml from 'js-yaml';
|
||||
|
||||
// Match Jekyll-style front matter blocks (https://jekyllrb.com/docs/front-matter/).
|
||||
// Based on regex used by Jekyll: https://github.com/jekyll/jekyll/blob/6dd3cc21c40b98054851846425af06c64f9fb466/lib/jekyll/document.rb#L10
|
||||
// Note that JS doesn't support the "\A" anchor, which means we can't use
|
||||
// multiline mode.
|
||||
// Relevant YAML spec: https://yaml.org/spec/1.2.2/#914-explicit-documents
|
||||
export const frontMatterRegex = /^-{3}\s*[\n\r](.*?)[\n\r]-{3}\s*[\n\r]+/s;
|
||||
|
||||
type FrontMatterMetadata = {
|
||||
interface FrontMatterMetadata {
|
||||
title?: string;
|
||||
// Allows custom display modes. Currently used for compact mode in gantt charts.
|
||||
displayMode?: string;
|
||||
};
|
||||
config?: MermaidConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract and parse frontmatter from text, if present, and sets appropriate
|
||||
* properties in the provided db.
|
||||
* @param text - The text that may have a YAML frontmatter.
|
||||
* @param db - Diagram database, could be of any diagram.
|
||||
* @param setDiagramConfig - Optional function to set diagram config.
|
||||
* @returns text with frontmatter stripped out
|
||||
*/
|
||||
export function extractFrontMatter(text: string, db: DiagramDB): string {
|
||||
export function extractFrontMatter(
|
||||
text: string,
|
||||
db: DiagramDB,
|
||||
setDiagramConfig?: (config: MermaidConfig) => void
|
||||
): string {
|
||||
const matches = text.match(frontMatterRegex);
|
||||
if (matches) {
|
||||
const parsed: FrontMatterMetadata = yaml.load(matches[1], {
|
||||
// To keep things simple, only allow strings, arrays, and plain objects.
|
||||
// https://www.yaml.org/spec/1.2/spec.html#id2802346
|
||||
schema: yaml.FAILSAFE_SCHEMA,
|
||||
}) as FrontMatterMetadata;
|
||||
|
||||
if (parsed?.title) {
|
||||
db.setDiagramTitle?.(parsed.title);
|
||||
}
|
||||
|
||||
if (parsed?.displayMode) {
|
||||
db.setDisplayMode?.(parsed.displayMode);
|
||||
}
|
||||
|
||||
return text.slice(matches[0].length);
|
||||
} else {
|
||||
if (!matches) {
|
||||
return text;
|
||||
}
|
||||
|
||||
const parsed: FrontMatterMetadata = yaml.load(matches[1], {
|
||||
// To support config, we need JSON schema.
|
||||
// https://www.yaml.org/spec/1.2/spec.html#id2803231
|
||||
schema: yaml.JSON_SCHEMA,
|
||||
}) as FrontMatterMetadata;
|
||||
|
||||
if (parsed?.title) {
|
||||
// toString() is necessary because YAML could parse the title as a number/boolean
|
||||
db.setDiagramTitle?.(parsed.title.toString());
|
||||
}
|
||||
|
||||
if (parsed?.displayMode) {
|
||||
// toString() is necessary because YAML could parse the title as a number/boolean
|
||||
db.setDisplayMode?.(parsed.displayMode.toString());
|
||||
}
|
||||
|
||||
if (parsed?.config) {
|
||||
setDiagramConfig?.(parsed.config);
|
||||
}
|
||||
|
||||
return text.slice(matches[0].length);
|
||||
}
|
||||
|
||||
36
packages/mermaid/src/diagram-api/loadDiagram.ts
Normal file
36
packages/mermaid/src/diagram-api/loadDiagram.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { log } from '../logger.js';
|
||||
import { detectors } from './detectType.js';
|
||||
import { getDiagram, registerDiagram } from './diagramAPI.js';
|
||||
|
||||
export const loadRegisteredDiagrams = async () => {
|
||||
log.debug(`Loading registered diagrams`);
|
||||
// Load all lazy loaded diagrams in parallel
|
||||
const results = await Promise.allSettled(
|
||||
Object.entries(detectors).map(async ([key, { detector, loader }]) => {
|
||||
if (loader) {
|
||||
try {
|
||||
getDiagram(key);
|
||||
} catch (error) {
|
||||
try {
|
||||
// Register diagram if it is not already registered
|
||||
const { diagram, id } = await loader();
|
||||
registerDiagram(id, diagram, detector);
|
||||
} catch (err) {
|
||||
// Remove failed diagram from detectors
|
||||
log.error(`Failed to load external diagram with key ${key}. Removing from detectors.`);
|
||||
delete detectors[key];
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
const failed = results.filter((result) => result.status === 'rejected');
|
||||
if (failed.length > 0) {
|
||||
log.error(`Failed to load ${failed.length} external diagrams`);
|
||||
for (const res of failed) {
|
||||
log.error(res);
|
||||
}
|
||||
throw new Error(`Failed to load ${failed.length} external diagrams`);
|
||||
}
|
||||
};
|
||||
11
packages/mermaid/src/diagram-api/regexes.ts
Normal file
11
packages/mermaid/src/diagram-api/regexes.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
// Match Jekyll-style front matter blocks (https://jekyllrb.com/docs/front-matter/).
|
||||
// Based on regex used by Jekyll: https://github.com/jekyll/jekyll/blob/6dd3cc21c40b98054851846425af06c64f9fb466/lib/jekyll/document.rb#L10
|
||||
// Note that JS doesn't support the "\A" anchor, which means we can't use
|
||||
// multiline mode.
|
||||
// Relevant YAML spec: https://yaml.org/spec/1.2.2/#914-explicit-documents
|
||||
export const frontMatterRegex = /^-{3}\s*[\n\r](.*?)[\n\r]-{3}\s*[\n\r]+/s;
|
||||
|
||||
export const directiveRegex =
|
||||
/%{2}{\s*(?:(\w+)\s*:|(\w+))\s*(?:(\w+)|((?:(?!}%{2}).|\r?\n)*))?\s*(?:}%{2})?/gi;
|
||||
|
||||
export const anyCommentRegex = /\s*%%.*\n/gm;
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Diagram } from '../Diagram.js';
|
||||
import { MermaidConfig } from '../config.type.js';
|
||||
import type { BaseDiagramConfig, MermaidConfig } from '../config.type.js';
|
||||
import type * as d3 from 'd3';
|
||||
|
||||
export interface InjectUtils {
|
||||
@@ -16,18 +16,26 @@ export interface InjectUtils {
|
||||
* Generic Diagram DB that may apply to any diagram type.
|
||||
*/
|
||||
export interface DiagramDB {
|
||||
// config
|
||||
getConfig?: () => BaseDiagramConfig | undefined;
|
||||
|
||||
// db
|
||||
clear?: () => void;
|
||||
setDiagramTitle?: (title: string) => void;
|
||||
setDisplayMode?: (title: string) => void;
|
||||
getDiagramTitle?: () => string;
|
||||
setAccTitle?: (title: string) => void;
|
||||
getAccTitle?: () => string;
|
||||
setAccDescription?: (describetion: string) => void;
|
||||
getAccDescription?: () => string;
|
||||
|
||||
setDisplayMode?: (title: string) => void;
|
||||
bindFunctions?: (element: Element) => void;
|
||||
}
|
||||
|
||||
export interface DiagramDefinition {
|
||||
db: DiagramDB;
|
||||
renderer: any;
|
||||
parser: any;
|
||||
parser: ParserDefinition;
|
||||
styles?: any;
|
||||
init?: (config: MermaidConfig) => void;
|
||||
injectUtils?: (
|
||||
@@ -70,6 +78,11 @@ export type DrawDefinition = (
|
||||
diagramObject: Diagram
|
||||
) => void;
|
||||
|
||||
export interface ParserDefinition {
|
||||
parse: (text: string) => void;
|
||||
parser: { yy: DiagramDB };
|
||||
}
|
||||
|
||||
/**
|
||||
* Type for function parse directive from diagram code.
|
||||
*
|
||||
@@ -79,8 +92,10 @@ export type DrawDefinition = (
|
||||
*/
|
||||
export type ParseDirectiveDefinition = (statement: string, context: string, type: string) => void;
|
||||
|
||||
export type HTML = d3.Selection<HTMLIFrameElement, unknown, Element, unknown>;
|
||||
export type HTML = d3.Selection<HTMLIFrameElement, unknown, Element | null, unknown>;
|
||||
|
||||
export type SVG = d3.Selection<SVGSVGElement, unknown, Element, unknown>;
|
||||
export type SVG = d3.Selection<SVGSVGElement, unknown, Element | null, unknown>;
|
||||
|
||||
export type Group = d3.Selection<SVGGElement, unknown, Element | null, unknown>;
|
||||
|
||||
export type DiagramStylesProvider = (options?: any) => string;
|
||||
|
||||
@@ -49,7 +49,7 @@ describe('diagram detection', () => {
|
||||
"Parse error on line 2:
|
||||
graph TD; A-->
|
||||
--------------^
|
||||
Expecting 'AMP', 'ALPHA', 'COLON', 'PIPE', 'TESTSTR', 'DOWN', 'DEFAULT', 'NUM', 'COMMA', 'MINUS', 'BRKT', 'DOT', 'PUNCTUATION', 'UNICODE_TEXT', 'PLUS', 'EQUALS', 'MULT', 'UNDERSCORE', got 'EOF'"
|
||||
Expecting 'AMP', 'COLON', 'PIPE', 'TESTSTR', 'DOWN', 'DEFAULT', 'NUM', 'COMMA', 'NODE_STRING', 'BRKT', 'MINUS', 'MULT', 'UNICODE_TEXT', got 'EOF'"
|
||||
`);
|
||||
await expect(getDiagramFromText('sequenceDiagram; A-->B')).rejects
|
||||
.toThrowErrorMatchingInlineSnapshot(`
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @ts-ignore: TODO Fix ts errors
|
||||
// @ts-ignore: JISON doesn't support types
|
||||
import c4Parser from './parser/c4Diagram.jison';
|
||||
import c4Db from './c4Db.js';
|
||||
import c4Renderer from './c4Renderer.js';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import common from '../common/common.js';
|
||||
import * as svgDrawCommon from '../common/svgDrawCommon';
|
||||
import * as svgDrawCommon from '../common/svgDrawCommon.js';
|
||||
import { sanitizeUrl } from '@braintree/sanitize-url';
|
||||
|
||||
export const drawRect = function (elem, rectData) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DiagramDefinition } from '../../diagram-api/types.js';
|
||||
// @ts-ignore: TODO Fix ts errors
|
||||
// @ts-ignore: JISON doesn't support types
|
||||
import parser from './parser/classDiagram.jison';
|
||||
import db from './classDb.js';
|
||||
import styles from './styles.js';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DiagramDefinition } from '../../diagram-api/types.js';
|
||||
// @ts-ignore: TODO Fix ts errors
|
||||
// @ts-ignore: JISON doesn't support types
|
||||
import parser from './parser/classDiagram.jison';
|
||||
import db from './classDb.js';
|
||||
import styles from './styles.js';
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { sanitizeText, removeScript, parseGenericTypes } from './common.js';
|
||||
|
||||
describe('when securityLevel is antiscript, all script must be removed', function () {
|
||||
describe('when securityLevel is antiscript, all script must be removed', () => {
|
||||
/**
|
||||
* @param {string} original The original text
|
||||
* @param {string} result The expected sanitized text
|
||||
* @param original - The original text
|
||||
* @param result - The expected sanitized text
|
||||
*/
|
||||
function compareRemoveScript(original, result) {
|
||||
function compareRemoveScript(original: string, result: string) {
|
||||
expect(removeScript(original).trim()).toEqual(result);
|
||||
}
|
||||
|
||||
it('should remove all script block, script inline.', function () {
|
||||
it('should remove all script block, script inline.', () => {
|
||||
const labelString = `1
|
||||
Act1: Hello 1<script src="http://abc.com/script1.js"></script>1
|
||||
<b>Act2</b>:
|
||||
@@ -25,7 +25,7 @@ describe('when securityLevel is antiscript, all script must be removed', functio
|
||||
compareRemoveScript(labelString, exactlyString);
|
||||
});
|
||||
|
||||
it('should remove all javascript urls', function () {
|
||||
it('should remove all javascript urls', () => {
|
||||
compareRemoveScript(
|
||||
`This is a <a href="javascript:runHijackingScript();">clean link</a> + <a href="javascript:runHijackingScript();">clean link</a>
|
||||
and <a href="javascript:bipassedMining();">me too</a>`,
|
||||
@@ -34,11 +34,11 @@ describe('when securityLevel is antiscript, all script must be removed', functio
|
||||
);
|
||||
});
|
||||
|
||||
it('should detect malicious images', function () {
|
||||
it('should detect malicious images', () => {
|
||||
compareRemoveScript(`<img onerror="alert('hello');">`, `<img>`);
|
||||
});
|
||||
|
||||
it('should detect iframes', function () {
|
||||
it('should detect iframes', () => {
|
||||
compareRemoveScript(
|
||||
`<iframe src="http://abc.com/script1.js"></iframe>
|
||||
<iframe src="http://example.com/iframeexample"></iframe>`,
|
||||
@@ -47,8 +47,8 @@ describe('when securityLevel is antiscript, all script must be removed', functio
|
||||
});
|
||||
});
|
||||
|
||||
describe('Sanitize text', function () {
|
||||
it('should remove script tag', function () {
|
||||
describe('Sanitize text', () => {
|
||||
it('should remove script tag', () => {
|
||||
const maliciousStr = 'javajavascript:script:alert(1)';
|
||||
const result = sanitizeText(maliciousStr, {
|
||||
securityLevel: 'strict',
|
||||
@@ -58,8 +58,8 @@ describe('Sanitize text', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('generic parser', function () {
|
||||
it('should parse generic types', function () {
|
||||
describe('generic parser', () => {
|
||||
it('should parse generic types', () => {
|
||||
expect(parseGenericTypes('test~T~')).toEqual('test<T>');
|
||||
expect(parseGenericTypes('test~Array~Array~string~~~')).toEqual('test<Array<Array<string>>>');
|
||||
expect(parseGenericTypes('test~Array~Array~string[]~~~')).toEqual(
|
||||
@@ -1,6 +1,7 @@
|
||||
import DOMPurify from 'dompurify';
|
||||
import { MermaidConfig } from '../../config.type.js';
|
||||
|
||||
// Remove and ignore br:s
|
||||
export const lineBreakRegex = /<br\s*\/?>/gi;
|
||||
|
||||
/**
|
||||
|
||||
58
packages/mermaid/src/diagrams/common/commonTypes.ts
Normal file
58
packages/mermaid/src/diagrams/common/commonTypes.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
export interface RectData {
|
||||
x: number;
|
||||
y: number;
|
||||
fill: string;
|
||||
width: number;
|
||||
height: number;
|
||||
stroke: string;
|
||||
class?: string;
|
||||
color?: string;
|
||||
rx?: number;
|
||||
ry?: number;
|
||||
attrs?: Record<string, string | number>;
|
||||
anchor?: string;
|
||||
}
|
||||
|
||||
export interface Bound {
|
||||
startx: number;
|
||||
stopx: number;
|
||||
starty: number;
|
||||
stopy: number;
|
||||
fill: string;
|
||||
stroke: string;
|
||||
}
|
||||
|
||||
export interface TextData {
|
||||
x: number;
|
||||
y: number;
|
||||
anchor: string;
|
||||
text: string;
|
||||
textMargin: number;
|
||||
class?: string;
|
||||
}
|
||||
|
||||
export interface TextObject {
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
fill?: string;
|
||||
anchor?: string;
|
||||
'text-anchor': string;
|
||||
style: string;
|
||||
textMargin: number;
|
||||
rx: number;
|
||||
ry: number;
|
||||
tspan: boolean;
|
||||
valign?: string;
|
||||
}
|
||||
|
||||
export type D3RectElement = d3.Selection<SVGRectElement, unknown, Element | null, unknown>;
|
||||
|
||||
export type D3UseElement = d3.Selection<SVGUseElement, unknown, Element | null, unknown>;
|
||||
|
||||
export type D3ImageElement = d3.Selection<SVGImageElement, unknown, Element | null, unknown>;
|
||||
|
||||
export type D3TextElement = d3.Selection<SVGTextElement, unknown, Element | null, unknown>;
|
||||
|
||||
export type D3TSpanElement = d3.Selection<SVGTSpanElement, unknown, Element | null, unknown>;
|
||||
@@ -1,114 +0,0 @@
|
||||
import { sanitizeUrl } from '@braintree/sanitize-url';
|
||||
|
||||
export const drawRect = function (elem, rectData) {
|
||||
const rectElem = elem.append('rect');
|
||||
rectElem.attr('x', rectData.x);
|
||||
rectElem.attr('y', rectData.y);
|
||||
rectElem.attr('fill', rectData.fill);
|
||||
rectElem.attr('stroke', rectData.stroke);
|
||||
rectElem.attr('width', rectData.width);
|
||||
rectElem.attr('height', rectData.height);
|
||||
rectElem.attr('rx', rectData.rx);
|
||||
rectElem.attr('ry', rectData.ry);
|
||||
|
||||
if (rectData.attrs !== 'undefined' && rectData.attrs !== null) {
|
||||
for (let attrKey in rectData.attrs) {
|
||||
rectElem.attr(attrKey, rectData.attrs[attrKey]);
|
||||
}
|
||||
}
|
||||
|
||||
if (rectData.class !== 'undefined') {
|
||||
rectElem.attr('class', rectData.class);
|
||||
}
|
||||
|
||||
return rectElem;
|
||||
};
|
||||
|
||||
/**
|
||||
* Draws a background rectangle
|
||||
*
|
||||
* @param {any} elem Diagram (reference for bounds)
|
||||
* @param {any} bounds Shape of the rectangle
|
||||
*/
|
||||
export const drawBackgroundRect = function (elem, bounds) {
|
||||
const rectElem = drawRect(elem, {
|
||||
x: bounds.startx,
|
||||
y: bounds.starty,
|
||||
width: bounds.stopx - bounds.startx,
|
||||
height: bounds.stopy - bounds.starty,
|
||||
fill: bounds.fill,
|
||||
stroke: bounds.stroke,
|
||||
class: 'rect',
|
||||
});
|
||||
rectElem.lower();
|
||||
};
|
||||
|
||||
export const drawText = function (elem, textData) {
|
||||
// Remove and ignore br:s
|
||||
const nText = textData.text.replace(/<br\s*\/?>/gi, ' ');
|
||||
|
||||
const textElem = elem.append('text');
|
||||
textElem.attr('x', textData.x);
|
||||
textElem.attr('y', textData.y);
|
||||
textElem.attr('class', 'legend');
|
||||
|
||||
textElem.style('text-anchor', textData.anchor);
|
||||
|
||||
if (textData.class !== undefined) {
|
||||
textElem.attr('class', textData.class);
|
||||
}
|
||||
|
||||
const span = textElem.append('tspan');
|
||||
span.attr('x', textData.x + textData.textMargin * 2);
|
||||
span.text(nText);
|
||||
|
||||
return textElem;
|
||||
};
|
||||
|
||||
export const drawImage = function (elem, x, y, link) {
|
||||
const imageElem = elem.append('image');
|
||||
imageElem.attr('x', x);
|
||||
imageElem.attr('y', y);
|
||||
var sanitizedLink = sanitizeUrl(link);
|
||||
imageElem.attr('xlink:href', sanitizedLink);
|
||||
};
|
||||
|
||||
export const drawEmbeddedImage = function (elem, x, y, link) {
|
||||
const imageElem = elem.append('use');
|
||||
imageElem.attr('x', x);
|
||||
imageElem.attr('y', y);
|
||||
const sanitizedLink = sanitizeUrl(link);
|
||||
imageElem.attr('xlink:href', '#' + sanitizedLink);
|
||||
};
|
||||
|
||||
export const getNoteRect = function () {
|
||||
return {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 100,
|
||||
height: 100,
|
||||
fill: '#EDF2AE',
|
||||
stroke: '#666',
|
||||
anchor: 'start',
|
||||
rx: 0,
|
||||
ry: 0,
|
||||
};
|
||||
};
|
||||
|
||||
export const getTextObj = function () {
|
||||
return {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 100,
|
||||
height: 100,
|
||||
fill: undefined,
|
||||
anchor: undefined,
|
||||
'text-anchor': 'start',
|
||||
style: '#666',
|
||||
textMargin: 0,
|
||||
rx: 0,
|
||||
ry: 0,
|
||||
tspan: true,
|
||||
valign: undefined,
|
||||
};
|
||||
};
|
||||
126
packages/mermaid/src/diagrams/common/svgDrawCommon.ts
Normal file
126
packages/mermaid/src/diagrams/common/svgDrawCommon.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
import { sanitizeUrl } from '@braintree/sanitize-url';
|
||||
import type { Group, SVG } from '../../diagram-api/types.js';
|
||||
import type {
|
||||
Bound,
|
||||
D3ImageElement,
|
||||
D3RectElement,
|
||||
D3TSpanElement,
|
||||
D3TextElement,
|
||||
D3UseElement,
|
||||
RectData,
|
||||
TextData,
|
||||
TextObject,
|
||||
} from './commonTypes.js';
|
||||
import { lineBreakRegex } from './common.js';
|
||||
|
||||
export const drawRect = (element: SVG | Group, rectData: RectData): D3RectElement => {
|
||||
const rectElement: D3RectElement = element.append('rect');
|
||||
rectElement.attr('x', rectData.x);
|
||||
rectElement.attr('y', rectData.y);
|
||||
rectElement.attr('fill', rectData.fill);
|
||||
rectElement.attr('stroke', rectData.stroke);
|
||||
rectElement.attr('width', rectData.width);
|
||||
rectElement.attr('height', rectData.height);
|
||||
rectData.rx !== undefined && rectElement.attr('rx', rectData.rx);
|
||||
rectData.ry !== undefined && rectElement.attr('ry', rectData.ry);
|
||||
|
||||
if (rectData.attrs !== undefined) {
|
||||
for (const attrKey in rectData.attrs) {
|
||||
rectElement.attr(attrKey, rectData.attrs[attrKey]);
|
||||
}
|
||||
}
|
||||
|
||||
rectData.class !== undefined && rectElement.attr('class', rectData.class);
|
||||
|
||||
return rectElement;
|
||||
};
|
||||
|
||||
/**
|
||||
* Draws a background rectangle
|
||||
*
|
||||
* @param element - Diagram (reference for bounds)
|
||||
* @param bounds - Shape of the rectangle
|
||||
*/
|
||||
export const drawBackgroundRect = (element: SVG | Group, bounds: Bound): void => {
|
||||
const rectData: RectData = {
|
||||
x: bounds.startx,
|
||||
y: bounds.starty,
|
||||
width: bounds.stopx - bounds.startx,
|
||||
height: bounds.stopy - bounds.starty,
|
||||
fill: bounds.fill,
|
||||
stroke: bounds.stroke,
|
||||
class: 'rect',
|
||||
};
|
||||
const rectElement: D3RectElement = drawRect(element, rectData);
|
||||
rectElement.lower();
|
||||
};
|
||||
|
||||
export const drawText = (element: SVG | Group, textData: TextData): D3TextElement => {
|
||||
const nText: string = textData.text.replace(lineBreakRegex, ' ');
|
||||
|
||||
const textElem: D3TextElement = element.append('text');
|
||||
textElem.attr('x', textData.x);
|
||||
textElem.attr('y', textData.y);
|
||||
textElem.attr('class', 'legend');
|
||||
|
||||
textElem.style('text-anchor', textData.anchor);
|
||||
textData.class !== undefined && textElem.attr('class', textData.class);
|
||||
|
||||
const tspan: D3TSpanElement = textElem.append('tspan');
|
||||
tspan.attr('x', textData.x + textData.textMargin * 2);
|
||||
tspan.text(nText);
|
||||
|
||||
return textElem;
|
||||
};
|
||||
|
||||
export const drawImage = (elem: SVG | Group, x: number, y: number, link: string): void => {
|
||||
const imageElement: D3ImageElement = elem.append('image');
|
||||
imageElement.attr('x', x);
|
||||
imageElement.attr('y', y);
|
||||
const sanitizedLink: string = sanitizeUrl(link);
|
||||
imageElement.attr('xlink:href', sanitizedLink);
|
||||
};
|
||||
|
||||
export const drawEmbeddedImage = (
|
||||
element: SVG | Group,
|
||||
x: number,
|
||||
y: number,
|
||||
link: string
|
||||
): void => {
|
||||
const imageElement: D3UseElement = element.append('use');
|
||||
imageElement.attr('x', x);
|
||||
imageElement.attr('y', y);
|
||||
const sanitizedLink: string = sanitizeUrl(link);
|
||||
imageElement.attr('xlink:href', `#${sanitizedLink}`);
|
||||
};
|
||||
|
||||
export const getNoteRect = (): RectData => {
|
||||
const noteRectData: RectData = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 100,
|
||||
height: 100,
|
||||
fill: '#EDF2AE',
|
||||
stroke: '#666',
|
||||
anchor: 'start',
|
||||
rx: 0,
|
||||
ry: 0,
|
||||
};
|
||||
return noteRectData;
|
||||
};
|
||||
|
||||
export const getTextObj = (): TextObject => {
|
||||
const testObject: TextObject = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 100,
|
||||
height: 100,
|
||||
'text-anchor': 'start',
|
||||
style: '#666',
|
||||
textMargin: 0,
|
||||
rx: 0,
|
||||
ry: 0,
|
||||
tspan: true,
|
||||
};
|
||||
return testObject;
|
||||
};
|
||||
@@ -555,7 +555,6 @@ const drawRelationshipFromLayout = function (svg, rel, g, insert, diagObj) {
|
||||
export const draw = function (text, id, _version, diagObj) {
|
||||
conf = getConfig().er;
|
||||
log.info('Drawing ER diagram');
|
||||
// diag.db.clear();
|
||||
const securityLevel = getConfig().securityLevel;
|
||||
// Handle root and Document for when rendering in sandbox mode
|
||||
let sandboxElement;
|
||||
|
||||
@@ -1,23 +1,15 @@
|
||||
import { DiagramDefinition } from '../../diagram-api/types.js';
|
||||
import styles from './styles.js';
|
||||
import renderer from './errorRenderer.js';
|
||||
export const diagram: DiagramDefinition = {
|
||||
db: {
|
||||
clear: () => {
|
||||
// Quite ok, clear needs to be there for error to work as a regular diagram
|
||||
},
|
||||
},
|
||||
styles,
|
||||
import type { DiagramDefinition } from '../../diagram-api/types.js';
|
||||
import { renderer } from './errorRenderer.js';
|
||||
|
||||
const diagram: DiagramDefinition = {
|
||||
db: {},
|
||||
renderer,
|
||||
parser: {
|
||||
parser: { yy: {} },
|
||||
parse: () => {
|
||||
// no op
|
||||
parse: (): void => {
|
||||
return;
|
||||
},
|
||||
},
|
||||
init: () => {
|
||||
// no op
|
||||
},
|
||||
};
|
||||
|
||||
export default diagram;
|
||||
|
||||
@@ -1,100 +1,81 @@
|
||||
/** Created by knut on 14-12-11. */
|
||||
// @ts-ignore TODO: Investigate D3 issue
|
||||
import { select } from 'd3';
|
||||
import { log } from '../../logger.js';
|
||||
import { getErrorMessage } from '../../utils.js';
|
||||
|
||||
/**
|
||||
* Merges the value of `conf` with the passed `cnf`
|
||||
*
|
||||
* @param cnf - Config to merge
|
||||
*/
|
||||
export const setConf = function () {
|
||||
// no-op
|
||||
};
|
||||
import type { Group, SVG } from '../../diagram-api/types.js';
|
||||
import { selectSvgElement } from '../../rendering-util/selectSvgElement.js';
|
||||
import { configureSvgSize } from '../../setupGraphViewbox.js';
|
||||
|
||||
/**
|
||||
* Draws a an info picture in the tag with id: id based on the graph definition in text.
|
||||
*
|
||||
* @param _text - Mermaid graph definition.
|
||||
* @param id - The text for the error
|
||||
* @param mermaidVersion - The version
|
||||
* @param version - The version
|
||||
*/
|
||||
export const draw = (_text: string, id: string, mermaidVersion: string) => {
|
||||
try {
|
||||
log.debug('Renering svg for syntax error\n');
|
||||
export const draw = (_text: string, id: string, version: string) => {
|
||||
log.debug('renering svg for syntax error\n');
|
||||
|
||||
const svg = select('#' + id);
|
||||
const svg: SVG = selectSvgElement(id);
|
||||
svg.attr('viewBox', '0 0 2412 512');
|
||||
configureSvgSize(svg, 100, 512, true);
|
||||
|
||||
const g = svg.append('g');
|
||||
const g: Group = svg.append('g');
|
||||
g.append('path')
|
||||
.attr('class', 'error-icon')
|
||||
.attr(
|
||||
'd',
|
||||
'm411.313,123.313c6.25-6.25 6.25-16.375 0-22.625s-16.375-6.25-22.625,0l-32,32-9.375,9.375-20.688-20.688c-12.484-12.5-32.766-12.5-45.25,0l-16,16c-1.261,1.261-2.304,2.648-3.31,4.051-21.739-8.561-45.324-13.426-70.065-13.426-105.867,0-192,86.133-192,192s86.133,192 192,192 192-86.133 192-192c0-24.741-4.864-48.327-13.426-70.065 1.402-1.007 2.79-2.049 4.051-3.31l16-16c12.5-12.492 12.5-32.758 0-45.25l-20.688-20.688 9.375-9.375 32.001-31.999zm-219.313,100.687c-52.938,0-96,43.063-96,96 0,8.836-7.164,16-16,16s-16-7.164-16-16c0-70.578 57.422-128 128-128 8.836,0 16,7.164 16,16s-7.164,16-16,16z'
|
||||
);
|
||||
|
||||
g.append('path')
|
||||
.attr('class', 'error-icon')
|
||||
.attr(
|
||||
'd',
|
||||
'm411.313,123.313c6.25-6.25 6.25-16.375 0-22.625s-16.375-6.25-22.625,0l-32,32-9.375,9.375-20.688-20.688c-12.484-12.5-32.766-12.5-45.25,0l-16,16c-1.261,1.261-2.304,2.648-3.31,4.051-21.739-8.561-45.324-13.426-70.065-13.426-105.867,0-192,86.133-192,192s86.133,192 192,192 192-86.133 192-192c0-24.741-4.864-48.327-13.426-70.065 1.402-1.007 2.79-2.049 4.051-3.31l16-16c12.5-12.492 12.5-32.758 0-45.25l-20.688-20.688 9.375-9.375 32.001-31.999zm-219.313,100.687c-52.938,0-96,43.063-96,96 0,8.836-7.164,16-16,16s-16-7.164-16-16c0-70.578 57.422-128 128-128 8.836,0 16,7.164 16,16s-7.164,16-16,16z'
|
||||
);
|
||||
g.append('path')
|
||||
.attr('class', 'error-icon')
|
||||
.attr(
|
||||
'd',
|
||||
'm459.02,148.98c-6.25-6.25-16.375-6.25-22.625,0s-6.25,16.375 0,22.625l16,16c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688 6.25-6.25 6.25-16.375 0-22.625l-16.001-16z'
|
||||
);
|
||||
|
||||
g.append('path')
|
||||
.attr('class', 'error-icon')
|
||||
.attr(
|
||||
'd',
|
||||
'm459.02,148.98c-6.25-6.25-16.375-6.25-22.625,0s-6.25,16.375 0,22.625l16,16c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688 6.25-6.25 6.25-16.375 0-22.625l-16.001-16z'
|
||||
);
|
||||
g.append('path')
|
||||
.attr('class', 'error-icon')
|
||||
.attr(
|
||||
'd',
|
||||
'm340.395,75.605c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688 6.25-6.25 6.25-16.375 0-22.625l-16-16c-6.25-6.25-16.375-6.25-22.625,0s-6.25,16.375 0,22.625l15.999,16z'
|
||||
);
|
||||
|
||||
g.append('path')
|
||||
.attr('class', 'error-icon')
|
||||
.attr(
|
||||
'd',
|
||||
'm340.395,75.605c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688 6.25-6.25 6.25-16.375 0-22.625l-16-16c-6.25-6.25-16.375-6.25-22.625,0s-6.25,16.375 0,22.625l15.999,16z'
|
||||
);
|
||||
g.append('path')
|
||||
.attr('class', 'error-icon')
|
||||
.attr(
|
||||
'd',
|
||||
'm400,64c8.844,0 16-7.164 16-16v-32c0-8.836-7.156-16-16-16-8.844,0-16,7.164-16,16v32c0,8.836 7.156,16 16,16z'
|
||||
);
|
||||
|
||||
g.append('path')
|
||||
.attr('class', 'error-icon')
|
||||
.attr(
|
||||
'd',
|
||||
'm400,64c8.844,0 16-7.164 16-16v-32c0-8.836-7.156-16-16-16-8.844,0-16,7.164-16,16v32c0,8.836 7.156,16 16,16z'
|
||||
);
|
||||
g.append('path')
|
||||
.attr('class', 'error-icon')
|
||||
.attr(
|
||||
'd',
|
||||
'm496,96.586h-32c-8.844,0-16,7.164-16,16 0,8.836 7.156,16 16,16h32c8.844,0 16-7.164 16-16 0-8.836-7.156-16-16-16z'
|
||||
);
|
||||
|
||||
g.append('path')
|
||||
.attr('class', 'error-icon')
|
||||
.attr(
|
||||
'd',
|
||||
'm496,96.586h-32c-8.844,0-16,7.164-16,16 0,8.836 7.156,16 16,16h32c8.844,0 16-7.164 16-16 0-8.836-7.156-16-16-16z'
|
||||
);
|
||||
g.append('path')
|
||||
.attr('class', 'error-icon')
|
||||
.attr(
|
||||
'd',
|
||||
'm436.98,75.605c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688l32-32c6.25-6.25 6.25-16.375 0-22.625s-16.375-6.25-22.625,0l-32,32c-6.251,6.25-6.251,16.375-0.001,22.625z'
|
||||
);
|
||||
|
||||
g.append('path')
|
||||
.attr('class', 'error-icon')
|
||||
.attr(
|
||||
'd',
|
||||
'm436.98,75.605c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688l32-32c6.25-6.25 6.25-16.375 0-22.625s-16.375-6.25-22.625,0l-32,32c-6.251,6.25-6.251,16.375-0.001,22.625z'
|
||||
);
|
||||
|
||||
g.append('text') // text label for the x axis
|
||||
.attr('class', 'error-text')
|
||||
.attr('x', 1440)
|
||||
.attr('y', 250)
|
||||
.attr('font-size', '150px')
|
||||
.style('text-anchor', 'middle')
|
||||
.text('Syntax error in text');
|
||||
g.append('text') // text label for the x axis
|
||||
.attr('class', 'error-text')
|
||||
.attr('x', 1250)
|
||||
.attr('y', 400)
|
||||
.attr('font-size', '100px')
|
||||
.style('text-anchor', 'middle')
|
||||
.text('mermaid version ' + mermaidVersion);
|
||||
|
||||
svg.attr('height', 100);
|
||||
svg.attr('width', 500);
|
||||
svg.attr('viewBox', '768 0 912 512');
|
||||
} catch (e) {
|
||||
log.error('Error while rendering info diagram');
|
||||
log.error(getErrorMessage(e));
|
||||
}
|
||||
g.append('text') // text label for the x axis
|
||||
.attr('class', 'error-text')
|
||||
.attr('x', 1440)
|
||||
.attr('y', 250)
|
||||
.attr('font-size', '150px')
|
||||
.style('text-anchor', 'middle')
|
||||
.text('Syntax error in text');
|
||||
g.append('text') // text label for the x axis
|
||||
.attr('class', 'error-text')
|
||||
.attr('x', 1250)
|
||||
.attr('y', 400)
|
||||
.attr('font-size', '100px')
|
||||
.style('text-anchor', 'middle')
|
||||
.text(`mermaid version ${version}`);
|
||||
};
|
||||
|
||||
export default {
|
||||
setConf,
|
||||
draw,
|
||||
};
|
||||
export const renderer = { draw };
|
||||
|
||||
export default renderer;
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
const getStyles = () => ``;
|
||||
|
||||
export default getStyles;
|
||||
@@ -655,14 +655,7 @@ const addMarkersToEdge = function (svgPath, edgeData, diagramType, arrowMarkerAb
|
||||
*/
|
||||
export const getClasses = function (text, diagObj) {
|
||||
log.info('Extracting classes');
|
||||
diagObj.db.clear('ver-2');
|
||||
try {
|
||||
// Parse the graph definition
|
||||
diagObj.parse(text);
|
||||
return diagObj.db.getClasses();
|
||||
} catch (e) {
|
||||
return {};
|
||||
}
|
||||
return diagObj.db.getClasses();
|
||||
};
|
||||
|
||||
const addSubGraphs = function (db) {
|
||||
@@ -766,14 +759,8 @@ const insertChildren = (nodeArray, parentLookupDb) => {
|
||||
*/
|
||||
|
||||
export const draw = async function (text, id, _version, diagObj) {
|
||||
// Add temporary render element
|
||||
diagObj.db.clear();
|
||||
nodeDb = {};
|
||||
portPos = {};
|
||||
diagObj.db.setGen('gen-2');
|
||||
// Parse the graph definition
|
||||
diagObj.parser.parse(text);
|
||||
|
||||
const renderEl = select('body').append('div').attr('style', 'height:400px').attr('id', 'cy');
|
||||
let graph = {
|
||||
id: 'root',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @ts-ignore: TODO Fix ts errors
|
||||
// @ts-ignore: JISON doesn't support types
|
||||
import flowParser from './parser/flow.jison';
|
||||
import flowDb from './flowDb.js';
|
||||
import flowRendererV2 from './flowRenderer-v2.js';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @ts-ignore: TODO Fix ts errors
|
||||
// @ts-ignore: JISON doesn't support types
|
||||
import flowParser from './parser/flow.jison';
|
||||
import flowDb from './flowDb.js';
|
||||
import flowRenderer from './flowRenderer.js';
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import * as graphlib from 'dagre-d3-es/src/graphlib/index.js';
|
||||
import { select, curveLinear, selectAll } from 'd3';
|
||||
|
||||
import flowDb from './flowDb.js';
|
||||
import { getConfig } from '../../config.js';
|
||||
import utils from '../../utils.js';
|
||||
|
||||
import { render } from '../../dagre-wrapper/index.js';
|
||||
import { addHtmlLabel } from 'dagre-d3-es/src/dagre-js/label/add-html-label.js';
|
||||
import { log } from '../../logger.js';
|
||||
@@ -344,15 +341,7 @@ export const addEdges = function (edges, g, diagObj) {
|
||||
* @returns {object} ClassDef styles
|
||||
*/
|
||||
export const getClasses = function (text, diagObj) {
|
||||
log.info('Extracting classes');
|
||||
diagObj.db.clear();
|
||||
try {
|
||||
// Parse the graph definition
|
||||
diagObj.parse(text);
|
||||
return diagObj.db.getClasses();
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
return diagObj.db.getClasses();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -364,10 +353,6 @@ export const getClasses = function (text, diagObj) {
|
||||
|
||||
export const draw = async function (text, id, _version, diagObj) {
|
||||
log.info('Drawing flowchart');
|
||||
diagObj.db.clear();
|
||||
flowDb.setGen('gen-2');
|
||||
// Parse the graph definition
|
||||
diagObj.parser.parse(text);
|
||||
|
||||
// Fetch the default direction, use TD if none was found
|
||||
let dir = diagObj.db.getDirection();
|
||||
|
||||
@@ -273,15 +273,7 @@ export const addEdges = function (edges, g, diagObj) {
|
||||
*/
|
||||
export const getClasses = function (text, diagObj) {
|
||||
log.info('Extracting classes');
|
||||
diagObj.db.clear();
|
||||
try {
|
||||
// Parse the graph definition
|
||||
diagObj.parse(text);
|
||||
return diagObj.db.getClasses();
|
||||
} catch (e) {
|
||||
log.error(e);
|
||||
return {};
|
||||
}
|
||||
return diagObj.db.getClasses();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -294,7 +286,6 @@ export const getClasses = function (text, diagObj) {
|
||||
*/
|
||||
export const draw = function (text, id, _version, diagObj) {
|
||||
log.info('Drawing flowchart');
|
||||
diagObj.db.clear();
|
||||
const { securityLevel, flowchart: conf } = getConfig();
|
||||
let sandboxElement;
|
||||
if (securityLevel === 'sandbox') {
|
||||
|
||||
@@ -6,6 +6,40 @@ setConfig({
|
||||
securityLevel: 'strict',
|
||||
});
|
||||
|
||||
const keywords = [
|
||||
'graph',
|
||||
'flowchart',
|
||||
'flowchart-elk',
|
||||
'style',
|
||||
'default',
|
||||
'linkStyle',
|
||||
'interpolate',
|
||||
'classDef',
|
||||
'class',
|
||||
'href',
|
||||
'call',
|
||||
'click',
|
||||
'_self',
|
||||
'_blank',
|
||||
'_parent',
|
||||
'_top',
|
||||
'end',
|
||||
'subgraph',
|
||||
'kitty',
|
||||
];
|
||||
|
||||
const doubleEndedEdges = [
|
||||
{ edgeStart: 'x--', edgeEnd: '--x', stroke: 'normal', type: 'double_arrow_cross' },
|
||||
{ edgeStart: 'x==', edgeEnd: '==x', stroke: 'thick', type: 'double_arrow_cross' },
|
||||
{ edgeStart: 'x-.', edgeEnd: '.-x', stroke: 'dotted', type: 'double_arrow_cross' },
|
||||
{ edgeStart: 'o--', edgeEnd: '--o', stroke: 'normal', type: 'double_arrow_circle' },
|
||||
{ edgeStart: 'o==', edgeEnd: '==o', stroke: 'thick', type: 'double_arrow_circle' },
|
||||
{ edgeStart: 'o-.', edgeEnd: '.-o', stroke: 'dotted', type: 'double_arrow_circle' },
|
||||
{ edgeStart: '<--', edgeEnd: '-->', stroke: 'normal', type: 'double_arrow_point' },
|
||||
{ edgeStart: '<==', edgeEnd: '==>', stroke: 'thick', type: 'double_arrow_point' },
|
||||
{ edgeStart: '<-.', edgeEnd: '.->', stroke: 'dotted', type: 'double_arrow_point' },
|
||||
];
|
||||
|
||||
describe('[Edges] when parsing', () => {
|
||||
beforeEach(function () {
|
||||
flow.parser.yy = flowDb;
|
||||
@@ -39,211 +73,62 @@ describe('[Edges] when parsing', () => {
|
||||
expect(edges[0].type).toBe('arrow_circle');
|
||||
});
|
||||
|
||||
describe('cross', function () {
|
||||
it('should handle double edged nodes and edges', function () {
|
||||
const res = flow.parser.parse('graph TD;\nA x--x B;');
|
||||
describe('edges', function () {
|
||||
doubleEndedEdges.forEach((edgeType) => {
|
||||
it(`should handle ${edgeType.stroke} ${edgeType.type} with no text`, function () {
|
||||
const res = flow.parser.parse(`graph TD;\nA ${edgeType.edgeStart}${edgeType.edgeEnd} B;`);
|
||||
|
||||
const vert = flow.parser.yy.getVertices();
|
||||
const edges = flow.parser.yy.getEdges();
|
||||
const vert = flow.parser.yy.getVertices();
|
||||
const edges = flow.parser.yy.getEdges();
|
||||
|
||||
expect(vert['A'].id).toBe('A');
|
||||
expect(vert['B'].id).toBe('B');
|
||||
expect(edges.length).toBe(1);
|
||||
expect(edges[0].start).toBe('A');
|
||||
expect(edges[0].end).toBe('B');
|
||||
expect(edges[0].type).toBe('double_arrow_cross');
|
||||
expect(edges[0].text).toBe('');
|
||||
expect(edges[0].stroke).toBe('normal');
|
||||
expect(edges[0].length).toBe(1);
|
||||
});
|
||||
expect(vert['A'].id).toBe('A');
|
||||
expect(vert['B'].id).toBe('B');
|
||||
expect(edges.length).toBe(1);
|
||||
expect(edges[0].start).toBe('A');
|
||||
expect(edges[0].end).toBe('B');
|
||||
expect(edges[0].type).toBe(`${edgeType.type}`);
|
||||
expect(edges[0].text).toBe('');
|
||||
expect(edges[0].stroke).toBe(`${edgeType.stroke}`);
|
||||
});
|
||||
|
||||
it('should handle double edged nodes with text', function () {
|
||||
const res = flow.parser.parse('graph TD;\nA x-- text --x B;');
|
||||
it(`should handle ${edgeType.stroke} ${edgeType.type} with text`, function () {
|
||||
const res = flow.parser.parse(
|
||||
`graph TD;\nA ${edgeType.edgeStart} text ${edgeType.edgeEnd} B;`
|
||||
);
|
||||
|
||||
const vert = flow.parser.yy.getVertices();
|
||||
const edges = flow.parser.yy.getEdges();
|
||||
const vert = flow.parser.yy.getVertices();
|
||||
const edges = flow.parser.yy.getEdges();
|
||||
|
||||
expect(vert['A'].id).toBe('A');
|
||||
expect(vert['B'].id).toBe('B');
|
||||
expect(edges.length).toBe(1);
|
||||
expect(edges[0].start).toBe('A');
|
||||
expect(edges[0].end).toBe('B');
|
||||
expect(edges[0].type).toBe('double_arrow_cross');
|
||||
expect(edges[0].text).toBe('text');
|
||||
expect(edges[0].stroke).toBe('normal');
|
||||
expect(edges[0].length).toBe(1);
|
||||
});
|
||||
expect(vert['A'].id).toBe('A');
|
||||
expect(vert['B'].id).toBe('B');
|
||||
expect(edges.length).toBe(1);
|
||||
expect(edges[0].start).toBe('A');
|
||||
expect(edges[0].end).toBe('B');
|
||||
expect(edges[0].type).toBe(`${edgeType.type}`);
|
||||
expect(edges[0].text).toBe('text');
|
||||
expect(edges[0].stroke).toBe(`${edgeType.stroke}`);
|
||||
});
|
||||
|
||||
it('should handle double edged nodes and edges on thick arrows', function () {
|
||||
const res = flow.parser.parse('graph TD;\nA x==x B;');
|
||||
it.each(keywords)(
|
||||
`should handle ${edgeType.stroke} ${edgeType.type} with %s text`,
|
||||
function (keyword) {
|
||||
const res = flow.parser.parse(
|
||||
`graph TD;\nA ${edgeType.edgeStart} ${keyword} ${edgeType.edgeEnd} B;`
|
||||
);
|
||||
|
||||
const vert = flow.parser.yy.getVertices();
|
||||
const edges = flow.parser.yy.getEdges();
|
||||
const vert = flow.parser.yy.getVertices();
|
||||
const edges = flow.parser.yy.getEdges();
|
||||
|
||||
expect(vert['A'].id).toBe('A');
|
||||
expect(vert['B'].id).toBe('B');
|
||||
expect(edges.length).toBe(1);
|
||||
expect(edges[0].start).toBe('A');
|
||||
expect(edges[0].end).toBe('B');
|
||||
expect(edges[0].type).toBe('double_arrow_cross');
|
||||
expect(edges[0].text).toBe('');
|
||||
expect(edges[0].stroke).toBe('thick');
|
||||
expect(edges[0].length).toBe(1);
|
||||
});
|
||||
|
||||
it('should handle double edged nodes with text on thick arrows', function () {
|
||||
const res = flow.parser.parse('graph TD;\nA x== text ==x B;');
|
||||
|
||||
const vert = flow.parser.yy.getVertices();
|
||||
const edges = flow.parser.yy.getEdges();
|
||||
|
||||
expect(vert['A'].id).toBe('A');
|
||||
expect(vert['B'].id).toBe('B');
|
||||
expect(edges.length).toBe(1);
|
||||
expect(edges[0].start).toBe('A');
|
||||
expect(edges[0].end).toBe('B');
|
||||
expect(edges[0].type).toBe('double_arrow_cross');
|
||||
expect(edges[0].text).toBe('text');
|
||||
expect(edges[0].stroke).toBe('thick');
|
||||
expect(edges[0].length).toBe(1);
|
||||
});
|
||||
|
||||
it('should handle double edged nodes and edges on dotted arrows', function () {
|
||||
const res = flow.parser.parse('graph TD;\nA x-.-x B;');
|
||||
|
||||
const vert = flow.parser.yy.getVertices();
|
||||
const edges = flow.parser.yy.getEdges();
|
||||
|
||||
expect(vert['A'].id).toBe('A');
|
||||
expect(vert['B'].id).toBe('B');
|
||||
expect(edges.length).toBe(1);
|
||||
expect(edges[0].start).toBe('A');
|
||||
expect(edges[0].end).toBe('B');
|
||||
expect(edges[0].type).toBe('double_arrow_cross');
|
||||
expect(edges[0].text).toBe('');
|
||||
expect(edges[0].stroke).toBe('dotted');
|
||||
expect(edges[0].length).toBe(1);
|
||||
});
|
||||
|
||||
it('should handle double edged nodes with text on dotted arrows', function () {
|
||||
const res = flow.parser.parse('graph TD;\nA x-. text .-x B;');
|
||||
|
||||
const vert = flow.parser.yy.getVertices();
|
||||
const edges = flow.parser.yy.getEdges();
|
||||
|
||||
expect(vert['A'].id).toBe('A');
|
||||
expect(vert['B'].id).toBe('B');
|
||||
expect(edges.length).toBe(1);
|
||||
expect(edges[0].start).toBe('A');
|
||||
expect(edges[0].end).toBe('B');
|
||||
expect(edges[0].type).toBe('double_arrow_cross');
|
||||
expect(edges[0].text).toBe('text');
|
||||
expect(edges[0].stroke).toBe('dotted');
|
||||
expect(edges[0].length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('circle', function () {
|
||||
it('should handle double edged nodes and edges', function () {
|
||||
const res = flow.parser.parse('graph TD;\nA o--o B;');
|
||||
|
||||
const vert = flow.parser.yy.getVertices();
|
||||
const edges = flow.parser.yy.getEdges();
|
||||
|
||||
expect(vert['A'].id).toBe('A');
|
||||
expect(vert['B'].id).toBe('B');
|
||||
expect(edges.length).toBe(1);
|
||||
expect(edges[0].start).toBe('A');
|
||||
expect(edges[0].end).toBe('B');
|
||||
expect(edges[0].type).toBe('double_arrow_circle');
|
||||
expect(edges[0].text).toBe('');
|
||||
expect(edges[0].stroke).toBe('normal');
|
||||
expect(edges[0].length).toBe(1);
|
||||
});
|
||||
|
||||
it('should handle double edged nodes with text', function () {
|
||||
const res = flow.parser.parse('graph TD;\nA o-- text --o B;');
|
||||
|
||||
const vert = flow.parser.yy.getVertices();
|
||||
const edges = flow.parser.yy.getEdges();
|
||||
|
||||
expect(vert['A'].id).toBe('A');
|
||||
expect(vert['B'].id).toBe('B');
|
||||
expect(edges.length).toBe(1);
|
||||
expect(edges[0].start).toBe('A');
|
||||
expect(edges[0].end).toBe('B');
|
||||
expect(edges[0].type).toBe('double_arrow_circle');
|
||||
expect(edges[0].text).toBe('text');
|
||||
expect(edges[0].stroke).toBe('normal');
|
||||
expect(edges[0].length).toBe(1);
|
||||
});
|
||||
|
||||
it('should handle double edged nodes and edges on thick arrows', function () {
|
||||
const res = flow.parser.parse('graph TD;\nA o==o B;');
|
||||
|
||||
const vert = flow.parser.yy.getVertices();
|
||||
const edges = flow.parser.yy.getEdges();
|
||||
|
||||
expect(vert['A'].id).toBe('A');
|
||||
expect(vert['B'].id).toBe('B');
|
||||
expect(edges.length).toBe(1);
|
||||
expect(edges[0].start).toBe('A');
|
||||
expect(edges[0].end).toBe('B');
|
||||
expect(edges[0].type).toBe('double_arrow_circle');
|
||||
expect(edges[0].text).toBe('');
|
||||
expect(edges[0].stroke).toBe('thick');
|
||||
expect(edges[0].length).toBe(1);
|
||||
});
|
||||
|
||||
it('should handle double edged nodes with text on thick arrows', function () {
|
||||
const res = flow.parser.parse('graph TD;\nA o== text ==o B;');
|
||||
|
||||
const vert = flow.parser.yy.getVertices();
|
||||
const edges = flow.parser.yy.getEdges();
|
||||
|
||||
expect(vert['A'].id).toBe('A');
|
||||
expect(vert['B'].id).toBe('B');
|
||||
expect(edges.length).toBe(1);
|
||||
expect(edges[0].start).toBe('A');
|
||||
expect(edges[0].end).toBe('B');
|
||||
expect(edges[0].type).toBe('double_arrow_circle');
|
||||
expect(edges[0].text).toBe('text');
|
||||
expect(edges[0].stroke).toBe('thick');
|
||||
expect(edges[0].length).toBe(1);
|
||||
});
|
||||
|
||||
it('should handle double edged nodes and edges on dotted arrows', function () {
|
||||
const res = flow.parser.parse('graph TD;\nA o-.-o B;');
|
||||
|
||||
const vert = flow.parser.yy.getVertices();
|
||||
const edges = flow.parser.yy.getEdges();
|
||||
|
||||
expect(vert['A'].id).toBe('A');
|
||||
expect(vert['B'].id).toBe('B');
|
||||
expect(edges.length).toBe(1);
|
||||
expect(edges[0].start).toBe('A');
|
||||
expect(edges[0].end).toBe('B');
|
||||
expect(edges[0].type).toBe('double_arrow_circle');
|
||||
expect(edges[0].text).toBe('');
|
||||
expect(edges[0].stroke).toBe('dotted');
|
||||
expect(edges[0].length).toBe(1);
|
||||
});
|
||||
|
||||
it('should handle double edged nodes with text on dotted arrows', function () {
|
||||
const res = flow.parser.parse('graph TD;\nA o-. text .-o B;');
|
||||
|
||||
const vert = flow.parser.yy.getVertices();
|
||||
const edges = flow.parser.yy.getEdges();
|
||||
|
||||
expect(vert['A'].id).toBe('A');
|
||||
expect(vert['B'].id).toBe('B');
|
||||
expect(edges.length).toBe(1);
|
||||
expect(edges[0].start).toBe('A');
|
||||
expect(edges[0].end).toBe('B');
|
||||
expect(edges[0].type).toBe('double_arrow_circle');
|
||||
expect(edges[0].text).toBe('text');
|
||||
expect(edges[0].stroke).toBe('dotted');
|
||||
expect(edges[0].length).toBe(1);
|
||||
expect(vert['A'].id).toBe('A');
|
||||
expect(vert['B'].id).toBe('B');
|
||||
expect(edges.length).toBe(1);
|
||||
expect(edges[0].start).toBe('A');
|
||||
expect(edges[0].end).toBe('B');
|
||||
expect(edges[0].type).toBe(`${edgeType.type}`);
|
||||
expect(edges[0].text).toBe(`${keyword}`);
|
||||
expect(edges[0].stroke).toBe(`${edgeType.stroke}`);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ A["\`The cat in **the** hat\`"]-- "\`The *bat* in the chat\`" -->B["The dog in t
|
||||
expect(vert['A'].labelType).toBe('markdown');
|
||||
expect(vert['B'].id).toBe('B');
|
||||
expect(vert['B'].text).toBe('The dog in the hog');
|
||||
expect(vert['B'].labelType).toBe('text');
|
||||
expect(vert['B'].labelType).toBe('string');
|
||||
expect(edges.length).toBe(2);
|
||||
expect(edges[0].start).toBe('A');
|
||||
expect(edges[0].end).toBe('B');
|
||||
@@ -35,7 +35,7 @@ A["\`The cat in **the** hat\`"]-- "\`The *bat* in the chat\`" -->B["The dog in t
|
||||
expect(edges[1].end).toBe('C');
|
||||
expect(edges[1].type).toBe('arrow_point');
|
||||
expect(edges[1].text).toBe('The rat in the mat');
|
||||
expect(edges[1].labelType).toBe('text');
|
||||
expect(edges[1].labelType).toBe('string');
|
||||
});
|
||||
it('mardown formatting in subgraphs', function () {
|
||||
const res = flow.parser.parse(`flowchart LR
|
||||
|
||||
@@ -6,6 +6,29 @@ setConfig({
|
||||
securityLevel: 'strict',
|
||||
});
|
||||
|
||||
const keywords = [
|
||||
'graph',
|
||||
'flowchart',
|
||||
'flowchart-elk',
|
||||
'style',
|
||||
'default',
|
||||
'linkStyle',
|
||||
'interpolate',
|
||||
'classDef',
|
||||
'class',
|
||||
'href',
|
||||
'call',
|
||||
'click',
|
||||
'_self',
|
||||
'_blank',
|
||||
'_parent',
|
||||
'_top',
|
||||
'end',
|
||||
'subgraph',
|
||||
];
|
||||
|
||||
const specialChars = ['#', ':', '0', '&', ',', '*', '.', '\\', 'v', '-', '/', '_'];
|
||||
|
||||
describe('[Singlenodes] when parsing', () => {
|
||||
beforeEach(function () {
|
||||
flow.parser.yy = flowDb;
|
||||
@@ -259,4 +282,90 @@ describe('[Singlenodes] when parsing', () => {
|
||||
expect(edges.length).toBe(0);
|
||||
expect(vert['i_d'].styles.length).toBe(0);
|
||||
});
|
||||
|
||||
it.each(keywords)('should handle keywords between dashes "-"', function (keyword) {
|
||||
const res = flow.parser.parse(`graph TD;a-${keyword}-node;`);
|
||||
const vert = flow.parser.yy.getVertices();
|
||||
expect(vert[`a-${keyword}-node`].text).toBe(`a-${keyword}-node`);
|
||||
});
|
||||
|
||||
it.each(keywords)('should handle keywords between periods "."', function (keyword) {
|
||||
const res = flow.parser.parse(`graph TD;a.${keyword}.node;`);
|
||||
const vert = flow.parser.yy.getVertices();
|
||||
expect(vert[`a.${keyword}.node`].text).toBe(`a.${keyword}.node`);
|
||||
});
|
||||
|
||||
it.each(keywords)('should handle keywords between underscores "_"', function (keyword) {
|
||||
const res = flow.parser.parse(`graph TD;a_${keyword}_node;`);
|
||||
const vert = flow.parser.yy.getVertices();
|
||||
expect(vert[`a_${keyword}_node`].text).toBe(`a_${keyword}_node`);
|
||||
});
|
||||
|
||||
it.each(keywords)('should handle nodes ending in %s', function (keyword) {
|
||||
const res = flow.parser.parse(`graph TD;node_${keyword};node.${keyword};node-${keyword};`);
|
||||
const vert = flow.parser.yy.getVertices();
|
||||
expect(vert[`node_${keyword}`].text).toBe(`node_${keyword}`);
|
||||
expect(vert[`node.${keyword}`].text).toBe(`node.${keyword}`);
|
||||
expect(vert[`node-${keyword}`].text).toBe(`node-${keyword}`);
|
||||
});
|
||||
|
||||
const errorKeywords = [
|
||||
'graph',
|
||||
'flowchart',
|
||||
'flowchart-elk',
|
||||
'style',
|
||||
'linkStyle',
|
||||
'interpolate',
|
||||
'classDef',
|
||||
'class',
|
||||
'_self',
|
||||
'_blank',
|
||||
'_parent',
|
||||
'_top',
|
||||
'end',
|
||||
'subgraph',
|
||||
];
|
||||
it.each(errorKeywords)('should throw error at nodes beginning with %s', function (keyword) {
|
||||
const str = `graph TD;${keyword}.node;${keyword}-node;${keyword}/node`;
|
||||
const vert = flow.parser.yy.getVertices();
|
||||
|
||||
expect(() => flow.parser.parse(str)).toThrowError();
|
||||
});
|
||||
|
||||
const workingKeywords = ['default', 'href', 'click', 'call'];
|
||||
|
||||
it.each(workingKeywords)('should parse node beginning with %s', function (keyword) {
|
||||
flow.parser.parse(`graph TD; ${keyword}.node;${keyword}-node;${keyword}/node;`);
|
||||
const vert = flow.parser.yy.getVertices();
|
||||
expect(vert[`${keyword}.node`].text).toBe(`${keyword}.node`);
|
||||
expect(vert[`${keyword}-node`].text).toBe(`${keyword}-node`);
|
||||
expect(vert[`${keyword}/node`].text).toBe(`${keyword}/node`);
|
||||
});
|
||||
|
||||
it.each(specialChars)(
|
||||
'should allow node ids of single special characters',
|
||||
function (specialChar) {
|
||||
flow.parser.parse(`graph TD; ${specialChar} --> A`);
|
||||
const vert = flow.parser.yy.getVertices();
|
||||
expect(vert[`${specialChar}`].text).toBe(`${specialChar}`);
|
||||
}
|
||||
);
|
||||
|
||||
it.each(specialChars)(
|
||||
'should allow node ids with special characters at start of id',
|
||||
function (specialChar) {
|
||||
flow.parser.parse(`graph TD; ${specialChar}node --> A`);
|
||||
const vert = flow.parser.yy.getVertices();
|
||||
expect(vert[`${specialChar}node`].text).toBe(`${specialChar}node`);
|
||||
}
|
||||
);
|
||||
|
||||
it.each(specialChars)(
|
||||
'should allow node ids with special characters at end of id',
|
||||
function (specialChar) {
|
||||
flow.parser.parse(`graph TD; node${specialChar} --> A`);
|
||||
const vert = flow.parser.yy.getVertices();
|
||||
expect(vert[`node${specialChar}`].text).toBe(`node${specialChar}`);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -26,15 +26,6 @@ describe('[Style] when parsing', () => {
|
||||
expect(vert['Q'].styles[0]).toBe('background:#fff');
|
||||
});
|
||||
|
||||
// log.debug(flow.parser.parse('graph TD;style Q background:#fff;'));
|
||||
it('should handle styles for edges', function () {
|
||||
const res = flow.parser.parse('graph TD;a-->b;\nstyle #0 stroke: #f66;');
|
||||
|
||||
const edges = flow.parser.yy.getEdges();
|
||||
|
||||
expect(edges.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should handle multiple styles for a vortex', function () {
|
||||
const res = flow.parser.parse('graph TD;style R background:#fff,border:1px solid red;');
|
||||
|
||||
|
||||
@@ -305,6 +305,95 @@ describe('[Text] when parsing', () => {
|
||||
expect(vert['C'].type).toBe('round');
|
||||
expect(vert['C'].text).toBe('Chimpansen hoppar');
|
||||
});
|
||||
|
||||
const keywords = [
|
||||
'graph',
|
||||
'flowchart',
|
||||
'flowchart-elk',
|
||||
'style',
|
||||
'default',
|
||||
'linkStyle',
|
||||
'interpolate',
|
||||
'classDef',
|
||||
'class',
|
||||
'href',
|
||||
'call',
|
||||
'click',
|
||||
'_self',
|
||||
'_blank',
|
||||
'_parent',
|
||||
'_top',
|
||||
'end',
|
||||
'subgraph',
|
||||
'kitty',
|
||||
];
|
||||
|
||||
const shapes = [
|
||||
{ start: '[', end: ']', name: 'square' },
|
||||
{ start: '(', end: ')', name: 'round' },
|
||||
{ start: '{', end: '}', name: 'diamond' },
|
||||
{ start: '(-', end: '-)', name: 'ellipse' },
|
||||
{ start: '([', end: '])', name: 'stadium' },
|
||||
{ start: '>', end: ']', name: 'odd' },
|
||||
{ start: '[(', end: ')]', name: 'cylinder' },
|
||||
{ start: '(((', end: ')))', name: 'doublecircle' },
|
||||
{ start: '[/', end: '\\]', name: 'trapezoid' },
|
||||
{ start: '[\\', end: '/]', name: 'inv_trapezoid' },
|
||||
{ start: '[/', end: '/]', name: 'lean_right' },
|
||||
{ start: '[\\', end: '\\]', name: 'lean_left' },
|
||||
{ start: '[[', end: ']]', name: 'subroutine' },
|
||||
{ start: '{{', end: '}}', name: 'hexagon' },
|
||||
];
|
||||
|
||||
shapes.forEach((shape) => {
|
||||
it.each(keywords)(`should handle %s keyword in ${shape.name} vertex`, function (keyword) {
|
||||
const rest = flow.parser.parse(
|
||||
`graph TD;A_${keyword}_node-->B${shape.start}This node has a ${keyword} as text${shape.end};`
|
||||
);
|
||||
|
||||
const vert = flow.parser.yy.getVertices();
|
||||
const edges = flow.parser.yy.getEdges();
|
||||
expect(vert['B'].type).toBe(`${shape.name}`);
|
||||
expect(vert['B'].text).toBe(`This node has a ${keyword} as text`);
|
||||
});
|
||||
});
|
||||
|
||||
it.each(keywords)('should handle %s keyword in rect vertex', function (keyword) {
|
||||
const rest = flow.parser.parse(
|
||||
`graph TD;A_${keyword}_node-->B[|borders:lt|This node has a ${keyword} as text];`
|
||||
);
|
||||
|
||||
const vert = flow.parser.yy.getVertices();
|
||||
const edges = flow.parser.yy.getEdges();
|
||||
expect(vert['B'].type).toBe('rect');
|
||||
expect(vert['B'].text).toBe(`This node has a ${keyword} as text`);
|
||||
});
|
||||
|
||||
it('should handle edge case for odd vertex with node id ending with minus', function () {
|
||||
const res = flow.parser.parse('graph TD;A_node-->odd->Vertex Text];');
|
||||
const vert = flow.parser.yy.getVertices();
|
||||
|
||||
expect(vert['odd-'].type).toBe('odd');
|
||||
expect(vert['odd-'].text).toBe('Vertex Text');
|
||||
});
|
||||
it('should allow forward slashes in lean_right vertices', function () {
|
||||
const rest = flow.parser.parse(`graph TD;A_node-->B[/This node has a / as text/];`);
|
||||
|
||||
const vert = flow.parser.yy.getVertices();
|
||||
const edges = flow.parser.yy.getEdges();
|
||||
expect(vert['B'].type).toBe('lean_right');
|
||||
expect(vert['B'].text).toBe(`This node has a / as text`);
|
||||
});
|
||||
|
||||
it('should allow back slashes in lean_left vertices', function () {
|
||||
const rest = flow.parser.parse(`graph TD;A_node-->B[\\This node has a \\ as text\\];`);
|
||||
|
||||
const vert = flow.parser.yy.getVertices();
|
||||
const edges = flow.parser.yy.getEdges();
|
||||
expect(vert['B'].type).toBe('lean_left');
|
||||
expect(vert['B'].text).toBe(`This node has a \\ as text`);
|
||||
});
|
||||
|
||||
it('should handle åäö and minus', function () {
|
||||
const res = flow.parser.parse('graph TD;A-->C{Chimpansen hoppar åäö-ÅÄÖ};');
|
||||
|
||||
@@ -484,4 +573,33 @@ describe('[Text] when parsing', () => {
|
||||
expect(vert['A'].text).toBe(',.?!+-*');
|
||||
expect(edges[0].text).toBe(',.?!+-*');
|
||||
});
|
||||
|
||||
it('should throw error at nested set of brackets', function () {
|
||||
const str = 'graph TD; A[This is a () in text];';
|
||||
expect(() => flow.parser.parse(str)).toThrowError("got 'PS'");
|
||||
});
|
||||
|
||||
it('should throw error for strings and text at the same time', function () {
|
||||
const str = 'graph TD;A(this node has "string" and text)-->|this link has "string" and text|C;';
|
||||
|
||||
expect(() => flow.parser.parse(str)).toThrowError("got 'STR'");
|
||||
});
|
||||
|
||||
it('should throw error for escaping quotes in text state', function () {
|
||||
//prettier-ignore
|
||||
const str = 'graph TD; A[This is a \"()\" in text];'; //eslint-disable-line no-useless-escape
|
||||
|
||||
expect(() => flow.parser.parse(str)).toThrowError("got 'STR'");
|
||||
});
|
||||
|
||||
it('should throw error for nested quoatation marks', function () {
|
||||
const str = 'graph TD; A["This is a "()" in text"];';
|
||||
|
||||
expect(() => flow.parser.parse(str)).toThrowError("Expecting 'SQE'");
|
||||
});
|
||||
|
||||
it('should throw error', function () {
|
||||
const str = `graph TD; node[hello ) world] --> works`;
|
||||
expect(() => flow.parser.parse(str)).toThrowError("got 'PE'");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,6 +13,12 @@
|
||||
%x acc_descr_multiline
|
||||
%x dir
|
||||
%x vertex
|
||||
%x text
|
||||
%x ellipseText
|
||||
%x trapText
|
||||
%x edgeText
|
||||
%x thickEdgeText
|
||||
%x dottedEdgeText
|
||||
%x click
|
||||
%x href
|
||||
%x callbackname
|
||||
@@ -23,41 +29,19 @@
|
||||
%x close_directive
|
||||
|
||||
%%
|
||||
\%\%\{ { this.begin('open_directive'); return 'open_directive'; }
|
||||
<open_directive>((?:(?!\}\%\%)[^:.])*) { this.begin('type_directive'); return 'type_directive'; }
|
||||
<type_directive>":" { this.popState(); this.begin('arg_directive'); return ':'; }
|
||||
<type_directive,arg_directive>\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
|
||||
<arg_directive>((?:(?!\}\%\%).|\n)*) return 'arg_directive';
|
||||
accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; }
|
||||
<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"; }
|
||||
accDescr\s*"{"\s* { this.begin("acc_descr_multiline");}
|
||||
\%\%\{ { this.begin('open_directive'); return 'open_directive'; }
|
||||
<open_directive>((?:(?!\}\%\%)[^:.])*) { this.begin('type_directive'); return 'type_directive'; }
|
||||
<type_directive>":" { this.popState(); this.begin('arg_directive'); return ':'; }
|
||||
<type_directive,arg_directive>\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
|
||||
<arg_directive>((?:(?!\}\%\%).|\n)*) return 'arg_directive';
|
||||
accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; }
|
||||
<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"; }
|
||||
accDescr\s*"{"\s* { this.begin("acc_descr_multiline");}
|
||||
<acc_descr_multiline>[\}] { this.popState(); }
|
||||
<acc_descr_multiline>[^\}]* return "acc_descr_multiline_value";
|
||||
// <acc_descr_multiline>.*[^\n]* { return "acc_descr_line"}
|
||||
["][`] { this.begin("md_string");}
|
||||
<md_string>[^`"]+ { return "MD_STR";}
|
||||
<md_string>[`]["] { this.popState();}
|
||||
["] this.begin("string");
|
||||
<string>["] this.popState();
|
||||
<string>[^"]* return "STR";
|
||||
"style" return 'STYLE';
|
||||
"default" return 'DEFAULT';
|
||||
"linkStyle" return 'LINKSTYLE';
|
||||
"interpolate" return 'INTERPOLATE';
|
||||
"classDef" return 'CLASSDEF';
|
||||
"class" return 'CLASS';
|
||||
|
||||
/*
|
||||
---interactivity command---
|
||||
'href' adds a link to the specified node. 'href' can only be specified when the
|
||||
line was introduced with 'click'.
|
||||
'href "<link>"' attaches the specified link to the node that was specified by 'click'.
|
||||
*/
|
||||
"href"[\s]+["] this.begin("href");
|
||||
<href>["] this.popState();
|
||||
<href>[^"]* return 'HREF';
|
||||
// <acc_descr_multiline>.*[^\n]* { return "acc_descr_line"}
|
||||
|
||||
/*
|
||||
---interactivity command---
|
||||
@@ -74,88 +58,128 @@ Function arguments are optional: 'call <callbackname>()' simply executes 'callba
|
||||
<callbackargs>\) this.popState();
|
||||
<callbackargs>[^)]* return 'CALLBACKARGS';
|
||||
|
||||
<md_string>[^`"]+ { return "MD_STR";}
|
||||
<md_string>[`]["] { this.popState();}
|
||||
<*>["][`] { this.begin("md_string");}
|
||||
<string>[^"]+ return "STR";
|
||||
<string>["] this.popState();
|
||||
<*>["] this.pushState("string");
|
||||
"style" return 'STYLE';
|
||||
"default" return 'DEFAULT';
|
||||
"linkStyle" return 'LINKSTYLE';
|
||||
"interpolate" return 'INTERPOLATE';
|
||||
"classDef" return 'CLASSDEF';
|
||||
"class" return 'CLASS';
|
||||
|
||||
/*
|
||||
---interactivity command---
|
||||
'href' adds a link to the specified node. 'href' can only be specified when the
|
||||
line was introduced with 'click'.
|
||||
'href "<link>"' attaches the specified link to the node that was specified by 'click'.
|
||||
*/
|
||||
"href"[\s] return 'HREF';
|
||||
|
||||
|
||||
/*
|
||||
'click' is the keyword to introduce a line that contains interactivity commands.
|
||||
'click' must be followed by an existing node-id. All commands are attached to
|
||||
that id.
|
||||
'click <id>' can be followed by href or call commands in any desired order
|
||||
*/
|
||||
"click"[\s]+ this.begin("click");
|
||||
<click>[\s\n] this.popState();
|
||||
<click>[^\s\n]* return 'CLICK';
|
||||
"click"[\s]+ this.begin("click");
|
||||
<click>[\s\n] this.popState();
|
||||
<click>[^\s\n]* return 'CLICK';
|
||||
|
||||
"flowchart-elk" {if(yy.lex.firstGraph()){this.begin("dir");} return 'GRAPH';}
|
||||
"graph" {if(yy.lex.firstGraph()){this.begin("dir");} return 'GRAPH';}
|
||||
"flowchart" {if(yy.lex.firstGraph()){this.begin("dir");} return 'GRAPH';}
|
||||
"subgraph" return 'subgraph';
|
||||
"end"\b\s* return 'end';
|
||||
"flowchart-elk" {if(yy.lex.firstGraph()){this.begin("dir");} return 'GRAPH';}
|
||||
"graph" {if(yy.lex.firstGraph()){this.begin("dir");} return 'GRAPH';}
|
||||
"flowchart" {if(yy.lex.firstGraph()){this.begin("dir");} return 'GRAPH';}
|
||||
"subgraph" return 'subgraph';
|
||||
"end"\b\s* return 'end';
|
||||
|
||||
"_self" return 'LINK_TARGET';
|
||||
"_blank" return 'LINK_TARGET';
|
||||
"_parent" return 'LINK_TARGET';
|
||||
"_top" return 'LINK_TARGET';
|
||||
"_self" return 'LINK_TARGET';
|
||||
"_blank" return 'LINK_TARGET';
|
||||
"_parent" return 'LINK_TARGET';
|
||||
"_top" return 'LINK_TARGET';
|
||||
|
||||
<dir>(\r?\n)*\s*\n { this.popState(); return 'NODIR'; }
|
||||
<dir>\s*"LR" { this.popState(); return 'DIR'; }
|
||||
<dir>\s*"RL" { this.popState(); return 'DIR'; }
|
||||
<dir>\s*"TB" { this.popState(); return 'DIR'; }
|
||||
<dir>\s*"BT" { this.popState(); return 'DIR'; }
|
||||
<dir>\s*"TD" { this.popState(); return 'DIR'; }
|
||||
<dir>\s*"BR" { this.popState(); return 'DIR'; }
|
||||
<dir>\s*"<" { this.popState(); return 'DIR'; }
|
||||
<dir>\s*">" { this.popState(); return 'DIR'; }
|
||||
<dir>\s*"^" { this.popState(); return 'DIR'; }
|
||||
<dir>\s*"v" { this.popState(); return 'DIR'; }
|
||||
<dir>(\r?\n)*\s*\n { this.popState(); return 'NODIR'; }
|
||||
<dir>\s*"LR" { this.popState(); return 'DIR'; }
|
||||
<dir>\s*"RL" { this.popState(); return 'DIR'; }
|
||||
<dir>\s*"TB" { this.popState(); return 'DIR'; }
|
||||
<dir>\s*"BT" { this.popState(); return 'DIR'; }
|
||||
<dir>\s*"TD" { this.popState(); return 'DIR'; }
|
||||
<dir>\s*"BR" { this.popState(); return 'DIR'; }
|
||||
<dir>\s*"<" { this.popState(); return 'DIR'; }
|
||||
<dir>\s*">" { this.popState(); return 'DIR'; }
|
||||
<dir>\s*"^" { this.popState(); return 'DIR'; }
|
||||
<dir>\s*"v" { this.popState(); return 'DIR'; }
|
||||
|
||||
.*direction\s+TB[^\n]* return 'direction_tb';
|
||||
.*direction\s+BT[^\n]* return 'direction_bt';
|
||||
.*direction\s+RL[^\n]* return 'direction_rl';
|
||||
.*direction\s+LR[^\n]* return 'direction_lr';
|
||||
|
||||
[0-9]+ return 'NUM';
|
||||
\# return 'BRKT';
|
||||
":::" return 'STYLE_SEPARATOR';
|
||||
":" return 'COLON';
|
||||
"&" return 'AMP';
|
||||
";" return 'SEMI';
|
||||
"," return 'COMMA';
|
||||
"*" return 'MULT';
|
||||
|
||||
<INITIAL,edgeText>\s*[xo<]?\-\-+[-xo>]\s* { this.popState(); return 'LINK'; }
|
||||
<INITIAL>\s*[xo<]?\-\-\s* { this.pushState("edgeText"); return 'START_LINK'; }
|
||||
<edgeText>[^-]|\-(?!\-)+ return 'EDGE_TEXT';
|
||||
|
||||
<INITIAL,thickEdgeText>\s*[xo<]?\=\=+[=xo>]\s* { this.popState(); return 'LINK'; }
|
||||
<INITIAL>\s*[xo<]?\=\=\s* { this.pushState("thickEdgeText"); return 'START_LINK'; }
|
||||
<thickEdgeText>[^=]|\=(?!=) return 'EDGE_TEXT';
|
||||
|
||||
<INITIAL,dottedEdgeText>\s*[xo<]?\-?\.+\-[xo>]?\s* { this.popState(); return 'LINK'; }
|
||||
<INITIAL>\s*[xo<]?\-\.\s* { this.pushState("dottedEdgeText"); return 'START_LINK'; }
|
||||
<dottedEdgeText>[^\.]|\.(?!-) return 'EDGE_TEXT';
|
||||
|
||||
|
||||
<*>\s*\~\~[\~]+\s* return 'LINK';
|
||||
|
||||
<ellipseText>[-/\)][\)] { this.popState(); return '-)'; }
|
||||
<ellipseText>[^\(\)\[\]\{\}]|-/!\)+ return "TEXT"
|
||||
<*>"(-" { this.pushState("ellipseText"); return '(-'; }
|
||||
|
||||
<text>"])" { this.popState(); return 'STADIUMEND'; }
|
||||
<*>"([" { this.pushState("text"); return 'STADIUMSTART'; }
|
||||
|
||||
<text>"]]" { this.popState(); return 'SUBROUTINEEND'; }
|
||||
<*>"[[" { this.pushState("text"); return 'SUBROUTINESTART'; }
|
||||
|
||||
"[|" { return 'VERTEX_WITH_PROPS_START'; }
|
||||
|
||||
\> { this.pushState("text"); return 'TAGEND'; }
|
||||
|
||||
<text>")]" { this.popState(); return 'CYLINDEREND'; }
|
||||
<*>"[(" { this.pushState("text") ;return 'CYLINDERSTART'; }
|
||||
|
||||
<text>")))" { this.popState(); return 'DOUBLECIRCLEEND'; }
|
||||
<*>"(((" { this.pushState("text"); return 'DOUBLECIRCLESTART'; }
|
||||
|
||||
<trapText>[\\(?=\])][\]] { this.popState(); return 'TRAPEND'; }
|
||||
<trapText>\/(?=\])\] { this.popState(); return 'INVTRAPEND'; }
|
||||
<trapText>\/(?!\])|\\(?!\])|[^\\\[\]\(\)\{\}\/]+ return 'TEXT';
|
||||
<*>"[/" { this.pushState("trapText"); return 'TRAPSTART'; }
|
||||
|
||||
<*>"[\\" { this.pushState("trapText"); return 'INVTRAPSTART'; }
|
||||
|
||||
.*direction\s+TB[^\n]* return 'direction_tb';
|
||||
.*direction\s+BT[^\n]* return 'direction_bt';
|
||||
.*direction\s+RL[^\n]* return 'direction_rl';
|
||||
.*direction\s+LR[^\n]* return 'direction_lr';
|
||||
|
||||
[0-9]+ { return 'NUM';}
|
||||
\# return 'BRKT';
|
||||
":::" return 'STYLE_SEPARATOR';
|
||||
":" return 'COLON';
|
||||
"&" return 'AMP';
|
||||
";" return 'SEMI';
|
||||
"," return 'COMMA';
|
||||
"*" return 'MULT';
|
||||
\s*[xo<]?\-\-+[-xo>]\s* return 'LINK';
|
||||
\s*[xo<]?\=\=+[=xo>]\s* return 'LINK';
|
||||
\s*[xo<]?\-?\.+\-[xo>]?\s* return 'LINK';
|
||||
\s*\~\~[\~]+\s* return 'LINK';
|
||||
\s*[xo<]?\-\-\s* return 'START_LINK';
|
||||
\s*[xo<]?\=\=\s* return 'START_LINK';
|
||||
\s*[xo<]?\-\.\s* return 'START_LINK';
|
||||
"(-" return '(-';
|
||||
"-)" return '-)';
|
||||
"([" return 'STADIUMSTART';
|
||||
"])" return 'STADIUMEND';
|
||||
"[[" return 'SUBROUTINESTART';
|
||||
"]]" return 'SUBROUTINEEND';
|
||||
"[|" return 'VERTEX_WITH_PROPS_START';
|
||||
"[(" return 'CYLINDERSTART';
|
||||
")]" return 'CYLINDEREND';
|
||||
"(((" return 'DOUBLECIRCLESTART';
|
||||
")))" return 'DOUBLECIRCLEEND';
|
||||
\- return 'MINUS';
|
||||
"." return 'DOT';
|
||||
[\_] return 'UNDERSCORE';
|
||||
\+ return 'PLUS';
|
||||
\% return 'PCT';
|
||||
"=" return 'EQUALS';
|
||||
\= return 'EQUALS';
|
||||
"<" return 'TAGSTART';
|
||||
">" return 'TAGEND';
|
||||
"^" return 'UP';
|
||||
"\|" return 'SEP';
|
||||
"v" return 'DOWN';
|
||||
[A-Za-z]+ return 'ALPHA';
|
||||
"\\]" return 'TRAPEND';
|
||||
"[/" return 'TRAPSTART';
|
||||
"/]" return 'INVTRAPEND';
|
||||
"[\\" return 'INVTRAPSTART';
|
||||
[!"#$%&'*+,-.`?\\_/] return 'PUNCTUATION';
|
||||
"*" return 'MULT';
|
||||
"#" return 'BRKT';
|
||||
"&" return 'AMP';
|
||||
([A-Za-z0-9!"\#$%&'*+\.`?\\_\/]|\-(?=[^\>\-\.])|=(?!=))+ return 'NODE_STRING';
|
||||
"-" return 'MINUS'
|
||||
[\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]|
|
||||
[\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]|
|
||||
[\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5]|
|
||||
@@ -218,13 +242,20 @@ that id.
|
||||
[\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF]|
|
||||
[\uFFD2-\uFFD7\uFFDA-\uFFDC]
|
||||
return 'UNICODE_TEXT';
|
||||
"|" return 'PIPE';
|
||||
"(" return 'PS';
|
||||
")" return 'PE';
|
||||
"[" return 'SQS';
|
||||
"]" return 'SQE';
|
||||
"{" return 'DIAMOND_START'
|
||||
"}" return 'DIAMOND_STOP'
|
||||
|
||||
<text>"|" { this.popState(); return 'PIPE'; }
|
||||
<*>"|" { this.pushState("text"); return 'PIPE'; }
|
||||
|
||||
<text>")" { this.popState(); return 'PE'; }
|
||||
<*>"(" { this.pushState("text"); return 'PS'; }
|
||||
|
||||
<text>"]" { this.popState(); return 'SQE'; }
|
||||
<*>"[" { this.pushState("text"); return 'SQS'; }
|
||||
|
||||
<text>(\}) { this.popState(); return 'DIAMOND_STOP' }
|
||||
<*>"{" { this.pushState("text"); return 'DIAMOND_START' }
|
||||
<text>[^\[\]\(\)\{\}\|\"]+ return "TEXT";
|
||||
|
||||
"\"" return 'QUOTE';
|
||||
(\r?\n)+ return 'NEWLINE';
|
||||
\s return 'SPACE';
|
||||
@@ -255,11 +286,11 @@ openDirective
|
||||
;
|
||||
|
||||
typeDirective
|
||||
: type_directive { yy.parseDirective($1, 'type_directive'); }
|
||||
: type_directive { yy.parseDirective($type_directive, 'type_directive'); }
|
||||
;
|
||||
|
||||
argDirective
|
||||
: arg_directive { $1 = $1.trim().replace(/'/g, '"'); yy.parseDirective($1, 'arg_directive'); }
|
||||
: arg_directive { $arg_directive = $arg_directive.trim().replace(/'/g, '"'); yy.parseDirective($arg_directive, 'arg_directive'); }
|
||||
;
|
||||
|
||||
closeDirective
|
||||
@@ -275,15 +306,15 @@ document
|
||||
{ $$ = [];}
|
||||
| document line
|
||||
{
|
||||
if(!Array.isArray($2) || $2.length > 0){
|
||||
$1.push($2);
|
||||
if(!Array.isArray($line) || $line.length > 0){
|
||||
$document.push($line);
|
||||
}
|
||||
$$=$1;}
|
||||
$$=$document;}
|
||||
;
|
||||
|
||||
line
|
||||
: statement
|
||||
{$$=$1;}
|
||||
{$$=$statement;}
|
||||
| SEMI
|
||||
| NEWLINE
|
||||
| SPACE
|
||||
@@ -296,15 +327,15 @@ graphConfig
|
||||
| GRAPH NODIR
|
||||
{ yy.setDirection('TB');$$ = 'TB';}
|
||||
| GRAPH DIR FirstStmtSeperator
|
||||
{ yy.setDirection($2);$$ = $2;}
|
||||
{ yy.setDirection($DIR);$$ = $DIR;}
|
||||
// | GRAPH SPACE TAGEND FirstStmtSeperator
|
||||
// { yy.setDirection("LR");$$ = $3;}
|
||||
// { yy.setDirection("LR");$$ = $TAGEND;}
|
||||
// | GRAPH SPACE TAGSTART FirstStmtSeperator
|
||||
// { yy.setDirection("RL");$$ = $3;}
|
||||
// { yy.setDirection("RL");$$ = $TAGSTART;}
|
||||
// | GRAPH SPACE UP FirstStmtSeperator
|
||||
// { yy.setDirection("BT");$$ = $3;}
|
||||
// { yy.setDirection("BT");$$ = $UP;}
|
||||
// | GRAPH SPACE DOWN FirstStmtSeperator
|
||||
// { yy.setDirection("TB");$$ = $3;}
|
||||
// { yy.setDirection("TB");$$ = $DOWN;}
|
||||
;
|
||||
|
||||
ending: endToken ending
|
||||
@@ -332,7 +363,7 @@ spaceList
|
||||
|
||||
statement
|
||||
: verticeStatement separator
|
||||
{ /* console.warn('finat vs', $1.nodes); */ $$=$1.nodes}
|
||||
{ /* console.warn('finat vs', $verticeStatement.nodes); */ $$=$verticeStatement.nodes}
|
||||
| styleStatement separator
|
||||
{$$=[];}
|
||||
| linkStyleStatement separator
|
||||
@@ -343,110 +374,121 @@ statement
|
||||
{$$=[];}
|
||||
| clickStatement separator
|
||||
{$$=[];}
|
||||
| subgraph SPACE text SQS text SQE separator document end
|
||||
{$$=yy.addSubGraph($3,$8,$5);}
|
||||
| subgraph SPACE text separator document end
|
||||
{$$=yy.addSubGraph($3,$5,$3);}
|
||||
// | subgraph SPACE text separator document end
|
||||
// {$$=yy.addSubGraph($3,$5,$3);}
|
||||
| subgraph SPACE textNoTags SQS text SQE separator document end
|
||||
{$$=yy.addSubGraph($textNoTags,$document,$text);}
|
||||
| subgraph SPACE textNoTags separator document end
|
||||
{$$=yy.addSubGraph($textNoTags,$document,$textNoTags);}
|
||||
// | subgraph SPACE textNoTags separator document end
|
||||
// {$$=yy.addSubGraph($textNoTags,$document,$textNoTags);}
|
||||
| subgraph separator document end
|
||||
{$$=yy.addSubGraph(undefined,$3,undefined);}
|
||||
{$$=yy.addSubGraph(undefined,$document,undefined);}
|
||||
| direction
|
||||
| acc_title acc_title_value { $$=$2.trim();yy.setAccTitle($$); }
|
||||
| acc_descr acc_descr_value { $$=$2.trim();yy.setAccDescription($$); }
|
||||
| acc_descr_multiline_value { $$=$1.trim();yy.setAccDescription($$); }
|
||||
| acc_title acc_title_value { $$=$acc_title_value.trim();yy.setAccTitle($$); }
|
||||
| acc_descr acc_descr_value { $$=$acc_descr_value.trim();yy.setAccDescription($$); }
|
||||
| acc_descr_multiline_value { $$=$acc_descr_multiline_value.trim();yy.setAccDescription($$); }
|
||||
;
|
||||
|
||||
separator: NEWLINE | SEMI | EOF ;
|
||||
|
||||
|
||||
|
||||
verticeStatement: verticeStatement link node
|
||||
{ /* console.warn('vs',$1.stmt,$3); */ yy.addLink($1.stmt,$3,$2); $$ = { stmt: $3, nodes: $3.concat($1.nodes) } }
|
||||
{ /* console.warn('vs',$verticeStatement.stmt,$node); */ yy.addLink($verticeStatement.stmt,$node,$link); $$ = { stmt: $node, nodes: $node.concat($verticeStatement.nodes) } }
|
||||
| verticeStatement link node spaceList
|
||||
{ /* console.warn('vs',$1.stmt,$3); */ yy.addLink($1.stmt,$3,$2); $$ = { stmt: $3, nodes: $3.concat($1.nodes) } }
|
||||
|node spaceList {/*console.warn('noda', $1);*/ $$ = {stmt: $1, nodes:$1 }}
|
||||
|node { /*console.warn('noda', $1);*/ $$ = {stmt: $1, nodes:$1 }}
|
||||
{ /* console.warn('vs',$verticeStatement.stmt,$node); */ yy.addLink($verticeStatement.stmt,$node,$link); $$ = { stmt: $node, nodes: $node.concat($verticeStatement.nodes) } }
|
||||
|node spaceList {/*console.warn('noda', $node);*/ $$ = {stmt: $node, nodes:$node }}
|
||||
|node { /*console.warn('noda', $node);*/ $$ = {stmt: $node, nodes:$node }}
|
||||
;
|
||||
|
||||
node: styledVertex
|
||||
{ /* console.warn('nod', $1); */ $$ = [$1];}
|
||||
{ /* console.warn('nod', $styledVertex); */ $$ = [$styledVertex];}
|
||||
| node spaceList AMP spaceList styledVertex
|
||||
{ $$ = $1.concat($5); /* console.warn('pip', $1[0], $5, $$); */ }
|
||||
{ $$ = $node.concat($styledVertex); /* console.warn('pip', $node[0], $styledVertex, $$); */ }
|
||||
;
|
||||
|
||||
styledVertex: vertex
|
||||
{ /* console.warn('nod', $1); */ $$ = $1;}
|
||||
{ /* console.warn('nod', $vertex); */ $$ = $vertex;}
|
||||
| vertex STYLE_SEPARATOR idString
|
||||
{$$ = $1;yy.setClass($1,$3)}
|
||||
{$$ = $vertex;yy.setClass($vertex,$idString)}
|
||||
;
|
||||
|
||||
vertex: idString SQS text SQE
|
||||
{$$ = $1;yy.addVertex($1,$3,'square');}
|
||||
{$$ = $idString;yy.addVertex($idString,$text,'square');}
|
||||
| idString DOUBLECIRCLESTART text DOUBLECIRCLEEND
|
||||
{$$ = $1;yy.addVertex($1,$3,'doublecircle');}
|
||||
{$$ = $idString;yy.addVertex($idString,$text,'doublecircle');}
|
||||
| idString PS PS text PE PE
|
||||
{$$ = $1;yy.addVertex($1,$4,'circle');}
|
||||
{$$ = $idString;yy.addVertex($idString,$text,'circle');}
|
||||
| idString '(-' text '-)'
|
||||
{$$ = $1;yy.addVertex($1,$3,'ellipse');}
|
||||
{$$ = $idString;yy.addVertex($idString,$text,'ellipse');}
|
||||
| idString STADIUMSTART text STADIUMEND
|
||||
{$$ = $1;yy.addVertex($1,$3,'stadium');}
|
||||
{$$ = $idString;yy.addVertex($idString,$text,'stadium');}
|
||||
| idString SUBROUTINESTART text SUBROUTINEEND
|
||||
{$$ = $1;yy.addVertex($1,$3,'subroutine');}
|
||||
| idString VERTEX_WITH_PROPS_START ALPHA COLON ALPHA PIPE text SQE
|
||||
{$$ = $1;yy.addVertex($1,$7,'rect',undefined,undefined,undefined, Object.fromEntries([[$3, $5]]));}
|
||||
{$$ = $idString;yy.addVertex($idString,$text,'subroutine');}
|
||||
| idString VERTEX_WITH_PROPS_START NODE_STRING\[field] COLON NODE_STRING\[value] PIPE text SQE
|
||||
{$$ = $idString;yy.addVertex($idString,$text,'rect',undefined,undefined,undefined, Object.fromEntries([[$field, $value]]));}
|
||||
| idString CYLINDERSTART text CYLINDEREND
|
||||
{$$ = $1;yy.addVertex($1,$3,'cylinder');}
|
||||
{$$ = $idString;yy.addVertex($idString,$text,'cylinder');}
|
||||
| idString PS text PE
|
||||
{$$ = $1;yy.addVertex($1,$3,'round');}
|
||||
{$$ = $idString;yy.addVertex($idString,$text,'round');}
|
||||
| idString DIAMOND_START text DIAMOND_STOP
|
||||
{$$ = $1;yy.addVertex($1,$3,'diamond');}
|
||||
{$$ = $idString;yy.addVertex($idString,$text,'diamond');}
|
||||
| idString DIAMOND_START DIAMOND_START text DIAMOND_STOP DIAMOND_STOP
|
||||
{$$ = $1;yy.addVertex($1,$4,'hexagon');}
|
||||
{$$ = $idString;yy.addVertex($idString,$text,'hexagon');}
|
||||
| idString TAGEND text SQE
|
||||
{$$ = $1;yy.addVertex($1,$3,'odd');}
|
||||
{$$ = $idString;yy.addVertex($idString,$text,'odd');}
|
||||
| idString TRAPSTART text TRAPEND
|
||||
{$$ = $1;yy.addVertex($1,$3,'trapezoid');}
|
||||
{$$ = $idString;yy.addVertex($idString,$text,'trapezoid');}
|
||||
| idString INVTRAPSTART text INVTRAPEND
|
||||
{$$ = $1;yy.addVertex($1,$3,'inv_trapezoid');}
|
||||
{$$ = $idString;yy.addVertex($idString,$text,'inv_trapezoid');}
|
||||
| idString TRAPSTART text INVTRAPEND
|
||||
{$$ = $1;yy.addVertex($1,$3,'lean_right');}
|
||||
{$$ = $idString;yy.addVertex($idString,$text,'lean_right');}
|
||||
| idString INVTRAPSTART text TRAPEND
|
||||
{$$ = $1;yy.addVertex($1,$3,'lean_left');}
|
||||
{$$ = $idString;yy.addVertex($idString,$text,'lean_left');}
|
||||
| idString
|
||||
{ /*console.warn('h: ', $1);*/$$ = $1;yy.addVertex($1);}
|
||||
{ /*console.warn('h: ', $idString);*/$$ = $idString;yy.addVertex($idString);}
|
||||
;
|
||||
|
||||
|
||||
|
||||
link: linkStatement arrowText
|
||||
{$1.text = $2;$$ = $1;}
|
||||
{$linkStatement.text = $arrowText;$$ = $linkStatement;}
|
||||
| linkStatement TESTSTR SPACE
|
||||
{$1.text = $2;$$ = $1;}
|
||||
{$linkStatement.text = $TESTSTR;$$ = $linkStatement;}
|
||||
| linkStatement arrowText SPACE
|
||||
{$1.text = $2;$$ = $1;}
|
||||
{$linkStatement.text = $arrowText;$$ = $linkStatement;}
|
||||
| linkStatement
|
||||
{$$ = $1;}
|
||||
| START_LINK text LINK
|
||||
{var inf = yy.destructLink($3, $1); $$ = {"type":inf.type,"stroke":inf.stroke,"length":inf.length,"text":$2};}
|
||||
{$$ = $linkStatement;}
|
||||
| START_LINK edgeText LINK
|
||||
{var inf = yy.destructLink($LINK, $START_LINK); $$ = {"type":inf.type,"stroke":inf.stroke,"length":inf.length,"text":$edgeText};}
|
||||
;
|
||||
|
||||
edgeText: edgeTextToken
|
||||
{$$={text:$edgeTextToken, type:'text'};}
|
||||
| edgeText edgeTextToken
|
||||
{$$={text:$edgeText.text+''+$edgeTextToken, type:$edgeText.type};}
|
||||
|STR
|
||||
{$$={text: $STR, type: 'string'};}
|
||||
| MD_STR
|
||||
{$$={text:$MD_STR, type:'markdown'};}
|
||||
;
|
||||
|
||||
|
||||
linkStatement: LINK
|
||||
{var inf = yy.destructLink($1);$$ = {"type":inf.type,"stroke":inf.stroke,"length":inf.length};}
|
||||
{var inf = yy.destructLink($LINK);$$ = {"type":inf.type,"stroke":inf.stroke,"length":inf.length};}
|
||||
;
|
||||
|
||||
arrowText:
|
||||
PIPE text PIPE
|
||||
{$$ = $2;}
|
||||
{$$ = $text;}
|
||||
;
|
||||
|
||||
text: textToken
|
||||
{ $$={text:$1, type: 'text'};}
|
||||
{ $$={text:$textToken, type: 'text'};}
|
||||
| text textToken
|
||||
{ $$={text:$1.text+''+$2, type: $1.type};}
|
||||
{ $$={text:$text.text+''+$textToken, type: $text.type};}
|
||||
| STR
|
||||
{ $$={text: $1, type: 'text'};}
|
||||
{ $$ = {text: $STR, type: 'string'};}
|
||||
| MD_STR
|
||||
{ $$={text: $1, type: 'markdown'};}
|
||||
{ $$={text: $MD_STR, type: 'markdown'};}
|
||||
;
|
||||
|
||||
|
||||
@@ -456,109 +498,104 @@ keywords
|
||||
|
||||
|
||||
textNoTags: textNoTagsToken
|
||||
{$$=$1;}
|
||||
{$$={text:$textNoTagsToken, type: 'text'};}
|
||||
| textNoTags textNoTagsToken
|
||||
{$$=$1+''+$2;}
|
||||
{$$={text:$textNoTags.text+''+$textNoTagsToken, type: $textNoTags.type};}
|
||||
| STR
|
||||
{ $$={text: $STR, type: 'text'};}
|
||||
| MD_STR
|
||||
{ $$={text: $MD_STR, type: 'markdown'};}
|
||||
;
|
||||
|
||||
|
||||
classDefStatement:CLASSDEF SPACE DEFAULT SPACE stylesOpt
|
||||
{$$ = $1;yy.addClass($3,$5);}
|
||||
| CLASSDEF SPACE alphaNum SPACE stylesOpt
|
||||
{$$ = $1;yy.addClass($3,$5);}
|
||||
classDefStatement:CLASSDEF SPACE idString SPACE stylesOpt
|
||||
{$$ = $CLASSDEF;yy.addClass($idString,$stylesOpt);}
|
||||
;
|
||||
|
||||
classStatement:CLASS SPACE alphaNum SPACE alphaNum
|
||||
{$$ = $1;yy.setClass($3, $5);}
|
||||
classStatement:CLASS SPACE idString\[vertex] SPACE idString\[class]
|
||||
{$$ = $CLASS;yy.setClass($vertex, $class);}
|
||||
;
|
||||
|
||||
clickStatement
|
||||
: CLICK CALLBACKNAME {$$ = $1;yy.setClickEvent($1, $2);}
|
||||
| CLICK CALLBACKNAME SPACE STR {$$ = $1;yy.setClickEvent($1, $2);yy.setTooltip($1, $4);}
|
||||
| CLICK CALLBACKNAME CALLBACKARGS {$$ = $1;yy.setClickEvent($1, $2, $3);}
|
||||
| CLICK CALLBACKNAME CALLBACKARGS SPACE STR {$$ = $1;yy.setClickEvent($1, $2, $3);yy.setTooltip($1, $5);}
|
||||
| CLICK HREF {$$ = $1;yy.setLink($1, $2);}
|
||||
| CLICK HREF SPACE STR {$$ = $1;yy.setLink($1, $2);yy.setTooltip($1, $4);}
|
||||
| CLICK HREF SPACE LINK_TARGET {$$ = $1;yy.setLink($1, $2, $4);}
|
||||
| CLICK HREF SPACE STR SPACE LINK_TARGET {$$ = $1;yy.setLink($1, $2, $6);yy.setTooltip($1, $4);}
|
||||
| CLICK alphaNum {$$ = $1;yy.setClickEvent($1, $2);}
|
||||
| CLICK alphaNum SPACE STR {$$ = $1;yy.setClickEvent($1, $2);yy.setTooltip($1, $4);}
|
||||
| CLICK STR {$$ = $1;yy.setLink($1, $2);}
|
||||
| CLICK STR SPACE STR {$$ = $1;yy.setLink($1, $2);yy.setTooltip($1, $4);}
|
||||
| CLICK STR SPACE LINK_TARGET {$$ = $1;yy.setLink($1, $2, $4);}
|
||||
| CLICK STR SPACE STR SPACE LINK_TARGET {$$ = $1;yy.setLink($1, $2, $6);yy.setTooltip($1, $4);}
|
||||
: CLICK CALLBACKNAME {$$ = $CLICK;yy.setClickEvent($CLICK, $CALLBACKNAME);}
|
||||
| CLICK CALLBACKNAME SPACE STR {$$ = $CLICK;yy.setClickEvent($CLICK, $CALLBACKNAME);yy.setTooltip($CLICK, $STR);}
|
||||
| CLICK CALLBACKNAME CALLBACKARGS {$$ = $CLICK;yy.setClickEvent($CLICK, $CALLBACKNAME, $CALLBACKARGS);}
|
||||
| CLICK CALLBACKNAME CALLBACKARGS SPACE STR {$$ = $CLICK;yy.setClickEvent($CLICK, $CALLBACKNAME, $CALLBACKARGS);yy.setTooltip($CLICK, $STR);}
|
||||
| CLICK HREF STR {$$ = $CLICK;yy.setLink($CLICK, $STR);}
|
||||
| CLICK HREF STR SPACE STR {$$ = $CLICK;yy.setLink($CLICK, $STR1);yy.setTooltip($CLICK, $STR2);}
|
||||
| CLICK HREF STR SPACE LINK_TARGET {$$ = $CLICK;yy.setLink($CLICK, $STR, $LINK_TARGET);}
|
||||
| CLICK HREF STR\[link] SPACE STR\[tooltip] SPACE LINK_TARGET {$$ = $CLICK;yy.setLink($CLICK, $link, $LINK_TARGET);yy.setTooltip($CLICK, $tooltip);}
|
||||
| CLICK alphaNum {$$ = $CLICK;yy.setClickEvent($CLICK, $alphaNum);}
|
||||
| CLICK alphaNum SPACE STR {$$ = $CLICK;yy.setClickEvent($CLICK, $alphaNum);yy.setTooltip($CLICK, $STR);}
|
||||
| CLICK STR {$$ = $CLICK;yy.setLink($CLICK, $STR);}
|
||||
| CLICK STR\[link] SPACE STR\[tooltip] {$$ = $CLICK;yy.setLink($CLICK, $link);yy.setTooltip($CLICK, $tooltip);}
|
||||
| CLICK STR SPACE LINK_TARGET {$$ = $CLICK;yy.setLink($CLICK, $STR, $LINK_TARGET);}
|
||||
| CLICK STR\[link] SPACE STR\[tooltip] SPACE LINK_TARGET {$$ = $CLICK;yy.setLink($CLICK, $link, $LINK_TARGET);yy.setTooltip($CLICK, $tooltip);}
|
||||
;
|
||||
|
||||
styleStatement:STYLE SPACE alphaNum SPACE stylesOpt
|
||||
{$$ = $1;yy.addVertex($3,undefined,undefined,$5);}
|
||||
| STYLE SPACE HEX SPACE stylesOpt
|
||||
{$$ = $1;yy.updateLink($3,$5);}
|
||||
styleStatement:STYLE SPACE idString SPACE stylesOpt
|
||||
{$$ = $STYLE;yy.addVertex($idString,undefined,undefined,$stylesOpt);}
|
||||
;
|
||||
|
||||
linkStyleStatement
|
||||
: LINKSTYLE SPACE DEFAULT SPACE stylesOpt
|
||||
{$$ = $1;yy.updateLink([$3],$5);}
|
||||
{$$ = $LINKSTYLE;yy.updateLink([$DEFAULT],$stylesOpt);}
|
||||
| LINKSTYLE SPACE numList SPACE stylesOpt
|
||||
{$$ = $1;yy.updateLink($3,$5);}
|
||||
{$$ = $LINKSTYLE;yy.updateLink($numList,$stylesOpt);}
|
||||
| LINKSTYLE SPACE DEFAULT SPACE INTERPOLATE SPACE alphaNum SPACE stylesOpt
|
||||
{$$ = $1;yy.updateLinkInterpolate([$3],$7);yy.updateLink([$3],$9);}
|
||||
{$$ = $LINKSTYLE;yy.updateLinkInterpolate([$DEFAULT],$alphaNum);yy.updateLink([$DEFAULT],$stylesOpt);}
|
||||
| LINKSTYLE SPACE numList SPACE INTERPOLATE SPACE alphaNum SPACE stylesOpt
|
||||
{$$ = $1;yy.updateLinkInterpolate($3,$7);yy.updateLink($3,$9);}
|
||||
{$$ = $LINKSTYLE;yy.updateLinkInterpolate($numList,$alphaNum);yy.updateLink($numList,$stylesOpt);}
|
||||
| LINKSTYLE SPACE DEFAULT SPACE INTERPOLATE SPACE alphaNum
|
||||
{$$ = $1;yy.updateLinkInterpolate([$3],$7);}
|
||||
{$$ = $LINKSTYLE;yy.updateLinkInterpolate([$DEFAULT],$alphaNum);}
|
||||
| LINKSTYLE SPACE numList SPACE INTERPOLATE SPACE alphaNum
|
||||
{$$ = $1;yy.updateLinkInterpolate($3,$7);}
|
||||
{$$ = $LINKSTYLE;yy.updateLinkInterpolate($numList,$alphaNum);}
|
||||
;
|
||||
|
||||
numList: NUM
|
||||
{$$ = [$1]}
|
||||
{$$ = [$NUM]}
|
||||
| numList COMMA NUM
|
||||
{$1.push($3);$$ = $1;}
|
||||
{$numList.push($NUM);$$ = $numList;}
|
||||
;
|
||||
|
||||
stylesOpt: style
|
||||
{$$ = [$1]}
|
||||
{$$ = [$style]}
|
||||
| stylesOpt COMMA style
|
||||
{$1.push($3);$$ = $1;}
|
||||
{$stylesOpt.push($style);$$ = $stylesOpt;}
|
||||
;
|
||||
|
||||
style: styleComponent
|
||||
|style styleComponent
|
||||
{$$ = $1 + $2;}
|
||||
{$$ = $style + $styleComponent;}
|
||||
;
|
||||
|
||||
styleComponent: ALPHA | COLON | MINUS | NUM | UNIT | SPACE | HEX | BRKT | DOT | STYLE | PCT ;
|
||||
styleComponent: NUM | NODE_STRING| COLON | UNIT | SPACE | BRKT | STYLE | PCT ;
|
||||
|
||||
/* Token lists */
|
||||
idStringToken : NUM | NODE_STRING | DOWN | MINUS | DEFAULT | COMMA | COLON | AMP | BRKT | MULT | UNICODE_TEXT;
|
||||
|
||||
textToken : textNoTagsToken | TAGSTART | TAGEND | START_LINK | PCT | DEFAULT;
|
||||
textToken : TEXT | TAGSTART | TAGEND | UNICODE_TEXT;
|
||||
|
||||
textNoTagsToken: alphaNumToken | SPACE | MINUS | keywords ;
|
||||
textNoTagsToken: NUM | NODE_STRING | SPACE | MINUS | AMP | UNICODE_TEXT | COLON | MULT | BRKT | keywords | START_LINK ;
|
||||
|
||||
edgeTextToken : EDGE_TEXT | UNICODE_TEXT ;
|
||||
|
||||
alphaNumToken : NUM | UNICODE_TEXT | NODE_STRING | DIR | DOWN | MINUS | COMMA | COLON | AMP | BRKT | MULT;
|
||||
|
||||
idString
|
||||
:idStringToken
|
||||
{$$=$1}
|
||||
{$$=$idStringToken}
|
||||
| idString idStringToken
|
||||
{$$=$1+''+$2}
|
||||
{$$=$idString+''+$idStringToken}
|
||||
;
|
||||
|
||||
alphaNum
|
||||
: alphaNumStatement
|
||||
{$$=$1;}
|
||||
| alphaNum alphaNumStatement
|
||||
{$$=$1+''+$2;}
|
||||
: alphaNumToken
|
||||
{$$=$alphaNumToken;}
|
||||
| alphaNum alphaNumToken
|
||||
{$$=$alphaNum+''+$alphaNumToken;}
|
||||
;
|
||||
|
||||
alphaNumStatement
|
||||
: DIR
|
||||
{$$=$1;}
|
||||
| alphaNumToken
|
||||
{$$=$1;}
|
||||
| DOWN
|
||||
{$$='v';}
|
||||
| MINUS
|
||||
{$$='-';}
|
||||
;
|
||||
|
||||
direction
|
||||
: direction_tb
|
||||
@@ -571,9 +608,4 @@ direction
|
||||
{ $$={stmt:'dir', value:'LR'};}
|
||||
;
|
||||
|
||||
alphaNumToken : PUNCTUATION | AMP | UNICODE_TEXT | NUM| ALPHA | COLON | COMMA | PLUS | EQUALS | MULT | DOT | BRKT| UNDERSCORE ;
|
||||
|
||||
idStringToken : ALPHA|UNDERSCORE |UNICODE_TEXT | NUM| COLON | COMMA | PLUS | MINUS | DOWN |EQUALS | MULT | BRKT | DOT | PUNCTUATION | AMP | DEFAULT;
|
||||
|
||||
graphCodeTokens: STADIUMSTART | STADIUMEND | SUBROUTINESTART | SUBROUTINEEND | VERTEX_WITH_PROPS_START | CYLINDERSTART | CYLINDEREND | TRAPSTART | TRAPEND | INVTRAPSTART | INVTRAPEND | PIPE | PS | PE | SQS | SQE | DIAMOND_START | DIAMOND_STOP | TAGSTART | TAGEND | ARROW_CROSS | ARROW_POINT | ARROW_CIRCLE | ARROW_OPEN | QUOTE | SEMI;
|
||||
%%
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @ts-ignore: TODO Fix ts errors
|
||||
// @ts-ignore: JISON doesn't support types
|
||||
import ganttParser from './parser/gantt.jison';
|
||||
import ganttDb from './ganttDb.js';
|
||||
import ganttRenderer from './ganttRenderer.js';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @ts-ignore: TODO Fix ts errors
|
||||
// @ts-ignore: JISON doesn't support types
|
||||
import gitGraphParser from './parser/gitGraph.jison';
|
||||
import gitGraphDb from './gitGraphAst.js';
|
||||
import gitGraphRenderer from './gitGraphRenderer.js';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { select } from 'd3';
|
||||
import { log } from '../../logger.js';
|
||||
import { getConfig } from '../../config.js';
|
||||
import type { DrawDefinition, HTML, SVG } from '../../diagram-api/types.js';
|
||||
import { configureSvgSize } from '../../setupGraphViewbox.js';
|
||||
import type { DrawDefinition, Group, SVG } from '../../diagram-api/types.js';
|
||||
import { selectSvgElement } from '../../rendering-util/selectSvgElement.js';
|
||||
|
||||
/**
|
||||
* Draws a an info picture in the tag with id: id based on the graph definition in text.
|
||||
@@ -11,40 +11,20 @@ import type { DrawDefinition, HTML, SVG } from '../../diagram-api/types.js';
|
||||
* @param version - MermaidJS version.
|
||||
*/
|
||||
const draw: DrawDefinition = (text, id, version) => {
|
||||
try {
|
||||
log.debug('rendering info diagram\n' + text);
|
||||
log.debug('rendering info diagram\n' + text);
|
||||
|
||||
const { securityLevel } = getConfig();
|
||||
// handle root and document for when rendering in sandbox mode
|
||||
let sandboxElement: HTML | undefined;
|
||||
let document: Document | null | undefined;
|
||||
if (securityLevel === 'sandbox') {
|
||||
sandboxElement = select('#i' + id);
|
||||
document = sandboxElement.nodes()[0].contentDocument;
|
||||
}
|
||||
const svg: SVG = selectSvgElement(id);
|
||||
configureSvgSize(svg, 100, 400, true);
|
||||
|
||||
// @ts-ignore - figure out how to assign HTML to document type
|
||||
const root: HTML =
|
||||
sandboxElement !== undefined && document !== undefined && document !== null
|
||||
? select(document)
|
||||
: select('body');
|
||||
|
||||
const svg: SVG = root.select('#' + id);
|
||||
svg.attr('height', 100);
|
||||
svg.attr('width', 400);
|
||||
|
||||
const g = svg.append('g');
|
||||
|
||||
g.append('text') // text label for the x axis
|
||||
.attr('x', 100)
|
||||
.attr('y', 40)
|
||||
.attr('class', 'version')
|
||||
.attr('font-size', '32px')
|
||||
.style('text-anchor', 'middle')
|
||||
.text('v ' + version);
|
||||
} catch (e) {
|
||||
log.error('error while rendering info diagram', e);
|
||||
}
|
||||
const group: Group = svg.append('g');
|
||||
group
|
||||
.append('text')
|
||||
.attr('x', 100)
|
||||
.attr('y', 40)
|
||||
.attr('class', 'version')
|
||||
.attr('font-size', 32)
|
||||
.style('text-anchor', 'middle')
|
||||
.text(`v${version}`);
|
||||
};
|
||||
|
||||
export const renderer = { draw };
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @ts-ignore: TODO Fix ts errors
|
||||
// @ts-ignore: JISON doesn't support types
|
||||
import mindmapParser from './parser/mindmap.jison';
|
||||
import * as mindmapDb from './mindmapDb.js';
|
||||
import mindmapRenderer from './mindmapRenderer.js';
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
name,amounts
|
||||
Foo, 33
|
||||
Rishab, 12
|
||||
Alexis, 41
|
||||
Tom, 16
|
||||
Courtney, 59
|
||||
Christina, 38
|
||||
Jack, 21
|
||||
Mickey, 25
|
||||
Paul, 30
|
||||
|
@@ -1,132 +0,0 @@
|
||||
import pieDb from '../pieDb.js';
|
||||
import pie from './pie.jison';
|
||||
import { setConfig } from '../../../config.js';
|
||||
|
||||
setConfig({
|
||||
securityLevel: 'strict',
|
||||
});
|
||||
|
||||
describe('when parsing pie', function () {
|
||||
beforeEach(function () {
|
||||
pie.parser.yy = pieDb;
|
||||
pie.parser.yy.clear();
|
||||
});
|
||||
it('should handle very simple pie', function () {
|
||||
const res = pie.parser.parse(`pie
|
||||
"ash" : 100
|
||||
`);
|
||||
const sections = pieDb.getSections();
|
||||
const section1 = sections['ash'];
|
||||
expect(section1).toBe(100);
|
||||
});
|
||||
it('should handle simple pie', function () {
|
||||
const res = pie.parser.parse(`pie
|
||||
"ash" : 60
|
||||
"bat" : 40
|
||||
`);
|
||||
const sections = pieDb.getSections();
|
||||
const section1 = sections['ash'];
|
||||
expect(section1).toBe(60);
|
||||
});
|
||||
it('should handle simple pie with comments', function () {
|
||||
const res = pie.parser.parse(`pie
|
||||
%% comments
|
||||
"ash" : 60
|
||||
"bat" : 40
|
||||
`);
|
||||
const sections = pieDb.getSections();
|
||||
const section1 = sections['ash'];
|
||||
expect(section1).toBe(60);
|
||||
});
|
||||
|
||||
it('should handle simple pie with a directive', function () {
|
||||
const res = pie.parser.parse(`%%{init: {'logLevel':0}}%%
|
||||
pie
|
||||
"ash" : 60
|
||||
"bat" : 40
|
||||
`);
|
||||
const sections = pieDb.getSections();
|
||||
const section1 = sections['ash'];
|
||||
expect(section1).toBe(60);
|
||||
});
|
||||
|
||||
it('should handle simple pie with a title', function () {
|
||||
const res = pie.parser.parse(`pie title a 60/40 pie
|
||||
"ash" : 60
|
||||
"bat" : 40
|
||||
`);
|
||||
const sections = pieDb.getSections();
|
||||
const title = pieDb.getDiagramTitle();
|
||||
const section1 = sections['ash'];
|
||||
expect(section1).toBe(60);
|
||||
expect(title).toBe('a 60/40 pie');
|
||||
});
|
||||
|
||||
it('should handle simple pie without an acc description (accDescr)', function () {
|
||||
const res = pie.parser.parse(`pie title a neat chart
|
||||
"ash" : 60
|
||||
"bat" : 40
|
||||
`);
|
||||
|
||||
const sections = pieDb.getSections();
|
||||
const title = pieDb.getDiagramTitle();
|
||||
const description = pieDb.getAccDescription();
|
||||
const section1 = sections['ash'];
|
||||
expect(section1).toBe(60);
|
||||
expect(title).toBe('a neat chart');
|
||||
expect(description).toBe('');
|
||||
});
|
||||
|
||||
it('should handle simple pie with an acc description (accDescr)', function () {
|
||||
const res = pie.parser.parse(`pie title a neat chart
|
||||
accDescr: a neat description
|
||||
"ash" : 60
|
||||
"bat" : 40
|
||||
`);
|
||||
|
||||
const sections = pieDb.getSections();
|
||||
const title = pieDb.getDiagramTitle();
|
||||
const description = pieDb.getAccDescription();
|
||||
const section1 = sections['ash'];
|
||||
expect(section1).toBe(60);
|
||||
expect(title).toBe('a neat chart');
|
||||
expect(description).toBe('a neat description');
|
||||
});
|
||||
it('should handle simple pie with a multiline acc description (accDescr)', function () {
|
||||
const res = pie.parser.parse(`pie title a neat chart
|
||||
accDescr {
|
||||
a neat description
|
||||
on multiple lines
|
||||
}
|
||||
"ash" : 60
|
||||
"bat" : 40
|
||||
`);
|
||||
|
||||
const sections = pieDb.getSections();
|
||||
const title = pieDb.getDiagramTitle();
|
||||
const description = pieDb.getAccDescription();
|
||||
const section1 = sections['ash'];
|
||||
expect(section1).toBe(60);
|
||||
expect(title).toBe('a neat chart');
|
||||
expect(description).toBe('a neat description\non multiple lines');
|
||||
});
|
||||
|
||||
it('should handle simple pie with positive decimal', function () {
|
||||
const res = pie.parser.parse(`pie
|
||||
"ash" : 60.67
|
||||
"bat" : 40
|
||||
`);
|
||||
const sections = pieDb.getSections();
|
||||
const section1 = sections['ash'];
|
||||
expect(section1).toBe(60.67);
|
||||
});
|
||||
|
||||
it('should handle simple pie with negative decimal', function () {
|
||||
expect(() => {
|
||||
pie.parser.parse(`pie
|
||||
"ash" : 60.67
|
||||
"bat" : 40..12
|
||||
`);
|
||||
}).toThrowError();
|
||||
});
|
||||
});
|
||||
180
packages/mermaid/src/diagrams/pie/pie.spec.ts
Normal file
180
packages/mermaid/src/diagrams/pie/pie.spec.ts
Normal file
@@ -0,0 +1,180 @@
|
||||
// @ts-ignore: JISON doesn't support types
|
||||
import { parser } from './parser/pie.jison';
|
||||
import { DEFAULT_PIE_DB, db } from './pieDb.js';
|
||||
import { setConfig } from '../../config.js';
|
||||
|
||||
setConfig({
|
||||
securityLevel: 'strict',
|
||||
});
|
||||
|
||||
describe('pie', () => {
|
||||
beforeAll(() => {
|
||||
parser.yy = db;
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
parser.yy.clear();
|
||||
});
|
||||
|
||||
describe('parse', () => {
|
||||
it('should handle very simple pie', () => {
|
||||
parser.parse(`pie
|
||||
"ash": 100
|
||||
`);
|
||||
|
||||
const sections = db.getSections();
|
||||
expect(sections['ash']).toBe(100);
|
||||
});
|
||||
|
||||
it('should handle simple pie', () => {
|
||||
parser.parse(`pie
|
||||
"ash" : 60
|
||||
"bat" : 40
|
||||
`);
|
||||
|
||||
const sections = db.getSections();
|
||||
expect(sections['ash']).toBe(60);
|
||||
expect(sections['bat']).toBe(40);
|
||||
});
|
||||
|
||||
it('should handle simple pie with showData', () => {
|
||||
parser.parse(`pie showData
|
||||
"ash" : 60
|
||||
"bat" : 40
|
||||
`);
|
||||
|
||||
expect(db.getShowData()).toBeTruthy();
|
||||
|
||||
const sections = db.getSections();
|
||||
expect(sections['ash']).toBe(60);
|
||||
expect(sections['bat']).toBe(40);
|
||||
});
|
||||
|
||||
it('should handle simple pie with comments', () => {
|
||||
parser.parse(`pie
|
||||
%% comments
|
||||
"ash" : 60
|
||||
"bat" : 40
|
||||
`);
|
||||
|
||||
const sections = db.getSections();
|
||||
expect(sections['ash']).toBe(60);
|
||||
expect(sections['bat']).toBe(40);
|
||||
});
|
||||
|
||||
it('should handle simple pie with a directive', () => {
|
||||
parser.parse(`%%{init: {'logLevel':0}}%%
|
||||
pie
|
||||
"ash" : 60
|
||||
"bat" : 40
|
||||
`);
|
||||
const sections = db.getSections();
|
||||
expect(sections['ash']).toBe(60);
|
||||
expect(sections['bat']).toBe(40);
|
||||
});
|
||||
|
||||
it('should handle simple pie with a title', () => {
|
||||
parser.parse(`pie title a 60/40 pie
|
||||
"ash" : 60
|
||||
"bat" : 40
|
||||
`);
|
||||
|
||||
expect(db.getDiagramTitle()).toBe('a 60/40 pie');
|
||||
|
||||
const sections = db.getSections();
|
||||
expect(sections['ash']).toBe(60);
|
||||
expect(sections['bat']).toBe(40);
|
||||
});
|
||||
|
||||
it('should handle simple pie with an acc title (accTitle)', () => {
|
||||
parser.parse(`pie title a neat chart
|
||||
accTitle: a neat acc title
|
||||
"ash" : 60
|
||||
"bat" : 40
|
||||
`);
|
||||
|
||||
expect(db.getDiagramTitle()).toBe('a neat chart');
|
||||
|
||||
expect(db.getAccTitle()).toBe('a neat acc title');
|
||||
|
||||
const sections = db.getSections();
|
||||
expect(sections['ash']).toBe(60);
|
||||
expect(sections['bat']).toBe(40);
|
||||
});
|
||||
|
||||
it('should handle simple pie with an acc description (accDescr)', () => {
|
||||
parser.parse(`pie title a neat chart
|
||||
accDescr: a neat description
|
||||
"ash" : 60
|
||||
"bat" : 40
|
||||
`);
|
||||
|
||||
expect(db.getDiagramTitle()).toBe('a neat chart');
|
||||
|
||||
expect(db.getAccDescription()).toBe('a neat description');
|
||||
|
||||
const sections = db.getSections();
|
||||
expect(sections['ash']).toBe(60);
|
||||
expect(sections['bat']).toBe(40);
|
||||
});
|
||||
|
||||
it('should handle simple pie with a multiline acc description (accDescr)', () => {
|
||||
parser.parse(`pie title a neat chart
|
||||
accDescr {
|
||||
a neat description
|
||||
on multiple lines
|
||||
}
|
||||
"ash" : 60
|
||||
"bat" : 40
|
||||
`);
|
||||
|
||||
expect(db.getDiagramTitle()).toBe('a neat chart');
|
||||
|
||||
expect(db.getAccDescription()).toBe('a neat description\non multiple lines');
|
||||
|
||||
const sections = db.getSections();
|
||||
expect(sections['ash']).toBe(60);
|
||||
expect(sections['bat']).toBe(40);
|
||||
});
|
||||
|
||||
it('should handle simple pie with positive decimal', () => {
|
||||
parser.parse(`pie
|
||||
"ash" : 60.67
|
||||
"bat" : 40
|
||||
`);
|
||||
|
||||
const sections = db.getSections();
|
||||
expect(sections['ash']).toBe(60.67);
|
||||
expect(sections['bat']).toBe(40);
|
||||
});
|
||||
|
||||
it('should handle simple pie with negative decimal', () => {
|
||||
expect(() => {
|
||||
parser.parse(`pie
|
||||
"ash" : -60.67
|
||||
"bat" : 40.12
|
||||
`);
|
||||
}).toThrowError();
|
||||
});
|
||||
});
|
||||
|
||||
describe('config', () => {
|
||||
it.todo('setConfig', () => {
|
||||
// db.setConfig({ useWidth: 850, useMaxWidth: undefined });
|
||||
|
||||
const config = db.getConfig();
|
||||
expect(config.useWidth).toBe(850);
|
||||
expect(config.useMaxWidth).toBeTruthy();
|
||||
});
|
||||
|
||||
it('getConfig', () => {
|
||||
expect(db.getConfig()).toStrictEqual(DEFAULT_PIE_DB.config);
|
||||
});
|
||||
|
||||
it.todo('resetConfig', () => {
|
||||
// db.setConfig({ textPosition: 0 });
|
||||
// db.resetConfig();
|
||||
expect(db.getConfig().textPosition).toStrictEqual(DEFAULT_PIE_DB.config.textPosition);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,69 +0,0 @@
|
||||
import { log } from '../../logger.js';
|
||||
import mermaidAPI from '../../mermaidAPI.js';
|
||||
import * as configApi from '../../config.js';
|
||||
import common from '../common/common.js';
|
||||
import {
|
||||
setAccTitle,
|
||||
getAccTitle,
|
||||
setDiagramTitle,
|
||||
getDiagramTitle,
|
||||
getAccDescription,
|
||||
setAccDescription,
|
||||
clear as commonClear,
|
||||
} from '../../commonDb.js';
|
||||
|
||||
let sections = {};
|
||||
let showData = false;
|
||||
|
||||
export const parseDirective = function (statement, context, type) {
|
||||
mermaidAPI.parseDirective(this, statement, context, type);
|
||||
};
|
||||
|
||||
const addSection = function (id, value) {
|
||||
id = common.sanitizeText(id, configApi.getConfig());
|
||||
if (sections[id] === undefined) {
|
||||
sections[id] = value;
|
||||
log.debug('Added new section :', id);
|
||||
}
|
||||
};
|
||||
const getSections = () => sections;
|
||||
|
||||
const setShowData = function (toggle) {
|
||||
showData = toggle;
|
||||
};
|
||||
|
||||
const getShowData = function () {
|
||||
return showData;
|
||||
};
|
||||
|
||||
const cleanupValue = function (value) {
|
||||
if (value.substring(0, 1) === ':') {
|
||||
value = value.substring(1).trim();
|
||||
return Number(value.trim());
|
||||
} else {
|
||||
return Number(value.trim());
|
||||
}
|
||||
};
|
||||
|
||||
const clear = function () {
|
||||
sections = {};
|
||||
showData = false;
|
||||
commonClear();
|
||||
};
|
||||
|
||||
export default {
|
||||
parseDirective,
|
||||
getConfig: () => configApi.getConfig().pie,
|
||||
addSection,
|
||||
getSections,
|
||||
cleanupValue,
|
||||
clear,
|
||||
setAccTitle,
|
||||
getAccTitle,
|
||||
setDiagramTitle,
|
||||
getDiagramTitle,
|
||||
setShowData,
|
||||
getShowData,
|
||||
getAccDescription,
|
||||
setAccDescription,
|
||||
};
|
||||
84
packages/mermaid/src/diagrams/pie/pieDb.ts
Normal file
84
packages/mermaid/src/diagrams/pie/pieDb.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import { log } from '../../logger.js';
|
||||
import { parseDirective as _parseDirective } from '../../directiveUtils.js';
|
||||
import { getConfig as commonGetConfig } from '../../config.js';
|
||||
import { sanitizeText } from '../common/common.js';
|
||||
import {
|
||||
setAccTitle,
|
||||
getAccTitle,
|
||||
setDiagramTitle,
|
||||
getDiagramTitle,
|
||||
getAccDescription,
|
||||
setAccDescription,
|
||||
clear as commonClear,
|
||||
} from '../../commonDb.js';
|
||||
import type { ParseDirectiveDefinition } from '../../diagram-api/types.js';
|
||||
import type { PieFields, PieDB, Sections } from './pieTypes.js';
|
||||
import type { RequiredDeep } from 'type-fest';
|
||||
import type { PieDiagramConfig } from '../../config.type.js';
|
||||
import DEFAULT_CONFIG from '../../defaultConfig.js';
|
||||
|
||||
export const DEFAULT_PIE_CONFIG: Required<PieDiagramConfig> = DEFAULT_CONFIG.pie;
|
||||
|
||||
export const DEFAULT_PIE_DB: RequiredDeep<PieFields> = {
|
||||
sections: {},
|
||||
showData: false,
|
||||
config: DEFAULT_PIE_CONFIG,
|
||||
} as const;
|
||||
|
||||
let sections: Sections = DEFAULT_PIE_DB.sections;
|
||||
let showData: boolean = DEFAULT_PIE_DB.showData;
|
||||
const config: Required<PieDiagramConfig> = structuredClone(DEFAULT_PIE_CONFIG);
|
||||
|
||||
const getConfig = (): Required<PieDiagramConfig> => structuredClone(config);
|
||||
|
||||
const parseDirective: ParseDirectiveDefinition = (statement, context, type) => {
|
||||
_parseDirective(this, statement, context, type);
|
||||
};
|
||||
|
||||
const clear = (): void => {
|
||||
sections = structuredClone(DEFAULT_PIE_DB.sections);
|
||||
showData = DEFAULT_PIE_DB.showData;
|
||||
commonClear();
|
||||
};
|
||||
|
||||
const addSection = (label: string, value: number): void => {
|
||||
label = sanitizeText(label, commonGetConfig());
|
||||
if (sections[label] === undefined) {
|
||||
sections[label] = value;
|
||||
log.debug(`added new section: ${label}, with value: ${value}`);
|
||||
}
|
||||
};
|
||||
|
||||
const getSections = (): Sections => sections;
|
||||
|
||||
const cleanupValue = (value: string): number => {
|
||||
if (value.substring(0, 1) === ':') {
|
||||
value = value.substring(1).trim();
|
||||
}
|
||||
return Number(value.trim());
|
||||
};
|
||||
|
||||
const setShowData = (toggle: boolean): void => {
|
||||
showData = toggle;
|
||||
};
|
||||
|
||||
const getShowData = (): boolean => showData;
|
||||
|
||||
export const db: PieDB = {
|
||||
getConfig,
|
||||
|
||||
parseDirective,
|
||||
clear,
|
||||
setDiagramTitle,
|
||||
getDiagramTitle,
|
||||
setAccTitle,
|
||||
getAccTitle,
|
||||
setAccDescription,
|
||||
getAccDescription,
|
||||
|
||||
addSection,
|
||||
getSections,
|
||||
cleanupValue,
|
||||
setShowData,
|
||||
getShowData,
|
||||
};
|
||||
@@ -15,10 +15,8 @@ const loader: DiagramLoader = async () => {
|
||||
return { id, diagram };
|
||||
};
|
||||
|
||||
const plugin: ExternalDiagramDefinition = {
|
||||
export const pie: ExternalDiagramDefinition = {
|
||||
id,
|
||||
detector,
|
||||
loader,
|
||||
};
|
||||
|
||||
export default plugin;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { DiagramDefinition } from '../../diagram-api/types.js';
|
||||
// @ts-ignore: TODO Fix ts errors
|
||||
import type { DiagramDefinition } from '../../diagram-api/types.js';
|
||||
// @ts-ignore: JISON doesn't support types
|
||||
import parser from './parser/pie.jison';
|
||||
import db from './pieDb.js';
|
||||
import styles from './styles.js';
|
||||
import renderer from './pieRenderer.js';
|
||||
import { db } from './pieDb.js';
|
||||
import styles from './pieStyles.js';
|
||||
import { renderer } from './pieRenderer.js';
|
||||
|
||||
export const diagram: DiagramDefinition = {
|
||||
parser,
|
||||
|
||||
@@ -1,204 +0,0 @@
|
||||
/** Created by AshishJ on 11-09-2019. */
|
||||
import { select, scaleOrdinal, pie as d3pie, arc } from 'd3';
|
||||
import { log } from '../../logger.js';
|
||||
import { configureSvgSize } from '../../setupGraphViewbox.js';
|
||||
import * as configApi from '../../config.js';
|
||||
import { parseFontSize } from '../../utils.js';
|
||||
|
||||
let conf = configApi.getConfig();
|
||||
|
||||
/**
|
||||
* Draws a Pie Chart with the data given in text.
|
||||
*
|
||||
* @param text
|
||||
* @param id
|
||||
*/
|
||||
let width;
|
||||
const height = 450;
|
||||
export const draw = (txt, id, _version, diagObj) => {
|
||||
try {
|
||||
conf = configApi.getConfig();
|
||||
log.debug('Rendering info diagram\n' + txt);
|
||||
|
||||
const securityLevel = configApi.getConfig().securityLevel;
|
||||
// Handle root and Document for when rendering in sandbox mode
|
||||
let sandboxElement;
|
||||
if (securityLevel === 'sandbox') {
|
||||
sandboxElement = select('#i' + id);
|
||||
}
|
||||
const root =
|
||||
securityLevel === 'sandbox'
|
||||
? select(sandboxElement.nodes()[0].contentDocument.body)
|
||||
: select('body');
|
||||
const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
|
||||
|
||||
// Parse the Pie Chart definition
|
||||
const elem = doc.getElementById(id);
|
||||
width = elem.parentElement.offsetWidth;
|
||||
|
||||
if (width === undefined) {
|
||||
width = 1200;
|
||||
}
|
||||
|
||||
if (conf.useWidth !== undefined) {
|
||||
width = conf.useWidth;
|
||||
}
|
||||
if (conf.pie.useWidth !== undefined) {
|
||||
width = conf.pie.useWidth;
|
||||
}
|
||||
|
||||
const diagram = root.select('#' + id);
|
||||
configureSvgSize(diagram, height, width, conf.pie.useMaxWidth);
|
||||
|
||||
// Set viewBox
|
||||
elem.setAttribute('viewBox', '0 0 ' + width + ' ' + height);
|
||||
|
||||
// Fetch the default direction, use TD if none was found
|
||||
var margin = 40;
|
||||
var legendRectSize = 18;
|
||||
var legendSpacing = 4;
|
||||
|
||||
var radius = Math.min(width, height) / 2 - margin;
|
||||
|
||||
var svg = diagram
|
||||
.append('g')
|
||||
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');
|
||||
|
||||
var data = diagObj.db.getSections();
|
||||
var sum = 0;
|
||||
Object.keys(data).forEach(function (key) {
|
||||
sum += data[key];
|
||||
});
|
||||
|
||||
const themeVariables = conf.themeVariables;
|
||||
var myGeneratedColors = [
|
||||
themeVariables.pie1,
|
||||
themeVariables.pie2,
|
||||
themeVariables.pie3,
|
||||
themeVariables.pie4,
|
||||
themeVariables.pie5,
|
||||
themeVariables.pie6,
|
||||
themeVariables.pie7,
|
||||
themeVariables.pie8,
|
||||
themeVariables.pie9,
|
||||
themeVariables.pie10,
|
||||
themeVariables.pie11,
|
||||
themeVariables.pie12,
|
||||
];
|
||||
|
||||
const textPosition = conf.pie?.textPosition ?? 0.75;
|
||||
let [outerStrokeWidth] = parseFontSize(themeVariables.pieOuterStrokeWidth);
|
||||
outerStrokeWidth ??= 2;
|
||||
|
||||
// Set the color scale
|
||||
var color = scaleOrdinal().range(myGeneratedColors);
|
||||
|
||||
// Compute the position of each group on the pie:
|
||||
var pieData = Object.entries(data).map(function (el, idx) {
|
||||
return {
|
||||
order: idx,
|
||||
name: el[0],
|
||||
value: el[1],
|
||||
};
|
||||
});
|
||||
var pie = d3pie()
|
||||
.value(function (d) {
|
||||
return d.value;
|
||||
})
|
||||
.sort(function (a, b) {
|
||||
// Sort slices in clockwise direction
|
||||
return a.order - b.order;
|
||||
});
|
||||
var dataReady = pie(pieData);
|
||||
|
||||
// Shape helper to build arcs:
|
||||
var arcGenerator = arc().innerRadius(0).outerRadius(radius);
|
||||
var labelArcGenerator = arc()
|
||||
.innerRadius(radius * textPosition)
|
||||
.outerRadius(radius * textPosition);
|
||||
|
||||
svg
|
||||
.append('circle')
|
||||
.attr('cx', 0)
|
||||
.attr('cy', 0)
|
||||
.attr('r', radius + outerStrokeWidth / 2)
|
||||
.attr('class', 'pieOuterCircle');
|
||||
|
||||
// Build the pie chart: each part of the pie is a path that we build using the arc function.
|
||||
svg
|
||||
.selectAll('mySlices')
|
||||
.data(dataReady)
|
||||
.enter()
|
||||
.append('path')
|
||||
.attr('d', arcGenerator)
|
||||
.attr('fill', function (d) {
|
||||
return color(d.data.name);
|
||||
})
|
||||
.attr('class', 'pieCircle');
|
||||
|
||||
// Now add the percentage.
|
||||
// Use the centroid method to get the best coordinates.
|
||||
svg
|
||||
.selectAll('mySlices')
|
||||
.data(dataReady)
|
||||
.enter()
|
||||
.append('text')
|
||||
.text(function (d) {
|
||||
return ((d.data.value / sum) * 100).toFixed(0) + '%';
|
||||
})
|
||||
.attr('transform', function (d) {
|
||||
return 'translate(' + labelArcGenerator.centroid(d) + ')';
|
||||
})
|
||||
.style('text-anchor', 'middle')
|
||||
.attr('class', 'slice');
|
||||
|
||||
svg
|
||||
.append('text')
|
||||
.text(diagObj.db.getDiagramTitle())
|
||||
.attr('x', 0)
|
||||
.attr('y', -(height - 50) / 2)
|
||||
.attr('class', 'pieTitleText');
|
||||
|
||||
// Add the legends/annotations for each section
|
||||
var legend = svg
|
||||
.selectAll('.legend')
|
||||
.data(color.domain())
|
||||
.enter()
|
||||
.append('g')
|
||||
.attr('class', 'legend')
|
||||
.attr('transform', function (d, i) {
|
||||
const height = legendRectSize + legendSpacing;
|
||||
const offset = (height * color.domain().length) / 2;
|
||||
const horizontal = 12 * legendRectSize;
|
||||
const vertical = i * height - offset;
|
||||
return 'translate(' + horizontal + ',' + vertical + ')';
|
||||
});
|
||||
|
||||
legend
|
||||
.append('rect')
|
||||
.attr('width', legendRectSize)
|
||||
.attr('height', legendRectSize)
|
||||
.style('fill', color)
|
||||
.style('stroke', color);
|
||||
|
||||
legend
|
||||
.data(dataReady)
|
||||
.append('text')
|
||||
.attr('x', legendRectSize + legendSpacing)
|
||||
.attr('y', legendRectSize - legendSpacing)
|
||||
.text(function (d) {
|
||||
if (diagObj.db.getShowData() || conf.showData || conf.pie.showData) {
|
||||
return d.data.name + ' [' + d.data.value + ']';
|
||||
} else {
|
||||
return d.data.name;
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
log.error('Error while rendering info diagram');
|
||||
log.error(e);
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
draw,
|
||||
};
|
||||
179
packages/mermaid/src/diagrams/pie/pieRenderer.ts
Normal file
179
packages/mermaid/src/diagrams/pie/pieRenderer.ts
Normal file
@@ -0,0 +1,179 @@
|
||||
import d3, { scaleOrdinal, pie as d3pie, arc } from 'd3';
|
||||
|
||||
import { log } from '../../logger.js';
|
||||
import { configureSvgSize } from '../../setupGraphViewbox.js';
|
||||
import { getConfig } from '../../config.js';
|
||||
import { cleanAndMerge, parseFontSize } from '../../utils.js';
|
||||
import type { DrawDefinition, Group, SVG } from '../../diagram-api/types.js';
|
||||
import type { D3Sections, PieDB, Sections } from './pieTypes.js';
|
||||
import type { MermaidConfig, PieDiagramConfig } from '../../config.type.js';
|
||||
import { selectSvgElement } from '../../rendering-util/selectSvgElement.js';
|
||||
|
||||
const createPieArcs = (sections: Sections): d3.PieArcDatum<D3Sections>[] => {
|
||||
// Compute the position of each group on the pie:
|
||||
const pieData: D3Sections[] = Object.entries(sections).map(
|
||||
(element: [string, number]): D3Sections => {
|
||||
return {
|
||||
label: element[0],
|
||||
value: element[1],
|
||||
};
|
||||
}
|
||||
);
|
||||
const pie: d3.Pie<unknown, D3Sections> = d3pie<D3Sections>().value(
|
||||
(d3Section: D3Sections): number => d3Section.value
|
||||
);
|
||||
return pie(pieData);
|
||||
};
|
||||
|
||||
/**
|
||||
* Draws a Pie Chart with the data given in text.
|
||||
*
|
||||
* @param text - pie chart code
|
||||
* @param id - diagram id
|
||||
* @param _version - MermaidJS version from package.json.
|
||||
* @param diagObj - A standard diagram containing the DB and the text and type etc of the diagram.
|
||||
*/
|
||||
export const draw: DrawDefinition = (text, id, _version, diagObj) => {
|
||||
log.debug('rendering pie chart\n' + text);
|
||||
|
||||
const db = diagObj.db as PieDB;
|
||||
const globalConfig: MermaidConfig = getConfig();
|
||||
const pieConfig: Required<PieDiagramConfig> = cleanAndMerge(db.getConfig(), globalConfig.pie);
|
||||
|
||||
const height = 450;
|
||||
// TODO: remove document width
|
||||
const width: number =
|
||||
document.getElementById(id)?.parentElement?.offsetWidth ?? pieConfig.useWidth;
|
||||
const svg: SVG = selectSvgElement(id);
|
||||
// Set viewBox
|
||||
svg.attr('viewBox', `0 0 ${width} ${height}`);
|
||||
configureSvgSize(svg, height, width, pieConfig.useMaxWidth);
|
||||
|
||||
const MARGIN = 40;
|
||||
const LEGEND_RECT_SIZE = 18;
|
||||
const LEGEND_SPACING = 4;
|
||||
|
||||
const group: Group = svg.append('g');
|
||||
group.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');
|
||||
|
||||
const { themeVariables } = globalConfig;
|
||||
let [outerStrokeWidth] = parseFontSize(themeVariables.pieOuterStrokeWidth);
|
||||
outerStrokeWidth ??= 2;
|
||||
|
||||
const textPosition: number = pieConfig.textPosition;
|
||||
const radius: number = Math.min(width, height) / 2 - MARGIN;
|
||||
// Shape helper to build arcs:
|
||||
const arcGenerator: d3.Arc<unknown, d3.PieArcDatum<D3Sections>> = arc<
|
||||
d3.PieArcDatum<D3Sections>
|
||||
>()
|
||||
.innerRadius(0)
|
||||
.outerRadius(radius);
|
||||
const labelArcGenerator: d3.Arc<unknown, d3.PieArcDatum<D3Sections>> = arc<
|
||||
d3.PieArcDatum<D3Sections>
|
||||
>()
|
||||
.innerRadius(radius * textPosition)
|
||||
.outerRadius(radius * textPosition);
|
||||
|
||||
group
|
||||
.append('circle')
|
||||
.attr('cx', 0)
|
||||
.attr('cy', 0)
|
||||
.attr('r', radius + outerStrokeWidth / 2)
|
||||
.attr('class', 'pieOuterCircle');
|
||||
|
||||
const sections: Sections = db.getSections();
|
||||
const arcs: d3.PieArcDatum<D3Sections>[] = createPieArcs(sections);
|
||||
|
||||
const myGeneratedColors = [
|
||||
themeVariables.pie1,
|
||||
themeVariables.pie2,
|
||||
themeVariables.pie3,
|
||||
themeVariables.pie4,
|
||||
themeVariables.pie5,
|
||||
themeVariables.pie6,
|
||||
themeVariables.pie7,
|
||||
themeVariables.pie8,
|
||||
themeVariables.pie9,
|
||||
themeVariables.pie10,
|
||||
themeVariables.pie11,
|
||||
themeVariables.pie12,
|
||||
];
|
||||
// Set the color scale
|
||||
const color: d3.ScaleOrdinal<string, 12, never> = scaleOrdinal(myGeneratedColors);
|
||||
|
||||
// Build the pie chart: each part of the pie is a path that we build using the arc function.
|
||||
group
|
||||
.selectAll('mySlices')
|
||||
.data(arcs)
|
||||
.enter()
|
||||
.append('path')
|
||||
.attr('d', arcGenerator)
|
||||
.attr('fill', (datum: d3.PieArcDatum<D3Sections>) => {
|
||||
return color(datum.data.label);
|
||||
})
|
||||
.attr('class', 'pieCircle');
|
||||
|
||||
let sum = 0;
|
||||
Object.keys(sections).forEach((key: string): void => {
|
||||
sum += sections[key];
|
||||
});
|
||||
// Now add the percentage.
|
||||
// Use the centroid method to get the best coordinates.
|
||||
group
|
||||
.selectAll('mySlices')
|
||||
.data(arcs)
|
||||
.enter()
|
||||
.append('text')
|
||||
.text((datum: d3.PieArcDatum<D3Sections>): string => {
|
||||
return ((datum.data.value / sum) * 100).toFixed(0) + '%';
|
||||
})
|
||||
.attr('transform', (datum: d3.PieArcDatum<D3Sections>): string => {
|
||||
return 'translate(' + labelArcGenerator.centroid(datum) + ')';
|
||||
})
|
||||
.style('text-anchor', 'middle')
|
||||
.attr('class', 'slice');
|
||||
|
||||
group
|
||||
.append('text')
|
||||
.text(db.getDiagramTitle())
|
||||
.attr('x', 0)
|
||||
.attr('y', -(height - 50) / 2)
|
||||
.attr('class', 'pieTitleText');
|
||||
|
||||
// Add the legends/annotations for each section
|
||||
const legend = group
|
||||
.selectAll('.legend')
|
||||
.data(color.domain())
|
||||
.enter()
|
||||
.append('g')
|
||||
.attr('class', 'legend')
|
||||
.attr('transform', (_datum, index: number): string => {
|
||||
const height = LEGEND_RECT_SIZE + LEGEND_SPACING;
|
||||
const offset = (height * color.domain().length) / 2;
|
||||
const horizontal = 12 * LEGEND_RECT_SIZE;
|
||||
const vertical = index * height - offset;
|
||||
return 'translate(' + horizontal + ',' + vertical + ')';
|
||||
});
|
||||
|
||||
legend
|
||||
.append('rect')
|
||||
.attr('width', LEGEND_RECT_SIZE)
|
||||
.attr('height', LEGEND_RECT_SIZE)
|
||||
.style('fill', color)
|
||||
.style('stroke', color);
|
||||
|
||||
legend
|
||||
.data(arcs)
|
||||
.append('text')
|
||||
.attr('x', LEGEND_RECT_SIZE + LEGEND_SPACING)
|
||||
.attr('y', LEGEND_RECT_SIZE - LEGEND_SPACING)
|
||||
.text((datum: d3.PieArcDatum<D3Sections>): string => {
|
||||
const { label, value } = datum.data;
|
||||
if (db.getShowData()) {
|
||||
return `${label} [${value}]`;
|
||||
}
|
||||
return label;
|
||||
});
|
||||
};
|
||||
|
||||
export const renderer = { draw };
|
||||
@@ -1,4 +1,7 @@
|
||||
const getStyles = (options) =>
|
||||
import type { DiagramStylesProvider } from '../../diagram-api/types.js';
|
||||
import type { PieStyleOptions } from './pieTypes.js';
|
||||
|
||||
const getStyles: DiagramStylesProvider = (options: PieStyleOptions) =>
|
||||
`
|
||||
.pieCircle{
|
||||
stroke: ${options.pieStrokeColor};
|
||||
64
packages/mermaid/src/diagrams/pie/pieTypes.ts
Normal file
64
packages/mermaid/src/diagrams/pie/pieTypes.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import type { PieDiagramConfig } from '../../config.type.js';
|
||||
import type { DiagramDB, ParseDirectiveDefinition } from '../../diagram-api/types.js';
|
||||
|
||||
export interface PieFields {
|
||||
sections: Sections;
|
||||
showData: boolean;
|
||||
config: PieDiagramConfig;
|
||||
}
|
||||
|
||||
export interface PieStyleOptions {
|
||||
fontFamily: string;
|
||||
pie1: string;
|
||||
pie2: string;
|
||||
pie3: string;
|
||||
pie4: string;
|
||||
pie5: string;
|
||||
pie6: string;
|
||||
pie7: string;
|
||||
pie8: string;
|
||||
pie9: string;
|
||||
pie10: string;
|
||||
pie11: string;
|
||||
pie12: string;
|
||||
pieTitleTextSize: string;
|
||||
pieTitleTextColor: string;
|
||||
pieSectionTextSize: string;
|
||||
pieSectionTextColor: string;
|
||||
pieLegendTextSize: string;
|
||||
pieLegendTextColor: string;
|
||||
pieStrokeColor: string;
|
||||
pieStrokeWidth: string;
|
||||
pieOuterStrokeWidth: string;
|
||||
pieOuterStrokeColor: string;
|
||||
pieOpacity: string;
|
||||
}
|
||||
|
||||
export type Sections = Record<string, number>;
|
||||
|
||||
export interface D3Sections {
|
||||
label: string;
|
||||
value: number;
|
||||
}
|
||||
|
||||
export interface PieDB extends DiagramDB {
|
||||
// config
|
||||
getConfig: () => Required<PieDiagramConfig>;
|
||||
|
||||
// common db
|
||||
parseDirective: ParseDirectiveDefinition;
|
||||
clear: () => void;
|
||||
setDiagramTitle: (title: string) => void;
|
||||
getDiagramTitle: () => string;
|
||||
setAccTitle: (title: string) => void;
|
||||
getAccTitle: () => string;
|
||||
setAccDescription: (describetion: string) => void;
|
||||
getAccDescription: () => string;
|
||||
|
||||
// diagram db
|
||||
addSection: (label: string, value: number) => void;
|
||||
getSections: () => Sections;
|
||||
cleanupValue: (value: string) => number;
|
||||
setShowData: (toggle: boolean) => void;
|
||||
getShowData: () => boolean;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// @ts-ignore: TODO Fix ts errors
|
||||
// @ts-ignore: JISON doesn't support types
|
||||
import { parser } from './quadrant.jison';
|
||||
import { Mock, vi } from 'vitest';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DiagramDefinition } from '../../diagram-api/types.js';
|
||||
// @ts-ignore: TODO Fix ts errors
|
||||
// @ts-ignore: JISON doesn't support types
|
||||
import parser from './parser/quadrant.jison';
|
||||
import db from './quadrantDb.js';
|
||||
import renderer from './quadrantRenderer.js';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DiagramDefinition } from '../../diagram-api/types.js';
|
||||
// @ts-ignore: TODO Fix ts errors
|
||||
// @ts-ignore: JISON doesn't support types
|
||||
import parser from './parser/requirementDiagram.jison';
|
||||
import db from './requirementDb.js';
|
||||
import styles from './styles.js';
|
||||
|
||||
@@ -172,14 +172,11 @@ describe('more than one sequence diagram', () => {
|
||||
|
||||
describe('when parsing a sequenceDiagram', function () {
|
||||
beforeEach(function () {
|
||||
// diagram.db = sequenceDb;
|
||||
// diagram.db.clear();
|
||||
diagram = new Diagram(`
|
||||
sequenceDiagram
|
||||
Alice->Bob:Hello Bob, how are you?
|
||||
Note right of Bob: Bob thinks
|
||||
Bob-->Alice: I am good thanks!`);
|
||||
diagram.db.clear();
|
||||
});
|
||||
it('should handle a sequenceDiagram definition', async function () {
|
||||
const str = `
|
||||
@@ -1482,8 +1479,6 @@ describe('when checking the bounds in a sequenceDiagram', function () {
|
||||
let conf;
|
||||
beforeEach(function () {
|
||||
mermaidAPI.reset();
|
||||
// diagram.db = sequenceDb;
|
||||
// diagram.db.clear();
|
||||
diagram.renderer.bounds.init();
|
||||
conf = diagram.db.getConfig();
|
||||
});
|
||||
@@ -1635,7 +1630,6 @@ sequenceDiagram
|
||||
Alice->Bob:Hello Bob, how are you?
|
||||
Note right of Bob: Bob thinks
|
||||
Bob-->Alice: I am good thanks!`);
|
||||
diagram.db.clear();
|
||||
});
|
||||
['tspan', 'fo', 'old', undefined].forEach(function (textPlacement) {
|
||||
it(`
|
||||
@@ -2009,8 +2003,6 @@ describe('when rendering a sequenceDiagram with actor mirror activated', () => {
|
||||
let conf;
|
||||
beforeEach(function () {
|
||||
mermaidAPI.reset();
|
||||
// diagram.db = sequenceDb;
|
||||
diagram.db.clear();
|
||||
conf = diagram.db.getConfig();
|
||||
diagram.renderer.bounds.init();
|
||||
});
|
||||
@@ -2052,12 +2044,8 @@ describe('when rendering a sequenceDiagram with directives', () => {
|
||||
mermaidAPI.initialize({ sequence: conf });
|
||||
});
|
||||
|
||||
let conf;
|
||||
beforeEach(function () {
|
||||
mermaidAPI.reset();
|
||||
// diagram.db = sequenceDb;
|
||||
diagram.db.clear();
|
||||
conf = diagram.db.getConfig();
|
||||
diagram.renderer.bounds.init();
|
||||
});
|
||||
|
||||
@@ -2069,10 +2057,7 @@ sequenceDiagram
|
||||
participant Alice
|
||||
`;
|
||||
diagram = new Diagram(str);
|
||||
|
||||
diagram.renderer.bounds.init();
|
||||
await mermaidAPI.parse(str);
|
||||
|
||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
|
||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||
@@ -2093,7 +2078,7 @@ sequenceDiagram
|
||||
participant Alice
|
||||
`;
|
||||
|
||||
diagram.parse(str);
|
||||
diagram = new Diagram(str);
|
||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
|
||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||
@@ -2114,7 +2099,7 @@ Alice->Bob:Hello Bob, how are you?
|
||||
Note right of Bob: Bob thinks
|
||||
Bob-->Alice: I am good thanks!`;
|
||||
|
||||
await mermaidAPI.parse(str1);
|
||||
diagram = new Diagram(str1);
|
||||
diagram.renderer.draw(str1, 'tst', '1.2.3', diagram); // needs to be rendered for the correct value of visibility auto numbers
|
||||
expect(diagram.db.showSequenceNumbers()).toBe(true);
|
||||
|
||||
@@ -2124,7 +2109,7 @@ Alice->Bob:Hello Bob, how are you?
|
||||
Note right of Bob: Bob thinks
|
||||
Bob-->Alice: I am good thanks!`;
|
||||
|
||||
await mermaidAPI.parse(str2);
|
||||
diagram = new Diagram(str2);
|
||||
diagram.renderer.draw(str2, 'tst', '1.2.3', diagram);
|
||||
expect(diagram.db.showSequenceNumbers()).toBe(false);
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user