Compare commits

...

138 Commits

Author SHA1 Message Date
Knut Sveidqvist
b70959daa7 Version 10.5.1 2023-10-20 14:30:52 +02:00
Per Brolin
aaf0b474d4 Increased version to 10.5.1 2023-10-20 14:18:07 +02:00
Knut Sveidqvist
61bc293a95 #4967 Reverting optimimization that breaks subgraphs 2023-10-20 11:12:56 +02:00
Sidharth Vinod
d1b386b5c9 Revert PH changes (#4903) 2023-10-11 11:32:24 +05:30
sidharthv96
22b783e31d Update docs 2023-10-04 09:01:57 +00:00
Sidharth Vinod
345d0d0bd2 Fix lint issue 2023-10-04 14:26:03 +05:30
Sidharth Vinod
f49e8c5784 Fix release version 2023-10-04 14:19:19 +05:30
Sidharth Vinod
a6bb1ea28c Fix TopBar 2023-10-04 14:09:34 +05:30
Sidharth Vinod
cda7a61526 Add MC to integrations 2023-10-04 14:07:08 +05:30
Sidharth Vinod
d17c752229 Add TopBar 2023-10-04 14:06:43 +05:30
Steph
459f5a58a3 Docs: Add Product Hunt info (#4900)
* update card and announcements page

* update integrations link and add product hunt badge

* Revert navbar link change

---------

Co-authored-by: Sidharth Vinod <sidharthv96@gmail.com>
2023-10-04 07:32:30 +00:00
knsv
b83fff2fa0 Update docs 2023-10-02 08:24:42 +00:00
Knut Sveidqvist
e6bbc71f86 Merge branch 'release/10.5.0' 2023-10-02 10:19:52 +02:00
Knut Sveidqvist
9644df4021 Merge branch 'release/10.5.0' 2023-10-02 10:18:33 +02:00
Knut Sveidqvist
bb0d549d0d Mermaid release v10.5.0 2023-10-02 10:00:13 +02:00
Knut Sveidqvist
47acc1e423 Fix for issue with backticks in ids in classDiagrams 2023-09-29 13:38:00 +02:00
Knut Sveidqvist
7ad2ca7b0a Merge pull request #4871 from mermaid-js/add-git-graph-blog
Docs: update Latest News section (Git Graph blog post)
2023-09-29 10:39:27 +02:00
steph
f1895ac599 more fixes 2023-09-28 23:52:53 -07:00
steph
564cfbee5b fix typo 2023-09-28 23:46:32 -07:00
steph
2cb1a88913 more link fixes 2023-09-28 23:37:59 -07:00
steph
874ccfb5a7 fix links 2023-09-28 23:28:33 -07:00
Sidharth Vinod
f96d351fdc fix: Sequence loop rendering 2023-09-28 13:09:56 +05:30
Sidharth Vinod
ee58743a04 fix: Use log instead of console 2023-09-26 19:24:39 +05:30
Sidharth Vinod
63f4a56ec5 chore: Add test for gantt rendering 2023-09-26 19:21:14 +05:30
Sidharth Vinod
7cb1c2e0aa fix(gantt): Set max exclude interval length to 5 years 2023-09-26 19:21:05 +05:30
Sidharth Vinod
5f5b216428 fix: Performance issue in Gantt diagram 2023-09-26 19:15:44 +05:30
Sidharth Vinod
a3456ec933 fix: Sequence diagram loop rendering 2023-09-26 18:36:05 +05:30
Steph
a0e9b0c96d add git graph blog post 2023-09-22 10:09:05 -07:00
Knut Sveidqvist
fbe8d1085c Merge pull request #4817 from mermaid-js/update-ecosystem-section
Docs: update Ecosystem section
2023-09-22 08:45:34 +02:00
Sidharth Vinod
ad596086ea 10.5.0-rc.2 2023-09-15 01:24:02 +05:30
Knut Sveidqvist
9d1c109612 Merge branch 'develop' into release/10.5.0 2023-09-14 11:59:57 +02:00
Sidharth Vinod
4201e4775d fix: Arrow markers in flowchart-elk 2023-09-14 14:52:39 +05:30
Sidharth Vinod
46a5b9543e Bump version 2023-09-14 14:06:15 +05:30
Knut Sveidqvist
d539f7050f Merge pull request #4825 from chadfawcett/bug/1871_fix_duplicate_marker_ids
Give markers unique id's per graph
2023-09-14 08:03:01 +00:00
Sidharth Vinod
983cf45b2f Merge branch 'develop' into pr/chadfawcett/4825
* develop:
  chore: Fix type in 'getLineFunctionsWithOffset'
  refactor: Add getLineFunctionsWithOffset function
  refactor: Move EdgeData to types
  fix: PointStart marker refX
  chore(deps): update all patch dependencies
  refactor: Fix typings in utils.ts
  chore: Add @internal to createCSSStyles
  chore: Bump version
  refactor: Remove unused variables
  fix: #4818 support `getClasses` in external diagrams.
2023-09-14 13:06:35 +05:30
Sidharth Vinod
ce78ff116e chore: Fix type in 'getLineFunctionsWithOffset' 2023-09-14 13:05:21 +05:30
Knut Sveidqvist
ed9159cece Merge pull request #4830 from mermaid-js/fix/flowchartElkArrow
Fix: flowchartElk Arrow overlap
2023-09-14 06:41:56 +00:00
Sidharth Vinod
f208631a16 Merge branch 'develop' into fix/flowchartElkArrow 2023-09-13 22:30:57 +05:30
Sidharth Vinod
6219aa55f7 Merge pull request #4819 from mermaid-js/sidv/4818_supportClassDefsExternally
Support ClassDefs in external diagrams
2023-09-13 16:16:32 +00:00
Sidharth Vinod
feeb0073f2 Merge branch 'develop' into sidv/4818_supportClassDefsExternally 2023-09-13 19:49:31 +05:30
Knut Sveidqvist
d70c8cdb4c Merge pull request #4826 from mermaid-js/sidv/cleanupUtils
refactor: Fix typings in utils.ts
2023-09-13 07:36:55 +00:00
Chad Fawcett
bceae92d30 Update cypress/platform/marker_unique_id.html
Co-authored-by: Sidharth Vinod <sidharthv96@gmail.com>
2023-09-12 10:29:39 -07:00
Sidharth Vinod
3c34fbaacd refactor: Add getLineFunctionsWithOffset function 2023-09-12 14:08:02 +05:30
Sidharth Vinod
78346943a2 refactor: Move EdgeData to types 2023-09-12 14:06:53 +05:30
Sidharth Vinod
6b7a0e1d8e fix: PointStart marker refX 2023-09-12 14:04:54 +05:30
Chad Fawcett
924c9e913b Added cypress test 2023-09-11 11:59:28 -07:00
Sidharth Vinod
e561cbd3be Merge pull request #4828 from mermaid-js/renovate/patch-all-patch
chore(deps): update all patch dependencies (patch)
2023-09-11 14:48:52 +00:00
renovate[bot]
11aaee043f chore(deps): update all patch dependencies 2023-09-11 13:44:53 +00:00
Steph
3824daea0b add blog post (#4822) 2023-09-09 05:49:28 +00:00
Sidharth Vinod
f3e0d5a20a refactor: Fix typings in utils.ts 2023-09-09 11:15:55 +05:30
Chad Fawcett
552d3ec2e1 Give markers unique id's per graph 2023-09-08 15:28:41 -07:00
Steph
8a0ca8c43c update redirect 2023-09-08 10:28:50 -07:00
Sidharth Vinod
6eb3337d20 chore: Add @internal to createCSSStyles 2023-09-08 08:20:03 +05:30
Steph
809103ccdd add link to redirect file 2023-09-07 11:45:24 -07:00
Sidharth Vinod
00d06c7282 Merge pull request #4780 from mermaid-js/jgreywolf-patch-1
Update README.md
2023-09-07 18:13:01 +00:00
Sidharth Vinod
e75af86ef2 chore: Bump version 2023-09-07 12:46:58 +05:30
Sidharth Vinod
5ff308f0c6 Merge branch 'develop' into sidv/4818_supportClassDefsExternally
* develop:
  Remove unnecessary tests
  chore: Add deprecation notices, improve types
  chore: Cleanup gitGraph tests
  refactor: Move setWrap to individual diagrams as necessary.
  refactor: Remove directives from grammar
  refactor: Update DBs to remove directive handling
  refactor: Move directive processing before parsing
2023-09-07 12:43:18 +05:30
Sidharth Vinod
ef95919691 refactor: Remove unused variables 2023-09-07 12:40:56 +05:30
Knut Sveidqvist
07f460a184 Merge pull request #4759 from sidharthv96/sidv/removeDirectiveGrammar
Add directive support to all diagrams by preprocessing
2023-09-07 07:00:44 +00:00
Sidharth Vinod
38123d2f21 fix: #4818 support getClasses in external diagrams. 2023-09-07 12:01:22 +05:30
Steph
6155b2cc38 update content 2023-09-06 22:04:35 -07:00
Sidharth Vinod
667b17bf66 Remove unnecessary tests 2023-09-07 10:15:58 +05:30
Sidharth Vinod
6fa97a8b71 Merge branch 'pr/RohanHandore/4340' into develop
* pr/RohanHandore/4340:
  Remove optional chaining
2023-09-07 08:33:20 +05:30
Sidharth Vinod
56aa503ded Remove optional chaining 2023-09-07 08:31:05 +05:30
Sidharth Vinod
4f2902e0a8 Merge pull request #4340 from RohanHandore/patch-1
Refactor `cypress/helpers/util.ts`
2023-09-06 18:00:13 +00:00
Sidharth Vinod
4e142c438e Merge branch 'develop' into pr/RohanHandore/4340
* develop:
  chore: Update docs
2023-09-06 23:25:31 +05:30
Sidharth Vinod
ba66b219e2 chore: Update docs 2023-09-06 23:23:48 +05:30
Sidharth Vinod
f0d388851b Merge branch 'develop' into pr/RohanHandore/4340
* develop:
  Update flowchart.md (#4798)
  Update flowchart.md (#4792)
  chore: Fix warning formatting
  docs: Disable showValues in sankey example
2023-09-06 23:22:45 +05:30
Sidharth Vinod
7d2b39f69f refactor: Use || instead of ?? 2023-09-06 23:21:51 +05:30
Sidharth Vinod
19f0308142 Merge branch 'develop' of https://github.com/mermaid-js/mermaid into develop
* 'develop' of https://github.com/mermaid-js/mermaid:
  Update flowchart.md (#4792)
2023-09-06 23:15:41 +05:30
Sidharth Vinod
c12deca02a Merge branch 'master' into develop
* master:
  Update flowchart.md (#4798)
  chore: Fix warning formatting
  docs: Disable showValues in sankey example
2023-09-06 23:15:25 +05:30
Douglas Blank
2eb9afa2f2 Update flowchart.md (#4798)
* Update flowchart.md

Fixed typos

* docs: Fix proper doc

* docs: Fix npmjs link

---------

Co-authored-by: Sidharth Vinod <sidharthv96@gmail.com>
2023-09-06 17:44:35 +00:00
Oscar Andersson
53dee70292 Update flowchart.md (#4792)
* Update flowchart.md

Update from mermaid 8.4.8 to 10.4.0 for jsfiddle

* Update flowchart.md

* Update flowchart.md
2023-09-06 17:34:20 +00:00
Sidharth Vinod
8662b33de9 core: Adapt changes from 3f7bafb2d7
Co-authored-by: RohanHandore <110839432+RohanHandore@users.noreply.github.com>
2023-09-06 22:50:32 +05:30
Sidharth Vinod
bf18f25c82 Merge branch 'develop' into pr/RohanHandore/4340
* develop: (864 commits)
  chore: Update docs
  chore: Fix unit tests
  chore(deps): update all patch dependencies
  chore: Update docs
  Update docs
  New Mermaid Live Editor for Confluence Cloud (#4814)
  Update link to Discourse theme component (#4811)
  Update flowchart.md (#4810)
  chore: remove unneeded `CommomDB`
  chore: Update docs
  "CSS" instead of "css" in flowchart.md (#4797)
  Update CONTRIBUTING.md
  Update CONTRIBUTING.md
  fix: typos (#4801)
  chore: Align with convention
  fix: Add support for `~test Array~string~`
  chore: Add JSDoc to apply in sequenceDB
  refactor: Tidy up direction handling
  chore: Fix flowchart arrow
  chore: Add test to verify activate
  ...
2023-09-06 22:49:35 +05:30
Sidharth Vinod
fd39ea8049 Update cypress/helpers/util.js 2023-09-06 17:07:08 +00:00
Sidharth Vinod
773abbe7e2 Merge pull request #4701 from jonashaag/jonashaag-patch-1
Use utf8 encoding in Jupyter example
2023-09-06 16:44:06 +00:00
Sidharth Vinod
bcd03151e8 chore: Update docs 2023-09-06 22:09:15 +05:30
Sidharth Vinod
18079a44be chore: Add deprecation notices, improve types
Co-authored-by: Alois Klink <alois@aloisklink.com>
2023-09-06 22:06:05 +05:30
Sidharth Vinod
32729f82b9 Merge branch 'develop' into sidv/removeDirectiveGrammar
* develop: (26 commits)
  chore: Fix unit tests
  chore(deps): update all patch dependencies
  Update docs
  chore: remove unneeded `CommomDB`
  fix: Add support for `~test Array~string~`
  chore: Add JSDoc to apply in sequenceDB
  refactor: Tidy up direction handling
  chore: Fix flowchart arrow
  chore: Add test to verify activate
  chore: Update tests snapshot
  fix: #4691 Align arrowheads properly in sequenceDiagram
  chore: move `commonDb` into `diagrams/common/commonDb`
  Update docs
  run prettier fix
  Apply suggestions from code review
  chore: Add comments in edge handling
  chore: Make aggregation arrow transparent
  chore: Remove structuredClone
  chore: Make extension arrow transparent
  chore: Align edge markers properly in class
  ...
2023-09-06 18:36:02 +05:30
Sidharth Vinod
c212490248 Merge pull request #4778 from vertxxyz/feature/4771_tickInterval-support-for-sec-and-ms
Added support for millisecond and second to gantt tickInterval
2023-09-06 13:03:05 +00:00
Knut Sveidqvist
abcf2a2eab Merge pull request #4805 from mermaid-js/sidv/FixTilde
fix: Add support for `~test Array~string~` back in Class
2023-09-06 02:26:29 +00:00
Sidharth Vinod
a0e33bee71 chore: Cleanup gitGraph tests 2023-09-05 21:57:05 +05:30
Sidharth Vinod
ebaabbf19a Merge pull request #4786 from mermaid-js/sidv/2685_supportMember
Support member definition to initialize class
2023-09-05 16:16:15 +00:00
Sidharth Vinod
fd7406b94a chore: Fix unit tests 2023-09-05 21:41:49 +05:30
Sidharth Vinod
e5e44dbd39 Merge pull request #4802 from Yokozuna59/move-commondb-to-common-dir
chore: move `commonDb` into  `diagrams/common/commonDb`
2023-09-05 14:19:54 +00:00
Sidharth Vinod
7cc76505a8 Merge branch 'develop' into move-commondb-to-common-dir 2023-09-05 14:15:09 +00:00
Sidharth Vinod
c38478f6c3 Merge branch 'develop' into sidv/2685_supportMember 2023-09-05 13:58:53 +00:00
Knut Sveidqvist
3cf7649682 Merge pull request #4788 from mermaid-js/sidv/classEdgeMarkers
fix: Allow hollow markers on edges
2023-09-05 12:47:22 +00:00
Knut Sveidqvist
12d4386fd3 Merge pull request #4804 from mermaid-js/fix/4691_sequenceArrowHeads
Align arrows on sequence diagram
2023-09-05 12:40:49 +00:00
Sidharth Vinod
4f0abab91f Merge pull request #4809 from mermaid-js/renovate/patch-all-patch
chore(deps): update all patch dependencies (patch)
2023-09-05 11:47:44 +00:00
renovate[bot]
ce7783414b chore(deps): update all patch dependencies 2023-09-05 11:23:25 +00:00
Sidharth Vinod
1503306651 Merge pull request #4776 from tomperr/fix/4775_allow-leading-underscore-entity-name
fix(er): allow underscore as leading char
2023-09-05 11:16:57 +00:00
Sidharth Vinod
88d1885cdc Merge branch 'develop' into fix/4775_allow-leading-underscore-entity-name 2023-09-05 11:04:52 +00:00
Sidharth Vinod
4ab47a2ce7 Merge pull request #4781 from mermaid-js/jgreywolf-patch-2
Update classDiagram.md
2023-09-05 11:03:53 +00:00
Sidharth Vinod
5ed155508a Merge branch 'develop' into sidv/removeDirectiveGrammar
* develop: (31 commits)
  chore: Update docs
  New Mermaid Live Editor for Confluence Cloud (#4814)
  Update link to Discourse theme component (#4811)
  Update flowchart.md (#4810)
  chore: Update docs
  "CSS" instead of "css" in flowchart.md (#4797)
  Update CONTRIBUTING.md
  Update CONTRIBUTING.md
  fix: typos (#4801)
  chore: Align with convention
  add additional test case
  chore(deps): update all patch dependencies
  chore(deps): update all minor dependencies
  added test case
  add sanitize text
  Update docs
  modifications to generic parser
  improvements to parseGenericTypes
  Update packages/mermaid/src/diagrams/class/svgDraw.js
  return comment
  ...
2023-09-05 14:33:40 +05:30
Sidharth Vinod
8f3bcf401d Merge branch 'develop' into move-commondb-to-common-dir 2023-09-05 09:00:16 +00:00
Sidharth Vinod
3959e076cf chore: Update docs 2023-09-05 14:28:21 +05:30
sidharthv96
bc770c48c7 Update docs 2023-09-05 05:52:03 +00:00
Sidharth Vinod
82404dd0f6 Merge branch 'develop' into move-commondb-to-common-dir 2023-09-05 05:47:11 +00:00
Reda Al Sulais
bdaf58a322 chore: remove unneeded CommomDB 2023-09-03 17:31:08 +03:00
Sidharth Vinod
49446940f5 Merge branch 'develop' into sidv/FixTilde
* develop:
  chore: Align with convention
  chore(deps): update all patch dependencies
  chore(deps): update all minor dependencies
2023-09-03 12:27:22 +05:30
Sidharth Vinod
650d712bd8 fix: Add support for ~test Array~string~ 2023-09-03 11:55:32 +05:30
Sidharth Vinod
d61f9fbe62 Merge branch 'develop' into jgreywolf-patch-2 2023-09-03 05:31:14 +00:00
Sidharth Vinod
be3829232c chore: Add JSDoc to apply in sequenceDB
Co-authored-by: Alois Klink <alois@aloisklink.com>
2023-09-03 10:54:18 +05:30
Sidharth Vinod
20fd6d35f0 refactor: Tidy up direction handling 2023-09-03 10:46:26 +05:30
Sidharth Vinod
5b724b180f chore: Fix flowchart arrow 2023-09-03 03:02:58 +05:30
Sidharth Vinod
784e235ff9 chore: Add test to verify activate 2023-09-03 02:34:46 +05:30
Sidharth Vinod
02a0596e3c chore: Update tests snapshot 2023-09-03 02:33:06 +05:30
Sidharth Vinod
498f75eece fix: #4691 Align arrowheads properly in sequenceDiagram 2023-09-03 02:32:28 +05:30
Reda Al Sulais
a61887f5a2 chore: move commonDb into diagrams/common/commonDb 2023-09-02 17:51:33 +03:00
jgreywolf
44a6a6758e Update docs 2023-09-01 13:16:16 +00:00
Justin Greywolf
a12864d9e4 run prettier fix 2023-09-01 06:10:41 -07:00
Sidharth Vinod
bbbae7d59f Apply suggestions from code review
Co-authored-by: Alois Klink <alois@aloisklink.com>
2023-08-30 05:19:12 +00:00
Sidharth Vinod
e84ee32408 Merge branch 'develop' into feature/4771_tickInterval-support-for-sec-and-ms 2023-08-29 18:38:44 +00:00
Sidharth Vinod
cca9662365 chore: Add comments in edge handling 2023-08-29 23:49:00 +05:30
Sidharth Vinod
f30a23f41e chore: Make aggregation arrow transparent 2023-08-29 23:35:21 +05:30
Sidharth Vinod
58cb827839 chore: Remove structuredClone 2023-08-29 23:32:10 +05:30
Sidharth Vinod
7de1abbcc2 chore: Make extension arrow transparent 2023-08-29 23:28:45 +05:30
Sidharth Vinod
26e32665a4 chore: Align edge markers properly in class 2023-08-29 23:18:55 +05:30
Sidharth Vinod
c3df18fc5b feat: #2685 Support class member initialization by member definition 2023-08-28 18:57:34 +05:30
Sidharth Vinod
eca0cea339 refactor: Fir classDB types 2023-08-28 18:54:30 +05:30
Justin Greywolf
efa6cb3434 Update classDiagram.md 2023-08-28 06:20:49 -07:00
Thomas Ingram
4efac6721d Added missing integration tests and release version in docs. 2023-08-28 20:17:15 +10:00
Sidharth Vinod
02517e8a7d chore: Fix warning formatting 2023-08-28 12:31:40 +05:30
Sidharth Vinod
f6325f6906 docs: Disable showValues in sankey example 2023-08-28 12:30:39 +05:30
Justin Greywolf
6aa20ed64b Update classDiagram.md 2023-08-27 16:22:38 -07:00
Justin Greywolf
75737544c8 Update classDiagram.md 2023-08-27 15:39:07 -07:00
Justin Greywolf
b6d5497f51 Update README.md
Quick fix to remove namespace from readme class definition since github has not been update to the version that uses namespaces
2023-08-27 15:23:59 -07:00
Thomas Ingram
8f340094d9 Added support for millisecond and second to gantt tickInterval 2023-08-27 16:08:47 +10:00
Tom PERRILLAT-COLLOMB
264f7920f0 fix(er): allow underscore as leading char 2023-08-27 00:15:50 +02:00
Sidharth Vinod
066e0967de refactor: Move setWrap to individual diagrams as necessary. 2023-08-26 23:27:25 +05:30
Sidharth Vinod
04ce5d6a9a Merge branch 'develop' into sidv/removeDirectiveGrammar
* develop: (22 commits)
  docs: Fix link
  Update docs
  fix(pie): align slices and legend orders
  Mermaid version v10.4.0
  unique batches every time, if not repeated tests end up in the same batch
  Added missed .md
  Increase JS heap
  More tests for redirects + prettier
  Fixed redirects inside vitepress, extended tests
  chore: Explain redirect.ts clearly
  Reverted docker compose to develop branch
  Run GA
  Update docs
  Update docs
  fix(er): bug if relationship is declared first
  update latest news
  Removed all n00b file names and added redirects
  test(er): add cypress test on entity name alias
  feat(er): use square brackets to add aliases
  docs(er): add release version for entity name aliases
  ...
2023-08-26 23:08:36 +05:30
Sidharth Vinod
1e0918c2ff refactor: Remove directives from grammar 2023-08-25 12:55:58 +05:30
Sidharth Vinod
f0883be0e3 refactor: Update DBs to remove directive handling 2023-08-25 12:55:35 +05:30
Sidharth Vinod
276fd7ad84 refactor: Move directive processing before parsing
Directives and fronmatter will be preprocessed
and removed from the text before parsing.
2023-08-25 12:54:44 +05:30
Jonas Haag
d2ed52461e Use utf8 encoding in Jupyter example
mermaid.ink can render some UTF-8 characters
2023-08-06 13:44:09 +02:00
RohanHandore
3f7bafb2d7 I refactored the code to improve its time complexity by removing unnecessary code and optimizing the existing code.
Here are the changes I made:

Removed unnecessary variables and assignments
Removed unnecessary object property assignments
Removed redundant code by consolidating similar conditionals
Removed unused parameters and default values
Simplified some conditionals with boolean expressions
Removed unused variables and imports
Extracted common code into a reusable function
By optimizing the code in these ways, we can reduce the time complexity of the code and improve its performance.
2023-04-24 12:14:40 +05:30
154 changed files with 2286 additions and 1861 deletions

View File

@@ -56,9 +56,9 @@ Mermaid addresses this problem by enabling users to create easily modifiable dia
Mermaid allows even non-programmers to easily create detailed diagrams through the [Mermaid Live Editor](https://mermaid.live/).<br/>
[Tutorials](./docs/config/Tutorials.md) has video tutorials.
Use Mermaid with your favorite applications, check out the list of [Integrations and Usages of Mermaid](./docs/ecosystem/integrations.md).
Use Mermaid with your favorite applications, check out the list of [Integrations and Usages of Mermaid](./docs/ecosystem/integrations-community.md).
You can also use Mermaid within [GitHub](https://github.blog/2022-02-14-include-diagrams-markdown-files-mermaid/) as well many of your other favorite applications—check out the list of [Integrations and Usages of Mermaid](./docs/ecosystem/integrations.md).
You can also use Mermaid within [GitHub](https://github.blog/2022-02-14-include-diagrams-markdown-files-mermaid/) as well many of your other favorite applications—check out the list of [Integrations and Usages of Mermaid](./docs/ecosystem/integrations-community.md).
For a more detailed introduction to Mermaid and some of its more basic uses, look to the [Beginner's Guide](./docs/intro/getting-started.md), [Usage](./docs/config/usage.md) and [Tutorials](./docs/config/Tutorials.md).
@@ -165,13 +165,7 @@ class Class10 {
int id
size()
}
namespace Namespace01 {
class Class11
class Class12 {
int id
size()
}
}
```
```mermaid
@@ -191,13 +185,7 @@ class Class10 {
int id
size()
}
namespace Namespace01 {
class Class11
class Class12 {
int id
size()
}
}
```
### State diagram [<a href="https://mermaid-js.github.io/mermaid/#/stateDiagram">docs</a> - <a href="https://mermaid.live/edit#pako:eNpdkEFvgzAMhf8K8nEqpYSNthx22Xbcqcexg0sCiZQQlDhIFeK_L8A6TfXp6fOz9ewJGssFVOAJSbwr7ByadGR1n8T6evpO0vQ1uZDSekOrXGFsPqJPO6q-2-imH8f_0TeHXm50lfelsAMjnEHFY6xpMdRAUhhRQxUlFy0GTTXU_RytYeAx-AdXZB1ULWovdoCB7OXWN1CRC-Ju-r3uz6UtchGHJqDbsPygU57iysb2reoWHpyOWBINvsqypb3vFMlw3TfWZF5xiY7keC6zkpUnZIUojwW-FAVvrvn51LLnvOXHQ84Q5nn-AVtLcwk">live editor</a>]

View File

@@ -53,7 +53,7 @@ Mermaid 是一个基于 Javascript 的图表绘制工具,通过解析类 Markd
Mermaid 通过允许用户创建便于修改的图表来解决这一难题,它也可以作为生产脚本(或其他代码)的一部分。<br/>
<br/>
Mermaid 甚至能让非程序员也能通过 [Mermaid Live Editor](https://mermaid.live/) 轻松创建详细的图表。<br/>
你可以访问 [教程](./docs/config/Tutorials.md) 来查看 Live Editor 的视频教程,也可以查看 [Mermaid 的集成和使用](./docs/ecosystem/integrations.md) 这个清单来检查你的文档工具是否已经集成了 Mermaid 支持。
你可以访问 [教程](./docs/config/Tutorials.md) 来查看 Live Editor 的视频教程,也可以查看 [Mermaid 的集成和使用](./docs/ecosystem/integrations-community.md) 这个清单来检查你的文档工具是否已经集成了 Mermaid 支持。
如果想要查看关于 Mermaid 更详细的介绍及基础使用方式,可以查看 [入门指引](./docs/intro/getting-started.md), [用法](./docs/config/usage.md) 和 [教程](./docs/config/Tutorials.md).

View File

@@ -22,6 +22,7 @@
"brkt",
"brolin",
"brotli",
"catmull",
"città",
"classdef",
"codedoc",

View File

@@ -52,29 +52,21 @@ export const imgSnapshotTest = (
api = false,
validation?: any
): void => {
cy.log(JSON.stringify(_options));
const options: CypressMermaidConfig = Object.assign(_options);
if (!options.fontFamily) {
options.fontFamily = 'courier';
}
if (!options.sequence) {
options.sequence = {};
}
if (!options.sequence || (options.sequence && !options.sequence.actorFontFamily)) {
options.sequence.actorFontFamily = 'courier';
}
if (options.sequence && !options.sequence.noteFontFamily) {
options.sequence.noteFontFamily = 'courier';
}
options.sequence.actorFontFamily = 'courier';
options.sequence.noteFontFamily = 'courier';
options.sequence.messageFontFamily = 'courier';
if (options.sequence && !options.sequence.actorFontFamily) {
options.sequence.actorFontFamily = 'courier';
}
if (!options.fontSize) {
options.fontSize = 16;
}
const options: CypressMermaidConfig = {
..._options,
fontFamily: _options.fontFamily || 'courier',
// @ts-ignore TODO: Fix type of fontSize
fontSize: _options.fontSize || '16px',
sequence: {
...(_options.sequence || {}),
actorFontFamily: 'courier',
noteFontFamily:
_options.sequence && _options.sequence.noteFontFamily
? _options.sequence.noteFontFamily
: 'courier',
messageFontFamily: 'courier',
},
};
const url: string = mermaidUrl(graphStr, options, api);
openURLAndVerifyRendering(url, options, validation);
@@ -82,11 +74,10 @@ export const imgSnapshotTest = (
export const urlSnapshotTest = (
url: string,
_options: CypressMermaidConfig,
options: CypressMermaidConfig,
_api = false,
validation?: any
): void => {
const options: CypressMermaidConfig = Object.assign(_options);
openURLAndVerifyRendering(url, options, validation);
};

View File

@@ -132,4 +132,9 @@ describe('XSS', () => {
cy.wait(1000);
cy.get('#the-malware').should('not.exist');
});
it('should sanitize backticks in class names properly', () => {
cy.visit('http://localhost:9000/xss24.html');
cy.wait(1000);
cy.get('#the-malware').should('not.exist');
});
});

View File

@@ -330,6 +330,48 @@ describe('Gantt diagram', () => {
);
});
it('should render a gantt diagram with tick is 2 milliseconds', () => {
imgSnapshotTest(
`
gantt
title A Gantt Diagram
dateFormat SSS
axisFormat %Lms
tickInterval 2millisecond
excludes weekends
section Section
A task : a1, 000, 6ms
Another task : after a1, 6ms
section Another
Task in sec : a2, 006, 3ms
another task : 3ms
`,
{}
);
});
it('should render a gantt diagram with tick is 2 seconds', () => {
imgSnapshotTest(
`
gantt
title A Gantt Diagram
dateFormat ss
axisFormat %Ss
tickInterval 2second
excludes weekends
section Section
A task : a1, 00, 6s
Another task : after a1, 6s
section Another
Task in sec : 06, 3s
another task : 3s
`,
{}
);
});
it('should render a gantt diagram with tick is 15 minutes', () => {
imgSnapshotTest(
`
@@ -478,6 +520,25 @@ describe('Gantt diagram', () => {
);
});
it('should render a gantt diagram with very large intervals, skipping excludes if interval > 5 years', () => {
imgSnapshotTest(
`gantt
title A long Gantt Diagram
dateFormat YYYY-MM-DD
axisFormat %m-%d
tickInterval 1day
excludes weekends
section Section
A task : a1, 9999-10-01, 30d
Another task : after a1, 20d
section Another
Task in sec : 2022-10-20, 12d
another task : 24d
`
);
});
it('should render when compact is true', () => {
imgSnapshotTest(
`

View File

@@ -0,0 +1,10 @@
import { urlSnapshotTest } from '../../helpers/util.ts';
describe('Marker Unique IDs Per Diagram', () => {
it('should render a blue arrow tip in second digram', () => {
urlSnapshotTest('http://localhost:9000/marker_unique_id.html', {
logLevel: 1,
flowchart: { htmlLabels: false },
});
});
});

View File

@@ -58,12 +58,12 @@
</head>
<body>
<pre id="diagram" class="mermaid">
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-elk LR
subgraph example
node
end
</pre>
<pre id="diagram" class="mermaid2">
flowchart
Node1:::class1 --> Node2:::class2
Node1:::class1 --> Node3:::class2

View File

@@ -0,0 +1,52 @@
<html>
<head> </head>
<body>
<h1>Example</h1>
<pre class="mermaid">
%%{init:{"theme":"base", "themeVariables": {"lineColor":"red"}}}%%
flowchart LR
subgraph red
A --> B
end
</pre>
<pre class="mermaid">
%%{init:{"theme":"base", "themeVariables": {"lineColor":"blue"}}}%%
flowchart LR
subgraph black
A --> B
end
</pre>
<pre class="mermaid">
---
config:
theme: base
themeVariables:
lineColor: yellow
---
flowchart LR
subgraph red
A --> B
end
</pre>
<pre class="mermaid">
---
config:
theme: base
themeVariables:
lineColor: green
---
flowchart LR
subgraph black
A --> B
end
</pre>
<script type="module">
import mermaid from './mermaid.esm.mjs';
mermaid.initialize({ startOnLoad: true, logLevel: 0 });
if (window.Cypress) {
window.rendered = true;
}
</script>
</body>
</html>

View File

@@ -42,6 +42,16 @@
font-size: 72px;
}
</style>
<script>
function xssAttack() {
const div = document.createElement('div');
div.id = 'the-malware';
div.className = 'malware';
div.innerHTML = 'XSS Succeeded';
document.getElementsByTagName('body')[0].appendChild(div);
throw new Error('XSS Succeeded');
}
</script>
</head>
<body>
<div>Security check</div>

View File

@@ -42,6 +42,16 @@
font-size: 72px;
}
</style>
<script>
function xssAttack() {
const div = document.createElement('div');
div.id = 'the-malware';
div.className = 'malware';
div.innerHTML = 'XSS Succeeded';
document.getElementsByTagName('body')[0].appendChild(div);
throw new Error('XSS Succeeded');
}
</script>
</head>
<body>
<div>Security check</div>

View File

@@ -42,6 +42,16 @@
font-size: 72px;
}
</style>
<script>
function xssAttack() {
const div = document.createElement('div');
div.id = 'the-malware';
div.className = 'malware';
div.innerHTML = 'XSS Succeeded';
document.getElementsByTagName('body')[0].appendChild(div);
throw new Error('XSS Succeeded');
}
</script>
</head>
<body>
<div>Security check</div>

View File

@@ -42,6 +42,16 @@
font-size: 72px;
}
</style>
<script>
function xssAttack() {
const div = document.createElement('div');
div.id = 'the-malware';
div.className = 'malware';
div.innerHTML = 'XSS Succeeded';
document.getElementsByTagName('body')[0].appendChild(div);
throw new Error('XSS Succeeded');
}
</script>
</head>
<body>
<div>Security check</div>

View File

@@ -42,6 +42,16 @@
font-size: 72px;
}
</style>
<script>
function xssAttack() {
const div = document.createElement('div');
div.id = 'the-malware';
div.className = 'malware';
div.innerHTML = 'XSS Succeeded';
document.getElementsByTagName('body')[0].appendChild(div);
throw new Error('XSS Succeeded');
}
</script>
</head>
<body>
<div>Security check</div>

View File

@@ -42,6 +42,16 @@
font-size: 72px;
}
</style>
<script>
function xssAttack() {
const div = document.createElement('div');
div.id = 'the-malware';
div.className = 'malware';
div.innerHTML = 'XSS Succeeded';
document.getElementsByTagName('body')[0].appendChild(div);
throw new Error('XSS Succeeded');
}
</script>
</head>
<body>
<div>Security check</div>

View File

@@ -42,6 +42,16 @@
font-size: 72px;
}
</style>
<script>
function xssAttack() {
const div = document.createElement('div');
div.id = 'the-malware';
div.className = 'malware';
div.innerHTML = 'XSS Succeeded';
document.getElementsByTagName('body')[0].appendChild(div);
throw new Error('XSS Succeeded');
}
</script>
</head>
<body>
<div>Security check</div>

View File

@@ -42,6 +42,16 @@
font-size: 72px;
}
</style>
<script>
function xssAttack() {
const div = document.createElement('div');
div.id = 'the-malware';
div.className = 'malware';
div.innerHTML = 'XSS Succeeded';
document.getElementsByTagName('body')[0].appendChild(div);
throw new Error('XSS Succeeded');
}
</script>
</head>
<body>
<div>Security check</div>

View File

@@ -42,6 +42,16 @@
font-size: 72px;
}
</style>
<script>
function xssAttack() {
const div = document.createElement('div');
div.id = 'the-malware';
div.className = 'malware';
div.innerHTML = 'XSS Succeeded';
document.getElementsByTagName('body')[0].appendChild(div);
throw new Error('XSS Succeeded');
}
</script>
</head>
<body>
<div>Security check</div>

View File

@@ -42,6 +42,16 @@
font-size: 72px;
}
</style>
<script>
function xssAttack() {
const div = document.createElement('div');
div.id = 'the-malware';
div.className = 'malware';
div.innerHTML = 'XSS Succeeded';
document.getElementsByTagName('body')[0].appendChild(div);
throw new Error('XSS Succeeded');
}
</script>
</head>
<body>
<div>Security check</div>

View File

@@ -42,6 +42,16 @@
font-size: 72px;
}
</style>
<script>
function xssAttack() {
const div = document.createElement('div');
div.id = 'the-malware';
div.className = 'malware';
div.innerHTML = 'XSS Succeeded';
document.getElementsByTagName('body')[0].appendChild(div);
throw new Error('XSS Succeeded');
}
</script>
</head>
<body>
<div>Security check</div>

View File

@@ -42,6 +42,16 @@
font-size: 72px;
}
</style>
<script>
function xssAttack() {
const div = document.createElement('div');
div.id = 'the-malware';
div.className = 'malware';
div.innerHTML = 'XSS Succeeded';
document.getElementsByTagName('body')[0].appendChild(div);
throw new Error('XSS Succeeded');
}
</script>
</head>
<body>
<div>Security check</div>

109
cypress/platform/xss24.html Normal file
View File

@@ -0,0 +1,109 @@
<html>
<head>
<link href="https://fonts.googleapis.com/css?family=Montserrat&display=swap" rel="stylesheet" />
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet" />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
/>
<link
href="https://fonts.googleapis.com/css?family=Noto+Sans+SC&display=swap"
rel="stylesheet"
/>
<style>
body {
/* background: rgb(221, 208, 208); */
/* background:#333; */
font-family: 'Arial';
/* font-size: 18px !important; */
}
h1 {
color: grey;
}
.mermaid2 {
display: none;
}
.mermaid svg {
/* font-size: 18px !important; */
}
.malware {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 150px;
background: red;
color: black;
display: flex;
display: flex;
justify-content: center;
align-items: center;
font-family: monospace;
font-size: 72px;
}
</style>
<script>
function xssAttack() {
const div = document.createElement('div');
div.id = 'the-malware';
div.className = 'malware';
div.innerHTML = 'XSS Succeeded';
document.getElementsByTagName('body')[0].appendChild(div);
throw new Error('XSS Succeeded');
}
</script>
</head>
<body>
<div>Security check</div>
<div class="flex">
<div id="diagram" class="mermaid"></div>
<div id="res" class=""></div>
</div>
<script type="module">
import mermaid from './mermaid.esm.mjs';
mermaid.parseError = function (err, hash) {
// console.error('Mermaid error: ', err);
};
mermaid.initialize({
theme: 'forest',
arrowMarkerAbsolute: true,
// themeCSS: '.edgePath .path {stroke: red;} .arrowheadPath {fill: red;}',
logLevel: 0,
state: {
defaultRenderer: 'dagre-wrapper',
},
flowchart: {
// defaultRenderer: 'dagre-wrapper',
nodeSpacing: 10,
curve: 'cardinal',
htmlLabels: true,
},
htmlLabels: false,
// gantt: { axisFormat: '%m/%d/%Y' },
sequence: { actorFontFamily: 'courier', actorMargin: 50, showSequenceNumbers: false },
// sequenceDiagram: { actorMargin: 300 } // deprecated
// fontFamily: '"times", sans-serif',
// fontFamily: 'courier',
fontSize: 18,
curve: 'basis',
securityLevel: 'strict',
startOnLoad: false,
secure: ['secure', 'securityLevel', 'startOnLoad', 'maxTextSize'],
// themeVariables: {relationLabelColor: 'red'}
});
function callback() {
alert('It worked');
}
let diagram = 'classDiagram\n';
diagram += '`Class<img src=x on';
diagram += 'error=xssAttack()>` <|-- `Class2<img src=x on';
diagram += 'error=xssAttack()>`';
console.log(diagram);
// document.querySelector('#diagram').innerHTML = diagram;
const { svg } = await mermaid.render('diagram', diagram);
document.querySelector('#res').innerHTML = svg;
</script>
</body>
</html>

View File

@@ -42,6 +42,16 @@
font-size: 72px;
}
</style>
<script>
function xssAttack() {
const div = document.createElement('div');
div.id = 'the-malware';
div.className = 'malware';
div.innerHTML = 'XSS Succeeded';
document.getElementsByTagName('body')[0].appendChild(div);
throw new Error('XSS Succeeded');
}
</script>
</head>
<body>
<div>Security check</div>
@@ -84,14 +94,6 @@
function callback() {
alert('It worked');
}
function xssAttack() {
const div = document.createElement('div');
div.id = 'the-malware';
div.className = 'malware';
div.innerHTML = 'XSS Succeeded';
document.getElementsByTagName('body')[0].appendChild(div);
throw new Error('XSS Succeeded');
}
let diagram = 'graph LR\n';
diagram += 'B-->D("<img onerror=location=`java';
// diagram += "script\u003aalert\u0028document.domain\u0029\` src=x>\"\);\n";

View File

@@ -42,6 +42,16 @@
font-size: 72px;
}
</style>
<script>
function xssAttack() {
const div = document.createElement('div');
div.id = 'the-malware';
div.className = 'malware';
div.innerHTML = 'XSS Succeeded';
document.getElementsByTagName('body')[0].appendChild(div);
throw new Error('XSS Succeeded');
}
</script>
</head>
<body>
<div>Security check</div>

View File

@@ -42,6 +42,16 @@
font-size: 72px;
}
</style>
<script>
function xssAttack() {
const div = document.createElement('div');
div.id = 'the-malware';
div.className = 'malware';
div.innerHTML = 'XSS Succeeded';
document.getElementsByTagName('body')[0].appendChild(div);
throw new Error('XSS Succeeded');
}
</script>
</head>
<body>
<div>Security check</div>

View File

@@ -42,6 +42,16 @@
font-size: 72px;
}
</style>
<script>
function xssAttack() {
const div = document.createElement('div');
div.id = 'the-malware';
div.className = 'malware';
div.innerHTML = 'XSS Succeeded';
document.getElementsByTagName('body')[0].appendChild(div);
throw new Error('XSS Succeeded');
}
</script>
</head>
<body>
<div>Security check</div>

View File

@@ -42,6 +42,16 @@
font-size: 72px;
}
</style>
<script>
function xssAttack() {
const div = document.createElement('div');
div.id = 'the-malware';
div.className = 'malware';
div.innerHTML = 'XSS Succeeded';
document.getElementsByTagName('body')[0].appendChild(div);
throw new Error('XSS Succeeded');
}
</script>
</head>
<body>
<div>Security check</div>

View File

@@ -125,6 +125,21 @@
</pre>
<hr />
<pre class="mermaid">
erDiagram
_customer_order {
bigint id PK
bigint customer_id FK
text shipping_address
text delivery_method
timestamp_with_time_zone ordered_at
numeric total_tax_amount
numeric total_price
text payment_method
}
</pre>
<hr />
<script type="module">
import mermaid from './mermaid.esm.mjs';
mermaid.initialize({

View File

@@ -62,10 +62,10 @@ from IPython.display import Image, display
import matplotlib.pyplot as plt
def mm(graph):
graphbytes = graph.encode("ascii")
base64_bytes = base64.b64encode(graphbytes)
base64_string = base64_bytes.decode("ascii")
display(Image(url="https://mermaid.ink/img/" + base64_string))
graphbytes = graph.encode("utf8")
base64_bytes = base64.b64encode(graphbytes)
base64_string = base64_bytes.decode("ascii")
display(Image(url="https://mermaid.ink/img/" + base64_string))
mm("""
graph LR;

View File

@@ -10,7 +10,7 @@ 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**.
- 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.
- Frontmatter (v10.5.0+) - 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.

View File

@@ -7,7 +7,7 @@
# 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 are deprecated from v10.5.0. Please use the `config` key in frontmatter to pass configuration. See [Configuration](./configuration.md) for more details.
## Directives

View File

@@ -16,4 +16,4 @@
#### Defined in
[mermaidAPI.ts:78](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L78)
[mermaidAPI.ts:59](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L59)

View File

@@ -39,7 +39,7 @@ bindFunctions?.(div); // To call bindFunctions only if it's present.
#### Defined in
[mermaidAPI.ts:98](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L98)
[mermaidAPI.ts:79](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L79)
---
@@ -51,4 +51,4 @@ The svg code for the rendered graph.
#### Defined in
[mermaidAPI.ts:88](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L88)
[mermaidAPI.ts:69](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L69)

View File

@@ -25,13 +25,13 @@ Renames and re-exports [mermaidAPI](mermaidAPI.md#mermaidapi)
#### Defined in
[mermaidAPI.ts:82](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L82)
[mermaidAPI.ts:63](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L63)
## Variables
### mermaidAPI
`Const` **mermaidAPI**: `Readonly`<{ `defaultConfig`: `MermaidConfig` = configApi.defaultConfig; `getConfig`: () => `MermaidConfig` = configApi.getConfig; `getDiagramFromText`: (`text`: `string`) => `Promise`<`Diagram`> ; `getSiteConfig`: () => `MermaidConfig` = configApi.getSiteConfig; `globalReset`: () => `void` ; `initialize`: (`options`: `MermaidConfig`) => `void` ; `parse`: (`text`: `string`, `parseOptions?`: [`ParseOptions`](../interfaces/mermaidAPI.ParseOptions.md)) => `Promise`<`boolean`> ; `parseDirective`: (`p`: `any`, `statement`: `string`, `context`: `string`, `type`: `string`) => `void` ; `render`: (`id`: `string`, `text`: `string`, `svgContainingElement?`: `Element`) => `Promise`<[`RenderResult`](../interfaces/mermaidAPI.RenderResult.md)> ; `reset`: () => `void` ; `setConfig`: (`conf`: `MermaidConfig`) => `MermaidConfig` = configApi.setConfig; `updateSiteConfig`: (`conf`: `MermaidConfig`) => `MermaidConfig` = configApi.updateSiteConfig }>
`Const` **mermaidAPI**: `Readonly`<{ `defaultConfig`: `MermaidConfig` = configApi.defaultConfig; `getConfig`: () => `MermaidConfig` = configApi.getConfig; `getDiagramFromText`: (`text`: `string`, `metadata`: `Pick`<`DiagramMetadata`, `"title"`>) => `Promise`<`Diagram`> ; `getSiteConfig`: () => `MermaidConfig` = configApi.getSiteConfig; `globalReset`: () => `void` ; `initialize`: (`options`: `MermaidConfig`) => `void` ; `parse`: (`text`: `string`, `parseOptions?`: [`ParseOptions`](../interfaces/mermaidAPI.ParseOptions.md)) => `Promise`<`boolean`> ; `render`: (`id`: `string`, `text`: `string`, `svgContainingElement?`: `Element`) => `Promise`<[`RenderResult`](../interfaces/mermaidAPI.RenderResult.md)> ; `reset`: () => `void` ; `setConfig`: (`conf`: `MermaidConfig`) => `MermaidConfig` = configApi.setConfig; `updateSiteConfig`: (`conf`: `MermaidConfig`) => `MermaidConfig` = configApi.updateSiteConfig }>
## mermaidAPI configuration defaults
@@ -96,7 +96,7 @@ mermaid.initialize(config);
#### Defined in
[mermaidAPI.ts:673](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L673)
[mermaidAPI.ts:641](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L641)
## Functions
@@ -127,7 +127,7 @@ Return the last node appended
#### Defined in
[mermaidAPI.ts:310](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L310)
[mermaidAPI.ts:299](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L299)
---
@@ -153,13 +153,13 @@ the cleaned up svgCode
#### Defined in
[mermaidAPI.ts:256](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L256)
[mermaidAPI.ts:245](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L245)
---
### createCssStyles
**createCssStyles**(`config`, `graphType`, `classDefs?`): `string`
**createCssStyles**(`config`, `classDefs?`): `string`
Create the user styles
@@ -168,7 +168,6 @@ Create the user styles
| Name | Type | Description |
| :---------- | :------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------ |
| `config` | `MermaidConfig` | configuration that has style and theme settings to use |
| `graphType` | `string` | used for checking if classDefs should be applied |
| `classDefs` | `undefined` \| `null` \| `Record`<`string`, `DiagramStyleClassDef`> | the classDefs in the diagram text. Might be null if none were defined. Usually is the result of a call to getClasses(...) |
#### Returns
@@ -179,7 +178,7 @@ the string with all the user styles
#### Defined in
[mermaidAPI.ts:185](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L185)
[mermaidAPI.ts:175](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L175)
---
@@ -189,12 +188,12 @@ the string with all the user styles
#### Parameters
| Name | Type |
| :---------- | :----------------------------------------- |
| `config` | `MermaidConfig` |
| `graphType` | `string` |
| `classDefs` | `Record`<`string`, `DiagramStyleClassDef`> |
| `svgId` | `string` |
| Name | Type |
| :---------- | :-------------------------------------------------------- |
| `config` | `MermaidConfig` |
| `graphType` | `string` |
| `classDefs` | `undefined` \| `Record`<`string`, `DiagramStyleClassDef`> |
| `svgId` | `string` |
#### Returns
@@ -202,7 +201,7 @@ the string with all the user styles
#### Defined in
[mermaidAPI.ts:233](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L233)
[mermaidAPI.ts:222](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L222)
---
@@ -229,7 +228,7 @@ with an enclosing block that has each of the cssClasses followed by !important;
#### Defined in
[mermaidAPI.ts:169](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L169)
[mermaidAPI.ts:160](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L160)
---
@@ -249,7 +248,7 @@ with an enclosing block that has each of the cssClasses followed by !important;
#### Defined in
[mermaidAPI.ts:155](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L155)
[mermaidAPI.ts:146](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L146)
---
@@ -269,7 +268,7 @@ with an enclosing block that has each of the cssClasses followed by !important;
#### Defined in
[mermaidAPI.ts:126](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L126)
[mermaidAPI.ts:117](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L117)
---
@@ -295,7 +294,7 @@ Put the svgCode into an iFrame. Return the iFrame code
#### Defined in
[mermaidAPI.ts:287](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L287)
[mermaidAPI.ts:276](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L276)
---
@@ -320,4 +319,4 @@ Remove any existing elements from the given document
#### Defined in
[mermaidAPI.ts:360](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L360)
[mermaidAPI.ts:349](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L349)

View File

@@ -2,53 +2,46 @@
>
> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT.
>
> ## Please edit the corresponding file in [/packages/mermaid/src/docs/ecosystem/integrations.md](../../packages/mermaid/src/docs/ecosystem/integrations.md).
> ## Please edit the corresponding file in [/packages/mermaid/src/docs/ecosystem/integrations-community.md](../../packages/mermaid/src/docs/ecosystem/integrations-community.md).
# Integrations
## Recommendations
## Official integration: [Mermaid Chart](./mermaid-chart.md)
### File Extension
We're excited about the growth of the Mermaid community, and the number of plugins and integrations that have been created with Mermaid.
Applications that support mermaid files [SHOULD](https://datatracker.ietf.org/doc/html/rfc2119#section-3) use `.mermaid` or `.mmd` file extensions.
## Community integrations
### MIME Type
Below are a list of community plugins and integrations created with Mermaid.
The recommended [MIME type](https://www.iana.org/assignments/media-types/media-types.xhtml) for mermaid media is `text/vnd.mermaid`.
### Productivity tools
[IANA](https://www.iana.org/) recognition pending.
✅ = Native support
---
The following list is a compilation of different integrations and plugins that allow the rendering of mermaid definitions within other applications.
They also serve as proof of concept, for the variety of things that can be built with mermaid.
## Productivity
- [GitHub](https://github.com) (**Native support**)
- [Using code blocks](https://github.blog/2022-02-14-include-diagrams-markdown-files-mermaid/) (**Native support**)
- [GitHub](https://github.com) ✅
- [Using code blocks](https://github.blog/2022-02-14-include-diagrams-markdown-files-mermaid/) ✅
- [GitHub action: Compile mermaid to image](https://github.com/neenjaw/compile-mermaid-markdown-action)
- [svg-generator](https://github.com/SimonKenyonShepard/mermaidjs-github-svg-generator)
- [GitHub Writer](https://github.com/ckeditor/github-writer)
- [GitLab](https://docs.gitlab.com/ee/user/markdown.html#diagrams-and-flowcharts) (**Native support**)
- [Gitea](https://gitea.io) (**Native support**)
- [Azure Devops](https://docs.microsoft.com/en-us/azure/devops/project/wiki/wiki-markdown-guidance?view=azure-devops#add-mermaid-diagrams-to-a-wiki-page) (**Native support**)
- [Tuleap](https://docs.tuleap.org/user-guide/writing-in-tuleap.html#graphs) (**Native support**)
- [Mermaid Flow Visual Editor](https://www.mermaidflow.app) (**Native support**)
- [Deepdwn](https://billiam.itch.io/deepdwn) (**Native support**)
- [Joplin](https://joplinapp.org) (**Native support**)
- [Slab](https://slab.com) (**Native support**)
- [Swimm](https://swimm.io) (**Native support**)
- [Notion](https://notion.so) (**Native support**)
- [Observable](https://observablehq.com/@observablehq/mermaid) (**Native support**)
- [Obsidian](https://help.obsidian.md/Editing+and+formatting/Advanced+formatting+syntax#Diagram) (**Native support**)
- [GitLab](https://docs.gitlab.com/ee/user/markdown.html#diagrams-and-flowcharts)
- [Gitea](https://gitea.io)
- [Azure Devops](https://docs.microsoft.com/en-us/azure/devops/project/wiki/wiki-markdown-guidance?view=azure-devops#add-mermaid-diagrams-to-a-wiki-page)
- [Tuleap](https://docs.tuleap.org/user-guide/writing-in-tuleap.html#graphs)
- [Mermaid Flow Visual Editor](https://www.mermaidflow.app)
- [Deepdwn](https://billiam.itch.io/deepdwn)
- [Joplin](https://joplinapp.org)
- [Slab](https://slab.com)
- [Swimm](https://swimm.io)
- [Notion](https://notion.so)
- [Observable](https://observablehq.com/@observablehq/mermaid)
- [Obsidian](https://help.obsidian.md/Editing+and+formatting/Advanced+formatting+syntax#Diagram)
- [GitBook](https://gitbook.com)
- [Mermaid Plugin](https://github.com/JozoVilcek/gitbook-plugin-mermaid)
- [Markdown with Mermaid CLI](https://github.com/miao1007/gitbook-plugin-mermaid-cli)
- [Mermaid plugin for GitBook](https://github.com/wwformat/gitbook-plugin-mermaid-pdf)
- [LiveBook](https://livebook.dev) (**Native support**)
- [LiveBook](https://livebook.dev)
- [Atlassian Products](https://www.atlassian.com)
- [Mermaid Live Editor for Confluence Cloud](https://marketplace.atlassian.com/apps/1231571/mermaid-live-editor-for-confluence?hosting=cloud&tab=overview)
- [Mermaid Plugin for Confluence](https://marketplace.atlassian.com/apps/1214124/mermaid-plugin-for-confluence?hosting=server&tab=overview)
- [CloudScript.io Addon](https://marketplace.atlassian.com/apps/1219878/cloudscript-io-mermaid-addon?hosting=cloud&tab=overview)
- [Auto convert diagrams in Jira](https://github.com/coddingtonbear/jirafs-mermaid)
@@ -60,11 +53,15 @@ They also serve as proof of concept, for the variety of things that can be built
- [mermerd](https://github.com/KarnerTh/mermerd)
- Visual Studio Code [Polyglot Interactive Notebooks](https://github.com/dotnet/interactive#net-interactive)
## CRM/ERP/Similar
### CRM/ERP
Customer Relationship Management/Enterprise Resource Planning
- [coreBOS](https://blog.corebos.org/blog/december2019)
## Blogs
### Blogging
Blogging frameworks and platforms
- [WordPress](https://wordpress.org)
- [WordPress Markdown Editor](https://wordpress.org/plugins/wp-githuber-md)
@@ -76,7 +73,9 @@ They also serve as proof of concept, for the variety of things that can be built
- [Nextra](https://nextra.site/)
- [Mermaid](https://nextra.site/docs/guide/mermaid)
## CMS
### CMS/ECM
Content Management Systems/Enterprise Content Management
- [VitePress](https://vitepress.vuejs.org/)
- [Plugin for Mermaid.js](https://emersonbottero.github.io/vitepress-plugin-mermaid/)
@@ -86,10 +85,12 @@ They also serve as proof of concept, for the variety of things that can be built
- [Mermaid Diagrams](https://github.com/DanielFlaum/grav-plugin-mermaid-diagrams)
- [GitLab Markdown Adapter](https://github.com/Goutte/grav-plugin-gitlab-markdown-adapter)
## Communication
### Communication
Communication tools and platforms
- [Discourse](https://discourse.org)
- [Mermaid Theme Component](https://meta.discourse.org/t/discourse-mermaid/218242)
- [Mermaid Plugin](https://github.com/pnewell/discourse-mermaid), [And](https://github.com/unfoldingWord-dev/discourse-mermaid)
- [Mattermost](https://mattermost.com/)
- [Mermaid Plugin](https://github.com/SpikeTings/Mermaid)
- [phpBB](https://phpbb.com)
@@ -97,7 +98,7 @@ They also serve as proof of concept, for the variety of things that can be built
- [NodeBB](https://nodebb.org)
- [Mermaid Plugin](https://www.npmjs.com/package/nodebb-plugin-mermaid)
## Wikis
### Wikis
- [MediaWiki](https://www.mediawiki.org)
- [Mermaid Extension](https://www.mediawiki.org/wiki/Extension:Mermaid)
@@ -113,7 +114,7 @@ They also serve as proof of concept, for the variety of things that can be built
- [mermaid-tw5: full js library](https://github.com/efurlanm/mermaid-tw5)
- [tw5-mermaid: wrapper for Mermaid Live](https://github.com/jasonmhoule/tw5-mermaid)
## Editor Plugins
### Editor Plugins
- [VS Code](https://code.visualstudio.com/)
- [Markdown Preview Mermaid Support](https://marketplace.visualstudio.com/items?itemName=bierner.markdown-mermaid)
@@ -164,8 +165,9 @@ They also serve as proof of concept, for the variety of things that can be built
- [Standard Notes](https://standardnotes.com/)
- [sn-mermaid](https://github.com/nienow/sn-mermaid)
## Document Generation
### Document Generation
- [Swimm - Up-to-date diagrams with Swimm, the knowledge management tool for code](https://docs.swimm.io/Features/diagrams-and-charts)
- [Sphinx](https://www.sphinx-doc.org/en/master/)
- [sphinxcontrib-mermaid](https://github.com/mgaitan/sphinxcontrib-mermaid)
- [remark](https://remark.js.org/)
@@ -181,15 +183,15 @@ They also serve as proof of concept, for the variety of things that can be built
- [mkdocs-material](https://github.com/squidfunk/mkdocs-material), check the [docs](https://squidfunk.github.io/mkdocs-material/reference/diagrams/)
- [Type Doc](https://typedoc.org/)
- [typedoc-plugin-mermaid](https://www.npmjs.com/package/typedoc-plugin-mermaid)
- [Docsy Hugo Theme](https://www.docsy.dev/docs/adding-content/lookandfeel/#diagrams-with-mermaid) (Native support in theme)
- [Docsy Hugo Theme](https://www.docsy.dev/docs/adding-content/lookandfeel/#diagrams-with-mermaid) (native support in theme)
- [Codedoc](https://codedoc.cc/)
- [codedoc-mermaid-plugin](https://www.npmjs.com/package/codedoc-mermaid-plugin)
- [mdbook](https://rust-lang.github.io/mdBook/index.html)
- [mdbook-mermaid](https://github.com/badboy/mdbook-mermaid)
- [Quarto](https://quarto.org/)
- [Typora](https://typora.io/) ([Native support](https://support.typora.io/Draw-Diagrams-With-Markdown/#mermaid))
- [Typora](https://typora.io/) ([native support](https://support.typora.io/Draw-Diagrams-With-Markdown/#mermaid))
## Browser Extensions
### Browser Extensions
| Name | Chrome Web Store | Firefox Add-ons | Opera | Edge | Source/Repository |
| ------------------------ | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
@@ -204,7 +206,7 @@ They also serve as proof of concept, for the variety of things that can be built
| Monkeys | [🎡🔗](https://chrome.google.com/webstore/detail/monkeys-mermaid-for-githu/cplfdpoajbclbgphaphphcldamfkjlgi) | - | - | - | - |
| Mermaid Previewer | [🎡🔗](https://chrome.google.com/webstore/detail/mermaid-previewer/oidjnlhbegipkcklbdfnbkikplpghfdl) | - | - | - | - |
## Other
### Other
- [Jekyll](https://jekyllrb.com/)
- [jekyll-mermaid](https://rubygems.org/gems/jekyll-mermaid)

View File

@@ -0,0 +1,31 @@
> **Warning**
>
> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT.
>
> ## Please edit the corresponding file in [/packages/mermaid/src/docs/ecosystem/integrations-create.md](../../packages/mermaid/src/docs/ecosystem/integrations-create.md).
# Integrations - create
## Recommendations
Below are recommendations for creating plugins and integrations with Mermaid.
### File Extension
Applications that support Mermaid files [SHOULD](https://datatracker.ietf.org/doc/html/rfc2119#section-3) use `.mermaid` or `.mmd` file extensions.
### MIME Type
The recommended [MIME type](https://www.iana.org/assignments/media-types/media-types.xhtml) for Mermaid media is `text/vnd.mermaid`.
Currently pending [IANA](https://www.iana.org/) recognition.
## Showcase
### Mermaid Slack workspace
We would love to see what you create with Mermaid. Please share your creations with us in our [Slack](https://join.slack.com/t/mermaid-talk/shared_invite/zt-22p2r8p9y-qiyP1H38GjFQ6S6jbBkOxQ) workspace [#community-showcase](https://mermaid-talk.slack.com/archives/C05NK37LT40) channel.
### Add to Mermaid Ecosystem
If you have a plugin or integration that you'd like to add to our [Community integrations](/ecosystem/integrations-community) list, please [open a pull request](https://github.com/mermaid-js/mermaid).

View File

@@ -0,0 +1,47 @@
> **Warning**
>
> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT.
>
> ## Please edit the corresponding file in [/packages/mermaid/src/docs/ecosystem/mermaid-chart.md](../../packages/mermaid/src/docs/ecosystem/mermaid-chart.md).
# Mermaid Chart
<br />
<a href="https://www.producthunt.com/posts/mermaid-chart?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-mermaid&#0045;chart" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=416671&theme=light" alt="Mermaid&#0032;Chart - A&#0032;smarter&#0032;way&#0032;to&#0032;create&#0032;diagrams | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
## About
[Mermaid Chart](https://www.mermaidchart.com) was born out of the Mermaid open source project and was founded by Knut Sveidqvist together with Open Core Ventures. The lead developers from Mermaid have joined the company and there is a strong connection between the project we all love and Mermaid Chart. Mermaid Chart brings resources to the open source development of Mermaid and makes it possible to work with Mermaid professionally.
## Features
- **Editor** - A web based editor for creating and editing Mermaid diagrams.
- **Presentation** - A presentation mode for viewing Mermaid diagrams in a slideshow format.
- **Collaboration** - A web based collaboration feature for multi-user editing on Mermaid diagrams in real-time (Pro plan).
- **Plugins** - A plugin system for extending the functionality of Mermaid. Currently includes [VS Code](https://marketplace.visualstudio.com/items?itemName=MermaidChart.vscode-mermaid-chart) and [ChatGPT](https://www.mermaidchart.com/plugins/chatgpt).
- **AI** - An AI chatbot that can generate Mermaid diagrams from text (Pro plan).
- **More** - To learn more, visit our [Product](https://www.mermaidchart.com/product) page.
## Plans
- **Free** - A free plan that includes five diagrams.
- **Pro** - A paid plan that includes unlimited diagrams, access to the collaboration feature, and more.
- **Enterprise** - A paid plan for enterprise use that includes all Pro features, and more.
## Access
Sign up for a free account at [Mermaid Chart](https://www.mermaidchart.com/app/sign-up).
Mermaid Chart is currently offering a 30-day free trial of our newly-launched Pro tier. To learn more, visit our [Pricing](https://mermaidchart.com/pricing) page.
## Mermaid JS contributions
First time contributors are eligible for a free Pro tier account for 1 year.

View File

@@ -1,9 +0,0 @@
> **Warning**
>
> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT.
>
> ## Please edit the corresponding file in [/packages/mermaid/src/docs/ecosystem/showcases.md](../../packages/mermaid/src/docs/ecosystem/showcases.md).
# Showcases
- [Swimm - Up-to-date diagrams with Swimm, the knowledge management tool for code](https://docs.swimm.io/Features/diagrams-and-charts).

View File

@@ -17,7 +17,7 @@ This section talks about the different ways to deploy Mermaid. Learning the [Syn
## Four ways of using mermaid:
1. Using the Mermaid Live Editor at [mermaid.live](https://mermaid.live).
2. Using [mermaid plugins](../ecosystem/integrations.md) with programs you are familiar with.
2. Using [mermaid plugins](../ecosystem/integrations-community.md) with programs you are familiar with.
3. Calling the Mermaid JavaScript API.
4. Deploying Mermaid as a dependency.
@@ -85,7 +85,7 @@ and to View, <https://mermaid.live/view?gist=https://gist.github.com/sidharthv96
## 2. Using Mermaid Plugins:
You can generate mermaid diagrams from within popular applications using plug-ins. It can be done in the same way, you would use the Live Editor. Here's a list of [Mermaid Plugins](../ecosystem/integrations.md).
You can generate mermaid diagrams from within popular applications using plug-ins. It can be done in the same way, you would use the Live Editor. Here's a list of [Mermaid Plugins](../ecosystem/integrations-community.md).
**This is covered in greater detail in the [Usage section](../config/usage.md)**

View File

@@ -42,11 +42,12 @@ But not having diagrams or docs ruins productivity and hurts organizational lear
Mermaid addresses this problem by enabling users to create easily modifiable diagrams, it can also be made part of production scripts (and other pieces of code).<br/> <br/>
Mermaid allows even non-programmers to easily create detailed and diagrams through the [Mermaid Live Editor](https://mermaid.live/).<br/>
[Tutorials](../config/Tutorials.md) has video tutorials.
Use Mermaid with your favorite applications, check out the list of [Integrations and Usages of Mermaid](../ecosystem/integrations.md).
Use Mermaid with your favorite applications, check out the list of [Community Integrations](../ecosystem/integrations-community.md).
For a more detailed introduction to Mermaid and some of its more basic uses, look to the [Beginner's Guide](../intro/getting-started.md) and [Usage](../config/usage.md).
🌐 [CDN](https://www.jsdelivr.com/package/npm/mermaid) | 📖 [Documentation](https://mermaidjs.github.io) | 🙌 [Contribution](../community/development.md) | 🔌 [Plug-Ins](../ecosystem/integrations.md)
🌐 [CDN](https://www.jsdelivr.com/package/npm/mermaid) | 📖 [Documentation](https://mermaidjs.github.io) | 🙌 [Contribution](../community/development.md) | 🔌 [Plug-Ins](../ecosystem/integrations-community.md)
> 🖖 Keep a steady pulse: mermaid needs more Collaborators, [Read More](https://github.com/knsv/mermaid/issues/866).

View File

@@ -6,8 +6,18 @@
# Announcements
## [Special cases broke Microsoft Zune and can ruin your code base too](https://www.mermaidchart.com/blog/posts/special-cases-broke-microsoft-zune-and-can-ruin-your-code-base-too/)
<br />
23 August 2023 · 15 mins
<a href="https://www.producthunt.com/posts/mermaid-chart?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-mermaid&#0045;chart" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=416671&theme=light" alt="Mermaid&#0032;Chart - A&#0032;smarter&#0032;way&#0032;to&#0032;create&#0032;diagrams | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
Read about the pitfalls of special cases in programming, illustrating how they can lead to complexity, diminish readability, and create maintenance challenges.
## Calling all fans of Mermaid and Mermaid Chart! 🎉
Weve officially made our Product Hunt debut, and would love any and all support from the community!
[Click here](https://www.producthunt.com/posts/mermaid-chart?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-mermaid-chart) to check out our Product Hunt launch.
Feel free to drop us a comment and let us know what you think. All new sign ups will receive a 30-day free trial of our Pro subscription, plus 25% off your first year.
Were on a mission to make text-based diagramming fun again. And we need your help to make that happen.
Your support means the world to us. Thank you for being part of the diagramming movement.

View File

@@ -6,6 +6,18 @@
# Blog
## [How to Make a Git Graph with Mermaid Chart](https://www.mermaidchart.com/blog/posts/how-to-make-a-git-graph-with-mermaid-chart/)
22 September 2023 · 7 mins
A git graph is one of the more useful forms of diagrams for developers and DevOps professionals.
## [Present flow data using Sankey diagrams in Mermaid, thanks to Nikolay Rozhkov](https://www.mermaidchart.com/blog/posts/present-flow-data-using-sankey-diagrams/)
8 September 2023 · 4 mins
Sankey diagrams are a powerful tool for visualizing flow data.
## [Special cases broke Microsoft Zune and can ruin your code base too](https://www.mermaidchart.com/blog/posts/special-cases-broke-microsoft-zune-and-can-ruin-your-code-base-too/)
23 August 2023 · 15 mins

View File

@@ -90,7 +90,7 @@ Mermaid syntax for ER diagrams is compatible with PlantUML, with an extension to
Where:
- `first-entity` is the name of an entity. Names must begin with an alphabetic character and may also contain digits, hyphens, and underscores.
- `first-entity` is the name of an entity. Names must begin with an alphabetic character or an underscore (from v10.5.0+), and may also contain digits and hyphens.
- `relationship` describes the way that both entities inter-relate. See below.
- `second-entity` is the name of the other entity.
- `relationship-label` describes the relationship from the perspective of the first entity.
@@ -198,7 +198,7 @@ erDiagram
The `type` values must begin with an alphabetic character and may contain digits, hyphens, underscores, parentheses and square brackets. The `name` values follow a similar format to `type`, but may start with an asterisk as another option to indicate an attribute is a primary key. Other than that, there are no restrictions, and there is no implicit set of valid data types.
### Entity Name Aliases (v\<MERMAID_RELEASE_VERSION>+)
### Entity Name Aliases (v10.5.0+)
An alias can be added to an entity using square brackets. If provided, the alias will be showed in the diagram instead of the entity name.

View File

@@ -467,7 +467,7 @@ flowchart TB
A & B--> C & D
```
If you describe the same diagram using the basic syntax, it will take four lines. A
If you describe the same diagram using the the basic syntax, it will take four lines. A
word of warning, one could go overboard with this making the flowchart harder to read in
markdown form. The Swedish word `lagom` comes to mind. It means, not too much and not too little.
This goes for expressive syntaxes as well.
@@ -860,8 +860,8 @@ flowchart LR
C-->D
click A callback "Tooltip for a callback"
click B "https://www.github.com" "This is a tooltip for a link"
click A call callback() "Tooltip for a callback"
click B href "https://www.github.com" "This is a tooltip for a link"
click C call callback() "Tooltip for a callback"
click D href "https://www.github.com" "This is a tooltip for a link"
```
```mermaid
@@ -871,13 +871,13 @@ flowchart LR
C-->D
click A callback "Tooltip for a callback"
click B "https://www.github.com" "This is a tooltip for a link"
click A call callback() "Tooltip for a callback"
click B href "https://www.github.com" "This is a tooltip for a link"
click C call callback() "Tooltip for a callback"
click D href "https://www.github.com" "This is a tooltip for a link"
```
> **Success** The tooltip functionality and the ability to link to urls are available from version 0.5.2.
?> Due to limitations with how Docsify handles JavaScript callback functions, an alternate working demo for the above code can be viewed at [this jsfiddle](https://jsfiddle.net/s37cjoau/3/).
?> Due to limitations with how Docsify handles JavaScript callback functions, an alternate working demo for the above code can be viewed at [this jsfiddle](https://jsfiddle.net/Ogglas/2o73vdez/7).
Links are opened in the same browser tab/window by default. It is possible to change this by adding a link target to the click definition (`_self`, `_blank`, `_parent` and `_top` are supported):

View File

@@ -241,7 +241,7 @@ The following formatting strings are supported:
More info in: <https://github.com/d3/d3-time-format/tree/v4.0.0#locale_format>
### Axis ticks
### Axis ticks (v10.3.0+)
The default output ticks are auto. You can custom your `tickInterval`, like `1day` or `1week`.
@@ -252,7 +252,7 @@ tickInterval 1day
The pattern is:
```javascript
/^([1-9][0-9]*)(minute|hour|day|week|month)$/;
/^([1-9][0-9]*)(millisecond|second|minute|hour|day|week|month)$/;
```
More info in: <https://github.com/d3/d3-time#interval_every>
@@ -271,7 +271,7 @@ gantt
weekday monday
```
Support: v10.3.0+
> **Warning** > `millisecond` and `second` support was added in vMERMAID_RELEASE_VERSION
## Output in compact mode

View File

@@ -8,9 +8,8 @@
> A sankey diagram is a visualization used to depict a flow from one set of values to another.
::: warning
This is an experimental diagram. Its syntax are very close to plain CSV, but it is to be extended in the nearest future.
:::
> **Warning**
> This is an experimental diagram. Its syntax are very close to plain CSV, but it is to be extended in the nearest future.
The things being connected are called nodes and the connections are called links.
@@ -19,6 +18,11 @@ The things being connected are called nodes and the connections are called links
This example taken from [observable](https://observablehq.com/@d3/sankey/2?collection=@d3/d3-sankey). It may be rendered a little bit differently, though, in terms of size and colors.
```mermaid-example
---
config:
sankey:
showValues: false
---
sankey-beta
Agricultural 'waste',Bio-conversion,124.729
@@ -92,6 +96,11 @@ Wind,Electricity grid,289.366
```
```mermaid
---
config:
sankey:
showValues: false
---
sankey-beta
Agricultural 'waste',Bio-conversion,124.729

View File

@@ -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.7.0",
"packageManager": "pnpm@8.7.5",
"keywords": [
"diagram",
"markdown",
@@ -19,6 +19,7 @@
"build:mermaid": "pnpm build:vite --mermaid",
"build:viz": "pnpm build:mermaid --visualize",
"build:types": "tsc -p ./packages/mermaid/tsconfig.json --emitDeclarationOnly && tsc -p ./packages/mermaid-zenuml/tsconfig.json --emitDeclarationOnly && tsc -p ./packages/mermaid-example-diagram/tsconfig.json --emitDeclarationOnly",
"build:types:watch": "tsc -p ./packages/mermaid/tsconfig.json --emitDeclarationOnly --watch",
"build:watch": "pnpm build:vite --watch",
"build": "pnpm run -r clean && pnpm build:types && pnpm build:vite",
"dev": "concurrently \"pnpm build:vite --watch\" \"ts-node-esm .vite/server.ts\"",

View File

@@ -1,6 +1,6 @@
{
"name": "mermaid",
"version": "10.4.0",
"version": "10.5.1",
"description": "Markdown-ish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
"type": "module",
"module": "./dist/mermaid.core.mjs",
@@ -40,7 +40,7 @@
"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"
"prepublishOnly": "cpy '../../README.*' ./ --cwd=. && pnpm docs:release-version && pnpm -w run build"
},
"repository": {
"type": "git",

View File

@@ -21,7 +21,7 @@ const versionPlaceholder = '<MERMAID_RELEASE_VERSION>';
const main = async () => {
const sourceDirGlob = posix.join('.', SOURCE_DOCS_DIR, '**');
const mdFileGlobs = getGlobs([posix.join(sourceDirGlob, '*.md')]);
mdFileGlobs.push('!**/community/development.md');
mdFileGlobs.push('!**/community/development.md', '!**/community/code.md');
const mdFiles = await getFilesFromGlobs(mdFileGlobs);
mdFiles.sort();
const mdFilesWithPlaceholder: string[] = [];

View File

@@ -2,11 +2,9 @@ import * as configApi from './config.js';
import { log } from './logger.js';
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 { cleanupComments } from './diagram-api/comments.js';
import type { DetailedError } from './utils.js';
import type { DiagramDefinition } from './diagram-api/types.js';
import type { DiagramDefinition, DiagramMetadata } from './diagram-api/types.js';
export type ParseErrorFunction = (err: string | DetailedError | unknown, hash?: any) => void;
@@ -22,7 +20,7 @@ export class Diagram {
private init?: DiagramDefinition['init'];
private detectError?: UnknownDiagramError;
constructor(public text: string) {
constructor(public text: string, public metadata: Pick<DiagramMetadata, 'title'> = {}) {
this.text += '\n';
const cnf = configApi.getConfig();
try {
@@ -37,19 +35,6 @@ export class Diagram {
this.db = diagram.db;
this.renderer = diagram.renderer;
this.parser = diagram.parser;
const originalParse = this.parser.parse.bind(this.parser);
// Wrap the jison parse() method to handle extracting frontmatter.
//
// This can't be done in this.parse() because some code
// directly calls diagram.parser.parse(), bypassing this.parse().
//
// Similarly, we can't do this in getDiagramFromText() because some code
// calls diagram.db.clear(), which would reset anything set by
// extractFrontMatter().
this.parser.parse = (text: string) =>
originalParse(cleanupComments(extractFrontMatter(text, this.db, configApi.addDirective)));
this.parser.parser.yy = this.db;
this.init = diagram.init;
this.parse();
@@ -60,7 +45,12 @@ export class Diagram {
throw this.detectError;
}
this.db.clear?.();
this.init?.(configApi.getConfig());
const config = configApi.getConfig();
this.init?.(config);
// This block was added for legacy compatibility. Use frontmatter instead of adding more special cases.
if (this.metadata.title) {
this.db.setDiagramTitle?.(this.metadata.title);
}
this.parser.parse(this.text);
}
@@ -82,11 +72,15 @@ export class Diagram {
* **Warning:** This function may be changed in the future.
* @alpha
* @param text - The mermaid diagram definition.
* @param metadata - Diagram metadata, defined in YAML.
* @returns A the Promise of a Diagram object.
* @throws {@link UnknownDiagramError} if the diagram type can not be found.
* @privateRemarks This is exported as part of the public mermaidAPI.
*/
export const getDiagramFromText = async (text: string): Promise<Diagram> => {
export const getDiagramFromText = async (
text: string,
metadata: Pick<DiagramMetadata, 'title'> = {}
): Promise<Diagram> => {
const type = detectType(text, configApi.getConfig());
try {
// Trying to find the diagram
@@ -101,5 +95,5 @@ export const getDiagramFromText = async (text: string): Promise<Diagram> => {
const { id, diagram } = await loader();
registerDiagram(id, diagram);
}
return new Diagram(text);
return new Diagram(text, metadata);
};

View File

@@ -13,7 +13,6 @@ export const mermaidAPI = {
svg: '<svg></svg>',
}),
parse: mAPI.parse,
parseDirective: vi.fn(),
initialize: vi.fn(),
getConfig: configApi.getConfig,
setConfig: configApi.setConfig,

View File

@@ -1,47 +0,0 @@
import { sanitizeText as _sanitizeText } from './diagrams/common/common.js';
import { getConfig } from './config.js';
let title = '';
let diagramTitle = '';
let description = '';
const sanitizeText = (txt: string): string => _sanitizeText(txt, getConfig());
export const clear = function (): void {
title = '';
description = '';
diagramTitle = '';
};
export const setAccTitle = function (txt: string): void {
title = sanitizeText(txt).replace(/^\s+/g, '');
};
export const getAccTitle = function (): string {
return title || diagramTitle;
};
export const setAccDescription = function (txt: string): void {
description = sanitizeText(txt).replace(/\n\s+/g, '\n');
};
export const getAccDescription = function (): string {
return description;
};
export const setDiagramTitle = function (txt: string): void {
diagramTitle = sanitizeText(txt);
};
export const getDiagramTitle = function (): string {
return diagramTitle;
};
export default {
getAccTitle,
setAccTitle,
getDiagramTitle,
setDiagramTitle,
getAccDescription,
setAccDescription,
clear,
};

View File

@@ -3,7 +3,7 @@ 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';
import { sanitizeDirective } from './utils/sanitizeDirective.js';
export const defaultConfig: MermaidConfig = Object.freeze(config);

View File

@@ -1048,7 +1048,7 @@ export interface GanttDiagramConfig extends BaseDiagramConfig {
* Pattern is:
*
* ```javascript
* /^([1-9][0-9]*)(minute|hour|day|week|month)$/
* /^([1-9][0-9]*)(millisecond|second|minute|hour|day|week|month)$/
* ```
*
*/

View File

@@ -5,6 +5,7 @@ import { line, curveBasis, select } from 'd3';
import { getConfig } from '../config.js';
import utils from '../utils.js';
import { evaluate } from '../diagrams/common/common.js';
import { getLineFunctionsWithOffset } from '../utils/lineWithOffset.js';
let edgeLabels = {};
let terminalLabels = {};
@@ -368,8 +369,7 @@ const cutPathAtIntersect = (_points, boundryNode) => {
return points;
};
//(edgePaths, e, edge, clusterDb, diagramtype, graph)
export const insertEdge = function (elem, e, edge, clusterDb, diagramType, graph) {
export const insertEdge = function (elem, e, edge, clusterDb, diagramType, graph, id) {
let points = edge.points;
let pointsHasChanged = false;
const tail = graph.node(e.v);
@@ -435,24 +435,16 @@ export const insertEdge = function (elem, e, edge, clusterDb, diagramType, graph
const lineData = points.filter((p) => !Number.isNaN(p.y));
// This is the accessor function we talked about above
let curve;
let curve = curveBasis;
// Currently only flowcharts get the curve from the settings, perhaps this should
// be expanded to a common setting? Restricting it for now in order not to cause side-effects that
// have not been thought through
if (diagramType === 'graph' || diagramType === 'flowchart') {
curve = edge.curve || curveBasis;
} else {
curve = curveBasis;
if (edge.curve && (diagramType === 'graph' || diagramType === 'flowchart')) {
curve = edge.curve;
}
// curve = curveLinear;
const lineFunction = line()
.x(function (d) {
return d.x;
})
.y(function (d) {
return d.y;
})
.curve(curve);
const { x, y } = getLineFunctionsWithOffset(edge);
const lineFunction = line().x(x).y(y).curve(curve);
// Construct stroke classes based on properties
let strokeClasses;
@@ -516,61 +508,103 @@ export const insertEdge = function (elem, e, edge, clusterDb, diagramType, graph
switch (edge.arrowTypeStart) {
case 'arrow_cross':
svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-crossStart' + ')');
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-crossStart' + ')'
);
break;
case 'arrow_point':
svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-pointStart' + ')');
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-pointStart' + ')'
);
break;
case 'arrow_barb':
svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-barbStart' + ')');
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-barbStart' + ')'
);
break;
case 'arrow_circle':
svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-circleStart' + ')');
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-circleStart' + ')'
);
break;
case 'aggregation':
svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-aggregationStart' + ')');
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-aggregationStart' + ')'
);
break;
case 'extension':
svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-extensionStart' + ')');
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-extensionStart' + ')'
);
break;
case 'composition':
svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-compositionStart' + ')');
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-compositionStart' + ')'
);
break;
case 'dependency':
svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-dependencyStart' + ')');
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-dependencyStart' + ')'
);
break;
case 'lollipop':
svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-lollipopStart' + ')');
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-lollipopStart' + ')'
);
break;
default:
}
switch (edge.arrowTypeEnd) {
case 'arrow_cross':
svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-crossEnd' + ')');
svgPath.attr('marker-end', 'url(' + url + '#' + id + '_' + diagramType + '-crossEnd' + ')');
break;
case 'arrow_point':
svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-pointEnd' + ')');
svgPath.attr('marker-end', 'url(' + url + '#' + id + '_' + diagramType + '-pointEnd' + ')');
break;
case 'arrow_barb':
svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-barbEnd' + ')');
svgPath.attr('marker-end', 'url(' + url + '#' + id + '_' + diagramType + '-barbEnd' + ')');
break;
case 'arrow_circle':
svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-circleEnd' + ')');
svgPath.attr('marker-end', 'url(' + url + '#' + id + '_' + diagramType + '-circleEnd' + ')');
break;
case 'aggregation':
svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-aggregationEnd' + ')');
svgPath.attr(
'marker-end',
'url(' + url + '#' + id + '_' + diagramType + '-aggregationEnd' + ')'
);
break;
case 'extension':
svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-extensionEnd' + ')');
svgPath.attr(
'marker-end',
'url(' + url + '#' + id + '_' + diagramType + '-extensionEnd' + ')'
);
break;
case 'composition':
svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-compositionEnd' + ')');
svgPath.attr(
'marker-end',
'url(' + url + '#' + id + '_' + diagramType + '-compositionEnd' + ')'
);
break;
case 'dependency':
svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-dependencyEnd' + ')');
svgPath.attr(
'marker-end',
'url(' + url + '#' + id + '_' + diagramType + '-dependencyEnd' + ')'
);
break;
case 'lollipop':
svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-lollipopEnd' + ')');
svgPath.attr(
'marker-end',
'url(' + url + '#' + id + '_' + diagramType + '-lollipopEnd' + ')'
);
break;
default:
}

View File

@@ -14,7 +14,7 @@ import { insertCluster, clear as clearClusters } from './clusters.js';
import { insertEdgeLabel, positionEdgeLabel, insertEdge, clear as clearEdges } from './edges.js';
import { log } from '../logger.js';
const recursiveRender = async (_elem, graph, diagramtype, parentCluster) => {
const recursiveRender = async (_elem, graph, diagramtype, id, parentCluster) => {
log.info('Graph in recursive render: XXX', graphlibJson.write(graph), parentCluster);
const dir = graph.graph().rankdir;
log.trace('Dir in recursive render - dir:', dir);
@@ -52,7 +52,7 @@ const recursiveRender = async (_elem, graph, diagramtype, parentCluster) => {
if (node && node.clusterNode) {
// const children = graph.children(v);
log.info('Cluster identified', v, node.width, graph.node(v));
const o = await recursiveRender(nodes, node.graph, diagramtype, graph.node(v));
const o = await recursiveRender(nodes, node.graph, diagramtype, id, graph.node(v));
const newEl = o.elem;
updateNodeBounds(node, newEl);
node.diff = o.diff || 0;
@@ -134,7 +134,7 @@ const recursiveRender = async (_elem, graph, diagramtype, parentCluster) => {
const edge = graph.edge(e);
log.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(edge), edge);
const paths = insertEdge(edgePaths, e, edge, clusterDb, diagramtype, graph);
const paths = insertEdge(edgePaths, e, edge, clusterDb, diagramtype, graph, id);
positionEdgeLabel(edge, paths);
});
@@ -155,11 +155,11 @@ export const render = async (elem, graph, markers, diagramtype, id) => {
clearClusters();
clearGraphlib();
log.warn('Graph at first:', graphlibJson.write(graph));
log.warn('Graph at first:', JSON.stringify(graphlibJson.write(graph)));
adjustClustersAndEdges(graph);
log.warn('Graph after:', graphlibJson.write(graph));
log.warn('Graph after:', JSON.stringify(graphlibJson.write(graph)));
// log.warn('Graph ever after:', graphlibJson.write(graph.node('A').graph));
await recursiveRender(elem, graph, diagramtype);
await recursiveRender(elem, graph, diagramtype, id);
};
// const shapeDefinitions = {};

View File

@@ -14,9 +14,9 @@ const extension = (elem, type, id) => {
elem
.append('defs')
.append('marker')
.attr('id', type + '-extensionStart')
.attr('id', id + '_' + type + '-extensionStart')
.attr('class', 'marker extension ' + type)
.attr('refX', 0)
.attr('refX', 18)
.attr('refY', 7)
.attr('markerWidth', 190)
.attr('markerHeight', 240)
@@ -27,9 +27,9 @@ const extension = (elem, type, id) => {
elem
.append('defs')
.append('marker')
.attr('id', type + '-extensionEnd')
.attr('id', id + '_' + type + '-extensionEnd')
.attr('class', 'marker extension ' + type)
.attr('refX', 19)
.attr('refX', 1)
.attr('refY', 7)
.attr('markerWidth', 20)
.attr('markerHeight', 28)
@@ -38,13 +38,13 @@ const extension = (elem, type, id) => {
.attr('d', 'M 1,1 V 13 L18,7 Z'); // this is actual shape for arrowhead
};
const composition = (elem, type) => {
const composition = (elem, type, id) => {
elem
.append('defs')
.append('marker')
.attr('id', type + '-compositionStart')
.attr('id', id + '_' + type + '-compositionStart')
.attr('class', 'marker composition ' + type)
.attr('refX', 0)
.attr('refX', 18)
.attr('refY', 7)
.attr('markerWidth', 190)
.attr('markerHeight', 240)
@@ -55,9 +55,9 @@ const composition = (elem, type) => {
elem
.append('defs')
.append('marker')
.attr('id', type + '-compositionEnd')
.attr('id', id + '_' + type + '-compositionEnd')
.attr('class', 'marker composition ' + type)
.attr('refX', 19)
.attr('refX', 1)
.attr('refY', 7)
.attr('markerWidth', 20)
.attr('markerHeight', 28)
@@ -65,13 +65,13 @@ const composition = (elem, type) => {
.append('path')
.attr('d', 'M 18,7 L9,13 L1,7 L9,1 Z');
};
const aggregation = (elem, type) => {
const aggregation = (elem, type, id) => {
elem
.append('defs')
.append('marker')
.attr('id', type + '-aggregationStart')
.attr('id', id + '_' + type + '-aggregationStart')
.attr('class', 'marker aggregation ' + type)
.attr('refX', 0)
.attr('refX', 18)
.attr('refY', 7)
.attr('markerWidth', 190)
.attr('markerHeight', 240)
@@ -82,9 +82,9 @@ const aggregation = (elem, type) => {
elem
.append('defs')
.append('marker')
.attr('id', type + '-aggregationEnd')
.attr('id', id + '_' + type + '-aggregationEnd')
.attr('class', 'marker aggregation ' + type)
.attr('refX', 19)
.attr('refX', 1)
.attr('refY', 7)
.attr('markerWidth', 20)
.attr('markerHeight', 28)
@@ -92,13 +92,13 @@ const aggregation = (elem, type) => {
.append('path')
.attr('d', 'M 18,7 L9,13 L1,7 L9,1 Z');
};
const dependency = (elem, type) => {
const dependency = (elem, type, id) => {
elem
.append('defs')
.append('marker')
.attr('id', type + '-dependencyStart')
.attr('id', id + '_' + type + '-dependencyStart')
.attr('class', 'marker dependency ' + type)
.attr('refX', 0)
.attr('refX', 6)
.attr('refY', 7)
.attr('markerWidth', 190)
.attr('markerHeight', 240)
@@ -109,9 +109,9 @@ const dependency = (elem, type) => {
elem
.append('defs')
.append('marker')
.attr('id', type + '-dependencyEnd')
.attr('id', id + '_' + type + '-dependencyEnd')
.attr('class', 'marker dependency ' + type)
.attr('refX', 19)
.attr('refX', 13)
.attr('refY', 7)
.attr('markerWidth', 20)
.attr('markerHeight', 28)
@@ -119,31 +119,48 @@ const dependency = (elem, type) => {
.append('path')
.attr('d', 'M 18,7 L9,13 L14,7 L9,1 Z');
};
const lollipop = (elem, type) => {
const lollipop = (elem, type, id) => {
elem
.append('defs')
.append('marker')
.attr('id', type + '-lollipopStart')
.attr('id', id + '_' + type + '-lollipopStart')
.attr('class', 'marker lollipop ' + type)
.attr('refX', 0)
.attr('refX', 13)
.attr('refY', 7)
.attr('markerWidth', 190)
.attr('markerHeight', 240)
.attr('orient', 'auto')
.append('circle')
.attr('stroke', 'black')
.attr('fill', 'white')
.attr('cx', 6)
.attr('fill', 'transparent')
.attr('cx', 7)
.attr('cy', 7)
.attr('r', 6);
elem
.append('defs')
.append('marker')
.attr('id', id + '_' + type + '-lollipopEnd')
.attr('class', 'marker lollipop ' + type)
.attr('refX', 1)
.attr('refY', 7)
.attr('markerWidth', 190)
.attr('markerHeight', 240)
.attr('orient', 'auto')
.append('circle')
.attr('stroke', 'black')
.attr('fill', 'transparent')
.attr('cx', 7)
.attr('cy', 7)
.attr('r', 6);
};
const point = (elem, type) => {
const point = (elem, type, id) => {
elem
.append('marker')
.attr('id', type + '-pointEnd')
.attr('id', id + '_' + type + '-pointEnd')
.attr('class', 'marker ' + type)
.attr('viewBox', '0 0 10 10')
.attr('refX', 10)
.attr('refX', 6)
.attr('refY', 5)
.attr('markerUnits', 'userSpaceOnUse')
.attr('markerWidth', 12)
@@ -156,10 +173,10 @@ const point = (elem, type) => {
.style('stroke-dasharray', '1,0');
elem
.append('marker')
.attr('id', type + '-pointStart')
.attr('id', id + '_' + type + '-pointStart')
.attr('class', 'marker ' + type)
.attr('viewBox', '0 0 10 10')
.attr('refX', 0)
.attr('refX', 4.5)
.attr('refY', 5)
.attr('markerUnits', 'userSpaceOnUse')
.attr('markerWidth', 12)
@@ -171,10 +188,10 @@ const point = (elem, type) => {
.style('stroke-width', 1)
.style('stroke-dasharray', '1,0');
};
const circle = (elem, type) => {
const circle = (elem, type, id) => {
elem
.append('marker')
.attr('id', type + '-circleEnd')
.attr('id', id + '_' + type + '-circleEnd')
.attr('class', 'marker ' + type)
.attr('viewBox', '0 0 10 10')
.attr('refX', 11)
@@ -193,7 +210,7 @@ const circle = (elem, type) => {
elem
.append('marker')
.attr('id', type + '-circleStart')
.attr('id', id + '_' + type + '-circleStart')
.attr('class', 'marker ' + type)
.attr('viewBox', '0 0 10 10')
.attr('refX', -1)
@@ -210,10 +227,10 @@ const circle = (elem, type) => {
.style('stroke-width', 1)
.style('stroke-dasharray', '1,0');
};
const cross = (elem, type) => {
const cross = (elem, type, id) => {
elem
.append('marker')
.attr('id', type + '-crossEnd')
.attr('id', id + '_' + type + '-crossEnd')
.attr('class', 'marker cross ' + type)
.attr('viewBox', '0 0 11 11')
.attr('refX', 12)
@@ -231,7 +248,7 @@ const cross = (elem, type) => {
elem
.append('marker')
.attr('id', type + '-crossStart')
.attr('id', id + '_' + type + '-crossStart')
.attr('class', 'marker cross ' + type)
.attr('viewBox', '0 0 11 11')
.attr('refX', -1)
@@ -247,11 +264,11 @@ const cross = (elem, type) => {
.style('stroke-width', 2)
.style('stroke-dasharray', '1,0');
};
const barb = (elem, type) => {
const barb = (elem, type, id) => {
elem
.append('defs')
.append('marker')
.attr('id', type + '-barbEnd')
.attr('id', id + '_' + type + '-barbEnd')
.attr('refX', 19)
.attr('refY', 7)
.attr('markerWidth', 20)

View File

@@ -291,8 +291,8 @@ export const adjustClustersAndEdges = (graph, depth) => {
shape: 'labelRect',
style: '',
});
const edge1 = JSON.parse(JSON.stringify(edge));
const edge2 = JSON.parse(JSON.stringify(edge));
const edge1 = structuredClone(edge);
const edge2 = structuredClone(edge);
edge1.label = '';
edge1.arrowTypeEnd = 'none';
edge2.label = '';

View File

@@ -4,5 +4,5 @@
* @returns cleaned text
*/
export const cleanupComments = (text: string): string => {
return text.trimStart().replace(/^\s*%%(?!{)[^\n]+\n?/gm, '');
return text.replace(/^\s*%%(?!{)[^\n]+\n?/gm, '').trimStart();
};

View File

@@ -43,7 +43,11 @@ export const addDiagrams = () => {
},
},
styles: {}, // should never be used
renderer: {}, // should never be used
renderer: {
draw: () => {
// should never be used
},
},
parser: {
parser: { yy: {} },
parse: () => {

View File

@@ -41,7 +41,11 @@ describe('DiagramAPI', () => {
},
parser: { yy: {} },
},
renderer: {},
renderer: {
draw: () => {
// no-op
},
},
styles: {},
},
detector

View File

@@ -5,8 +5,7 @@ import { sanitizeText as _sanitizeText } from '../diagrams/common/common.js';
import { setupGraphViewbox as _setupGraphViewbox } from '../setupGraphViewbox.js';
import { addStylesForDiagram } from '../styles.js';
import type { DiagramDefinition, DiagramDetector } from './types.js';
import * as _commonDb from '../commonDb.js';
import { parseDirective as _parseDirective } from '../directiveUtils.js';
import * as _commonDb from '../diagrams/common/commonDb.js';
/*
Packaging and exposing resources for external diagrams so that they can import
@@ -21,8 +20,6 @@ export const setupGraphViewbox = _setupGraphViewbox;
export const getCommonDb = () => {
return _commonDb;
};
export const parseDirective = (p: any, statement: string, context: string, type: string) =>
_parseDirective(p, statement, context, type);
const diagrams: Record<string, DiagramDefinition> = {};
export interface Detectors {
@@ -52,17 +49,18 @@ export const registerDiagram = (
}
addStylesForDiagram(id, diagram.styles);
if (diagram.injectUtils) {
diagram.injectUtils(
log,
setLogLevel,
getConfig,
sanitizeText,
setupGraphViewbox,
getCommonDb(),
parseDirective
);
}
diagram.injectUtils?.(
log,
setLogLevel,
getConfig,
sanitizeText,
setupGraphViewbox,
getCommonDb(),
() => {
// parseDirective is removed in https://github.com/mermaid-js/mermaid/pull/4759.
// This is a no-op for legacy support.
}
);
};
export const getDiagram = (name: string): DiagramDefinition => {

View File

@@ -1,84 +1,139 @@
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');
expect(extractFrontMatter('diagram')).toMatchInlineSnapshot(`
{
"metadata": {},
"text": "diagram",
}
`);
});
it('returns text unchanged if frontmatter lacks closing delimiter', () => {
const text = `---\ntitle: foo\ndiagram`;
expect(extractFrontMatter(text, dbMock())).toEqual(text);
expect(extractFrontMatter(text)).toMatchInlineSnapshot(`
{
"metadata": {},
"text": "---
title: foo
diagram",
}
`);
});
it('handles empty frontmatter', () => {
const db = dbMock();
const text = `---\n\n---\ndiagram`;
expect(extractFrontMatter(text, db)).toEqual('diagram');
expect(db.setDiagramTitle).not.toHaveBeenCalled();
expect(extractFrontMatter(text)).toMatchInlineSnapshot(`
{
"metadata": {},
"text": "diagram",
}
`);
});
it('handles frontmatter without mappings', () => {
const db = dbMock();
const text = `---\n1\n---\ndiagram`;
expect(extractFrontMatter(text, db)).toEqual('diagram');
expect(db.setDiagramTitle).not.toHaveBeenCalled();
expect(extractFrontMatter(`---\n1\n---\ndiagram`)).toMatchInlineSnapshot(`
{
"metadata": {},
"text": "diagram",
}
`);
expect(extractFrontMatter(`---\n-1\n-2\n---\ndiagram`)).toMatchInlineSnapshot(`
{
"metadata": {},
"text": "diagram",
}
`);
expect(extractFrontMatter(`---\nnull\n---\ndiagram`)).toMatchInlineSnapshot(`
{
"metadata": {},
"text": "diagram",
}
`);
});
it('does not try to parse frontmatter at the end', () => {
const db = dbMock();
const text = `diagram\n---\ntitle: foo\n---\n`;
expect(extractFrontMatter(text, db)).toEqual(text);
expect(db.setDiagramTitle).not.toHaveBeenCalled();
expect(extractFrontMatter(text)).toMatchInlineSnapshot(`
{
"metadata": {},
"text": "diagram
---
title: foo
---
",
}
`);
});
it('handles frontmatter with multiple delimiters', () => {
const db = dbMock();
const text = `---\ntitle: foo---bar\n---\ndiagram\n---\ntest`;
expect(extractFrontMatter(text, db)).toEqual('diagram\n---\ntest');
expect(db.setDiagramTitle).toHaveBeenCalledWith('foo---bar');
expect(extractFrontMatter(text)).toMatchInlineSnapshot(`
{
"metadata": {
"title": "foo---bar",
},
"text": "diagram
---
test",
}
`);
});
it('handles frontmatter with multi-line string and multiple delimiters', () => {
const db = dbMock();
const text = `---\ntitle: |\n multi-line string\n ---\n---\ndiagram`;
expect(extractFrontMatter(text, db)).toEqual('diagram');
expect(db.setDiagramTitle).toHaveBeenCalledWith('multi-line string\n---\n');
expect(extractFrontMatter(text)).toMatchInlineSnapshot(`
{
"metadata": {
"title": "multi-line string
---
",
},
"text": "diagram",
}
`);
});
it('handles frontmatter with title', () => {
const db = dbMock();
const text = `---\ntitle: foo\n---\ndiagram`;
expect(extractFrontMatter(text, db)).toEqual('diagram');
expect(db.setDiagramTitle).toHaveBeenCalledWith('foo');
expect(extractFrontMatter(text)).toMatchInlineSnapshot(`
{
"metadata": {
"title": "foo",
},
"text": "diagram",
}
`);
});
it('handles booleans in frontmatter properly', () => {
const db = dbMock();
const text = `---\ntitle: true\n---\ndiagram`;
expect(extractFrontMatter(text, db)).toEqual('diagram');
expect(db.setDiagramTitle).toHaveBeenCalledWith('true');
expect(extractFrontMatter(text)).toMatchInlineSnapshot(`
{
"metadata": {
"title": "true",
},
"text": "diagram",
}
`);
});
it('ignores unspecified frontmatter keys', () => {
const db = dbMock();
const text = `---\ninvalid: true\ntitle: foo\ntest: bar\n---\ndiagram`;
expect(extractFrontMatter(text, db)).toEqual('diagram');
expect(db.setDiagramTitle).toHaveBeenCalledWith('foo');
expect(extractFrontMatter(text)).toMatchInlineSnapshot(`
{
"metadata": {
"title": "foo",
},
"text": "diagram",
}
`);
});
it('throws exception for invalid YAML syntax', () => {
const text = `---\n!!!\n---\ndiagram`;
expect(() => extractFrontMatter(text, dbMock())).toThrow(
'tag suffix cannot contain exclamation marks'
);
expect(() => extractFrontMatter(text)).toThrow('tag suffix cannot contain exclamation marks');
});
it('handles frontmatter with config', () => {
@@ -92,9 +147,25 @@ config:
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] },
});
expect(extractFrontMatter(text)).toMatchInlineSnapshot(`
{
"metadata": {
"config": {
"graph": {
"array": [
1,
2,
3,
],
"boolean": false,
"number": 14,
"string": "hello",
},
},
"title": "hello",
},
"text": "diagram",
}
`);
});
});

View File

@@ -1,6 +1,5 @@
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';
@@ -11,43 +10,51 @@ interface FrontMatterMetadata {
config?: MermaidConfig;
}
export interface FrontMatterResult {
text: string;
metadata: FrontMatterMetadata;
}
/**
* 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,
setDiagramConfig?: (config: MermaidConfig) => void
): string {
export function extractFrontMatter(text: string): FrontMatterResult {
const matches = text.match(frontMatterRegex);
if (!matches) {
return text;
return {
text,
metadata: {},
};
}
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;
let 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,
}) ?? {};
if (parsed?.title) {
// toString() is necessary because YAML could parse the title as a number/boolean
db.setDiagramTitle?.(parsed.title.toString());
// To handle runtime data type changes
parsed = typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
const metadata: FrontMatterMetadata = {};
// Only add properties that are explicitly supported, if they exist
if (parsed.displayMode) {
metadata.displayMode = parsed.displayMode.toString();
}
if (parsed.title) {
metadata.title = parsed.title.toString();
}
if (parsed.config) {
metadata.config = parsed.config;
}
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);
return {
text: text.slice(matches[0].length),
metadata,
};
}

View File

@@ -1,7 +1,13 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { Diagram } from '../Diagram.js';
import type { BaseDiagramConfig, MermaidConfig } from '../config.type.js';
import type * as d3 from 'd3';
export interface DiagramMetadata {
title?: string;
config?: MermaidConfig;
}
export interface InjectUtils {
_log: any;
_setLogLevel: any;
@@ -9,6 +15,7 @@ export interface InjectUtils {
_sanitizeText: any;
_setupGraphViewbox: any;
_commonDb: any;
/** @deprecated as directives will be pre-processed since https://github.com/mermaid-js/mermaid/pull/4759 */
_parseDirective: any;
}
@@ -32,9 +39,26 @@ export interface DiagramDB {
bindFunctions?: (element: Element) => void;
}
// This is what is returned from getClasses(...) methods.
// It is slightly renamed to ..StyleClassDef instead of just ClassDef because "class" is a greatly ambiguous and overloaded word.
// It makes it clear we're working with a style class definition, even though defining the type is currently difficult.
export interface DiagramStyleClassDef {
id: string;
styles?: string[];
textStyles?: string[];
}
export interface DiagramRenderer {
draw: DrawDefinition;
getClasses?: (
text: string,
diagram: Pick<DiagramDefinition, 'db'>
) => Record<string, DiagramStyleClassDef>;
}
export interface DiagramDefinition {
db: DiagramDB;
renderer: any;
renderer: DiagramRenderer;
parser: ParserDefinition;
styles?: any;
init?: (config: MermaidConfig) => void;
@@ -45,6 +69,7 @@ export interface DiagramDefinition {
_sanitizeText: InjectUtils['_sanitizeText'],
_setupGraphViewbox: InjectUtils['_setupGraphViewbox'],
_commonDb: InjectUtils['_commonDb'],
/** @deprecated as directives will be pre-processed since https://github.com/mermaid-js/mermaid/pull/4759 */
_parseDirective: InjectUtils['_parseDirective']
) => void;
}
@@ -76,22 +101,13 @@ export type DrawDefinition = (
id: string,
version: string,
diagramObject: Diagram
) => void;
) => void | Promise<void>;
export interface ParserDefinition {
parse: (text: string) => void;
parser: { yy: DiagramDB };
}
/**
* Type for function parse directive from diagram code.
*
* @param statement -
* @param context -
* @param type -
*/
export type ParseDirectiveDefinition = (statement: string, context: string, type: string) => void;
export type HTML = d3.Selection<HTMLIFrameElement, unknown, Element | null, unknown>;
export type SVG = d3.Selection<SVGSVGElement, unknown, Element | null, unknown>;

View File

@@ -34,7 +34,11 @@ describe('diagram detection', () => {
yy: {},
},
},
renderer: {},
renderer: {
draw: () => {
// no-op
},
},
styles: {},
},
})

View File

@@ -1,7 +1,11 @@
import mermaidAPI from '../../mermaidAPI.js';
import * as configApi from '../../config.js';
import { sanitizeText } from '../common/common.js';
import { setAccTitle, getAccTitle, getAccDescription, setAccDescription } from '../../commonDb.js';
import {
setAccTitle,
getAccTitle,
getAccDescription,
setAccDescription,
} from '../common/commonDb.js';
let c4ShapeArray = [];
let boundaryParseStack = [''];
@@ -33,10 +37,6 @@ export const setC4Type = function (c4TypeParam) {
c4Type = sanitizedText;
};
export const parseDirective = function (statement, context, type) {
mermaidAPI.parseDirective(this, statement, context, type);
};
//type, from, to, label, ?techn, ?descr, ?sprite, ?tags, $link
export const addRel = function (type, from, to, label, techn, descr, sprite, tags, link) {
// Don't allow label nulling
@@ -816,7 +816,6 @@ export default {
getAccTitle,
getAccDescription,
setAccDescription,
parseDirective,
getConfig: () => configApi.getConfig().c4,
clear,
LINETYPE,

View File

@@ -1,17 +1,18 @@
// @ts-ignore: JISON doesn't support types
import c4Parser from './parser/c4Diagram.jison';
import c4Db from './c4Db.js';
import c4Renderer from './c4Renderer.js';
import c4Styles from './styles.js';
import parser from './parser/c4Diagram.jison';
import db from './c4Db.js';
import renderer from './c4Renderer.js';
import styles from './styles.js';
import type { MermaidConfig } from '../../config.type.js';
import type { DiagramDefinition } from '../../diagram-api/types.js';
export const diagram: DiagramDefinition = {
parser: c4Parser,
db: c4Db,
renderer: c4Renderer,
styles: c4Styles,
init: (cnf: MermaidConfig) => {
c4Renderer.setConf(cnf.c4);
parser,
db,
renderer,
styles,
init: ({ c4, wrap }: MermaidConfig) => {
renderer.setConf(c4);
db.setWrap(wrap);
},
};

View File

@@ -72,25 +72,16 @@
%x string_kv_key
%x string_kv_value
%x open_directive
%x type_directive
%x arg_directive
%x close_directive
%x acc_title
%x acc_descr
%x acc_descr_multiline
%%
\%\%\{ { this.begin('open_directive'); return 'open_directive'; }
.*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';
<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';
"title"\s[^#\n;]+ return 'title';
@@ -207,7 +198,6 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multiline");}
start
: mermaidDoc
| direction
| directive start
;
direction
@@ -225,26 +215,6 @@ mermaidDoc
: graphConfig
;
directive
: openDirective typeDirective closeDirective NEWLINE
| openDirective typeDirective ':' argDirective closeDirective NEWLINE
;
openDirective
: open_directive { yy.parseDirective('%%{', 'open_directive'); }
;
typeDirective
: type_directive { }
;
argDirective
: arg_directive { $1 = $1.trim().replace(/'/g, '"'); yy.parseDirective($1, 'arg_directive'); }
;
closeDirective
: close_directive { yy.parseDirective('}%%', 'close_directive', 'c4Context'); }
;
graphConfig
: C4_CONTEXT NEWLINE statements EOF {yy.setC4Type($1)}

View File

@@ -1,11 +1,9 @@
// @ts-nocheck - don't check until handle it
import type { Selection } from 'd3';
import { select } from 'd3';
import { log } from '../../logger.js';
import * as configApi from '../../config.js';
import common from '../common/common.js';
import utils from '../../utils.js';
import mermaidAPI from '../../mermaidAPI.js';
import {
setAccTitle,
getAccTitle,
@@ -14,7 +12,7 @@ import {
clear as commonClear,
setDiagramTitle,
getDiagramTitle,
} from '../../commonDb.js';
} from '../common/commonDb.js';
import { ClassMember } from './classTypes.js';
import type {
ClassRelation,
@@ -38,12 +36,8 @@ let functions: any[] = [];
const sanitizeText = (txt: string) => common.sanitizeText(txt, configApi.getConfig());
export const parseDirective = function (statement: string, context: string, type: string) {
// @ts-ignore Don't wanna mess it up
mermaidAPI.parseDirective(this, statement, context, type);
};
const splitClassNameAndType = function (id: string) {
const splitClassNameAndType = function (_id: string) {
const id = common.sanitizeText(_id, configApi.getConfig());
let genericType = '';
let className = id;
@@ -56,7 +50,8 @@ const splitClassNameAndType = function (id: string) {
return { className: className, type: genericType };
};
export const setClassLabel = function (id: string, label: string) {
export const setClassLabel = function (_id: string, label: string) {
const id = common.sanitizeText(_id, configApi.getConfig());
if (label) {
label = sanitizeText(label);
}
@@ -71,22 +66,25 @@ export const setClassLabel = function (id: string, label: string) {
* @param id - Id of the class to add
* @public
*/
export const addClass = function (id: string) {
const classId = splitClassNameAndType(id);
export const addClass = function (_id: string) {
const id = common.sanitizeText(_id, configApi.getConfig());
const { className, type } = splitClassNameAndType(id);
// Only add class if not exists
if (classes[classId.className] !== undefined) {
if (Object.hasOwn(classes, className)) {
return;
}
classes[classId.className] = {
id: classId.className,
type: classId.type,
label: classId.className,
// alert('Adding class: ' + className);
const name = common.sanitizeText(className, configApi.getConfig());
// alert('Adding class after: ' + name);
classes[name] = {
id: name,
type: type,
label: name,
cssClasses: [],
methods: [],
members: [],
annotations: [],
domId: MERMAID_DOM_ID_PREFIX + classId.className + '-' + classCounter,
domId: MERMAID_DOM_ID_PREFIX + name + '-' + classCounter,
} as ClassNode;
classCounter++;
@@ -98,7 +96,8 @@ export const addClass = function (id: string) {
* @param id - class ID to lookup
* @public
*/
export const lookUpDomId = function (id: string): string {
export const lookUpDomId = function (_id: string): string {
const id = common.sanitizeText(_id, configApi.getConfig());
if (id in classes) {
return classes[id].domId;
}
@@ -176,6 +175,8 @@ export const addAnnotation = function (className: string, annotation: string) {
* @public
*/
export const addMember = function (className: string, member: string) {
addClass(className);
const validatedClassName = splitClassNameAndType(className).className;
const theClass = classes[validatedClassName];
@@ -301,7 +302,8 @@ export const setClickEvent = function (ids: string, functionName: string, functi
setCssClass(ids, 'clickable');
};
const setClickFunc = function (domId: string, functionName: string, functionArgs: string) {
const setClickFunc = function (_domId: string, functionName: string, functionArgs: string) {
const domId = common.sanitizeText(_domId, configApi.getConfig());
const config = configApi.getConfig();
if (config.securityLevel !== 'loose') {
return;
@@ -370,6 +372,7 @@ export const relationType = {
const setupToolTips = function (element: Element) {
let tooltipElem: Selection<HTMLDivElement, unknown, HTMLElement, unknown> =
select('.mermaidTooltip');
// @ts-expect-error - Incorrect types
if ((tooltipElem._groups || tooltipElem)[0][0] === null) {
tooltipElem = select('body').append('div').attr('class', 'mermaidTooltip').style('opacity', 0);
}
@@ -379,7 +382,6 @@ const setupToolTips = function (element: Element) {
const nodes = svg.selectAll('g.node');
nodes
.on('mouseover', function () {
// @ts-expect-error - select is not part of the d3 type definition
const el = select(this);
const title = el.attr('title');
// Don't try to draw a tooltip if no data is provided
@@ -389,6 +391,7 @@ const setupToolTips = function (element: Element) {
// @ts-ignore - getBoundingClientRect is not part of the d3 type definition
const rect = this.getBoundingClientRect();
// @ts-expect-error - Incorrect types
tooltipElem.transition().duration(200).style('opacity', '.9');
tooltipElem
.text(el.attr('title'))
@@ -398,8 +401,8 @@ const setupToolTips = function (element: Element) {
el.classed('hover', true);
})
.on('mouseout', function () {
// @ts-expect-error - Incorrect types
tooltipElem.transition().duration(500).style('opacity', 0);
// @ts-expect-error - select is not part of the d3 type definition
const el = select(this);
el.classed('hover', false);
});
@@ -458,7 +461,6 @@ export const addClassesToNamespace = function (id: string, classNames: string[])
};
export default {
parseDirective,
setAccTitle,
getAccTitle,
getAccDescription,

View File

@@ -813,6 +813,20 @@ describe('given a class diagram with members and methods ', function () {
parser.parse(str);
});
it('should handle direct member declaration', function () {
parser.parse('classDiagram\n' + 'Car : wheels');
const car = classDb.getClass('Car');
expect(car.members.length).toBe(1);
expect(car.members[0].id).toBe('wheels');
});
it('should handle direct member declaration with type', function () {
parser.parse('classDiagram\n' + 'Car : int wheels');
const car = classDb.getClass('Car');
expect(car.members.length).toBe(1);
expect(car.members[0].id).toBe('int wheels');
});
it('should handle simple member declaration with type', function () {
const str = 'classDiagram\n' + 'class Car\n' + 'Car : int wheels';

View File

@@ -8,7 +8,8 @@ import utils from '../../utils.js';
import { interpolateToCurve, getStylesFromArray } from '../../utils.js';
import { setupGraphViewbox } from '../../setupGraphViewbox.js';
import common from '../common/common.js';
import type { ClassRelation, ClassNote, ClassMap, EdgeData, NamespaceMap } from './classTypes.js';
import type { ClassRelation, ClassNote, ClassMap, NamespaceMap } from './classTypes.js';
import type { EdgeData } from '../../types.js';
const sanitizeText = (txt: string) => common.sanitizeText(txt, getConfig());

View File

@@ -137,24 +137,6 @@ export interface ClassNote {
text: string;
}
export interface EdgeData {
arrowheadStyle?: string;
labelpos?: string;
labelType?: string;
label?: string;
classes: string;
pattern: string;
id: string;
arrowhead: string;
startLabelRight: string;
endLabelLeft: string;
arrowTypeStart: string;
arrowTypeEnd: string;
style: string;
labelStyle: string;
curve: any;
}
export type ClassRelation = {
id1: string;
id2: string;

View File

@@ -13,9 +13,6 @@
%x href
%x callback_name
%x callback_args
%x open_directive
%x type_directive
%x arg_directive
%x acc_title
%x acc_descr
%x acc_descr_multiline
@@ -24,15 +21,10 @@
%x namespace
%x namespace-body
%%
\%\%\{ { this.begin('open_directive'); return 'open_directive'; }
.*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';
<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';
\%\%(?!\{)*[^\n]*(\r?\n?)+ /* skip comments */
\%\%[^\n]*(\r?\n)* /* skip comments */
accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; }
@@ -220,7 +212,6 @@ line was introduced with 'click'.
start
: mermaidDoc
| directive start
| statements
;
@@ -232,27 +223,6 @@ graphConfig
: CLASS_DIAGRAM NEWLINE statements EOF
;
directive
: openDirective typeDirective closeDirective NEWLINE
| openDirective typeDirective ':' argDirective closeDirective NEWLINE
;
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', 'class'); }
;
statements
: statement
| statement NEWLINE

View File

@@ -109,25 +109,25 @@ g.classGroup line {
}
#extensionStart, .extension {
fill: ${options.mainBkg} !important;
fill: transparent !important;
stroke: ${options.lineColor} !important;
stroke-width: 1;
}
#extensionEnd, .extension {
fill: ${options.mainBkg} !important;
fill: transparent !important;
stroke: ${options.lineColor} !important;
stroke-width: 1;
}
#aggregationStart, .aggregation {
fill: ${options.mainBkg} !important;
fill: transparent !important;
stroke: ${options.lineColor} !important;
stroke-width: 1;
}
#aggregationEnd, .aggregation {
fill: ${options.mainBkg} !important;
fill: transparent !important;
stroke: ${options.lineColor} !important;
stroke-width: 1;
}

View File

@@ -1,4 +1,4 @@
import { sanitizeText, removeScript, parseGenericTypes } from './common.js';
import { sanitizeText, removeScript, parseGenericTypes, countOccurrence } from './common.js';
describe('when securityLevel is antiscript, all script must be removed', () => {
/**
@@ -59,15 +59,29 @@ describe('Sanitize text', () => {
});
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(
'test<Array<Array<string[]>>>'
);
expect(parseGenericTypes('test ~Array~Array~string[]~~~')).toEqual(
'test <Array<Array<string[]>>>'
);
expect(parseGenericTypes('~test')).toEqual('~test');
it.each([
['test~T~', 'test<T>'],
['test~Array~Array~string~~~', 'test<Array<Array<string>>>'],
['test~Array~Array~string[]~~~', 'test<Array<Array<string[]>>>'],
['test ~Array~Array~string[]~~~', 'test <Array<Array<string[]>>>'],
['~test', '~test'],
['~test~T~', '~test<T>'],
])('should parse generic types: %s to %s', (input: string, expected: string) => {
expect(parseGenericTypes(input)).toEqual(expected);
});
});
it.each([
['', '', 0],
['', 'x', 0],
['test', 'x', 0],
['test', 't', 2],
['test', 'te', 1],
['test~T~', '~', 2],
['test~Array~Array~string~~~', '~', 6],
])(
'should count `%s` to contain occurrences of `%s` to be `%i`',
(str: string, substring: string, count: number) => {
expect(countOccurrence(str, substring)).toEqual(count);
}
);

View File

@@ -208,21 +208,33 @@ export const parseGenericTypes = function (input: string): string {
return output.join('');
};
export const countOccurrence = (string: string, substring: string): number => {
return Math.max(0, string.split(substring).length - 1);
};
const shouldCombineSets = (previousSet: string, nextSet: string): boolean => {
const prevCount = [...previousSet].reduce((count, char) => (char === '~' ? count + 1 : count), 0);
const nextCount = [...nextSet].reduce((count, char) => (char === '~' ? count + 1 : count), 0);
const prevCount = countOccurrence(previousSet, '~');
const nextCount = countOccurrence(nextSet, '~');
return prevCount === 1 && nextCount === 1;
};
const processSet = (input: string): string => {
const chars = [...input];
const tildeCount = chars.reduce((count, char) => (char === '~' ? count + 1 : count), 0);
const tildeCount = countOccurrence(input, '~');
let hasStartingTilde = false;
if (tildeCount <= 1) {
return input;
}
// If there is an odd number of tildes, and the input starts with a tilde, we need to remove it and add it back in later
if (tildeCount % 2 !== 0 && input.startsWith('~')) {
input = input.substring(1);
hasStartingTilde = true;
}
const chars = [...input];
let first = chars.indexOf('~');
let last = chars.lastIndexOf('~');
@@ -234,6 +246,11 @@ const processSet = (input: string): string => {
last = chars.lastIndexOf('~');
}
// Add the starting tilde back in if we removed it
if (hasStartingTilde) {
chars.unshift('~');
}
return chars.join('');
};

View File

@@ -0,0 +1,32 @@
import { sanitizeText as _sanitizeText } from './common.js';
import { getConfig } from '../../config.js';
let accTitle = '';
let diagramTitle = '';
let accDescription = '';
const sanitizeText = (txt: string): string => _sanitizeText(txt, getConfig());
export const clear = (): void => {
accTitle = '';
accDescription = '';
diagramTitle = '';
};
export const setAccTitle = (txt: string): void => {
accTitle = sanitizeText(txt).replace(/^\s+/g, '');
};
export const getAccTitle = (): string => accTitle;
export const setAccDescription = (txt: string): void => {
accDescription = sanitizeText(txt).replace(/\n\s+/g, '\n');
};
export const getAccDescription = (): string => accDescription;
export const setDiagramTitle = (txt: string): void => {
diagramTitle = sanitizeText(txt);
};
export const getDiagramTitle = (): string => diagramTitle;

View File

@@ -1,5 +1,4 @@
import { log } from '../../logger.js';
import mermaidAPI from '../../mermaidAPI.js';
import * as configApi from '../../config.js';
import {
@@ -10,7 +9,7 @@ import {
clear as commonClear,
setDiagramTitle,
getDiagramTitle,
} from '../../commonDb.js';
} from '../common/commonDb.js';
let entities = {};
let relationships = [];
@@ -28,10 +27,6 @@ const Identification = {
IDENTIFYING: 'IDENTIFYING',
};
export const parseDirective = function (statement, context, type) {
mermaidAPI.parseDirective(this, statement, context, type);
};
const addEntity = function (name, alias = undefined) {
if (entities[name] === undefined) {
entities[name] = { attributes: [], alias: alias };
@@ -88,7 +83,6 @@ const clear = function () {
export default {
Cardinality,
Identification,
parseDirective,
getConfig: () => configApi.getConfig().er,
addEntity,
addAttributes,

View File

@@ -1,7 +1,7 @@
%lex
%options case-insensitive
%x open_directive type_directive arg_directive block
%x block
%x acc_title
%x acc_descr
%x acc_descr_multiline
@@ -14,11 +14,6 @@ accDescr\s*":"\s* { this.begin("ac
accDescr\s*"{"\s* { this.begin("acc_descr_multiline");}
<acc_descr_multiline>[\}] { this.popState(); }
<acc_descr_multiline>[^\}]* return "acc_descr_multiline_value";
\%\%\{ { 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';
[\n]+ return 'NEWLINE';
\s+ /* skip whitespace */
[\s]+ return 'SPACE';
@@ -66,7 +61,7 @@ o\{ return 'ZERO_OR_MORE';
"optionally to" return 'NON_IDENTIFYING';
\.\- return 'NON_IDENTIFYING';
\-\. return 'NON_IDENTIFYING';
[A-Za-z][A-Za-z0-9\-_]* return 'ALPHANUM';
[A-Za-z_][A-Za-z0-9\-_]* return 'ALPHANUM';
. return yytext[0];
<<EOF>> return 'EOF';
@@ -77,7 +72,6 @@ o\{ return 'ZERO_OR_MORE';
start
: 'ER_DIAGRAM' document 'EOF' { /*console.log('finished parsing');*/ }
| directive start
;
document
@@ -92,14 +86,9 @@ line
| EOF { $$=[];}
;
directive
: openDirective typeDirective closeDirective 'NEWLINE'
| openDirective typeDirective ':' argDirective closeDirective 'NEWLINE'
;
statement
: directive
| entityName relSpec entityName ':' role
: entityName relSpec entityName ':' role
{
yy.addEntity($1);
yy.addEntity($3);
@@ -191,20 +180,4 @@ role
| 'ALPHANUM' { $$ = $1; }
;
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', 'er'); }
;
%%

View File

@@ -33,7 +33,7 @@ describe('when parsing ER diagram it...', function () {
describe('has non A-Za-z0-9_- chars', function () {
// these were entered using the Mac keyboard utility.
const chars =
"~ ` ! @ # $ ^ & * ( ) - _ = + [ ] { } | / ; : ' . ? ¡ ™ € £ ¢ ∞ fi § ‡ • ° ª · º ≠ ± œ Œ ∑ „ ® † ˇ ¥ Á ¨ ˆ ˆ Ø π ∏ “ « » å Å ß Í ∂ Î ƒ Ï © ˙ Ó ∆ Ô ˚  ¬ Ò … Ú æ Æ Ω ¸ ≈ π ˛ ç Ç √ ◊ ∫ ı ˜ µ  ≤ ¯ ≥ ˘ ÷ ¿";
"~ ` ! @ # $ ^ & * ( ) - = + [ ] { } | / ; : ' . ? ¡ ™ € £ ¢ ∞ fi § ‡ • ° ª · º ≠ ± œ Œ ∑ „ ® † ˇ ¥ Á ¨ ˆ ˆ Ø π ∏ “ « » å Å ß Í ∂ Î ƒ Ï © ˙ Ó ∆ Ô ˚  ¬ Ò … Ú æ Æ Ω ¸ ≈ π ˛ ç Ç √ ◊ ∫ ı ˜ µ  ≤ ¯ ≥ ˘ ÷ ¿";
const allowed = chars.split(' ');
allowed.forEach((allowedChar) => {
@@ -170,6 +170,13 @@ describe('when parsing ER diagram it...', function () {
expect(entities[firstEntity].alias).toBe(alias);
expect(entities[secondEntity].alias).toBeUndefined();
});
it('can start with an underscore', function () {
const entity = '_foo';
erDiagram.parser.parse(`erDiagram\n${entity}\n`);
const entities = erDb.getEntities();
expect(entities.hasOwnProperty(entity)).toBe(true);
});
});
describe('attribute name', () => {

View File

@@ -4,13 +4,14 @@ import insertMarkers from '../../../dagre-wrapper/markers.js';
import { insertEdgeLabel } from '../../../dagre-wrapper/edges.js';
import { findCommonAncestor } from './render-utils.js';
import { labelHelper } from '../../../dagre-wrapper/shapes/util.js';
import { addHtmlLabel } from 'dagre-d3-es/src/dagre-js/label/add-html-label.js';
import { getConfig } from '../../../config.js';
import { log } from '../../../logger.js';
import { setupGraphViewbox } from '../../../setupGraphViewbox.js';
import common, { evaluate } from '../../common/common.js';
import common from '../../common/common.js';
import { interpolateToCurve, getStylesFromArray } from '../../../utils.js';
import ELK from 'elkjs/lib/elk.bundled.js';
import { getLineFunctionsWithOffset } from '../../../utils/lineWithOffset.js';
const elk = new ELK();
let portPos = {};
@@ -568,8 +569,9 @@ export const addEdges = function (edges, diagObj, graph, svg) {
* @param edgeData
* @param diagramType
* @param arrowMarkerAbsolute
* @param id
*/
const addMarkersToEdge = function (svgPath, edgeData, diagramType, arrowMarkerAbsolute) {
const addMarkersToEdge = function (svgPath, edgeData, diagramType, arrowMarkerAbsolute, id) {
let url = '';
// Check configuration for absolute path
if (arrowMarkerAbsolute) {
@@ -586,61 +588,103 @@ const addMarkersToEdge = function (svgPath, edgeData, diagramType, arrowMarkerAb
// look in edge data and decide which marker to use
switch (edgeData.arrowTypeStart) {
case 'arrow_cross':
svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-crossStart' + ')');
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-crossStart' + ')'
);
break;
case 'arrow_point':
svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-pointStart' + ')');
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-pointStart' + ')'
);
break;
case 'arrow_barb':
svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-barbStart' + ')');
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-barbStart' + ')'
);
break;
case 'arrow_circle':
svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-circleStart' + ')');
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-circleStart' + ')'
);
break;
case 'aggregation':
svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-aggregationStart' + ')');
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-aggregationStart' + ')'
);
break;
case 'extension':
svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-extensionStart' + ')');
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-extensionStart' + ')'
);
break;
case 'composition':
svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-compositionStart' + ')');
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-compositionStart' + ')'
);
break;
case 'dependency':
svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-dependencyStart' + ')');
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-dependencyStart' + ')'
);
break;
case 'lollipop':
svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-lollipopStart' + ')');
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-lollipopStart' + ')'
);
break;
default:
}
switch (edgeData.arrowTypeEnd) {
case 'arrow_cross':
svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-crossEnd' + ')');
svgPath.attr('marker-end', 'url(' + url + '#' + id + '_' + diagramType + '-crossEnd' + ')');
break;
case 'arrow_point':
svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-pointEnd' + ')');
svgPath.attr('marker-end', 'url(' + url + '#' + id + '_' + diagramType + '-pointEnd' + ')');
break;
case 'arrow_barb':
svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-barbEnd' + ')');
svgPath.attr('marker-end', 'url(' + url + '#' + id + '_' + diagramType + '-barbEnd' + ')');
break;
case 'arrow_circle':
svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-circleEnd' + ')');
svgPath.attr('marker-end', 'url(' + url + '#' + id + '_' + diagramType + '-circleEnd' + ')');
break;
case 'aggregation':
svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-aggregationEnd' + ')');
svgPath.attr(
'marker-end',
'url(' + url + '#' + id + '_' + diagramType + '-aggregationEnd' + ')'
);
break;
case 'extension':
svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-extensionEnd' + ')');
svgPath.attr(
'marker-end',
'url(' + url + '#' + id + '_' + diagramType + '-extensionEnd' + ')'
);
break;
case 'composition':
svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-compositionEnd' + ')');
svgPath.attr(
'marker-end',
'url(' + url + '#' + id + '_' + diagramType + '-compositionEnd' + ')'
);
break;
case 'dependency':
svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-dependencyEnd' + ')');
svgPath.attr(
'marker-end',
'url(' + url + '#' + id + '_' + diagramType + '-dependencyEnd' + ')'
);
break;
case 'lollipop':
svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-lollipopEnd' + ')');
svgPath.attr(
'marker-end',
'url(' + url + '#' + id + '_' + diagramType + '-lollipopEnd' + ')'
);
break;
default:
}
@@ -651,7 +695,7 @@ const addMarkersToEdge = function (svgPath, edgeData, diagramType, arrowMarkerAb
*
* @param text
* @param diagObj
* @returns {object} ClassDef styles
* @returns {Record<string, import('../../../diagram-api/types.js').DiagramStyleClassDef>} ClassDef styles
*/
export const getClasses = function (text, diagObj) {
log.info('Extracting classes');
@@ -691,7 +735,7 @@ const calcOffset = function (src, dest, parentLookupDb) {
return { x: ancestorOffset.posX, y: ancestorOffset.posY };
};
const insertEdge = function (edgesEl, edge, edgeData, diagObj, parentLookupDb) {
const insertEdge = function (edgesEl, edge, edgeData, diagObj, parentLookupDb, id) {
const offset = calcOffset(edge.sourceId, edge.targetId, parentLookupDb);
const src = edge.sections[0].startPoint;
@@ -705,8 +749,8 @@ const insertEdge = function (edgesEl, edge, edgeData, diagObj, parentLookupDb) {
[dest.x + offset.x, dest.y + offset.y],
];
// const curve = line().curve(curveBasis);
const curve = line().curve(curveLinear);
const { x, y } = getLineFunctionsWithOffset(edge.edgeData);
const curve = line().x(x).y(y).curve(curveLinear);
const edgePath = edgesEl
.insert('path')
.attr('d', curve(points))
@@ -722,7 +766,7 @@ const insertEdge = function (edgesEl, edge, edgeData, diagObj, parentLookupDb) {
'transform',
`translate(${edge.labels[0].x + offset.x}, ${edge.labels[0].y + offset.y})`
);
addMarkersToEdge(edgePath, edgeData, diagObj.type, diagObj.arrowMarkerAbsolute);
addMarkersToEdge(edgePath, edgeData, diagObj.type, diagObj.arrowMarkerAbsolute, id);
};
/**
@@ -759,8 +803,14 @@ 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',
@@ -815,7 +865,7 @@ export const draw = async function (text, id, _version, diagObj) {
const markers = ['point', 'circle', 'cross'];
// Add the marker definitions to the svg as marker tags
insertMarkers(svg, markers, diagObj.type, diagObj.arrowMarkerAbsolute);
insertMarkers(svg, markers, diagObj.type, id);
// Fetch the vertices/nodes and edges/links from the parsed graph definition
const vert = diagObj.db.getVertices();
@@ -894,7 +944,7 @@ export const draw = async function (text, id, _version, diagObj) {
drawNodes(0, 0, g.children, svg, subGraphsEl, diagObj, 0);
log.info('after layout', g);
g.edges?.map((edge) => {
insertEdge(edgesEl, edge, edge.edgeData, diagObj, parentLookupDb);
insertEdge(edgesEl, edge, edge.edgeData, diagObj, parentLookupDb, id);
});
setupGraphViewbox({}, svg, conf.diagramPadding, conf.useMaxWidth);
// Remove element after layout

View File

@@ -2,7 +2,6 @@ import { select } from 'd3';
import utils from '../../utils.js';
import * as configApi from '../../config.js';
import common from '../common/common.js';
import mermaidAPI from '../../mermaidAPI.js';
import { log } from '../../logger.js';
import {
setAccTitle,
@@ -12,7 +11,7 @@ import {
clear as commonClear,
setDiagramTitle,
getDiagramTitle,
} from '../../commonDb.js';
} from '../common/commonDb.js';
const MERMAID_DOM_ID_PREFIX = 'flowchart-';
let vertexCounter = 0;
@@ -34,10 +33,6 @@ let funs = [];
const sanitizeText = (txt) => common.sanitizeText(txt, config);
export const parseDirective = function (statement, context, type) {
mermaidAPI.parseDirective(this, statement, context, type);
};
/**
* Function to lookup domId from id in the graph definition.
*
@@ -771,7 +766,6 @@ export const lex = {
firstGraph,
};
export default {
parseDirective,
defaultConfig: () => configApi.defaultConfig.flowchart,
setAccTitle,
getAccTitle,

View File

@@ -338,7 +338,7 @@ export const addEdges = function (edges, g, diagObj) {
*
* @param text
* @param diagObj
* @returns {object} ClassDef styles
* @returns {Record<string, import('../../diagram-api/types.js').DiagramStyleClassDef>} ClassDef styles
*/
export const getClasses = function (text, diagObj) {
return diagObj.db.getClasses();

View File

@@ -269,7 +269,7 @@ export const addEdges = function (edges, g, diagObj) {
*
* @param text
* @param diagObj
* @returns {object} ClassDef styles
* @returns {Record<string, import('../../diagram-api/types.js').DiagramStyleClassDef>} ClassDef styles
*/
export const getClasses = function (text, diagObj) {
log.info('Extracting classes');

View File

@@ -23,17 +23,8 @@
%x href
%x callbackname
%x callbackargs
%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';
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'; }
@@ -272,35 +263,10 @@ that id.
%% /* language grammar */
start
: mermaidDoc
| directive start
;
directive
: openDirective typeDirective closeDirective separator
| openDirective typeDirective ':' argDirective closeDirective separator
;
openDirective
: open_directive { yy.parseDirective('%%{', 'open_directive'); }
;
typeDirective
: type_directive { yy.parseDirective($type_directive, 'type_directive'); }
;
argDirective
: arg_directive { $arg_directive = $arg_directive.trim().replace(/'/g, '"'); yy.parseDirective($arg_directive, 'arg_directive'); }
;
closeDirective
: close_directive { yy.parseDirective('}%%', 'close_directive', 'flowchart'); }
;
mermaidDoc
: graphConfig document
;
document
: /* empty */
{ $$ = [];}

View File

@@ -6,7 +6,6 @@ import dayjsAdvancedFormat from 'dayjs/plugin/advancedFormat.js';
import { log } from '../../logger.js';
import * as configApi from '../../config.js';
import utils from '../../utils.js';
import mermaidAPI from '../../mermaidAPI.js';
import {
setAccTitle,
@@ -16,7 +15,7 @@ import {
clear as commonClear,
setDiagramTitle,
getDiagramTitle,
} from '../../commonDb.js';
} from '../common/commonDb.js';
dayjs.extend(dayjsIsoWeek);
dayjs.extend(dayjsCustomParseFormat);
@@ -42,10 +41,6 @@ let weekday = 'sunday';
// The serial order of the task in the script
let lastOrder = 0;
export const parseDirective = function (statement, context, type) {
mermaidAPI.parseDirective(this, statement, context, type);
};
export const clear = function () {
sections = [];
tasks = [];
@@ -730,7 +725,6 @@ export const bindFunctions = function (element) {
};
export default {
parseDirective,
getConfig: () => configApi.getConfig().gantt,
clear,
setDateFormat,

View File

@@ -10,6 +10,8 @@ import {
axisBottom,
axisTop,
timeFormat,
timeMillisecond,
timeSecond,
timeMinute,
timeHour,
timeDay,
@@ -495,20 +497,37 @@ export const draw = function (text, id, version, diagObj) {
* @param w
* @param h
* @param tasks
* @param excludes
* @param includes
* @param {unknown[]} excludes
* @param {unknown[]} includes
*/
function drawExcludeDays(theGap, theTopPad, theSidePad, w, h, tasks, excludes, includes) {
const minTime = tasks.reduce(
(min, { startTime }) => (min ? Math.min(min, startTime) : startTime),
0
);
const maxTime = tasks.reduce((max, { endTime }) => (max ? Math.max(max, endTime) : endTime), 0);
const dateFormat = diagObj.db.getDateFormat();
if (excludes.length === 0 && includes.length === 0) {
return;
}
let minTime;
let maxTime;
for (const { startTime, endTime } of tasks) {
if (minTime === undefined || startTime < minTime) {
minTime = startTime;
}
if (maxTime === undefined || endTime > maxTime) {
maxTime = endTime;
}
}
if (!minTime || !maxTime) {
return;
}
if (dayjs(maxTime).diff(dayjs(minTime), 'year') > 5) {
log.warn(
'The difference between the min and max time is more than 5 years. This will cause performance issues. Skipping drawing exclude days.'
);
return;
}
const dateFormat = diagObj.db.getDateFormat();
const excludeRanges = [];
let range = null;
let d = dayjs(minTime);
@@ -573,7 +592,7 @@ export const draw = function (text, id, version, diagObj) {
.tickSize(-h + theTopPad + conf.gridLineStartPadding)
.tickFormat(timeFormat(diagObj.db.getAxisFormat() || conf.axisFormat || '%Y-%m-%d'));
const reTickInterval = /^([1-9]\d*)(minute|hour|day|week|month)$/;
const reTickInterval = /^([1-9]\d*)(millisecond|second|minute|hour|day|week|month)$/;
const resultTickInterval = reTickInterval.exec(
diagObj.db.getTickInterval() || conf.tickInterval
);
@@ -584,6 +603,12 @@ export const draw = function (text, id, version, diagObj) {
const weekday = diagObj.db.getWeekday() || conf.weekday;
switch (interval) {
case 'millisecond':
bottomXAxis.ticks(timeMillisecond.every(every));
break;
case 'second':
bottomXAxis.ticks(timeSecond.every(every));
break;
case 'minute':
bottomXAxis.ticks(timeMinute.every(every));
break;
@@ -625,6 +650,12 @@ export const draw = function (text, id, version, diagObj) {
const weekday = diagObj.db.getWeekday() || conf.weekday;
switch (interval) {
case 'millisecond':
topXAxis.ticks(timeMillisecond.every(every));
break;
case 'second':
topXAxis.ticks(timeSecond.every(every));
break;
case 'minute':
topXAxis.ticks(timeMinute.every(every));
break;

View File

@@ -11,19 +11,11 @@
%x href
%x callbackname
%x callbackargs
%x open_directive
%x type_directive
%x arg_directive
%x close_directive
%x acc_title
%x acc_descr
%x 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"; }
@@ -112,8 +104,7 @@ weekday\s+sunday return 'weekday_sunday'
%% /* language grammar */
start
: directive start
| gantt document 'EOF' { return $2; }
: gantt document 'EOF' { return $2; }
;
document
@@ -155,13 +146,8 @@ statement
| section { yy.addSection($1.substr(8));$$=$1.substr(8); }
| clickStatement
| taskTxt taskData {yy.addTask($1,$2);$$='task';}
| directive
;
directive
: openDirective typeDirective closeDirective 'NL'
| openDirective typeDirective ':' argDirective closeDirective 'NL'
;
/*
click allows any combination of href and call.
@@ -192,20 +178,4 @@ clickStatementDebug
| click href {$$=$1 + ' ' + $2;}
;
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', 'gantt'); }
;
%%

View File

@@ -1,6 +1,5 @@
import { log } from '../../logger.js';
import { random } from '../../utils.js';
import mermaidAPI from '../../mermaidAPI.js';
import * as configApi from '../../config.js';
import { getConfig } from '../../config.js';
import common from '../common/common.js';
@@ -12,7 +11,7 @@ import {
clear as commonClear,
setDiagramTitle,
getDiagramTitle,
} from '../../commonDb.js';
} from '../common/commonDb.js';
let mainBranchName = getConfig().gitGraph.mainBranchName;
let mainBranchOrder = getConfig().gitGraph.mainBranchOrder;
@@ -33,10 +32,6 @@ function getId() {
return random({ length: 7 });
}
export const parseDirective = function (statement, context, type) {
mermaidAPI.parseDirective(this, statement, context, type);
};
// /**
// * @param currentCommit
// * @param otherCommit
@@ -507,7 +502,6 @@ export const commitType = {
};
export default {
parseDirective,
getConfig: () => configApi.getConfig().gitGraph,
setDirection,
setOptions,

View File

@@ -1,87 +1,72 @@
import gitGraphAst from './gitGraphAst.js';
import { parser } from './parser/gitGraph.jison';
// Todo reintroduce without cryptoRandomString
import gitGraphAst from './gitGraphAst';
import { parser } from './parser/gitGraph';
import randomString from 'crypto-random-string';
import cryptoRandomString from 'crypto-random-string';
jest.mock('crypto-random-string');
describe('when parsing a gitGraph', function() {
let randomNumber;
beforeEach(function() {
describe('when parsing a gitGraph', function () {
beforeEach(function () {
parser.yy = gitGraphAst;
parser.yy.clear();
randomNumber = 0;
cryptoRandomString.mockImplementation(() => {
randomNumber = randomNumber + 1;
return String(randomNumber);
});
});
afterEach(function() {
cryptoRandomString.mockReset();
});
it('should handle a gitGraph definition', function() {
it('should handle a gitGraph definition', function () {
const str = 'gitGraph:\n' + 'commit\n';
parser.parse(str);
const commits = parser.yy.getCommits();
expect(Object.keys(commits).length).toBe(1);
expect(parser.yy.getCurrentBranch()).toBe('master');
expect(parser.yy.getCurrentBranch()).toBe('main');
expect(parser.yy.getDirection()).toBe('LR');
expect(Object.keys(parser.yy.getBranches()).length).toBe(1);
});
it('should handle a gitGraph definition with empty options', function() {
const str = 'gitGraph:\n' + 'options\n' + 'end\n' + 'commit\n';
it('should handle a gitGraph definition with empty options', function () {
const str = 'gitGraph:\n' + 'options\n' + ' end\n' + 'commit\n';
parser.parse(str);
const commits = parser.yy.getCommits();
expect(parser.yy.getOptions()).toEqual({});
expect(Object.keys(commits).length).toBe(1);
expect(parser.yy.getCurrentBranch()).toBe('master');
expect(parser.yy.getCurrentBranch()).toBe('main');
expect(parser.yy.getDirection()).toBe('LR');
expect(Object.keys(parser.yy.getBranches()).length).toBe(1);
});
it('should handle a gitGraph definition with valid options', function() {
it('should handle a gitGraph definition with valid options', function () {
const str = 'gitGraph:\n' + 'options\n' + '{"key": "value"}\n' + 'end\n' + 'commit\n';
parser.parse(str);
const commits = parser.yy.getCommits();
expect(parser.yy.getOptions()['key']).toBe('value');
expect(Object.keys(commits).length).toBe(1);
expect(parser.yy.getCurrentBranch()).toBe('master');
expect(parser.yy.getCurrentBranch()).toBe('main');
expect(parser.yy.getDirection()).toBe('LR');
expect(Object.keys(parser.yy.getBranches()).length).toBe(1);
});
it('should not fail on a gitGraph with malformed json', function() {
it('should not fail on a gitGraph with malformed json', function () {
const str = 'gitGraph:\n' + 'options\n' + '{"key": "value"\n' + 'end\n' + 'commit\n';
parser.parse(str);
const commits = parser.yy.getCommits();
expect(Object.keys(commits).length).toBe(1);
expect(parser.yy.getCurrentBranch()).toBe('master');
expect(parser.yy.getCurrentBranch()).toBe('main');
expect(parser.yy.getDirection()).toBe('LR');
expect(Object.keys(parser.yy.getBranches()).length).toBe(1);
});
it('should handle set direction', function() {
const str = 'gitGraph BT:\n' + 'commit\n';
it('should handle set direction', function () {
const str = 'gitGraph TB:\n' + 'commit\n';
parser.parse(str);
const commits = parser.yy.getCommits();
expect(Object.keys(commits).length).toBe(1);
expect(parser.yy.getCurrentBranch()).toBe('master');
expect(parser.yy.getDirection()).toBe('BT');
expect(parser.yy.getCurrentBranch()).toBe('main');
expect(parser.yy.getDirection()).toBe('TB');
expect(Object.keys(parser.yy.getBranches()).length).toBe(1);
});
it('should checkout a branch', function() {
it('should checkout a branch', function () {
const str = 'gitGraph:\n' + 'branch new\n' + 'checkout new\n';
parser.parse(str);
@@ -91,7 +76,7 @@ describe('when parsing a gitGraph', function() {
expect(parser.yy.getCurrentBranch()).toBe('new');
});
it('should add commits to checked out branch', function() {
it('should add commits to checked out branch', function () {
const str = 'gitGraph:\n' + 'branch new\n' + 'checkout new\n' + 'commit\n' + 'commit\n';
parser.parse(str);
@@ -103,7 +88,7 @@ describe('when parsing a gitGraph', function() {
expect(branchCommit).not.toBeNull();
expect(commits[branchCommit].parent).not.toBeNull();
});
it('should handle commit with args', function() {
it('should handle commit with args', function () {
const str = 'gitGraph:\n' + 'commit "a commit"\n';
parser.parse(str);
@@ -112,10 +97,11 @@ describe('when parsing a gitGraph', function() {
expect(Object.keys(commits).length).toBe(1);
const key = Object.keys(commits)[0];
expect(commits[key].message).toBe('a commit');
expect(parser.yy.getCurrentBranch()).toBe('master');
expect(parser.yy.getCurrentBranch()).toBe('main');
});
it('should reset a branch', function() {
// Reset has been commented out in JISON
it.skip('should reset a branch', function () {
const str =
'gitGraph:\n' +
'commit\n' +
@@ -123,18 +109,18 @@ describe('when parsing a gitGraph', function() {
'branch newbranch\n' +
'checkout newbranch\n' +
'commit\n' +
'reset master\n';
'reset main\n';
parser.parse(str);
const commits = parser.yy.getCommits();
expect(Object.keys(commits).length).toBe(3);
expect(parser.yy.getCurrentBranch()).toBe('newbranch');
expect(parser.yy.getBranches()['newbranch']).toEqual(parser.yy.getBranches()['master']);
expect(parser.yy.getBranches()['newbranch']).toEqual(parser.yy.getBranches()['main']);
expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['newbranch']);
});
it('reset can take an argument', function() {
it.skip('reset can take an argument', function () {
const str =
'gitGraph:\n' +
'commit\n' +
@@ -142,18 +128,18 @@ describe('when parsing a gitGraph', function() {
'branch newbranch\n' +
'checkout newbranch\n' +
'commit\n' +
'reset master^\n';
'reset main^\n';
parser.parse(str);
const commits = parser.yy.getCommits();
expect(Object.keys(commits).length).toBe(3);
expect(parser.yy.getCurrentBranch()).toBe('newbranch');
const master = commits[parser.yy.getBranches()['master']];
expect(parser.yy.getHead().id).toEqual(master.parent);
const main = commits[parser.yy.getBranches()['main']];
expect(parser.yy.getHead().id).toEqual(main.parent);
});
it('should handle fast forwardable merges', function() {
it.skip('should handle fast forwardable merges', function () {
const str =
'gitGraph:\n' +
'commit\n' +
@@ -161,19 +147,19 @@ describe('when parsing a gitGraph', function() {
'checkout newbranch\n' +
'commit\n' +
'commit\n' +
'checkout master\n' +
'checkout main\n' +
'merge newbranch\n';
parser.parse(str);
const commits = parser.yy.getCommits();
expect(Object.keys(commits).length).toBe(3);
expect(parser.yy.getCurrentBranch()).toBe('master');
expect(parser.yy.getBranches()['newbranch']).toEqual(parser.yy.getBranches()['master']);
expect(Object.keys(commits).length).toBe(4);
expect(parser.yy.getCurrentBranch()).toBe('main');
expect(parser.yy.getBranches()['newbranch']).toEqual(parser.yy.getBranches()['main']);
expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['newbranch']);
});
it('should handle cases when merge is a noop', function() {
it('should handle cases when merge is a noop', function () {
const str =
'gitGraph:\n' +
'commit\n' +
@@ -181,18 +167,18 @@ describe('when parsing a gitGraph', function() {
'checkout newbranch\n' +
'commit\n' +
'commit\n' +
'merge master\n';
'merge main\n';
parser.parse(str);
const commits = parser.yy.getCommits();
expect(Object.keys(commits).length).toBe(3);
expect(Object.keys(commits).length).toBe(4);
expect(parser.yy.getCurrentBranch()).toBe('newbranch');
expect(parser.yy.getBranches()['newbranch']).not.toEqual(parser.yy.getBranches()['master']);
expect(parser.yy.getBranches()['newbranch']).not.toEqual(parser.yy.getBranches()['main']);
expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['newbranch']);
});
it('should handle merge with 2 parents', function() {
it('should handle merge with 2 parents', function () {
const str =
'gitGraph:\n' +
'commit\n' +
@@ -200,7 +186,7 @@ describe('when parsing a gitGraph', function() {
'checkout newbranch\n' +
'commit\n' +
'commit\n' +
'checkout master\n' +
'checkout main\n' +
'commit\n' +
'merge newbranch\n';
@@ -208,12 +194,12 @@ describe('when parsing a gitGraph', function() {
const commits = parser.yy.getCommits();
expect(Object.keys(commits).length).toBe(5);
expect(parser.yy.getCurrentBranch()).toBe('master');
expect(parser.yy.getBranches()['newbranch']).not.toEqual(parser.yy.getBranches()['master']);
expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['master']);
expect(parser.yy.getCurrentBranch()).toBe('main');
expect(parser.yy.getBranches()['newbranch']).not.toEqual(parser.yy.getBranches()['main']);
expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['main']);
});
it('should handle ff merge when history walk has two parents (merge commit)', function() {
it.skip('should handle ff merge when history walk has two parents (merge commit)', function () {
const str =
'gitGraph:\n' +
'commit\n' +
@@ -221,53 +207,25 @@ describe('when parsing a gitGraph', function() {
'checkout newbranch\n' +
'commit\n' +
'commit\n' +
'checkout master\n' +
'checkout main\n' +
'commit\n' +
'merge newbranch\n' +
'commit\n' +
'checkout newbranch\n' +
'merge master\n';
'merge main\n';
parser.parse(str);
const commits = parser.yy.getCommits();
expect(Object.keys(commits).length).toBe(6);
expect(Object.keys(commits).length).toBe(7);
expect(parser.yy.getCurrentBranch()).toBe('newbranch');
expect(parser.yy.getBranches()['newbranch']).toEqual(parser.yy.getBranches()['master']);
expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['master']);
expect(parser.yy.getBranches()['newbranch']).toEqual(parser.yy.getBranches()['main']);
expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['main']);
parser.yy.prettyPrint();
});
it('should generate a secure random ID for commits', function() {
const str = 'gitGraph:\n' + 'commit\n' + 'commit\n';
const EXPECTED_LENGTH = 7;
const EXPECTED_CHARACTERS = '0123456789abcdef';
let idCount = 0;
randomString.mockImplementation(options => {
if (
options.length === EXPECTED_LENGTH &&
options.characters === EXPECTED_CHARACTERS &&
Object.keys(options).length === 2
) {
const id = `abcdef${idCount}`;
idCount += 1;
return id;
}
return 'unexpected-ID';
});
parser.parse(str);
const commits = parser.yy.getCommits();
expect(Object.keys(commits)).toEqual(['abcdef0', 'abcdef1']);
Object.keys(commits).forEach(key => {
expect(commits[key].id).toEqual(key);
});
});
it('should generate an array of known branches', function() {
it('should generate an array of known branches', function () {
const str =
'gitGraph:\n' +
'commit\n' +
@@ -281,7 +239,7 @@ describe('when parsing a gitGraph', function() {
const branches = gitGraphAst.getBranchesAsObjArray();
expect(branches).toHaveLength(3);
expect(branches[0]).toHaveProperty('name', 'master');
expect(branches[0]).toHaveProperty('name', 'main');
expect(branches[1]).toHaveProperty('name', 'b1');
expect(branches[2]).toHaveProperty('name', 'b2');
});

View File

@@ -1,22 +1,11 @@
/* eslint-env jasmine */
// Todo reintroduce without cryptoRandomString
import gitGraphAst from './gitGraphAst.js';
import { parser } from './parser/gitGraph.jison';
//import randomString from 'crypto-random-string';
//import cryptoRandomString from 'crypto-random-string';
//jest.mock('crypto-random-string');
describe('when parsing a gitGraph', function () {
let randomNumber;
beforeEach(function () {
parser.yy = gitGraphAst;
parser.yy.clear();
randomNumber = 0;
});
// afterEach(function() {
// cryptoRandomString.mockReset();
// });
it('should handle a gitGraph commit with NO pararms, get auto-genrated reandom ID', function () {
const str = `gitGraph:
commit

View File

@@ -9,10 +9,6 @@
%x string
%x options
%x open_directive
%x type_directive
%x arg_directive
%x close_directive
%x acc_title
%x acc_descr
%x acc_descr_multiline
@@ -20,11 +16,6 @@
%%
\%\%\{ { 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'; }
@@ -76,7 +67,6 @@ checkout(?=\s|$) return 'CHECKOUT';
start
: eol start
| directive start
| GG document EOF{ return $3; }
| GG ':' document EOF{ return $3; }
| GG DIR ':' document EOF {yy.setDirection($2); return $4;}
@@ -240,27 +230,6 @@ commitType
| HIGHLIGHT { $$=yy.commitType.HIGHLIGHT;}
;
directive
: openDirective typeDirective closeDirective
| openDirective typeDirective ':' argDirective closeDirective
;
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', 'gitGraph'); }
;
ref
: ID
| STR

View File

@@ -8,19 +8,10 @@
%x string
%x title
%x open_directive
%x type_directive
%x arg_directive
%x close_directive
%x acc_title
%x acc_descr
%x 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';
\%\%(?!\{)[^\n]* /* skip comments */
[^\}]\%\%[^\n]* /* skip comments */{ /*console.log('');*/ }
[\n\r]+ return 'NEWLINE';
@@ -52,7 +43,6 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
start
: eol start
| directive start
| PIE document
| PIE showData document {yy.setShowData(true);}
;
@@ -73,34 +63,12 @@ statement
| 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($$); } | section {yy.addSection($1.substr(8));$$=$1.substr(8);}
| directive
;
directive
: openDirective typeDirective closeDirective
| openDirective typeDirective ':' argDirective closeDirective
;
eol
: NEWLINE
| ';'
| EOF
;
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', 'pie'); }
;
%%

View File

@@ -62,17 +62,6 @@ describe('pie', () => {
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

View File

@@ -1,5 +1,4 @@
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 {
@@ -10,8 +9,7 @@ import {
getAccDescription,
setAccDescription,
clear as commonClear,
} from '../../commonDb.js';
import type { ParseDirectiveDefinition } from '../../diagram-api/types.js';
} from '../common/commonDb.js';
import type { PieFields, PieDB, Sections } from './pieTypes.js';
import type { RequiredDeep } from 'type-fest';
import type { PieDiagramConfig } from '../../config.type.js';
@@ -31,10 +29,6 @@ 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;
@@ -67,7 +61,6 @@ const getShowData = (): boolean => showData;
export const db: PieDB = {
getConfig,
parseDirective,
clear,
setDiagramTitle,
getDiagramTitle,

View File

@@ -1,5 +1,5 @@
import type { PieDiagramConfig } from '../../config.type.js';
import type { DiagramDB, ParseDirectiveDefinition } from '../../diagram-api/types.js';
import type { DiagramDB } from '../../diagram-api/types.js';
export interface PieFields {
sections: Sections;
@@ -46,7 +46,6 @@ export interface PieDB extends DiagramDB {
getConfig: () => Required<PieDiagramConfig>;
// common db
parseDirective: ParseDirectiveDefinition;
clear: () => void;
setDiagramTitle: (title: string) => void;
getDiagramTitle: () => string;

View File

@@ -5,10 +5,6 @@
%x string
%x md_string
%x title
%x open_directive
%x type_directive
%x arg_directive
%x close_directive
%x acc_title
%x acc_descr
%x acc_descr_multiline
@@ -16,11 +12,6 @@
%x point_x
%x point_y
%%
\%\%\{ { 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';
\%\%(?!\{)[^\n]* /* skip comments */
[^\}]\%\%[^\n]* /* skip comments */
[\n\r]+ return 'NEWLINE';
@@ -87,7 +78,6 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multiline");}
start
: eol start
| SPACE start
| directive start
| QUADRANT document
;
@@ -110,7 +100,6 @@ statement
| 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($$); } | section {yy.addSection($1.substr(8));$$=$1.substr(8);}
| directive
;
points
@@ -133,33 +122,12 @@ quadrantDetails
| QUADRANT_4 text {yy.setQuadrant4Text($2)}
;
directive
: openDirective typeDirective closeDirective
| openDirective typeDirective ':' argDirective closeDirective
;
eol
: NEWLINE
| SEMI
| EOF
;
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', 'quadrantChart'); }
;
text: alphaNumToken
{ $$={text:$1, type: 'text'};}
| text textNoTagsToken

View File

@@ -19,7 +19,6 @@ const mockDB: Record<string, Mock<any, any>> = {
setYAxisTopText: vi.fn(),
setYAxisBottomText: vi.fn(),
setDiagramTitle: vi.fn(),
parseDirective: vi.fn(),
addPoint: vi.fn(),
};
@@ -45,23 +44,6 @@ describe('Testing quadrantChart jison file', () => {
expect(parserFnConstructor(str)).not.toThrow();
});
it('should be able to parse directive', () => {
const str =
'%%{init: {"quadrantChart": {"chartWidth": 600, "chartHeight": 600} } }%% \n quadrantChart';
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.parseDirective.mock.calls[0]).toEqual(['%%{', 'open_directive']);
expect(mockDB.parseDirective.mock.calls[1]).toEqual(['init', 'type_directive']);
expect(mockDB.parseDirective.mock.calls[2]).toEqual([
'{"quadrantChart": {"chartWidth": 600, "chartHeight": 600} }',
'arg_directive',
]);
expect(mockDB.parseDirective.mock.calls[3]).toEqual([
'}%%',
'close_directive',
'quadrantChart',
]);
});
it('should be able to parse xAxis text', () => {
let str = 'quadrantChart\nx-axis urgent --> not urgent';
expect(parserFnConstructor(str)).not.toThrow();
@@ -243,8 +225,7 @@ describe('Testing quadrantChart jison file', () => {
});
it('should be able to parse the whole chart', () => {
const str = `%%{init: {"quadrantChart": {"chartWidth": 600, "chartHeight": 600} } }%%
quadrantChart
const str = `quadrantChart
title Analytics and Business Intelligence Platforms
x-axis "Completeness of Vision ❤" --> "x-axis-2"
y-axis Ability to Execute --> "y-axis-2"
@@ -258,17 +239,6 @@ describe('Testing quadrantChart jison file', () => {
Incorta: [0.20, 0.30]`;
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.parseDirective.mock.calls[0]).toEqual(['%%{', 'open_directive']);
expect(mockDB.parseDirective.mock.calls[1]).toEqual(['init', 'type_directive']);
expect(mockDB.parseDirective.mock.calls[2]).toEqual([
'{"quadrantChart": {"chartWidth": 600, "chartHeight": 600} }',
'arg_directive',
]);
expect(mockDB.parseDirective.mock.calls[3]).toEqual([
'}%%',
'close_directive',
'quadrantChart',
]);
expect(mockDB.setXAxisLeftText).toHaveBeenCalledWith({
text: 'Completeness of Vision ❤',
type: 'text',

View File

@@ -4,17 +4,13 @@ import { log } from '../../logger.js';
import type { BaseDiagramConfig, QuadrantChartConfig } from '../../config.type.js';
import defaultConfig from '../../defaultConfig.js';
import { getThemeVariables } from '../../themes/theme-default.js';
import type { Point } from '../../types.js';
const defaultThemeVariables = getThemeVariables();
export type TextVerticalPos = 'left' | 'center' | 'right';
export type TextHorizontalPos = 'top' | 'middle' | 'bottom';
export interface Point {
x: number;
y: number;
}
export interface QuadrantPointInputType extends Point {
text: string;
}

Some files were not shown because too many files have changed in this diff Show More