From 3eb2bb9c0b3b06cfa1d72a1f850a5a4d223f2621 Mon Sep 17 00:00:00 2001
From: "Ashley Engelund (weedySeaDragon @ github)"
child 6))
- ::icon(mdi mdi-fire)
- `,
- {}
- );
- });
- it('text shouhld wrap with icon', () => {
- imgSnapshotTest(
- `mindmap
-root
- Child3(A node with an icon and with a long text that wraps to keep the node size in check)
- `,
- {}
- );
- });
-
- /* The end */
-});
diff --git a/cypress/integration/rendering/mindmap.spec.ts b/cypress/integration/rendering/mindmap.spec.ts
new file mode 100644
index 000000000..62c7e785b
--- /dev/null
+++ b/cypress/integration/rendering/mindmap.spec.ts
@@ -0,0 +1,233 @@
+import { imgSnapshotTest, renderGraph } from '../../helpers/util.js';
+
+/**
+ * Check whether the SVG Element has a Mindmap root
+ *
+ * Sometimes, Cypress takes a snapshot before the mermaid mindmap has finished
+ * generating the SVG.
+ *
+ * @param $p - The element to check.
+ */
+function shouldHaveRoot($p: JQuery
child 6))
+ ::icon(mdi mdi-fire)
+ `,
+ {},
+ undefined,
+ shouldHaveRoot
+ );
+ });
+ it('text shouhld wrap with icon', () => {
+ imgSnapshotTest(
+ `mindmap
+root
+ Child3(A node with an icon and with a long text that wraps to keep the node size in check)
+ `,
+ {},
+ undefined,
+ shouldHaveRoot
+ );
+ });
+ it('square shape', () => {
+ imgSnapshotTest(
+ `
+mindmap
+ root[
+ The root
+ ]
+ `,
+ {},
+ undefined,
+ shouldHaveRoot
+ );
+ cy.get('svg');
+ });
+ it('rounded rect shape', () => {
+ imgSnapshotTest(
+ `
+mindmap
+ root((
+ The root
+ ))
+ `,
+ {},
+ undefined,
+ shouldHaveRoot
+ );
+ cy.get('svg');
+ });
+ it('circle shape', () => {
+ imgSnapshotTest(
+ `
+mindmap
+ root(
+ The root
+ )
+ `,
+ {},
+ undefined,
+ shouldHaveRoot
+ );
+ cy.get('svg');
+ });
+ it('default shape', () => {
+ imgSnapshotTest(
+ `
+mindmap
+ The root
+ `,
+ {},
+ undefined,
+ shouldHaveRoot
+ );
+ cy.get('svg');
+ });
+ it('adding children', () => {
+ imgSnapshotTest(
+ `
+mindmap
+ The root
+ child1
+ child2
+ `,
+ {},
+ undefined,
+ shouldHaveRoot
+ );
+ cy.get('svg');
+ });
+ it('adding grand children', () => {
+ imgSnapshotTest(
+ `
+mindmap
+ The root
+ child1
+ child2
+ child3
+ `,
+ {},
+ undefined,
+ shouldHaveRoot
+ );
+ cy.get('svg');
+ });
+ /* The end */
+});
diff --git a/cypress/integration/rendering/stateDiagram-v2.spec.js b/cypress/integration/rendering/stateDiagram-v2.spec.js
index 5b43c890c..0eca01873 100644
--- a/cypress/integration/rendering/stateDiagram-v2.spec.js
+++ b/cypress/integration/rendering/stateDiagram-v2.spec.js
@@ -559,4 +559,16 @@ stateDiagram-v2
);
});
});
+ it('1433: should render a simple state diagram with a title', () => {
+ imgSnapshotTest(
+ `---
+title: simple state diagram
+---
+stateDiagram-v2
+[*] --> State1
+State1 --> [*]
+`,
+ {}
+ );
+ });
});
diff --git a/cypress/tsconfig.json b/cypress/tsconfig.json
new file mode 100644
index 000000000..e3351cebe
--- /dev/null
+++ b/cypress/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "compilerOptions": {
+ "target": "es2020",
+ "lib": ["es2020", "dom"],
+ "types": ["cypress", "node"]
+ },
+ "include": ["**/*.ts"]
+}
diff --git a/demos/classchart.html b/demos/classchart.html
index 5979152d6..031f3b608 100644
--- a/demos/classchart.html
+++ b/demos/classchart.html
@@ -17,6 +17,9 @@
Class diagram demos
+---
+title: Demo Class Diagram
+---
classDiagram
accTitle: Demo Class Diagram
accDescr: This class diagram show the abstract Animal class, and 3 classes that inherit from it: Duck, Fish, and Zebra.
diff --git a/demos/er.html b/demos/er.html
index 4c1a72c20..06fbf020e 100644
--- a/demos/er.html
+++ b/demos/er.html
@@ -20,6 +20,9 @@
+---
+title: This is a title
+---
erDiagram
%% title This is a title
%% accDescription Test a description
diff --git a/demos/flowchart.html b/demos/flowchart.html
index e11bfcb26..7251e586e 100644
--- a/demos/flowchart.html
+++ b/demos/flowchart.html
@@ -17,6 +17,9 @@
Sample 1
graph
+---
+title: This is a complicated flow
+---
graph LR
accTitle: This is a complicated flow
accDescr: This is the descriptoin for the complicated flow.
@@ -221,6 +224,9 @@
Sample 2
graph
+---
+title: What to buy
+---
graph TD
accTitle: What to buy
accDescr: Options of what to buy with Christmas money
diff --git a/demos/git.html b/demos/git.html
index 15b4401db..99c53d7d0 100644
--- a/demos/git.html
+++ b/demos/git.html
@@ -16,6 +16,9 @@
Git diagram demo
+---
+title: Simple Git diagram
+---
gitGraph:
options
{
diff --git a/demos/index.html b/demos/index.html
index a65681ddf..da27cdcff 100644
--- a/demos/index.html
+++ b/demos/index.html
@@ -48,6 +48,9 @@
Journey
Mindmap
+ Pie
Journey diagram demo
- journey
- title My working day
+---
+title: My working day
+---
+ journey
accTitle: Very simple journey demo
accDescr: 2 main sections: work and home, each with just a few tasks
diff --git a/demos/mindmap.html b/demos/mindmap.html
new file mode 100644
index 000000000..a5b554a1a
--- /dev/null
+++ b/demos/mindmap.html
@@ -0,0 +1,108 @@
+
+
+
+
+
+
Mindmap diagram demo
+
+ mindmap
+ root
+ child1((Circle))
+ grandchild 1
+ grandchild 2
+ child2(Round rectangle)
+ grandchild 3
+ grandchild 4
+ child3[Square]
+ grandchild 5
+ ::icon(mdi mdi-fire)
+ gc6((grand
+
+
child 6))
+ ::icon(mdi mdi-fire)
+ gc7((grand
grand
child 8))
+ Mindmap with root wrapping text and a shape
+
+ mindmap
+ root[A root with a long text that wraps to keep the node size in check]
+
+
+
+
+
diff --git a/demos/state.html b/demos/state.html
index dbe2286a3..3d070f379 100644
--- a/demos/state.html
+++ b/demos/state.html
@@ -1,5 +1,5 @@
-
+
@@ -17,6 +17,9 @@
State diagram demos
Very simple showing change from State1 to State2
+---
+title: Very simple diagram
+---
stateDiagram
accTitle: This is the accessible title
accDescr:This is an accessible description
@@ -30,8 +33,9 @@
classDef notMoving fill:white
- classDef movement font-style:italic;
- classDef badBadEvent fill:#f00,color:white,font-weight:bold
+ classDef movement font-style:italic
+ classDef badBadEvent
+ fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow
And these are how they are applied:
@@ -43,15 +47,20 @@
- stateDiagram-v2 +--- +title: Very simple diagram +--- + stateDiagram + direction TB + accTitle: This is the accessible title accDescr: This is an accessible description classDef notMoving fill:white - classDef movement font-style:italic; - classDef badBadEvent fill:#f00,color:white,font-weight:bold + classDef movement font-style:italic + classDef badBadEvent fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow - [*] --> Still + [*]--> Still Still --> [*] Still --> Moving Moving --> Still @@ -61,10 +70,57 @@ class Still notMoving class Moving, Crash movement class Crash badBadEvent + class end badBadEvent
classDef
statements:
+
+ classDef notMoving fill:white
+
+ classDef movement font-style:italic
+ classDef badBadEvent
+ fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow
+
+
+ [*] --> Still:::notMoving
+
+ ...
+ Still --> Moving:::movement
+ ...
+ Moving --> Crash:::movement
+ Crash:::badBadEvent --> [*]
+
+ Note that both the starting state and the end state have styles applied:
+ The start state has the start classDef style
and the end state has the
+ stop classDef style applied.
+
+ stateDiagram + direction TB + + accTitle: This is the accessible title + accDescr: This is an accessible description + + classDef notMoving fill:white + classDef movement font-style:italic + classDef badBadEvent fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow + + [*] --> Still:::notMoving + Still --> [*] + Still --> Moving:::movement + Moving --> Still + Moving --> Crash:::movement + Crash:::badBadEvent --> [*] ++
stateDiagram-v2 accTitle: very very simple state @@ -73,6 +129,20 @@
+ stateDiagram + classDef yourState font-style:italic,font-weight:bold,fill:white + + yswsii: Your state with spaces in it + [*] --> yswsii:::yourState + [*] --> SomeOtherState + SomeOtherState --> YetAnotherState + yswsii --> YetAnotherState + YetAnotherState --> [*] ++
stateDiagram-v2 @@ -121,7 +191,7 @@stateDiagram-v2 [*] --> S1 - S1 --> S2: This long line uses a br tag@@ -133,7 +203,7 @@ direction LR State1: A state with a note note right of State1 - Important information!
to create multiple
lines. + S1 --> S2: This long line uses a br tag
to create multiple
lines. S1 --> S3: This transition descripton uses \na newline character\nto create multiple\nlines.
You can write notes.
And\nthey\ncan\nbe\nmulti-\nline. + Important information!
You can write notes.
And\nthey\ncan\nbe\nmulti-\nline. end note State1 --> State2 note left of State2 : Notes can be to the left of a state\n(like this one). diff --git a/docs/config/setup/modules/defaultConfig.md b/docs/config/setup/modules/defaultConfig.md index c7ad1402f..05f6f8a2c 100644 --- a/docs/config/setup/modules/defaultConfig.md +++ b/docs/config/setup/modules/defaultConfig.md @@ -14,7 +14,7 @@ #### Defined in -[defaultConfig.ts:1881](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L1881) +[defaultConfig.ts:1933](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L1933) --- diff --git a/docs/config/usage.md b/docs/config/usage.md index a0fb371cd..29273e9b5 100644 --- a/docs/config/usage.md +++ b/docs/config/usage.md @@ -348,7 +348,7 @@ on what kind of integration you use. ## Using the mermaid object -Is it possible to set some configuration via the mermaid object. The two parameters that are supported using this +It is possible to set some configuration via the mermaid object. The two parameters that are supported using this approach are: - mermaid.startOnLoad diff --git a/docs/intro/index.md b/docs/intro/index.md index a3ed371ac..b8a27acff 100644 --- a/docs/intro/index.md +++ b/docs/intro/index.md @@ -14,7 +14,7 @@ It is a JavaScript based diagramming and charting tool that renders Markdown-ins-[](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [](https://www.npmjs.com/package/mermaid) [](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [](https://www.jsdelivr.com/package/npm/mermaid) [](https://www.npmjs.com/package/mermaid) [](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [](https://twitter.com/mermaidjs_) +[](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [](https://www.npmjs.com/package/mermaid) [](https://bundlephobia.com/package/mermaid) [](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [](https://www.jsdelivr.com/package/npm/mermaid) [](https://www.npmjs.com/package/mermaid) [](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [](https://twitter.com/mermaidjs_) diff --git a/docs/misc/integrations.md b/docs/misc/integrations.md index f9fe5761f..007b9e778 100644 --- a/docs/misc/integrations.md +++ b/docs/misc/integrations.md @@ -39,6 +39,7 @@ They also serve as proof of concept, for the variety of things that can be built - [markdown-for-mermaid-plugin](https://github.com/jamieh-mongolian/markdown-for-mermaid-plugin) - [JetBrains IDE eg Pycharm](https://www.jetbrains.com/go/guide/tips/mermaid-js-support-in-markdown/) - [mermerd](https://github.com/KarnerTh/mermerd) +- Visual Studio Code [Polyglot Interactive Notebooks](https://github.com/dotnet/interactive#net-interactive) ## CRM/ERP/Similar @@ -121,6 +122,7 @@ They also serve as proof of concept, for the variety of things that can be built - [Draw.io](https://draw.io) - [Plugin](https://github.com/nopeslide/drawio_mermaid_plugin) - [Inkdrop](https://www.inkdrop.app) - [Plugin](https://github.com/inkdropapp/inkdrop-mermaid) - [Vim](https://www.vim.org) + - [Official Vim Syntax and ftplugin](https://github.com/craigmac/vim-mermaid) - [Vim Diagram Syntax](https://github.com/zhaozg/vim-diagram) - [GNU Emacs](https://www.gnu.org/software/emacs/) - [Major mode for .mmd files](https://github.com/abrochard/mermaid-mode) diff --git a/docs/syntax/c4c.md b/docs/syntax/c4c.md index ef1295450..e946aedb6 100644 --- a/docs/syntax/c4c.md +++ b/docs/syntax/c4c.md @@ -567,7 +567,7 @@ UpdateRelStyle(customerA, bankA, $offsetY="60") Container(mobile, "Mobile App", "Xamarin", "Provides a limited subset of the Internet Banking functionality to customers via their mobile device.") } - Deployment_Node(comp, "Customer's computer", "Mircosoft Windows or Apple macOS"){ + Deployment_Node(comp, "Customer's computer", "Microsoft Windows or Apple macOS"){ Deployment_Node(browser, "Web Browser", "Google Chrome, Mozilla Firefox,
Apple Safari or Microsoft Edge"){ Container(spa, "Single Page Application", "JavaScript and Angular", "Provides all of the Internet Banking functionality to customers via their web browser.") } @@ -619,7 +619,7 @@ UpdateRelStyle(customerA, bankA, $offsetY="60") Container(mobile, "Mobile App", "Xamarin", "Provides a limited subset of the Internet Banking functionality to customers via their mobile device.") } - Deployment_Node(comp, "Customer's computer", "Mircosoft Windows or Apple macOS"){ + Deployment_Node(comp, "Customer's computer", "Microsoft Windows or Apple macOS"){ Deployment_Node(browser, "Web Browser", "Google Chrome, Mozilla Firefox,
Apple Safari or Microsoft Edge"){ Container(spa, "Single Page Application", "JavaScript and Angular", "Provides all of the Internet Banking functionality to customers via their web browser.") } diff --git a/docs/syntax/classDiagram.md b/docs/syntax/classDiagram.md index d57125c84..5870d0743 100644 --- a/docs/syntax/classDiagram.md +++ b/docs/syntax/classDiagram.md @@ -14,6 +14,9 @@ The class diagram is the main building block of object-oriented modeling. It is Mermaid can render class diagrams. ```mermaid-example +--- +title: Animal example +--- classDiagram note "From Duck till Zebra" Animal <|-- Duck @@ -40,6 +43,9 @@ classDiagram ``` ```mermaid +--- +title: Animal example +--- classDiagram note "From Duck till Zebra" Animal <|-- Duck @@ -77,6 +83,9 @@ A single instance of a class in the diagram contains three compartments: - The bottom compartment contains the operations the class can execute. They are also left-aligned and the first letter is lowercase. ```mermaid-example +--- +title: Bank example +--- classDiagram class BankAccount BankAccount : +String owner @@ -87,6 +96,9 @@ classDiagram ``` ```mermaid +--- +title: Bank example +--- classDiagram class BankAccount BankAccount : +String owner @@ -559,7 +571,7 @@ You would define these actions on a separate line after all classes have been de ## Notes -It is possible to add notes on digram using `note "line1\nline2"` or note for class using `note for class "line1\nline2"` +It is possible to add notes on diagram using `note "line1\nline2"` or note for class using `note for class "line1\nline2"` ### Examples diff --git a/docs/syntax/entityRelationshipDiagram.md b/docs/syntax/entityRelationshipDiagram.md index fef7b6fee..9b938bc36 100644 --- a/docs/syntax/entityRelationshipDiagram.md +++ b/docs/syntax/entityRelationshipDiagram.md @@ -13,6 +13,9 @@ Note that practitioners of ER modelling almost always refer to _entity types_ si Mermaid can render ER diagrams ```mermaid-example +--- +title: Order example +--- erDiagram CUSTOMER ||--o{ ORDER : places ORDER ||--|{ LINE-ITEM : contains @@ -20,6 +23,9 @@ erDiagram ``` ```mermaid +--- +title: Order example +--- erDiagram CUSTOMER ||--o{ ORDER : places ORDER ||--|{ LINE-ITEM : contains diff --git a/docs/syntax/flowchart.md b/docs/syntax/flowchart.md index 234f46236..8b3859f8a 100644 --- a/docs/syntax/flowchart.md +++ b/docs/syntax/flowchart.md @@ -15,11 +15,17 @@ It can also accommodate different arrow types, multi directional arrows, and lin ### A node (default) ```mermaid-example +--- +title: Node +--- flowchart LR id ``` ```mermaid +--- +title: Node +--- flowchart LR id ``` @@ -33,11 +39,17 @@ found for the node that will be used. Also if you define edges for the node late one previously defined will be used when rendering the box. ```mermaid-example +--- +title: Node with text +--- flowchart LR id1[This is the text in the box] ``` ```mermaid +--- +title: Node with text +--- flowchart LR id1[This is the text in the box] ``` @@ -980,7 +992,7 @@ flowchart LR ## Configuration... -Is it possible to adjust the width of the rendered flowchart. +It is possible to adjust the width of the rendered flowchart. This is done by defining **mermaid.flowchartConfig** or by the CLI to use a JSON file with the configuration. How to use the CLI is described in the mermaidCLI page. mermaid.flowchartConfig can be set to a JSON string with config parameters or the corresponding object. diff --git a/docs/syntax/gitgraph.md b/docs/syntax/gitgraph.md index cd1a3f12a..964fe3886 100644 --- a/docs/syntax/gitgraph.md +++ b/docs/syntax/gitgraph.md @@ -13,31 +13,37 @@ These kind of diagram are particularly helpful to developers and devops teams to Mermaid can render Git diagrams ```mermaid-example - gitGraph - commit - commit - branch develop - checkout develop - commit - commit - checkout main - merge develop - commit - commit +--- +title: Example Git diagram +--- +gitGraph + commit + commit + branch develop + checkout develop + commit + commit + checkout main + merge develop + commit + commit ``` ```mermaid - gitGraph - commit - commit - branch develop - checkout develop - commit - commit - checkout main - merge develop - commit - commit +--- +title: Example Git diagram +--- +gitGraph + commit + commit + branch develop + checkout develop + commit + commit + checkout main + merge develop + commit + commit ``` In Mermaid, we support the basic git operations like: diff --git a/docs/syntax/mindmap.md b/docs/syntax/mindmap.md index 26a1065be..628461c4f 100644 --- a/docs/syntax/mindmap.md +++ b/docs/syntax/mindmap.md @@ -21,7 +21,7 @@ mindmap Popularisation British popular psychology author Tony Buzan Research - On effectivness
and eatures + On effectiveness
and features On Automatic creation Uses Creative techniques @@ -42,7 +42,7 @@ mindmap Popularisation British popular psychology author Tony Buzan Research - On effectivness
and eatures + On effectiveness
and features On Automatic creation Uses Creative techniques @@ -152,6 +152,18 @@ mindmap id)I am a cloud( ``` +### Hexagon + +```mermaid-example +mindmap + id{{I am a hexagon}} +``` + +```mermaid +mindmap + id{{I am a hexagon}} +``` + ### Default ```mermaid-example diff --git a/docs/syntax/pie.md b/docs/syntax/pie.md index 6801b3619..63f371e87 100644 --- a/docs/syntax/pie.md +++ b/docs/syntax/pie.md @@ -35,7 +35,7 @@ Drawing a pie chart is really simple in mermaid. - Followed by dataSet. Pie slices will be ordered clockwise in the same order as the labels. - `label` for a section in the pie diagram within `" "` quotes. - Followed by `:` colon as separator - - Followed by `positive numeric value` (supported upto two decimal places) + - Followed by `positive numeric value` (supported up to two decimal places) \[pie] \[showData] (OPTIONAL) \[title] \[titlevalue] (OPTIONAL) diff --git a/docs/syntax/sequenceDiagram.md b/docs/syntax/sequenceDiagram.md index 4e89eb0c6..ad88249be 100644 --- a/docs/syntax/sequenceDiagram.md +++ b/docs/syntax/sequenceDiagram.md @@ -727,7 +727,7 @@ text.actor { ## Configuration -Is it possible to adjust the margins for rendering the sequence diagram. +It is possible to adjust the margins for rendering the sequence diagram. This is done by defining `mermaid.sequenceConfig` or by the CLI to use a json file with the configuration. How to use the CLI is described in the [mermaidCLI](../config/mermaidCLI.md) page. diff --git a/docs/syntax/stateDiagram.md b/docs/syntax/stateDiagram.md index ec91411f6..72d7ec63b 100644 --- a/docs/syntax/stateDiagram.md +++ b/docs/syntax/stateDiagram.md @@ -6,11 +6,17 @@ # State diagrams -> "A state diagram is a type of diagram used in computer science and related fields to describe the behavior of systems. State diagrams require that the system described is composed of a finite number of states; sometimes, this is indeed the case, while at other times this is a reasonable abstraction." Wikipedia +> "A state diagram is a type of diagram used in computer science and related fields to describe the behavior of systems. +> State diagrams require that the system described is composed of a finite number of states; sometimes, this is indeed the +> case, while at other times this is a reasonable abstraction." Wikipedia -Mermaid can render state diagrams. The syntax tries to be compliant with the syntax used in plantUml as this will make it easier for users to share diagrams between mermaid and plantUml. +Mermaid can render state diagrams. The syntax tries to be compliant with the syntax used in plantUml as this will make +it easier for users to share diagrams between mermaid and plantUml. ```mermaid-example +--- +title: Simple sample +--- stateDiagram-v2 [*] --> Still Still --> [*] @@ -22,6 +28,9 @@ stateDiagram-v2 ``` ```mermaid +--- +title: Simple sample +--- stateDiagram-v2 [*] --> Still Still --> [*] @@ -56,20 +65,23 @@ stateDiagram Crash --> [*] ``` -In state diagrams systems are described in terms of its states and how the systems state can change to another state via a transitions. The example diagram above shows three states **Still**, **Moving** and **Crash**. You start in the state of Still. From Still you can change the state to Moving. In Moving you can change the state either back to Still or to Crash. There is no transition from Still to Crash. +In state diagrams systems are described in terms of _states_ and how one _state_ can change to another _state_ via +a _transition._ The example diagram above shows three states: **Still**, **Moving** and **Crash**. You start in the +**Still** state. From **Still** you can change to the **Moving** state. From **Moving** you can change either back to the **Still** state or to +the **Crash** state. There is no transition from **Still** to **Crash**. (You can't crash if you're still.) ## States -A state can be declared in multiple ways. The simplest way is to define a state id as a description. +A state can be declared in multiple ways. The simplest way is to define a state with just an id: ```mermaid-example stateDiagram-v2 - s1 + stateId ``` ```mermaid stateDiagram-v2 - s1 + stateId ``` Another way is by using the state keyword with a description as per below: @@ -100,7 +112,8 @@ stateDiagram-v2 Transitions are path/edges when one state passes into another. This is represented using text arrow, "-->". -When you define a transition between two states and the states are not already defined the undefined states are defined with the id from the transition. You can later add descriptions to states defined this way. +When you define a transition between two states and the states are not already defined, the undefined states are defined +with the id from the transition. You can later add descriptions to states defined this way. ```mermaid-example stateDiagram-v2 @@ -112,7 +125,7 @@ stateDiagram-v2 s1 --> s2 ``` -It is possible to add text to a transition. To describe what it represents. +It is possible to add text to a transition to describe what it represents: ```mermaid-example stateDiagram-v2 @@ -126,7 +139,8 @@ stateDiagram-v2 ## Start and End -There are two special states indicating the start and stop of the diagram. These are written with the \[\*] syntax and the direction of the transition to it defines it either as a start or a stop state. +There are two special states indicating the start and stop of the diagram. These are written with the \[\*] syntax and +the direction of the transition to it defines it either as a start or a stop state. ```mermaid-example stateDiagram-v2 @@ -142,10 +156,11 @@ stateDiagram-v2 ## Composite states -In a real world use of state diagrams you often end up with diagrams that are multi-dimensional as one state can +In a real world use of state diagrams you often end up with diagrams that are multidimensional as one state can have several internal states. These are called composite states in this terminology. -In order to define a composite state you need to use the state keyword followed by an id and the body of the composite state between {}. See the example below: +In order to define a composite state you need to use the state keyword followed by an id and the body of the composite +state between {}. See the example below: ```mermaid-example stateDiagram-v2 @@ -305,7 +320,7 @@ It is possible to specify a fork in the diagram using <\> <\ >. ## Notes -Sometimes nothing says it better then a Post-it note. That is also the case in state diagrams. +Sometimes nothing says it better than a Post-it note. That is also the case in state diagrams. Here you can choose to put the note to the _right of_ or to the _left of_ a node. @@ -375,7 +390,8 @@ stateDiagram-v2 ## Setting the direction of the diagram -With state diagrams you can use the direction statement to set the direction which the diagram will render like in this example. +With state diagrams you can use the direction statement to set the direction which the diagram will render like in this +example. ```mermaid-example stateDiagram @@ -405,7 +421,9 @@ stateDiagram ## Comments -Comments can be entered within a state diagram chart, which will be ignored by the parser. Comments need to be on their own line, and must be prefaced with `%%` (double percent signs). Any text after the start of the comment to the next newline will be treated as a comment, including any diagram syntax +Comments can be entered within a state diagram chart, which will be ignored by the parser. Comments need to be on their +own line, and must be prefaced with `%%` (double percent signs). Any text after the start of the comment to the next +newline will be treated as a comment, including any diagram syntax ```mermaid-example stateDiagram-v2 @@ -429,22 +447,204 @@ stateDiagram-v2 Crash --> [*] ``` -## Styling +## Styling with classDefs -Styling of the a state diagram is done by defining a number of css classes. During rendering these classes are extracted from the file located at src/themes/state.scss +As with other diagrams (like flowcharts), you can define a style in the diagram itself and apply that named style to a +state or states in the diagram. + +**These are the current limitations with state diagram classDefs:** + +1. Cannot be applied to start or end states +2. Cannot be applied to or within composite states + +_These are in development and will be available in a future version._ + +You define a style using the `classDef` keyword, which is short for "class definition" (where "class" means something +like a _CSS class_) +followed by _a name for the style,_ +and then one or more _property-value pairs_. Each _property-value pair_ is +a _[valid CSS property name](https://www.w3.org/TR/CSS/#properties)_ followed by a colon (`:`) and then a _value._ + +Here is an example of a classDef with just one property-value pair: + + classDef movement font-style:italic; + +where + +- the _name_ of the style is `movement` +- the only _property_ is `font-style` and its _value_ is `italic` + +If you want to have more than one _property-value pair_ then you put a comma (`,`) between each _property-value pair._ + +Here is an example with three property-value pairs: + + classDef badBadEvent fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow + +where + +- the _name_ of the style is `badBadEvent` +- the first _property_ is `fill` and its _value_ is `#f00` +- the second _property_ is `color` and its _value_ is `white` +- the third _property_ is `font-weight` and its _value_ is `bold` +- the fourth _property_ is `stroke-width` and its _value_ is `2px` +- the fifth _property_ is `stroke` and its _value_ is `yello` + +### Apply classDef styles to states + +There are two ways to apply a `classDef` style to a state: + +1. use the `class` keyword to apply a classDef style to one or more states in a single statement, or +2. use the `:::` operator to apply a classDef style to a state as it is being used in a transition statement (e.g. with an arrow + to/from another state) + +#### 1. `class` statement + +A `class` statement tells Mermaid to apply the named classDef to one or more classes. The form is: + +```text + class [one or more state names, separated by commas] [name of a style defined with classDef] +``` + +Here is an example applying the `badBadEvent` style to a state named `Crash`: + +```text +class Crash badBadEvent +``` + +Here is an example applying the `movement` style to the two states `Moving` and `Crash`: + +```text +class Moving, Crash movement +``` + +Here is a diagram that shows the examples in use. Note that the `Crash` state has two classDef styles applied: `movement` +and `badBadEvent` + +```mermaid-example + stateDiagram + direction TB + + accTitle: This is the accessible title + accDescr: This is an accessible description + + classDef notMoving fill:white + classDef movement font-style:italic + classDef badBadEvent fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow + + [*]--> Still + Still --> [*] + Still --> Moving + Moving --> Still + Moving --> Crash + Crash --> [*] + + class Still notMoving + class Moving, Crash movement + class Crash badBadEvent + class end badBadEvent +``` + +```mermaid + stateDiagram + direction TB + + accTitle: This is the accessible title + accDescr: This is an accessible description + + classDef notMoving fill:white + classDef movement font-style:italic + classDef badBadEvent fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow + + [*]--> Still + Still --> [*] + Still --> Moving + Moving --> Still + Moving --> Crash + Crash --> [*] + + class Still notMoving + class Moving, Crash movement + class Crash badBadEvent + class end badBadEvent +``` + +#### 2. `:::` operator to apply a style to a state + +You can apply a classDef style to a state using the `:::` (three colons) operator. The syntax is + +```text +[state]:::[style name] +``` + +You can use this in a diagram within a statement using a class. This includes the start and end states. For example: + +```mermaid-example +stateDiagram + direction TB + + accTitle: This is the accessible title + accDescr: This is an accessible description + + classDef notMoving fill:white + classDef movement font-style:italic; + classDef badBadEvent fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow + + [*] --> Still:::notMoving + Still --> [*] + Still --> Moving:::movement + Moving --> Still + Moving --> Crash:::movement + Crash:::badBadEvent --> [*] +``` + +```mermaid +stateDiagram + direction TB + + accTitle: This is the accessible title + accDescr: This is an accessible description + + classDef notMoving fill:white + classDef movement font-style:italic; + classDef badBadEvent fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow + + [*] --> Still:::notMoving + Still --> [*] + Still --> Moving:::movement + Moving --> Still + Moving --> Crash:::movement + Crash:::badBadEvent --> [*] +``` ## Spaces in state names -Spaces can be added to a state by defining it at the top and referencing the acronym later. +Spaces can be added to a state by first defining the state with an id and then referencing the id later. + +In the following example there is a state with the id **yswsii** and description **Your state with spaces in it**. +After it has been defined, **yswsii** is used in the diagram in the first transition (`[*] --> yswsii`) +and also in the transition to **YetAnotherState** (`yswsii --> YetAnotherState`).\ +(**yswsii** has been styled so that it is different from the other states.) ```mermaid-example -stateDiagram-v2 - Yswsii: Your state with spaces in it - [*] --> Yswsii +stateDiagram + classDef yourState font-style:italic,font-weight:bold,fill:white + + yswsii: Your state with spaces in it + [*] --> yswsii:::yourState + [*] --> SomeOtherState + SomeOtherState --> YetAnotherState + yswsii --> YetAnotherState + YetAnotherState --> [*] ``` ```mermaid -stateDiagram-v2 - Yswsii: Your state with spaces in it - [*] --> Yswsii +stateDiagram + classDef yourState font-style:italic,font-weight:bold,fill:white + + yswsii: Your state with spaces in it + [*] --> yswsii:::yourState + [*] --> SomeOtherState + SomeOtherState --> YetAnotherState + yswsii --> YetAnotherState + YetAnotherState --> [*] ``` diff --git a/package.json b/package.json index 7bd648877..aec72da87 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "version": "9.2.2", "description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.", "type": "module", - "packageManager": "pnpm@7.15.0", + "packageManager": "pnpm@7.17.0", "keywords": [ "diagram", "markdown", @@ -15,8 +15,9 @@ "git graph" ], "scripts": { - "build:mermaid": "ts-node-esm --transpileOnly .vite/build.ts --mermaid", "build:vite": "ts-node-esm --transpileOnly .vite/build.ts", + "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-mindmap/tsconfig.json --emitDeclarationOnly", "build:watch": "pnpm build:vite --watch", "build": "pnpm run -r clean && concurrently \"pnpm build:vite\" \"pnpm build:types\"", @@ -59,11 +60,13 @@ "@cspell/eslint-plugin": "^6.14.2", "@types/eslint": "^8.4.10", "@types/express": "^4.17.14", + "@types/js-yaml": "^4.0.5", "@types/jsdom": "^20.0.1", "@types/lodash": "^4.14.188", "@types/mdast": "^3.0.10", "@types/node": "^18.11.9", "@types/prettier": "^2.7.1", + "@types/rollup-plugin-visualizer": "^4.2.1", "@typescript-eslint/eslint-plugin": "^5.42.1", "@typescript-eslint/parser": "^5.42.1", "@vitest/coverage-c8": "^0.25.1", @@ -80,15 +83,18 @@ "eslint-plugin-jest": "^27.1.5", "eslint-plugin-jsdoc": "^39.6.2", "eslint-plugin-json": "^3.1.0", + "eslint-plugin-lodash": "^7.4.0", "eslint-plugin-markdown": "^3.0.0", "eslint-plugin-no-only-tests": "^3.1.0", "eslint-plugin-tsdoc": "^0.2.17", + "eslint-plugin-unicorn": "^45.0.0", "express": "^4.18.2", "globby": "^13.1.2", "husky": "^8.0.2", "identity-obj-proxy": "^3.0.0", "jest": "^29.3.1", "jison": "^0.4.18", + "js-yaml": "^4.1.0", "jsdom": "^20.0.2", "lint-staged": "^13.0.3", "path-browserify": "^1.0.1", @@ -96,6 +102,7 @@ "prettier": "^2.7.1", "prettier-plugin-jsdoc": "^0.4.2", "rimraf": "^3.0.2", + "rollup-plugin-visualizer": "^5.8.3", "start-server-and-test": "^1.14.0", "ts-node": "^10.9.1", "typescript": "^4.8.4", @@ -103,15 +110,8 @@ "vitepress": "^1.0.0-alpha.28", "vitepress-plugin-mermaid": "^2.0.8", "vitepress-plugin-search": "^1.0.4-alpha.15", - "vitest": "^0.25.1" + "vitest": "^0.25.3" }, - "resolutions": { - "d3": "^7.6.1" - }, - "sideEffects": [ - "**/*.css", - "**/*.scss" - ], "volta": { "node": "18.12.1" } diff --git a/packages/mermaid-mindmap/src/mindmap.spec.js b/packages/mermaid-mindmap/src/mindmap.spec.js index b9e9abf6e..e3f018350 100644 --- a/packages/mermaid-mindmap/src/mindmap.spec.js +++ b/packages/mermaid-mindmap/src/mindmap.spec.js @@ -172,6 +172,18 @@ root expect(mm.children.length).toEqual(0); expect(mm.type).toEqual(mindmap.yy.nodeType.BANG); }); + + it('MMP-12-a mutiple types (hexagon)', function () { + let str = `mindmap + root{{the root}} +`; + + mindmap.parse(str); + const mm = mindmap.yy.getMindmap(); + expect(mm.type).toEqual(mindmap.yy.nodeType.HEXAGON); + expect(mm.descr).toEqual('the root'); + expect(mm.children.length).toEqual(0); + }); }); describe('decorations', function () { it('MMP-13 should be possible to set an icon for the node', function () { diff --git a/packages/mermaid-mindmap/src/mindmapDb.js b/packages/mermaid-mindmap/src/mindmapDb.js index 2ae98c223..16861cd23 100644 --- a/packages/mermaid-mindmap/src/mindmapDb.js +++ b/packages/mermaid-mindmap/src/mindmapDb.js @@ -42,6 +42,9 @@ export const addNode = (level, id, descr, type) => { case nodeType.RECT: node.padding = 2 * conf.mindmap.padding; break; + case nodeType.HEXAGON: + node.padding = 2 * conf.mindmap.padding; + break; default: node.padding = conf.mindmap.padding; } @@ -79,6 +82,7 @@ export const nodeType = { CIRCLE: 3, CLOUD: 4, BANG: 5, + HEXAGON: 6, }; export const getType = (startStr, endStr) => { @@ -94,6 +98,8 @@ export const getType = (startStr, endStr) => { return nodeType.CLOUD; case '))': return nodeType.BANG; + case '{{': + return nodeType.HEXAGON; default: return nodeType.DEFAULT; } @@ -127,6 +133,8 @@ export const type2Str = (type) => { return 'cloud'; case nodeType.BANG: return 'bang'; + case nodeType.HEXAGON: + return 'hexgon'; default: return 'no-border'; } diff --git a/packages/mermaid-mindmap/src/mindmapRenderer.js b/packages/mermaid-mindmap/src/mindmapRenderer.js index c0760e4ac..9fd557e51 100644 --- a/packages/mermaid-mindmap/src/mindmapRenderer.js +++ b/packages/mermaid-mindmap/src/mindmapRenderer.js @@ -92,10 +92,6 @@ function addNodes(mindmap, cy, conf, level) { */ function layoutMindmap(node, conf) { return new Promise((resolve) => { - if (node.children.length === 0) { - return node; - } - // Add temporary render element const renderEl = select('body').append('div').attr('id', 'cy').attr('style', 'display:none'); const cy = cytoscape({ diff --git a/packages/mermaid-mindmap/src/parser/mindmap.jison b/packages/mermaid-mindmap/src/parser/mindmap.jison index bd008db7f..a96ee6261 100644 --- a/packages/mermaid-mindmap/src/parser/mindmap.jison +++ b/packages/mermaid-mindmap/src/parser/mindmap.jison @@ -33,11 +33,12 @@ "))" { yy.getLogger().trace('Explosion Bang'); this.begin('NODE');return 'NODE_DSTART'; } ")" { yy.getLogger().trace('Cloud Bang'); this.begin('NODE');return 'NODE_DSTART'; } "((" { this.begin('NODE');return 'NODE_DSTART'; } +"{{" { this.begin('NODE');return 'NODE_DSTART'; } "(" { this.begin('NODE');return 'NODE_DSTART'; } "[" { this.begin('NODE');return 'NODE_DSTART'; } [\s]+ return 'SPACELIST' /* skip all whitespace */ ; // !(-\() return 'NODE_ID'; -[^\(\[\n\-\)]+ return 'NODE_ID'; +[^\(\[\n\-\)\{\}]+ return 'NODE_ID'; < > return 'EOF'; ["] { yy.getLogger().trace('Starting NSTR');this.begin("NSTR");} [^"]+ { yy.getLogger().trace('description:', yytext); return "NODE_DESCR";} @@ -45,11 +46,12 @@ [\)]\) {this.popState();yy.getLogger().trace('node end ))');return "NODE_DEND";} [\)] {this.popState();yy.getLogger().trace('node end )');return "NODE_DEND";} [\]] {this.popState();yy.getLogger().trace('node end ...',yytext);return "NODE_DEND";} + "}}" {this.popState();yy.getLogger().trace('node end ((');return "NODE_DEND";} "(-" {this.popState();yy.getLogger().trace('node end (-');return "NODE_DEND";} "-)" {this.popState();yy.getLogger().trace('node end (-');return "NODE_DEND";} "((" {this.popState();yy.getLogger().trace('node end ((');return "NODE_DEND";} - "(" {this.popState();yy.getLogger().trace('node end ((');return "NODE_DEND";} - [^\)\]\(]+ { yy.getLogger().trace('Long description:', yytext); return 'NODE_DESCR';} + "(" {this.popState();yy.getLogger().trace('node end ((');return "NODE_DEND";} + [^\)\]\(\}]+ { yy.getLogger().trace('Long description:', yytext); return 'NODE_DESCR';} .+(?!\(\() { yy.getLogger().trace('Long description:', yytext); return 'NODE_DESCR';} // [\[] return 'NODE_START'; // .+ return 'TXT' ; diff --git a/packages/mermaid-mindmap/src/styles.js b/packages/mermaid-mindmap/src/styles.js index a409aa4e5..986a04514 100644 --- a/packages/mermaid-mindmap/src/styles.js +++ b/packages/mermaid-mindmap/src/styles.js @@ -17,7 +17,7 @@ const genSections = (options) => { sections += ` .section-${i - 1} rect, .section-${i - 1} path, .section-${i - 1} circle, .section-${ i - 1 - } path { + } polygon, .section-${i - 1} path { fill: ${options['cScale' + i]}; } .section-${i - 1} text { @@ -55,7 +55,7 @@ const getStyles = (options) => stroke-width: 3; } ${genSections(options)} - .section-root rect, .section-root path, .section-root circle { + .section-root rect, .section-root path, .section-root circle, .section-root polygon { fill: ${options.git0}; } .section-root text { diff --git a/packages/mermaid-mindmap/src/svgDraw.js b/packages/mermaid-mindmap/src/svgDraw.js index 314503b20..d4f57f1f1 100644 --- a/packages/mermaid-mindmap/src/svgDraw.js +++ b/packages/mermaid-mindmap/src/svgDraw.js @@ -145,6 +145,45 @@ const circleBkg = function (elem, node) { .attr('class', 'node-bkg node-' + db.type2Str(node.type)) .attr('r', node.width / 2); }; + +/** + * + * @param parent + * @param w + * @param h + * @param points + * @param node + */ +function insertPolygonShape(parent, w, h, points, node) { + return parent + .insert('polygon', ':first-child') + .attr( + 'points', + points + .map(function (d) { + return d.x + ',' + d.y; + }) + .join(' ') + ) + .attr('transform', 'translate(' + (node.width - w) / 2 + ', ' + h + ')'); +} + +const hexagonBkg = function (elem, node) { + const h = node.height; + const f = 4; + const m = h / f; + const w = node.width - node.padding + 2 * m; + const points = [ + { x: m, y: 0 }, + { x: w - m, y: 0 }, + { x: w, y: -h / 2 }, + { x: w - m, y: -h }, + { x: m, y: -h }, + { x: 0, y: -h / 2 }, + ]; + const shapeSvg = insertPolygonShape(elem, w, h, points, node); +}; + const roundedRectBkg = function (elem, node) { elem .append('rect') @@ -252,6 +291,9 @@ export const drawNode = function (elem, node, fullSection, conf) { case db.nodeType.BANG: bangBkg(bkgElem, node, section, conf); break; + case db.nodeType.HEXAGON: + hexagonBkg(bkgElem, node, section, conf); + break; } // Position the node to its coordinate diff --git a/packages/mermaid/.lintstagedrc.json b/packages/mermaid/.lintstagedrc.json deleted file mode 100644 index c3cbb926b..000000000 --- a/packages/mermaid/.lintstagedrc.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "src/docs/**": ["pnpm --filter mermaid run docs:build --git"], - "src/docs.mts": ["pnpm --filter mermaid run docs:build --git"], - "src/(defaultConfig|config|mermaidAPI).ts": ["pnpm --filter mermaid run docs:build --git"], - "*.jison": ["pnpm run lint:jison"] -} diff --git a/packages/mermaid/.lintstagedrc.mjs b/packages/mermaid/.lintstagedrc.mjs new file mode 100644 index 000000000..fe79ad254 --- /dev/null +++ b/packages/mermaid/.lintstagedrc.mjs @@ -0,0 +1,7 @@ +import baseConfig from '../../.lintstagedrc.mjs'; +export default { + ...baseConfig, + 'src/docs/**': ['pnpm --filter mermaid run docs:build --git'], + 'src/docs.mts': ['pnpm --filter mermaid run docs:build --git'], + 'src/(defaultConfig|config|mermaidAPI).ts': ['pnpm --filter mermaid run docs:build --git'], +}; diff --git a/packages/mermaid/README.md b/packages/mermaid/README.md index 90ae1ad4c..91c2d1640 100644 --- a/packages/mermaid/README.md +++ b/packages/mermaid/README.md @@ -1,23 +1,6 @@ # mermaid -[](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [](https://www.npmjs.com/package/mermaid) [](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [](https://www.jsdelivr.com/package/npm/mermaid) [](https://www.npmjs.com/package/mermaid) [](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [](https://twitter.com/mermaidjs_) - -# Whoa, what's going on here? - -We are transforming the Mermaid repository to a so called mono-repo. This is a part of the effort to decouple the diagrams from the core of mermaid. This will: - -- Make it possible to select which diagrams to include on your site -- Open up for lazy loading -- Make it possible to add diagrams from outside of the Mermaid repository -- Separate the release flow between different diagrams and the Mermaid core - -As such be aware of some changes.. - -# We use pnpm now - -# The source code has moved - -It is now located in the src folder for each respective package located as subfolders in packages. +[](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [](https://www.npmjs.com/package/mermaid) [](https://bundlephobia.com/package/mermaid) [](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [](https://www.jsdelivr.com/package/npm/mermaid) [](https://www.npmjs.com/package/mermaid) [](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [](https://twitter.com/mermaidjs_) English | [简体中文](./README.zh-CN.md) diff --git a/packages/mermaid/README.zh-CN.md b/packages/mermaid/README.zh-CN.md index fcaa1f523..0ccef27e4 100644 --- a/packages/mermaid/README.zh-CN.md +++ b/packages/mermaid/README.zh-CN.md @@ -1,6 +1,6 @@ # mermaid -[](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [](https://www.npmjs.com/package/mermaid) [](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [](https://www.jsdelivr.com/package/npm/mermaid) [](https://www.npmjs.com/package/mermaid) [](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [](https://twitter.com/mermaidjs_) +[](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [](https://www.npmjs.com/package/mermaid) [](https://bundlephobia.com/package/mermaid) [](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [](https://www.jsdelivr.com/package/npm/mermaid) [](https://www.npmjs.com/package/mermaid) [](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [](https://twitter.com/mermaidjs_) [English](./README.md) | 简体中文 diff --git a/packages/mermaid/package.json b/packages/mermaid/package.json index 9a713ed9c..4b24fb661 100644 --- a/packages/mermaid/package.json +++ b/packages/mermaid/package.json @@ -1,7 +1,7 @@ { "name": "mermaid", "version": "9.2.2", - "description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.", + "description": "Markdown-ish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.", "main": "./dist/mermaid.min.js", "module": "./dist/mermaid.core.mjs", "types": "./dist/mermaid.d.ts", @@ -26,12 +26,13 @@ "scripts": { "clean": "rimraf dist", "docs:code": "typedoc src/defaultConfig.ts src/config.ts src/mermaidAPI.ts && prettier --write ./src/docs/config/setup", - "docs:build": "rimraf ../../docs && pnpm docs:code && ts-node-esm src/docs.mts", - "docs:verify": "pnpm docs:code && ts-node-esm src/docs.mts --verify", + "docs:build": "rimraf ../../docs && pnpm docs:spellcheck && pnpm docs:code && ts-node-esm src/docs.mts", + "docs:verify": "pnpm docs:spellcheck && pnpm docs:code && ts-node-esm src/docs.mts --verify", "docs:pre:vitepress": "rimraf src/vitepress && pnpm docs:code && ts-node-esm src/docs.mts --vitepress", "docs:build:vitepress": "pnpm docs:pre:vitepress && vitepress build src/vitepress", "docs:dev": "pnpm docs:pre:vitepress && concurrently \"vitepress dev src/vitepress\" \"ts-node-esm src/docs.mts --watch --vitepress\"", "docs:serve": "pnpm docs:build:vitepress && vitepress serve src/vitepress", + "docs:spellcheck": "cspell --config ../../cSpell.json \"src/docs/**/*.md\"", "release": "pnpm build", "prepublishOnly": "pnpm -w run build" }, @@ -54,13 +55,12 @@ "dependencies": { "@braintree/sanitize-url": "^6.0.0", "d3": "^7.0.0", - "dagre": "^0.8.5", - "dagre-d3": "^0.6.4", + "dagre-d3-es": "7.0.4", "dompurify": "2.4.1", "fast-clone": "^1.5.13", "graphlib": "^2.1.8", "khroma": "^2.0.0", - "lodash": "^4.17.21", + "lodash-es": "^4.17.21", "moment-mini": "^2.24.0", "non-layered-tidy-tree-layout": "^2.0.2", "stylis": "^4.1.2", @@ -70,7 +70,7 @@ "@types/d3": "^7.4.0", "@types/dompurify": "^2.4.0", "@types/jsdom": "^20.0.1", - "@types/lodash": "^4.14.188", + "@types/lodash-es": "^4.17.6", "@types/micromatch": "^4.0.2", "@types/prettier": "^2.7.1", "@types/stylis": "^4.0.2", @@ -80,6 +80,7 @@ "chokidar": "^3.5.3", "concurrently": "^7.5.0", "coveralls": "^3.1.1", + "cspell": "^6.14.3", "globby": "^13.1.2", "identity-obj-proxy": "^3.0.0", "jison": "^0.4.18", @@ -97,9 +98,6 @@ "typescript": "^4.8.4", "unist-util-flatmap": "^1.0.0" }, - "resolutions": { - "d3": "^7.0.0" - }, "files": [ "dist", "README.md" diff --git a/packages/mermaid/src/Diagram.ts b/packages/mermaid/src/Diagram.ts index 798adf501..a2349c255 100644 --- a/packages/mermaid/src/Diagram.ts +++ b/packages/mermaid/src/Diagram.ts @@ -2,6 +2,7 @@ import * as configApi from './config'; import { log } from './logger'; import { getDiagram, registerDiagram } from './diagram-api/diagramAPI'; import { detectType, getDiagramLoader } from './diagram-api/detectType'; +import { extractFrontMatter } from './diagram-api/frontmatter'; import { isDetailedError, type DetailedError } from './utils'; export type ParseErrorFunction = (err: string | DetailedError, hash?: any) => void; @@ -29,6 +30,16 @@ export class Diagram { this.db.clear?.(); 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(extractFrontMatter(text, this.db)); this.parser.parser.yy = this.db; if (diagram.init) { diagram.init(cnf); @@ -45,7 +56,7 @@ export class Diagram { } try { text = text + '\n'; - this.db.clear(); + this.db.clear?.(); this.parser.parse(text); return true; } catch (error) { diff --git a/packages/mermaid/src/config.type.ts b/packages/mermaid/src/config.type.ts index cbcd2f661..ff199ca8b 100644 --- a/packages/mermaid/src/config.type.ts +++ b/packages/mermaid/src/config.type.ts @@ -189,6 +189,7 @@ export interface C4DiagramConfig extends BaseDiagramConfig { } export interface GitGraphDiagramConfig extends BaseDiagramConfig { + titleTopMargin?: number; diagramPadding?: number; nodeLabel?: NodeLabel; mainBranchName?: string; @@ -227,6 +228,7 @@ export interface MindmapDiagramConfig extends BaseDiagramConfig { export type PieDiagramConfig = BaseDiagramConfig; export interface ErDiagramConfig extends BaseDiagramConfig { + titleTopMargin?: number; diagramPadding?: number; layoutDirection?: string; minEntityWidth?: number; @@ -238,6 +240,7 @@ export interface ErDiagramConfig extends BaseDiagramConfig { } export interface StateDiagramConfig extends BaseDiagramConfig { + titleTopMargin?: number; arrowMarkerAbsolute?: boolean; dividerMargin?: number; sizeUnit?: number; @@ -258,6 +261,7 @@ export interface StateDiagramConfig extends BaseDiagramConfig { } export interface ClassDiagramConfig extends BaseDiagramConfig { + titleTopMargin?: number; arrowMarkerAbsolute?: boolean; dividerMargin?: number; padding?: number; @@ -343,6 +347,7 @@ export interface SequenceDiagramConfig extends BaseDiagramConfig { } export interface FlowchartDiagramConfig extends BaseDiagramConfig { + titleTopMargin?: number; arrowMarkerAbsolute?: boolean; diagramPadding?: number; htmlLabels?: boolean; diff --git a/packages/mermaid/src/dagre-wrapper/index.js b/packages/mermaid/src/dagre-wrapper/index.js index 72652ff8c..8b77c8520 100644 --- a/packages/mermaid/src/dagre-wrapper/index.js +++ b/packages/mermaid/src/dagre-wrapper/index.js @@ -1,5 +1,5 @@ -import dagre from 'dagre'; -import graphlib from 'graphlib'; +import { layout as dagreLayout } from 'dagre-d3-es/src/dagre/index.js'; +import * as graphlibJson from 'dagre-d3-es/src/graphlib/json'; import insertMarkers from './markers'; import { updateNodeBounds } from './shapes/util'; import { @@ -15,7 +15,7 @@ import { insertEdgeLabel, positionEdgeLabel, insertEdge, clear as clearEdges } f import { log } from '../logger'; const recursiveRender = (_elem, graph, diagramtype, parentCluster) => { - log.info('Graph in recursive render: XXX', graphlib.json.write(graph), 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); @@ -95,8 +95,8 @@ const recursiveRender = (_elem, graph, diagramtype, parentCluster) => { log.info('### Layout ###'); log.info('#############################################'); log.info(graph); - dagre.layout(graph); - log.info('Graph after layout:', graphlib.json.write(graph)); + dagreLayout(graph); + log.info('Graph after layout:', graphlibJson.write(graph)); // Move the nodes to the correct place let diff = 0; sortNodesByHierarchy(graph).forEach(function (v) { @@ -153,10 +153,10 @@ export const render = (elem, graph, markers, diagramtype, id) => { clearClusters(); clearGraphlib(); - log.warn('Graph at first:', graphlib.json.write(graph)); + log.warn('Graph at first:', graphlibJson.write(graph)); adjustClustersAndEdges(graph); - log.warn('Graph after:', graphlib.json.write(graph)); - // log.warn('Graph ever after:', graphlib.json.write(graph.node('A').graph)); + log.warn('Graph after:', graphlibJson.write(graph)); + // log.warn('Graph ever after:', graphlibJson.write(graph.node('A').graph)); recursiveRender(elem, graph, diagramtype); }; diff --git a/packages/mermaid/src/dagre-wrapper/mermaid-graphlib.js b/packages/mermaid/src/dagre-wrapper/mermaid-graphlib.js index 56f656430..15fadc940 100644 --- a/packages/mermaid/src/dagre-wrapper/mermaid-graphlib.js +++ b/packages/mermaid/src/dagre-wrapper/mermaid-graphlib.js @@ -1,6 +1,7 @@ /** Decorates with functions required by mermaids dagre-wrapper. */ import { log } from '../logger'; -import graphlib from 'graphlib'; +import * as graphlibJson from 'dagre-d3-es/src/graphlib/json'; +import * as graphlib from 'dagre-d3-es/src/graphlib'; export let clusterDb = {}; let decendants = {}; @@ -322,7 +323,7 @@ export const adjustClustersAndEdges = (graph, depth) => { graph.setEdge(v, w, edge, e.name); } }); - log.warn('Adjusted Graph', graphlib.json.write(graph)); + log.warn('Adjusted Graph', graphlibJson.write(graph)); extractor(graph, 0); log.trace(clusterDb); @@ -336,7 +337,7 @@ export const adjustClustersAndEdges = (graph, depth) => { }; export const extractor = (graph, depth) => { - log.warn('extractor - ', depth, graphlib.json.write(graph), graph.children('D')); + log.warn('extractor - ', depth, graphlibJson.write(graph), graph.children('D')); if (depth > 10) { log.error('Bailing out'); return; @@ -415,7 +416,7 @@ export const extractor = (graph, depth) => { return {}; }); - log.warn('Old graph before copy', graphlib.json.write(graph)); + log.warn('Old graph before copy', graphlibJson.write(graph)); copy(node, graph, clusterGraph, node); graph.setNode(node, { clusterNode: true, @@ -424,8 +425,8 @@ export const extractor = (graph, depth) => { labelText: clusterDb[node].labelText, graph: clusterGraph, }); - log.warn('New graph after copy node: (', node, ')', graphlib.json.write(clusterGraph)); - log.debug('Old graph after copy', graphlib.json.write(graph)); + log.warn('New graph after copy node: (', node, ')', graphlibJson.write(clusterGraph)); + log.debug('Old graph after copy', graphlibJson.write(graph)); } else { log.warn( 'Cluster ** ', diff --git a/packages/mermaid/src/dagre-wrapper/mermaid-graphlib.spec.js b/packages/mermaid/src/dagre-wrapper/mermaid-graphlib.spec.js index 8155bbf70..49b022f4d 100644 --- a/packages/mermaid/src/dagre-wrapper/mermaid-graphlib.spec.js +++ b/packages/mermaid/src/dagre-wrapper/mermaid-graphlib.spec.js @@ -1,5 +1,5 @@ -import graphlib from 'graphlib'; -import dagre from 'dagre'; +import * as graphlibJson from 'dagre-d3-es/src/graphlib/json'; +import * as graphlib from 'dagre-d3-es/src/graphlib'; import { validate, adjustClustersAndEdges, @@ -233,9 +233,9 @@ describe('Graphlib decorations', () => { g.setParent('D', 'C'); // log.info('Graph before', g.node('D')) - // log.info('Graph before', graphlib.json.write(g)) + // log.info('Graph before', graphlibJson.write(g)) adjustClustersAndEdges(g); - // log.info('Graph after', graphlib.json.write(g), g.node('C').graph) + // log.info('Graph after', graphlibJson.write(g), g.node('C').graph) const CGraph = g.node('C').graph; const DGraph = CGraph.node('D').graph; @@ -279,9 +279,9 @@ describe('Graphlib decorations', () => { g.setEdge('A', 'C', { data: 'link2' }, '2'); log.info('Graph before', g.node('D')); - log.info('Graph before', graphlib.json.write(g)); + log.info('Graph before', graphlibJson.write(g)); adjustClustersAndEdges(g); - log.trace('Graph after', graphlib.json.write(g)); + log.trace('Graph after', graphlibJson.write(g)); expect(g.nodes()).toEqual(['C', 'B', 'A']); expect(g.nodes().length).toBe(3); expect(g.edges().length).toBe(2); @@ -334,11 +334,11 @@ describe('Graphlib decorations', () => { g.setEdge('c', 'd', { data: 'link2' }, '2'); g.setEdge('d', 'e', { data: 'link2' }, '2'); - log.info('Graph before', graphlib.json.write(g)); + log.info('Graph before', graphlibJson.write(g)); adjustClustersAndEdges(g); const bGraph = g.node('b').graph; - // log.trace('Graph after', graphlib.json.write(g)) - log.info('Graph after', graphlib.json.write(bGraph)); + // log.trace('Graph after', graphlibJson.write(g)) + log.info('Graph after', graphlibJson.write(bGraph)); expect(bGraph.nodes().length).toBe(3); expect(bGraph.edges().length).toBe(2); }); @@ -360,13 +360,13 @@ describe('Graphlib decorations', () => { g.setParent('c', 'b'); g.setParent('e', 'c'); - log.info('Graph before', graphlib.json.write(g)); + log.info('Graph before', graphlibJson.write(g)); adjustClustersAndEdges(g); const aGraph = g.node('a').graph; const bGraph = aGraph.node('b').graph; - log.info('Graph after', graphlib.json.write(aGraph)); + log.info('Graph after', graphlibJson.write(aGraph)); const cGraph = bGraph.node('c').graph; - // log.trace('Graph after', graphlib.json.write(g)) + // log.trace('Graph after', graphlibJson.write(g)) expect(aGraph.nodes().length).toBe(1); expect(bGraph.nodes().length).toBe(1); expect(cGraph.nodes().length).toBe(1); @@ -388,14 +388,14 @@ flowchart TB const exportedGraph = JSON.parse( '{"options":{"directed":true,"multigraph":true,"compound":true},"nodes":[{"v":"A","value":{"labelStyle":"","shape":"rect","labelText":"A","rx":0,"ry":0,"class":"default","style":"","id":"A","width":500,"type":"group","padding":15}},{"v":"B","value":{"labelStyle":"","shape":"rect","labelText":"B","rx":0,"ry":0,"class":"default","style":"","id":"B","width":500,"type":"group","padding":15},"parent":"A"},{"v":"b","value":{"labelStyle":"","shape":"rect","labelText":"b","rx":0,"ry":0,"class":"default","style":"","id":"b","padding":15},"parent":"A"},{"v":"c","value":{"labelStyle":"","shape":"rect","labelText":"c","rx":0,"ry":0,"class":"default","style":"","id":"c","padding":15},"parent":"B"},{"v":"a","value":{"labelStyle":"","shape":"rect","labelText":"a","rx":0,"ry":0,"class":"default","style":"","id":"a","padding":15},"parent":"A"}],"edges":[{"v":"b","w":"B","name":"1","value":{"minlen":1,"arrowhead":"normal","arrowTypeStart":"arrow_open","arrowTypeEnd":"arrow_point","thickness":"normal","pattern":"solid","style":"fill:none","labelStyle":"","arrowheadStyle":"fill: #333","labelpos":"c","labelType":"text","label":"","id":"L-b-B","classes":"flowchart-link LS-b LE-B"}},{"v":"a","w":"c","name":"2","value":{"minlen":1,"arrowhead":"normal","arrowTypeStart":"arrow_open","arrowTypeEnd":"arrow_point","thickness":"normal","pattern":"solid","style":"fill:none","labelStyle":"","arrowheadStyle":"fill: #333","labelpos":"c","labelType":"text","label":"","id":"L-a-c","classes":"flowchart-link LS-a LE-c"}}],"value":{"rankdir":"TB","nodesep":50,"ranksep":50,"marginx":8,"marginy":8}}' ); - const gr = graphlib.json.read(exportedGraph); + const gr = graphlibJson.read(exportedGraph); - log.info('Graph before', graphlib.json.write(gr)); + log.info('Graph before', graphlibJson.write(gr)); adjustClustersAndEdges(gr); const aGraph = gr.node('A').graph; const bGraph = aGraph.node('B').graph; - log.info('Graph after', graphlib.json.write(aGraph)); - // log.trace('Graph after', graphlib.json.write(g)) + log.info('Graph after', graphlibJson.write(aGraph)); + // log.trace('Graph after', graphlibJson.write(g)) expect(aGraph.parent('c')).toBe('B'); expect(aGraph.parent('B')).toBe(undefined); }); diff --git a/packages/mermaid/src/defaultConfig.ts b/packages/mermaid/src/defaultConfig.ts index 2ddae580c..37d4f71ff 100644 --- a/packages/mermaid/src/defaultConfig.ts +++ b/packages/mermaid/src/defaultConfig.ts @@ -154,6 +154,17 @@ const config: Partial = { /** The object containing configurations specific for flowcharts */ flowchart: { + /** + * ### titleTopMargin + * + * | Parameter | Description | Type | Required | Values | + * | -------------- | ---------------------------------------------- | ------- | -------- | ------------------ | + * | titleTopMargin | Margin top for the text over the flowchart | Integer | Required | Any Positive Value | + * + * **Notes:** Default value: 25 + */ + titleTopMargin: 25, + /** * | Parameter | Description | Type | Required | Values | * | -------------- | ----------------------------------------------- | ------- | -------- | ------------------ | @@ -851,6 +862,16 @@ const config: Partial = { sectionColours: ['#fff'], }, class: { + /** + * ### titleTopMargin + * + * | Parameter | Description | Type | Required | Values | + * | -------------- | ---------------------------------------------- | ------- | -------- | ------------------ | + * | titleTopMargin | Margin top for the text over the class diagram | Integer | Required | Any Positive Value | + * + * **Notes:** Default value: 25 + */ + titleTopMargin: 25, arrowMarkerAbsolute: false, dividerMargin: 10, padding: 5, @@ -884,6 +905,16 @@ const config: Partial = { defaultRenderer: 'dagre-wrapper', }, state: { + /** + * ### titleTopMargin + * + * | Parameter | Description | Type | Required | Values | + * | -------------- | ---------------------------------------------- | ------- | -------- | ------------------ | + * | titleTopMargin | Margin top for the text over the state diagram | Integer | Required | Any Positive Value | + * + * **Notes:** Default value: 25 + */ + titleTopMargin: 25, dividerMargin: 10, sizeUnit: 5, padding: 8, @@ -932,6 +963,17 @@ const config: Partial = { /** The object containing configurations specific for entity relationship diagrams */ er: { + /** + * ### titleTopMargin + * + * | Parameter | Description | Type | Required | Values | + * | -------------- | ---------------------------------------------- | ------- | -------- | ------------------ | + * | titleTopMargin | Margin top for the text over the diagram | Integer | Required | Any Positive Value | + * + * **Notes:** Default value: 25 + */ + titleTopMargin: 25, + /** * | Parameter | Description | Type | Required | Values | * | -------------- | ----------------------------------------------- | ------- | -------- | ------------------ | @@ -1085,6 +1127,16 @@ const config: Partial = { line_height: 20, }, gitGraph: { + /** + * ### titleTopMargin + * + * | Parameter | Description | Type | Required | Values | + * | -------------- | ---------------------------------------------- | ------- | -------- | ------------------ | + * | titleTopMargin | Margin top for the text over the Git diagram | Integer | Required | Any Positive Value | + * + * **Notes:** Default value: 25 + */ + titleTopMargin: 25, diagramPadding: 8, nodeLabel: { width: 75, diff --git a/packages/mermaid/src/diagram-api/detectType.ts b/packages/mermaid/src/diagram-api/detectType.ts index 1c1abc51c..6f9857221 100644 --- a/packages/mermaid/src/diagram-api/detectType.ts +++ b/packages/mermaid/src/diagram-api/detectType.ts @@ -1,6 +1,7 @@ import { MermaidConfig } from '../config.type'; import { log } from '../logger'; import { DetectorRecord, DiagramDetector, DiagramLoader } from './types'; +import { frontMatterRegex } from './frontmatter'; const directive = /[%]{2}[{]\s*(?:(?:(\w+)\s*:|(\w+))\s*(?:(?:(\w+))|((?:(?![}][%]{2}).|\r?\n)*))?\s*)(?:[}][%]{2})?/gi; @@ -31,7 +32,7 @@ const detectors: Record = {}; * @returns A graph definition key */ export const detectType = function (text: string, config?: MermaidConfig): string { - text = text.replace(directive, '').replace(anyComment, '\n'); + text = text.replace(frontMatterRegex, '').replace(directive, '').replace(anyComment, '\n'); for (const [key, { detector }] of Object.entries(detectors)) { const diagram = detector(text, config); if (diagram) { diff --git a/packages/mermaid/src/diagram-api/frontmatter.spec.ts b/packages/mermaid/src/diagram-api/frontmatter.spec.ts new file mode 100644 index 000000000..4eb9789e2 --- /dev/null +++ b/packages/mermaid/src/diagram-api/frontmatter.spec.ts @@ -0,0 +1,78 @@ +import { vi } from 'vitest'; +import { extractFrontMatter } from './frontmatter'; + +const dbMock = () => ({ setDiagramTitle: vi.fn() }); + +describe('extractFrontmatter', () => { + it('returns text unchanged if no frontmatter', () => { + expect(extractFrontMatter('diagram', dbMock())).toEqual('diagram'); + }); + + it('returns text unchanged if frontmatter lacks closing delimiter', () => { + const text = `---\ntitle: foo\ndiagram`; + expect(extractFrontMatter(text, dbMock())).toEqual(text); + }); + + it('handles empty frontmatter', () => { + const db = dbMock(); + const text = `---\n\n---\ndiagram`; + expect(extractFrontMatter(text, db)).toEqual('diagram'); + expect(db.setDiagramTitle).not.toHaveBeenCalled(); + }); + + it('handles frontmatter without mappings', () => { + const db = dbMock(); + const text = `---\n1\n---\ndiagram`; + expect(extractFrontMatter(text, db)).toEqual('diagram'); + expect(db.setDiagramTitle).not.toHaveBeenCalled(); + }); + + 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(); + }); + + 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'); + }); + + 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'); + }); + + 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'); + }); + + 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'); + }); + + 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'); + }); + + it('throws exception for invalid YAML syntax', () => { + const text = `---\n!!!\n---\ndiagram`; + expect(() => extractFrontMatter(text, dbMock())).toThrow( + 'tag suffix cannot contain exclamation marks' + ); + }); +}); diff --git a/packages/mermaid/src/diagram-api/frontmatter.ts b/packages/mermaid/src/diagram-api/frontmatter.ts new file mode 100644 index 000000000..800e7399b --- /dev/null +++ b/packages/mermaid/src/diagram-api/frontmatter.ts @@ -0,0 +1,40 @@ +import { DiagramDb } from './types'; +// The "* as yaml" part is necessary for tree-shaking +import * as yaml from 'js-yaml'; + +// Match Jekyll-style front matter blocks (https://jekyllrb.com/docs/front-matter/). +// Based on regex used by Jekyll: https://github.com/jekyll/jekyll/blob/6dd3cc21c40b98054851846425af06c64f9fb466/lib/jekyll/document.rb#L10 +// Note that JS doesn't support the "\A" anchor, which means we can't use +// multiline mode. +// Relevant YAML spec: https://yaml.org/spec/1.2.2/#914-explicit-documents +export const frontMatterRegex = /^(?:---\s*[\r\n])(.*?)(?:[\r\n]---\s*[\r\n]+)/s; + +type FrontMatterMetadata = { + title?: string; +}; + +/** + * 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. + * @returns text with frontmatter stripped out + */ +export function extractFrontMatter(text: string, db: DiagramDb): string { + const matches = text.match(frontMatterRegex); + if (matches) { + const parsed: FrontMatterMetadata = yaml.load(matches[1], { + // To keep things simple, only allow strings, arrays, and plain objects. + // https://www.yaml.org/spec/1.2/spec.html#id2802346 + schema: yaml.FAILSAFE_SCHEMA, + }) as FrontMatterMetadata; + + if (parsed?.title) { + db.setDiagramTitle?.(parsed.title); + } + + return text.slice(matches[0].length); + } else { + return text; + } +} diff --git a/packages/mermaid/src/diagram-api/types.ts b/packages/mermaid/src/diagram-api/types.ts index d45eac6aa..23810d133 100644 --- a/packages/mermaid/src/diagram-api/types.ts +++ b/packages/mermaid/src/diagram-api/types.ts @@ -8,8 +8,16 @@ export interface InjectUtils { _setupGraphViewbox: any; } +/** + * Generic Diagram DB that may apply to any diagram type. + */ +export interface DiagramDb { + clear?: () => void; + setDiagramTitle?: (title: string) => void; +} + export interface DiagramDefinition { - db: any; + db: DiagramDb; renderer: any; parser: any; styles: any; diff --git a/packages/mermaid/src/diagrams/class/classDb.js b/packages/mermaid/src/diagrams/class/classDb.js index 83ef6072b..9830c059e 100644 --- a/packages/mermaid/src/diagrams/class/classDb.js +++ b/packages/mermaid/src/diagrams/class/classDb.js @@ -10,6 +10,8 @@ import { getAccDescription, setAccDescription, clear as commonClear, + setDiagramTitle, + getDiagramTitle, } from '../../commonDb'; const MERMAID_DOM_ID_PREFIX = 'classid-'; @@ -408,4 +410,6 @@ export default { getTooltip, setTooltip, lookUpDomId, + setDiagramTitle, + getDiagramTitle, }; diff --git a/packages/mermaid/src/diagrams/class/classRenderer-v2.js b/packages/mermaid/src/diagrams/class/classRenderer-v2.js index fbc2e4833..905e6ee2f 100644 --- a/packages/mermaid/src/diagrams/class/classRenderer-v2.js +++ b/packages/mermaid/src/diagrams/class/classRenderer-v2.js @@ -1,8 +1,9 @@ import { select } from 'd3'; -import graphlib from 'graphlib'; +import * as graphlib from 'dagre-d3-es/src/graphlib'; import { log } from '../../logger'; import { getConfig } from '../../config'; import { render } from '../../dagre-wrapper/index.js'; +import utils from '../../utils'; import { curveLinear } from 'd3'; import { interpolateToCurve, getStylesFromArray } from '../../utils'; import { setupGraphViewbox } from '../../setupGraphViewbox'; @@ -429,6 +430,8 @@ export const draw = function (text, id, _version, diagObj) { id ); + utils.insertTitle(svg, 'classTitleText', conf.titleTopMargin, diagObj.db.getDiagramTitle()); + setupGraphViewbox(g, svg, conf.diagramPadding, conf.useMaxWidth); // Add label rects for non html labels diff --git a/packages/mermaid/src/diagrams/class/classRenderer.js b/packages/mermaid/src/diagrams/class/classRenderer.js index 23b586192..74e2a48c2 100644 --- a/packages/mermaid/src/diagrams/class/classRenderer.js +++ b/packages/mermaid/src/diagrams/class/classRenderer.js @@ -1,6 +1,6 @@ import { select } from 'd3'; -import dagre from 'dagre'; -import graphlib from 'graphlib'; +import { layout as dagreLayout } from 'dagre-d3-es/src/dagre/index.js'; +import * as graphlib from 'dagre-d3-es/src/graphlib/index.js'; import { log } from '../../logger'; import svgDraw from './svgDraw'; import { configureSvgSize } from '../../setupGraphViewbox'; @@ -238,7 +238,7 @@ export const draw = function (text, id, _version, diagObj) { } }); - dagre.layout(g); + dagreLayout(g); g.nodes().forEach(function (v) { if (typeof v !== 'undefined' && typeof g.node(v) !== 'undefined') { log.debug('Node ' + v + ': ' + JSON.stringify(g.node(v))); diff --git a/packages/mermaid/src/diagrams/class/styles.js b/packages/mermaid/src/diagrams/class/styles.js index bc391114e..981cd7b73 100644 --- a/packages/mermaid/src/diagrams/class/styles.js +++ b/packages/mermaid/src/diagrams/class/styles.js @@ -148,6 +148,11 @@ g.classGroup line { font-size: 11px; } +.classTitleText { + text-anchor: middle; + font-size: 18px; + fill: ${options.textColor}; +} `; export default getStyles; diff --git a/packages/mermaid/src/diagrams/er/erDb.js b/packages/mermaid/src/diagrams/er/erDb.js index ad3454f84..96b60836b 100644 --- a/packages/mermaid/src/diagrams/er/erDb.js +++ b/packages/mermaid/src/diagrams/er/erDb.js @@ -8,6 +8,8 @@ import { getAccDescription, setAccDescription, clear as commonClear, + setDiagramTitle, + getDiagramTitle, } from '../../commonDb'; let entities = {}; @@ -94,4 +96,6 @@ export default { getAccTitle, setAccDescription, getAccDescription, + setDiagramTitle, + getDiagramTitle, }; diff --git a/packages/mermaid/src/diagrams/er/erRenderer.js b/packages/mermaid/src/diagrams/er/erRenderer.js index 323bb4607..36267f415 100644 --- a/packages/mermaid/src/diagrams/er/erRenderer.js +++ b/packages/mermaid/src/diagrams/er/erRenderer.js @@ -1,8 +1,9 @@ -import graphlib from 'graphlib'; +import * as graphlib from 'dagre-d3-es/src/graphlib'; import { line, curveBasis, select } from 'd3'; -import dagre from 'dagre'; +import { layout as dagreLayout } from 'dagre-d3-es/src/dagre/index.js'; import { getConfig } from '../../config'; import { log } from '../../logger'; +import utils from '../../utils'; import erMarkers from './erMarkers'; import { configureSvgSize } from '../../setupGraphViewbox'; import addSVGAccessibilityFields from '../../accessibility'; @@ -210,9 +211,6 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => { const typeRect = groupNode .insert('rect', '#' + attributeNode.tn.node().id) .classed(`er ${attribStyle}`, true) - .style('fill', conf.fill) - .style('fill-opacity', '100%') - .style('stroke', conf.stroke) .attr('x', 0) .attr('y', heightOffset) .attr('width', maxTypeWidth + widthPadding * 2 + spareColumnWidth) @@ -230,9 +228,6 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => { const nameRect = groupNode .insert('rect', '#' + attributeNode.nn.node().id) .classed(`er ${attribStyle}`, true) - .style('fill', conf.fill) - .style('fill-opacity', '100%') - .style('stroke', conf.stroke) .attr('x', nameXOffset) .attr('y', heightOffset) .attr('width', maxNameWidth + widthPadding * 2 + spareColumnWidth) @@ -252,9 +247,6 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => { const keyTypeRect = groupNode .insert('rect', '#' + attributeNode.kn.node().id) .classed(`er ${attribStyle}`, true) - .style('fill', conf.fill) - .style('fill-opacity', '100%') - .style('stroke', conf.stroke) .attr('x', keyTypeAndCommentXOffset) .attr('y', heightOffset) .attr('width', maxKeyWidth + widthPadding * 2 + spareColumnWidth) @@ -275,9 +267,6 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => { groupNode .insert('rect', '#' + attributeNode.cn.node().id) .classed(`er ${attribStyle}`, 'true') - .style('fill', conf.fill) - .style('fill-opacity', '100%') - .style('stroke', conf.stroke) .attr('x', keyTypeAndCommentXOffset) .attr('y', heightOffset) .attr('width', maxCommentWidth + widthPadding * 2 + spareColumnWidth) @@ -347,9 +336,6 @@ const drawEntities = function (svgNode, entities, graph) { const rectNode = groupNode .insert('rect', '#' + textId) .classed('er entityBox', true) - .style('fill', conf.fill) - .style('fill-opacity', '100%') - .style('stroke', conf.stroke) .attr('x', 0) .attr('y', 0) .attr('width', entityWidth) @@ -547,9 +533,7 @@ const drawRelationshipFromLayout = function (svg, rel, g, insert, diagObj) { .attr('x', labelPoint.x - labelBBox.width / 2) .attr('y', labelPoint.y - labelBBox.height / 2) .attr('width', labelBBox.width) - .attr('height', labelBBox.height) - .style('fill', 'white') - .style('fill-opacity', '85%'); + .attr('height', labelBBox.height); }; /** @@ -637,7 +621,7 @@ export const draw = function (text, id, _version, diagObj) { // Add all the relationships to the graph const relationships = addRelationships(diagObj.db.getRelationships(), g); - dagre.layout(g); // Node and edge positions will be updated + dagreLayout(g); // Node and edge positions will be updated // Adjust the positions of the entities so that they adhere to the layout adjustEntities(svg, g); @@ -649,6 +633,8 @@ export const draw = function (text, id, _version, diagObj) { const padding = conf.diagramPadding; + utils.insertTitle(svg, 'entityTitleText', conf.titleTopMargin, diagObj.db.getDiagramTitle()); + const svgBounds = svg.node().getBBox(); const width = svgBounds.width + padding * 2; const height = svgBounds.height + padding * 2; diff --git a/packages/mermaid/src/diagrams/er/styles.js b/packages/mermaid/src/diagrams/er/styles.js index 907d813b6..42dbcebde 100644 --- a/packages/mermaid/src/diagrams/er/styles.js +++ b/packages/mermaid/src/diagrams/er/styles.js @@ -27,6 +27,12 @@ const getStyles = (options) => .relationshipLine { stroke: ${options.lineColor}; } + + .entityTitleText { + text-anchor: middle; + font-size: 18px; + fill: ${options.textColor}; + } `; export default getStyles; diff --git a/packages/mermaid/src/diagrams/flowchart/flowChartShapes.js b/packages/mermaid/src/diagrams/flowchart/flowChartShapes.js index b66bfe730..d02d484c4 100644 --- a/packages/mermaid/src/diagrams/flowchart/flowChartShapes.js +++ b/packages/mermaid/src/diagrams/flowchart/flowChartShapes.js @@ -1,4 +1,5 @@ -import dagreD3 from 'dagre-d3'; +import { intersectPolygon } from 'dagre-d3-es/src/dagre-js/intersect/intersect-polygon.js'; +import { intersectRect } from 'dagre-d3-es/src/dagre-js/intersect/intersect-rect.js'; /** * @param parent @@ -17,7 +18,7 @@ function question(parent, bbox, node) { ]; const shapeSvg = insertPolygonShape(parent, s, s, points); node.intersect = function (point) { - return dagreD3.intersect.polygon(node, points, point); + return intersectPolygon(node, points, point); }; return shapeSvg; } @@ -42,7 +43,7 @@ function hexagon(parent, bbox, node) { ]; const shapeSvg = insertPolygonShape(parent, w, h, points); node.intersect = function (point) { - return dagreD3.intersect.polygon(node, points, point); + return intersectPolygon(node, points, point); }; return shapeSvg; } @@ -64,7 +65,7 @@ function rect_left_inv_arrow(parent, bbox, node) { ]; const shapeSvg = insertPolygonShape(parent, w, h, points); node.intersect = function (point) { - return dagreD3.intersect.polygon(node, points, point); + return intersectPolygon(node, points, point); }; return shapeSvg; } @@ -85,7 +86,7 @@ function lean_right(parent, bbox, node) { ]; const shapeSvg = insertPolygonShape(parent, w, h, points); node.intersect = function (point) { - return dagreD3.intersect.polygon(node, points, point); + return intersectPolygon(node, points, point); }; return shapeSvg; } @@ -106,7 +107,7 @@ function lean_left(parent, bbox, node) { ]; const shapeSvg = insertPolygonShape(parent, w, h, points); node.intersect = function (point) { - return dagreD3.intersect.polygon(node, points, point); + return intersectPolygon(node, points, point); }; return shapeSvg; } @@ -127,7 +128,7 @@ function trapezoid(parent, bbox, node) { ]; const shapeSvg = insertPolygonShape(parent, w, h, points); node.intersect = function (point) { - return dagreD3.intersect.polygon(node, points, point); + return intersectPolygon(node, points, point); }; return shapeSvg; } @@ -148,7 +149,7 @@ function inv_trapezoid(parent, bbox, node) { ]; const shapeSvg = insertPolygonShape(parent, w, h, points); node.intersect = function (point) { - return dagreD3.intersect.polygon(node, points, point); + return intersectPolygon(node, points, point); }; return shapeSvg; } @@ -170,7 +171,7 @@ function rect_right_inv_arrow(parent, bbox, node) { ]; const shapeSvg = insertPolygonShape(parent, w, h, points); node.intersect = function (point) { - return dagreD3.intersect.polygon(node, points, point); + return intersectPolygon(node, points, point); }; return shapeSvg; } @@ -194,7 +195,7 @@ function stadium(parent, bbox, node) { .attr('height', h); node.intersect = function (point) { - return dagreD3.intersect.rect(node, point); + return intersectRect(node, point); }; return shapeSvg; } @@ -221,7 +222,7 @@ function subroutine(parent, bbox, node) { ]; const shapeSvg = insertPolygonShape(parent, w, h, points); node.intersect = function (point) { - return dagreD3.intersect.polygon(node, points, point); + return intersectPolygon(node, points, point); }; return shapeSvg; } @@ -270,7 +271,7 @@ function cylinder(parent, bbox, node) { .attr('transform', 'translate(' + -w / 2 + ',' + -(h / 2 + ry) + ')'); node.intersect = function (point) { - const pos = dagreD3.intersect.rect(node, point); + const pos = intersectRect(node, point); const x = pos.x - node.x; if ( diff --git a/packages/mermaid/src/diagrams/flowchart/flowDb.js b/packages/mermaid/src/diagrams/flowchart/flowDb.js index 6abc22659..38754c667 100644 --- a/packages/mermaid/src/diagrams/flowchart/flowDb.js +++ b/packages/mermaid/src/diagrams/flowchart/flowDb.js @@ -10,6 +10,8 @@ import { getAccDescription, setAccDescription, clear as commonClear, + setDiagramTitle, + getDiagramTitle, } from '../../commonDb'; const MERMAID_DOM_ID_PREFIX = 'flowchart-'; @@ -785,4 +787,6 @@ export default { }, exists, makeUniq, + setDiagramTitle, + getDiagramTitle, }; diff --git a/packages/mermaid/src/diagrams/flowchart/flowRenderer-v2.js b/packages/mermaid/src/diagrams/flowchart/flowRenderer-v2.js index 6b7c4c1bf..fa1b692cf 100644 --- a/packages/mermaid/src/diagrams/flowchart/flowRenderer-v2.js +++ b/packages/mermaid/src/diagrams/flowchart/flowRenderer-v2.js @@ -1,11 +1,12 @@ -import graphlib from 'graphlib'; +import * as graphlib from 'dagre-d3-es/src/graphlib'; import { select, curveLinear, selectAll } from 'd3'; import flowDb from './flowDb'; import { getConfig } from '../../config'; +import utils from '../../utils'; import { render } from '../../dagre-wrapper/index.js'; -import addHtmlLabel from 'dagre-d3/lib/label/add-html-label.js'; +import { addHtmlLabel } from 'dagre-d3-es/src/dagre-js/label/add-html-label.js'; import { log } from '../../logger'; import common, { evaluate } from '../common/common'; import { interpolateToCurve, getStylesFromArray } from '../../utils'; @@ -437,6 +438,8 @@ export const draw = function (text, id, _version, diagObj) { const element = root.select('#' + id + ' g'); render(element, g, ['point', 'circle', 'cross'], 'flowchart', id); + utils.insertTitle(svg, 'flowchartTitleText', conf.titleTopMargin, diagObj.db.getDiagramTitle()); + setupGraphViewbox(g, svg, conf.diagramPadding, conf.useMaxWidth); // Index nodes diff --git a/packages/mermaid/src/diagrams/flowchart/flowRenderer.js b/packages/mermaid/src/diagrams/flowchart/flowRenderer.js index c403b7fe3..7febc4034 100644 --- a/packages/mermaid/src/diagrams/flowchart/flowRenderer.js +++ b/packages/mermaid/src/diagrams/flowchart/flowRenderer.js @@ -1,8 +1,9 @@ -import graphlib from 'graphlib'; +import * as graphlib from 'dagre-d3-es/src/graphlib'; import { select, curveLinear, selectAll } from 'd3'; import { getConfig } from '../../config'; -import dagreD3 from 'dagre-d3'; -import addHtmlLabel from 'dagre-d3/lib/label/add-html-label.js'; +import { render as Render } from 'dagre-d3-es'; +import { applyStyle } from 'dagre-d3-es/src/dagre-js/util.js'; +import { addHtmlLabel } from 'dagre-d3-es/src/dagre-js/label/add-html-label.js'; import { log } from '../../logger'; import common, { evaluate } from '../common/common'; import { interpolateToCurve, getStylesFromArray } from '../../utils'; @@ -370,7 +371,6 @@ export const draw = function (text, id, _version, diagObj) { addEdges(edges, g, diagObj); // Create the renderer - const Render = dagreD3.render; const render = new Render(); // Add custom shapes @@ -390,7 +390,7 @@ export const draw = function (text, id, _version, diagObj) { .attr('orient', 'auto'); const path = marker.append('path').attr('d', 'M 0 0 L 0 0 L 0 0 z'); - dagreD3.util.applyStyle(path, edge[type + 'Style']); + applyStyle(path, edge[type + 'Style']); }; // Override normal arrowhead defined in d3. Remove style & add class to allow css styling. diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow-direction.spec.js b/packages/mermaid/src/diagrams/flowchart/parser/flow-direction.spec.js index 6b741fc12..6cba33f88 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow-direction.spec.js +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow-direction.spec.js @@ -1,6 +1,6 @@ import flowDb from '../flowDb'; import flow from './flow'; -import filter from 'lodash/filter'; +import filter from 'lodash-es/filter'; import { setConfig } from '../../../config'; setConfig({ diff --git a/packages/mermaid/src/diagrams/flowchart/parser/subgraph.spec.js b/packages/mermaid/src/diagrams/flowchart/parser/subgraph.spec.js index aa8e9217f..6fec233e7 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/subgraph.spec.js +++ b/packages/mermaid/src/diagrams/flowchart/parser/subgraph.spec.js @@ -1,6 +1,6 @@ import flowDb from '../flowDb'; import flow from './flow'; -import filter from 'lodash/filter'; +import filter from 'lodash-es/filter'; import { setConfig } from '../../../config'; setConfig({ diff --git a/packages/mermaid/src/diagrams/flowchart/styles.ts b/packages/mermaid/src/diagrams/flowchart/styles.ts index 82fb1f875..a89d33d3d 100644 --- a/packages/mermaid/src/diagrams/flowchart/styles.ts +++ b/packages/mermaid/src/diagrams/flowchart/styles.ts @@ -103,6 +103,12 @@ const getStyles = (options: FlowChartStyleOptions) => pointer-events: none; z-index: 100; } + + .flowchartTitleText { + text-anchor: middle; + font-size: 18px; + fill: ${options.textColor}; + } `; export default getStyles; diff --git a/packages/mermaid/src/diagrams/git/gitGraphAst.js b/packages/mermaid/src/diagrams/git/gitGraphAst.js index 496e578b7..65980933d 100644 --- a/packages/mermaid/src/diagrams/git/gitGraphAst.js +++ b/packages/mermaid/src/diagrams/git/gitGraphAst.js @@ -10,6 +10,8 @@ import { getAccDescription, setAccDescription, clear as commonClear, + setDiagramTitle, + getDiagramTitle, } from '../../commonDb'; let mainBranchName = getConfig().gitGraph.mainBranchName; @@ -529,5 +531,7 @@ export default { getAccTitle, getAccDescription, setAccDescription, + setDiagramTitle, + getDiagramTitle, commitType, }; diff --git a/packages/mermaid/src/diagrams/git/gitGraphRenderer.js b/packages/mermaid/src/diagrams/git/gitGraphRenderer.js index 71698a500..75e8d445d 100644 --- a/packages/mermaid/src/diagrams/git/gitGraphRenderer.js +++ b/packages/mermaid/src/diagrams/git/gitGraphRenderer.js @@ -1,6 +1,7 @@ import { select } from 'd3'; import { getConfig, setupGraphViewbox } from '../../diagram-api/diagramAPI'; import { log } from '../../logger'; +import utils from '../../utils'; import addSVGAccessibilityFields from '../../accessibility'; let allCommitsDict = {}; @@ -521,6 +522,12 @@ export const draw = function (txt, id, ver, diagObj) { } drawArrows(diagram, allCommitsDict); drawCommits(diagram, allCommitsDict, true); + utils.insertTitle( + diagram, + 'gitTitleText', + gitGraphConfig.titleTopMargin, + diagObj.db.getDiagramTitle() + ); // Setup the view box and size of the svg element setupGraphViewbox( diff --git a/packages/mermaid/src/diagrams/git/styles.js b/packages/mermaid/src/diagrams/git/styles.js index 7e09ff7e0..741760235 100644 --- a/packages/mermaid/src/diagrams/git/styles.js +++ b/packages/mermaid/src/diagrams/git/styles.js @@ -51,6 +51,11 @@ const getStyles = (options) => } .arrow { stroke-width: 8; stroke-linecap: round; fill: none} + .gitTitleText { + text-anchor: middle; + font-size: 18px; + fill: ${options.textColor}; + } } `; diff --git a/packages/mermaid/src/diagrams/pie/pieDetector.ts b/packages/mermaid/src/diagrams/pie/pieDetector.ts index 65a011c7a..e267c710a 100644 --- a/packages/mermaid/src/diagrams/pie/pieDetector.ts +++ b/packages/mermaid/src/diagrams/pie/pieDetector.ts @@ -1,5 +1,6 @@ import type { DiagramDetector } from '../../diagram-api/types'; export const pieDetector: DiagramDetector = (txt) => { - return txt.match(/^\s*pie/) !== null; + const logOutput = txt.match(/^\s*pie/) !== null || txt.match(/^\s*bar/) !== null; + return logOutput; }; diff --git a/packages/mermaid/src/diagrams/pie/pieRenderer.js b/packages/mermaid/src/diagrams/pie/pieRenderer.js index 6cbb99fa3..60e9a59c9 100644 --- a/packages/mermaid/src/diagrams/pie/pieRenderer.js +++ b/packages/mermaid/src/diagrams/pie/pieRenderer.js @@ -157,11 +157,11 @@ export const draw = (txt, id, _version, diagObj) => { .append('g') .attr('class', 'legend') .attr('transform', function (d, i) { - var height = legendRectSize + legendSpacing; - var offset = (height * color.domain().length) / 2; - var horz = 12 * legendRectSize; - var vert = i * height - offset; - return 'translate(' + horz + ',' + vert + ')'; + const height = legendRectSize + legendSpacing; + const offset = (height * color.domain().length) / 2; + const horizontal = 12 * legendRectSize; + const vertical = i * height - offset; + return 'translate(' + horizontal + ',' + vertical + ')'; }); legend diff --git a/packages/mermaid/src/diagrams/requirement/requirementRenderer.js b/packages/mermaid/src/diagrams/requirement/requirementRenderer.js index 79d67e76e..10469d50d 100644 --- a/packages/mermaid/src/diagrams/requirement/requirementRenderer.js +++ b/packages/mermaid/src/diagrams/requirement/requirementRenderer.js @@ -1,6 +1,6 @@ import { line, select } from 'd3'; -import dagre from 'dagre'; -import graphlib from 'graphlib'; +import { layout as dagreLayout } from 'dagre-d3-es/src/dagre/index.js'; +import * as graphlib from 'dagre-d3-es/src/graphlib/index.js'; import { log } from '../../logger'; import { configureSvgSize } from '../../setupGraphViewbox'; import common from '../common/common'; @@ -348,7 +348,7 @@ export const draw = (text, id, _version, diagObj) => { drawReqs(requirements, g, svg); drawElements(elements, g, svg); addRelationships(relationships, g); - dagre.layout(g); + dagreLayout(g); adjustEntities(svg, g); relationships.forEach(function (rel) { diff --git a/packages/mermaid/src/diagrams/state/stateDb.js b/packages/mermaid/src/diagrams/state/stateDb.js index 5e82eaf78..19ecbe65f 100644 --- a/packages/mermaid/src/diagrams/state/stateDb.js +++ b/packages/mermaid/src/diagrams/state/stateDb.js @@ -9,6 +9,8 @@ import { getAccDescription, setAccDescription, clear as commonClear, + setDiagramTitle, + getDiagramTitle, } from '../../commonDb'; import { @@ -571,4 +573,6 @@ export default { addStyleClass, setCssClass, addDescription, + setDiagramTitle, + getDiagramTitle, }; diff --git a/packages/mermaid/src/diagrams/state/stateRenderer-v2.js b/packages/mermaid/src/diagrams/state/stateRenderer-v2.js index 752b70e44..6fdeb723e 100644 --- a/packages/mermaid/src/diagrams/state/stateRenderer-v2.js +++ b/packages/mermaid/src/diagrams/state/stateRenderer-v2.js @@ -1,10 +1,11 @@ -import graphlib from 'graphlib'; +import * as graphlib from 'dagre-d3-es/src/graphlib'; import { select } from 'd3'; import { getConfig } from '../../config'; import { render } from '../../dagre-wrapper/index.js'; import { log } from '../../logger'; import { configureSvgSize } from '../../setupGraphViewbox'; import common from '../common/common'; +import utils from '../../utils'; import addSVGAccessibilityFields from '../../accessibility'; import { DEFAULT_DIAGRAM_DIRECTION, @@ -437,8 +438,9 @@ export const draw = function (text, id, _version, diag) { const padding = 8; - const bounds = svg.node().getBBox(); + utils.insertTitle(svg, 'statediagramTitleText', conf.titleTopMargin, diag.db.getDiagramTitle()); + const bounds = svg.node().getBBox(); const width = bounds.width + padding * 2; const height = bounds.height + padding * 2; diff --git a/packages/mermaid/src/diagrams/state/stateRenderer.js b/packages/mermaid/src/diagrams/state/stateRenderer.js index 73717a645..57b1d1665 100644 --- a/packages/mermaid/src/diagrams/state/stateRenderer.js +++ b/packages/mermaid/src/diagrams/state/stateRenderer.js @@ -1,6 +1,6 @@ import { select } from 'd3'; -import dagre from 'dagre'; -import graphlib from 'graphlib'; +import { layout as dagreLayout } from 'dagre-d3-es/src/dagre/index.js'; +import * as graphlib from 'dagre-d3-es/src/graphlib/index.js'; import { log } from '../../logger'; import common from '../common/common'; import { drawState, addTitleAndBox, drawEdge } from './shapes'; @@ -239,7 +239,7 @@ const renderDoc = (doc, diagram, parentId, altBkg, root, domDocument, diagObj) = ); }); - dagre.layout(graph); + dagreLayout(graph); log.debug('Graph after layout', graph.nodes()); const svgElem = diagram.node(); diff --git a/packages/mermaid/src/diagrams/state/styles.js b/packages/mermaid/src/diagrams/state/styles.js index 4a1c46512..f4783b477 100644 --- a/packages/mermaid/src/diagrams/state/styles.js +++ b/packages/mermaid/src/diagrams/state/styles.js @@ -194,6 +194,12 @@ g.stateGroup line { stroke: ${options.lineColor}; stroke-width: 1; } + +.statediagramTitleText { + text-anchor: middle; + font-size: 18px; + fill: ${options.textColor}; +} `; export default getStyles; diff --git a/packages/mermaid/src/docs/redirect.spec.ts b/packages/mermaid/src/docs/.vitepress/theme/redirect.spec.ts similarity index 96% rename from packages/mermaid/src/docs/redirect.spec.ts rename to packages/mermaid/src/docs/.vitepress/theme/redirect.spec.ts index 6bf25c03b..c26364108 100644 --- a/packages/mermaid/src/docs/redirect.spec.ts +++ b/packages/mermaid/src/docs/.vitepress/theme/redirect.spec.ts @@ -2,7 +2,7 @@ // Update https://github.com/mermaid-js/mermaid/blob/18c27c6f1d0537a738cbd95898df301b83c38ffc/packages/mermaid/src/docs.mts#L246 once fixed import { expect, test } from 'vitest'; -import { getRedirect } from './.vitepress/theme/redirect'; +import { getRedirect } from './redirect'; test.each([ ['http://localhost:1234/mermaid/#/flowchart.md', 'syntax/flowchart.html'], diff --git a/packages/mermaid/src/docs/config/usage.md b/packages/mermaid/src/docs/config/usage.md index 187f3f89c..3eac4ad6f 100644 --- a/packages/mermaid/src/docs/config/usage.md +++ b/packages/mermaid/src/docs/config/usage.md @@ -347,7 +347,7 @@ This is the preferred way of configuring mermaid. ## Using the mermaid object -Is it possible to set some configuration via the mermaid object. The two parameters that are supported using this +It is possible to set some configuration via the mermaid object. The two parameters that are supported using this approach are: - mermaid.startOnLoad diff --git a/packages/mermaid/src/docs/intro/index.md b/packages/mermaid/src/docs/intro/index.md index 80d806730..b58321e75 100644 --- a/packages/mermaid/src/docs/intro/index.md +++ b/packages/mermaid/src/docs/intro/index.md @@ -8,7 +8,7 @@ It is a JavaScript based diagramming and charting tool that renders Markdown-ins -[](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [](https://www.npmjs.com/package/mermaid) [](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [](https://www.jsdelivr.com/package/npm/mermaid) [](https://www.npmjs.com/package/mermaid) [](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [](https://twitter.com/mermaidjs_) +[](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [](https://www.npmjs.com/package/mermaid) [](https://bundlephobia.com/package/mermaid) [](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [](https://www.jsdelivr.com/package/npm/mermaid) [](https://www.npmjs.com/package/mermaid) [](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [](https://twitter.com/mermaidjs_) diff --git a/packages/mermaid/src/docs/misc/integrations.md b/packages/mermaid/src/docs/misc/integrations.md index 4c87d170e..06d09634f 100644 --- a/packages/mermaid/src/docs/misc/integrations.md +++ b/packages/mermaid/src/docs/misc/integrations.md @@ -33,6 +33,7 @@ They also serve as proof of concept, for the variety of things that can be built - [markdown-for-mermaid-plugin](https://github.com/jamieh-mongolian/markdown-for-mermaid-plugin) - [JetBrains IDE eg Pycharm](https://www.jetbrains.com/go/guide/tips/mermaid-js-support-in-markdown/) - [mermerd](https://github.com/KarnerTh/mermerd) +- Visual Studio Code [Polyglot Interactive Notebooks](https://github.com/dotnet/interactive#net-interactive) ## CRM/ERP/Similar @@ -115,6 +116,7 @@ They also serve as proof of concept, for the variety of things that can be built - [Draw.io](https://draw.io) - [Plugin](https://github.com/nopeslide/drawio_mermaid_plugin) - [Inkdrop](https://www.inkdrop.app) - [Plugin](https://github.com/inkdropapp/inkdrop-mermaid) - [Vim](https://www.vim.org) + - [Official Vim Syntax and ftplugin](https://github.com/craigmac/vim-mermaid) - [Vim Diagram Syntax](https://github.com/zhaozg/vim-diagram) - [GNU Emacs](https://www.gnu.org/software/emacs/) - [Major mode for .mmd files](https://github.com/abrochard/mermaid-mode) diff --git a/packages/mermaid/src/docs/syntax/c4c.md b/packages/mermaid/src/docs/syntax/c4c.md index f9850f2cd..78528f7b9 100644 --- a/packages/mermaid/src/docs/syntax/c4c.md +++ b/packages/mermaid/src/docs/syntax/c4c.md @@ -318,7 +318,7 @@ UpdateRelStyle(customerA, bankA, $offsetY="60") Container(mobile, "Mobile App", "Xamarin", "Provides a limited subset of the Internet Banking functionality to customers via their mobile device.") } - Deployment_Node(comp, "Customer's computer", "Mircosoft Windows or Apple macOS"){ + Deployment_Node(comp, "Customer's computer", "Microsoft Windows or Apple macOS"){ Deployment_Node(browser, "Web Browser", "Google Chrome, Mozilla Firefox,
Apple Safari or Microsoft Edge"){ Container(spa, "Single Page Application", "JavaScript and Angular", "Provides all of the Internet Banking functionality to customers via their web browser.") } diff --git a/packages/mermaid/src/docs/syntax/classDiagram.md b/packages/mermaid/src/docs/syntax/classDiagram.md index 20bdd657f..e9b918529 100644 --- a/packages/mermaid/src/docs/syntax/classDiagram.md +++ b/packages/mermaid/src/docs/syntax/classDiagram.md @@ -8,6 +8,9 @@ The class diagram is the main building block of object-oriented modeling. It is Mermaid can render class diagrams. ```mermaid-example +--- +title: Animal example +--- classDiagram note "From Duck till Zebra" Animal <|-- Duck @@ -45,6 +48,9 @@ A single instance of a class in the diagram contains three compartments: - The bottom compartment contains the operations the class can execute. They are also left-aligned and the first letter is lowercase. ```mermaid-example +--- +title: Bank example +--- classDiagram class BankAccount BankAccount : +String owner @@ -379,7 +385,7 @@ click className href "url" "tooltip" ## Notes -It is possible to add notes on digram using `note "line1\nline2"` or note for class using `note for class "line1\nline2"` +It is possible to add notes on diagram using `note "line1\nline2"` or note for class using `note for class "line1\nline2"` ### Examples diff --git a/packages/mermaid/src/docs/syntax/entityRelationshipDiagram.md b/packages/mermaid/src/docs/syntax/entityRelationshipDiagram.md index e52b0df4c..c666877c5 100644 --- a/packages/mermaid/src/docs/syntax/entityRelationshipDiagram.md +++ b/packages/mermaid/src/docs/syntax/entityRelationshipDiagram.md @@ -7,6 +7,9 @@ Note that practitioners of ER modelling almost always refer to _entity types_ si Mermaid can render ER diagrams ```mermaid-example +--- +title: Order example +--- erDiagram CUSTOMER ||--o{ ORDER : places ORDER ||--|{ LINE-ITEM : contains diff --git a/packages/mermaid/src/docs/syntax/flowchart.md b/packages/mermaid/src/docs/syntax/flowchart.md index 25252e54d..7f8284a2f 100644 --- a/packages/mermaid/src/docs/syntax/flowchart.md +++ b/packages/mermaid/src/docs/syntax/flowchart.md @@ -9,6 +9,9 @@ It can also accommodate different arrow types, multi directional arrows, and lin ### A node (default) ```mermaid-example +--- +title: Node +--- flowchart LR id ``` @@ -22,6 +25,9 @@ found for the node that will be used. Also if you define edges for the node late one previously defined will be used when rendering the box. ```mermaid-example +--- +title: Node with text +--- flowchart LR id1[This is the text in the box] ``` @@ -665,7 +671,7 @@ flowchart LR ## Configuration... -Is it possible to adjust the width of the rendered flowchart. +It is possible to adjust the width of the rendered flowchart. This is done by defining **mermaid.flowchartConfig** or by the CLI to use a JSON file with the configuration. How to use the CLI is described in the mermaidCLI page. mermaid.flowchartConfig can be set to a JSON string with config parameters or the corresponding object. diff --git a/packages/mermaid/src/docs/syntax/gitgraph.md b/packages/mermaid/src/docs/syntax/gitgraph.md index b19c1e2cd..f1930bb27 100644 --- a/packages/mermaid/src/docs/syntax/gitgraph.md +++ b/packages/mermaid/src/docs/syntax/gitgraph.md @@ -7,17 +7,20 @@ These kind of diagram are particularly helpful to developers and devops teams to Mermaid can render Git diagrams ```mermaid-example - gitGraph - commit - commit - branch develop - checkout develop - commit - commit - checkout main - merge develop - commit - commit +--- +title: Example Git diagram +--- +gitGraph + commit + commit + branch develop + checkout develop + commit + commit + checkout main + merge develop + commit + commit ``` In Mermaid, we support the basic git operations like: diff --git a/packages/mermaid/src/docs/syntax/mindmap.md b/packages/mermaid/src/docs/syntax/mindmap.md index af7a3df85..edad887c2 100644 --- a/packages/mermaid/src/docs/syntax/mindmap.md +++ b/packages/mermaid/src/docs/syntax/mindmap.md @@ -15,7 +15,7 @@ mindmap Popularisation British popular psychology author Tony Buzan Research - On effectivness
and eatures + On effectiveness
and features On Automatic creation Uses Creative techniques @@ -94,6 +94,13 @@ mindmap id)I am a cloud( ``` +### Hexagon + +```mermaid-example +mindmap + id{{I am a hexagon}} +``` + ### Default ```mermaid-example diff --git a/packages/mermaid/src/docs/syntax/pie.md b/packages/mermaid/src/docs/syntax/pie.md index 4e14efce1..2fe8c3e54 100644 --- a/packages/mermaid/src/docs/syntax/pie.md +++ b/packages/mermaid/src/docs/syntax/pie.md @@ -22,7 +22,7 @@ Drawing a pie chart is really simple in mermaid. - Followed by dataSet. Pie slices will be ordered clockwise in the same order as the labels. - `label` for a section in the pie diagram within `" "` quotes. - Followed by `:` colon as separator - - Followed by `positive numeric value` (supported upto two decimal places) + - Followed by `positive numeric value` (supported up to two decimal places) [pie] [showData] (OPTIONAL) [title] [titlevalue] (OPTIONAL) diff --git a/packages/mermaid/src/docs/syntax/sequenceDiagram.md b/packages/mermaid/src/docs/syntax/sequenceDiagram.md index beb417ee2..9c28883c9 100644 --- a/packages/mermaid/src/docs/syntax/sequenceDiagram.md +++ b/packages/mermaid/src/docs/syntax/sequenceDiagram.md @@ -534,7 +534,7 @@ text.actor { ## Configuration -Is it possible to adjust the margins for rendering the sequence diagram. +It is possible to adjust the margins for rendering the sequence diagram. This is done by defining `mermaid.sequenceConfig` or by the CLI to use a json file with the configuration. How to use the CLI is described in the [mermaidCLI](../config/mermaidCLI.md) page. diff --git a/packages/mermaid/src/docs/syntax/stateDiagram.md b/packages/mermaid/src/docs/syntax/stateDiagram.md index e28819e7a..29e355a72 100644 --- a/packages/mermaid/src/docs/syntax/stateDiagram.md +++ b/packages/mermaid/src/docs/syntax/stateDiagram.md @@ -1,10 +1,16 @@ # State diagrams -> "A state diagram is a type of diagram used in computer science and related fields to describe the behavior of systems. State diagrams require that the system described is composed of a finite number of states; sometimes, this is indeed the case, while at other times this is a reasonable abstraction." Wikipedia +> "A state diagram is a type of diagram used in computer science and related fields to describe the behavior of systems. +> State diagrams require that the system described is composed of a finite number of states; sometimes, this is indeed the +> case, while at other times this is a reasonable abstraction." Wikipedia -Mermaid can render state diagrams. The syntax tries to be compliant with the syntax used in plantUml as this will make it easier for users to share diagrams between mermaid and plantUml. +Mermaid can render state diagrams. The syntax tries to be compliant with the syntax used in plantUml as this will make +it easier for users to share diagrams between mermaid and plantUml. ```mermaid-example +--- +title: Simple sample +--- stateDiagram-v2 [*] --> Still Still --> [*] @@ -28,15 +34,18 @@ stateDiagram Crash --> [*] ``` -In state diagrams systems are described in terms of its states and how the systems state can change to another state via a transitions. The example diagram above shows three states **Still**, **Moving** and **Crash**. You start in the state of Still. From Still you can change the state to Moving. In Moving you can change the state either back to Still or to Crash. There is no transition from Still to Crash. +In state diagrams systems are described in terms of _states_ and how one _state_ can change to another _state_ via +a _transition._ The example diagram above shows three states: **Still**, **Moving** and **Crash**. You start in the +**Still** state. From **Still** you can change to the **Moving** state. From **Moving** you can change either back to the **Still** state or to +the **Crash** state. There is no transition from **Still** to **Crash**. (You can't crash if you're still.) ## States -A state can be declared in multiple ways. The simplest way is to define a state id as a description. +A state can be declared in multiple ways. The simplest way is to define a state with just an id: ```mermaid-example stateDiagram-v2 - s1 + stateId ``` Another way is by using the state keyword with a description as per below: @@ -57,14 +66,15 @@ stateDiagram-v2 Transitions are path/edges when one state passes into another. This is represented using text arrow, "\-\-\>". -When you define a transition between two states and the states are not already defined the undefined states are defined with the id from the transition. You can later add descriptions to states defined this way. +When you define a transition between two states and the states are not already defined, the undefined states are defined +with the id from the transition. You can later add descriptions to states defined this way. ```mermaid-example stateDiagram-v2 s1 --> s2 ``` -It is possible to add text to a transition. To describe what it represents. +It is possible to add text to a transition to describe what it represents: ```mermaid-example stateDiagram-v2 @@ -73,7 +83,8 @@ stateDiagram-v2 ## Start and End -There are two special states indicating the start and stop of the diagram. These are written with the [\*] syntax and the direction of the transition to it defines it either as a start or a stop state. +There are two special states indicating the start and stop of the diagram. These are written with the [\*] syntax and +the direction of the transition to it defines it either as a start or a stop state. ```mermaid-example stateDiagram-v2 @@ -83,10 +94,11 @@ stateDiagram-v2 ## Composite states -In a real world use of state diagrams you often end up with diagrams that are multi-dimensional as one state can +In a real world use of state diagrams you often end up with diagrams that are multidimensional as one state can have several internal states. These are called composite states in this terminology. -In order to define a composite state you need to use the state keyword followed by an id and the body of the composite state between \{\}. See the example below: +In order to define a composite state you need to use the state keyword followed by an id and the body of the composite +state between \{\}. See the example below: ```mermaid-example stateDiagram-v2 @@ -175,7 +187,7 @@ It is possible to specify a fork in the diagram using <<fork>> <& ## Notes -Sometimes nothing says it better then a Post-it note. That is also the case in state diagrams. +Sometimes nothing says it better than a Post-it note. That is also the case in state diagrams. Here you can choose to put the note to the _right of_ or to the _left of_ a node. @@ -215,7 +227,8 @@ stateDiagram-v2 ## Setting the direction of the diagram -With state diagrams you can use the direction statement to set the direction which the diagram will render like in this example. +With state diagrams you can use the direction statement to set the direction which the diagram will render like in this +example. ```mermaid-example stateDiagram @@ -232,7 +245,9 @@ stateDiagram ## Comments -Comments can be entered within a state diagram chart, which will be ignored by the parser. Comments need to be on their own line, and must be prefaced with `%%` (double percent signs). Any text after the start of the comment to the next newline will be treated as a comment, including any diagram syntax +Comments can be entered within a state diagram chart, which will be ignored by the parser. Comments need to be on their +own line, and must be prefaced with `%%` (double percent signs). Any text after the start of the comment to the next +newline will be treated as a comment, including any diagram syntax ```mmd stateDiagram-v2 @@ -245,16 +260,153 @@ stateDiagram-v2 Crash --> [*] ``` -## Styling +## Styling with classDefs -Styling of the a state diagram is done by defining a number of css classes. During rendering these classes are extracted from the file located at src/themes/state.scss +As with other diagrams (like flowcharts), you can define a style in the diagram itself and apply that named style to a +state or states in the diagram. + +**These are the current limitations with state diagram classDefs:** + +1. Cannot be applied to start or end states +2. Cannot be applied to or within composite states + +_These are in development and will be available in a future version._ + +You define a style using the `classDef` keyword, which is short for "class definition" (where "class" means something +like a _CSS class_) +followed by _a name for the style,_ +and then one or more _property-value pairs_. Each _property-value pair_ is +a _[valid CSS property name](https://www.w3.org/TR/CSS/#properties)_ followed by a colon (`:`) and then a _value._ + +Here is an example of a classDef with just one property-value pair: + +``` + classDef movement font-style:italic; +``` + +where + +- the _name_ of the style is `movement` +- the only _property_ is `font-style` and its _value_ is `italic` + +If you want to have more than one _property-value pair_ then you put a comma (`,`) between each _property-value pair._ + +Here is an example with three property-value pairs: + +``` + classDef badBadEvent fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow +``` + +where + +- the _name_ of the style is `badBadEvent` +- the first _property_ is `fill` and its _value_ is `#f00` +- the second _property_ is `color` and its _value_ is `white` +- the third _property_ is `font-weight` and its _value_ is `bold` +- the fourth _property_ is `stroke-width` and its _value_ is `2px` +- the fifth _property_ is `stroke` and its _value_ is `yello` + +### Apply classDef styles to states + +There are two ways to apply a `classDef` style to a state: + +1. use the `class` keyword to apply a classDef style to one or more states in a single statement, or +2. use the `:::` operator to apply a classDef style to a state as it is being used in a transition statement (e.g. with an arrow + to/from another state) + +#### 1. `class` statement + +A `class` statement tells Mermaid to apply the named classDef to one or more classes. The form is: + +```text + class [one or more state names, separated by commas] [name of a style defined with classDef] +``` + +Here is an example applying the `badBadEvent` style to a state named `Crash`: + +```text +class Crash badBadEvent +``` + +Here is an example applying the `movement` style to the two states `Moving` and `Crash`: + +```text +class Moving, Crash movement +``` + +Here is a diagram that shows the examples in use. Note that the `Crash` state has two classDef styles applied: `movement` +and `badBadEvent` + +```mermaid-example + stateDiagram + direction TB + + accTitle: This is the accessible title + accDescr: This is an accessible description + + classDef notMoving fill:white + classDef movement font-style:italic + classDef badBadEvent fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow + + [*]--> Still + Still --> [*] + Still --> Moving + Moving --> Still + Moving --> Crash + Crash --> [*] + + class Still notMoving + class Moving, Crash movement + class Crash badBadEvent + class end badBadEvent +``` + +#### 2. `:::` operator to apply a style to a state + +You can apply a classDef style to a state using the `:::` (three colons) operator. The syntax is + +```text +[state]:::[style name] +``` + +You can use this in a diagram within a statement using a class. This includes the start and end states. For example: + +```mermaid-example +stateDiagram + direction TB + + accTitle: This is the accessible title + accDescr: This is an accessible description + + classDef notMoving fill:white + classDef movement font-style:italic; + classDef badBadEvent fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow + + [*] --> Still:::notMoving + Still --> [*] + Still --> Moving:::movement + Moving --> Still + Moving --> Crash:::movement + Crash:::badBadEvent --> [*] +``` ## Spaces in state names -Spaces can be added to a state by defining it at the top and referencing the acronym later. +Spaces can be added to a state by first defining the state with an id and then referencing the id later. + +In the following example there is a state with the id **yswsii** and description **Your state with spaces in it**. +After it has been defined, **yswsii** is used in the diagram in the first transition (`[*] --> yswsii`) +and also in the transition to **YetAnotherState** (`yswsii --> YetAnotherState`). +(**yswsii** has been styled so that it is different from the other states.) ```mermaid-example -stateDiagram-v2 - Yswsii: Your state with spaces in it - [*] --> Yswsii +stateDiagram + classDef yourState font-style:italic,font-weight:bold,fill:white + + yswsii: Your state with spaces in it + [*] --> yswsii:::yourState + [*] --> SomeOtherState + SomeOtherState --> YetAnotherState + yswsii --> YetAnotherState + YetAnotherState --> [*] ``` diff --git a/packages/mermaid/src/mermaidAPI.ts b/packages/mermaid/src/mermaidAPI.ts index 0df1da305..0b2e85cd6 100644 --- a/packages/mermaid/src/mermaidAPI.ts +++ b/packages/mermaid/src/mermaidAPI.ts @@ -29,7 +29,7 @@ import utils, { directiveSanitizer } from './utils'; import DOMPurify from 'dompurify'; import { MermaidConfig } from './config.type'; import { evaluate } from './diagrams/common/common'; -import { isEmpty } from 'lodash'; +import isEmpty from 'lodash-es/isEmpty'; // diagram names that support classDef statements const CLASSDEF_DIAGRAMS = ['graph', 'flowchart', 'flowchart-v2', 'stateDiagram', 'stateDiagram-v2']; @@ -194,7 +194,7 @@ export const createCssStyles = ( const htmlLabels = config.htmlLabels || config.flowchart?.htmlLabels; // TODO why specifically check the Flowchart diagram config? const cssHtmlElements = ['> *', 'span']; // TODO make a constant - const cssShapeElements = ['rect', 'polygon', 'ellipse', 'circle']; // TODO make a constant + const cssShapeElements = ['rect', 'polygon', 'ellipse', 'circle', 'path']; // TODO make a constant const cssElements = htmlLabels ? cssHtmlElements : cssShapeElements; diff --git a/packages/mermaid/src/tests/setup.ts b/packages/mermaid/src/tests/setup.ts index e8058c517..b3330787c 100644 --- a/packages/mermaid/src/tests/setup.ts +++ b/packages/mermaid/src/tests/setup.ts @@ -1,3 +1,3 @@ import { vi } from 'vitest'; vi.mock('d3'); -vi.mock('dagre-d3'); +vi.mock('dagre-d3-es'); diff --git a/packages/mermaid/src/utils.spec.js b/packages/mermaid/src/utils.spec.js index 4a511b3c0..54262f10e 100644 --- a/packages/mermaid/src/utils.spec.js +++ b/packages/mermaid/src/utils.spec.js @@ -3,7 +3,8 @@ import utils from './utils'; import assignWithDepth from './assignWithDepth'; import { detectType } from './diagram-api/detectType'; import { addDiagrams } from './diagram-api/diagram-orchestration'; -import memoize from 'lodash/memoize'; +import memoize from 'lodash-es/memoize'; +import { MockD3 } from 'd3'; addDiagrams(); describe('when assignWithDepth: should merge objects within objects', function () { @@ -232,6 +233,15 @@ Alice->Bob: hi`; const type = detectType(str); expect(type).toBe('gitGraph'); }); + it('should handle frontmatter', function () { + const str = '---\ntitle: foo\n---\n gitGraph TB:\nbfs1:queue'; + const type = detectType(str); + expect(type).toBe('gitGraph'); + }); + it('should not allow frontmatter with leading spaces', function () { + const str = ' ---\ntitle: foo\n---\n gitGraph TB:\nbfs1:queue'; + expect(() => detectType(str)).toThrow('No diagram type detected for text'); + }); }); describe('when finding substring in array ', function () { it('should return the array index that contains the substring', function () { @@ -340,3 +350,23 @@ describe('when initializing the id generator', function () { expect(idGenerator.next()).toEqual(lastId + 1); }); }); + +describe('when inserting titles', function () { + it('should do nothing when title is empty', function () { + const svg = MockD3('svg'); + utils.insertTitle(svg, 'testClass', 0, ''); + expect(svg.__children.length).toBe(0); + }); + + it('should insert title centered', function () { + const svg = MockD3('svg'); + utils.insertTitle(svg, 'testClass', 5, 'test title'); + expect(svg.__children.length).toBe(1); + const text = svg.__children[0]; + expect(text.__name).toBe('text'); + expect(text.text).toHaveBeenCalledWith('test title'); + expect(text.attr).toHaveBeenCalledWith('x', 15); + expect(text.attr).toHaveBeenCalledWith('y', -5); + expect(text.attr).toHaveBeenCalledWith('class', 'testClass'); + }); +}); diff --git a/packages/mermaid/src/utils.ts b/packages/mermaid/src/utils.ts index 3eecd5f4f..3689a01a1 100644 --- a/packages/mermaid/src/utils.ts +++ b/packages/mermaid/src/utils.ts @@ -21,7 +21,7 @@ import { log } from './logger'; import { detectType } from './diagram-api/detectType'; import assignWithDepth from './assignWithDepth'; import { MermaidConfig } from './config.type'; -import memoize from 'lodash/memoize'; +import memoize from 'lodash-es/memoize'; // Effectively an enum of the supported curve types, accessible by name const d3CurveTypes = { @@ -561,54 +561,77 @@ export const drawSimpleText = function ( return textElem; }; -export const wrapLabel = memoize( - (label, maxWidth, config) => { - if (!label) { - return label; - } - config = Object.assign( - { fontSize: 12, fontWeight: 400, fontFamily: 'Arial', joinWith: '
' }, - config - ); - if (common.lineBreakRegex.test(label)) { - return label; - } - const words = label.split(' '); - const completedLines = []; - let nextLine = ''; - words.forEach((word, index) => { - const wordLength = calculateTextWidth(`${word} `, config); - const nextLineLength = calculateTextWidth(nextLine, config); - if (wordLength > maxWidth) { - const { hyphenatedStrings, remainingWord } = breakString(word, maxWidth, '-', config); - completedLines.push(nextLine, ...hyphenatedStrings); - nextLine = remainingWord; - } else if (nextLineLength + wordLength >= maxWidth) { - completedLines.push(nextLine); - nextLine = word; - } else { - nextLine = [nextLine, word].filter(Boolean).join(' '); - } - const currentWord = index + 1; - const isLastWord = currentWord === words.length; - if (isLastWord) { - completedLines.push(nextLine); - } - }); - return completedLines.filter((line) => line !== '').join(config.joinWith); - }, - (label, maxWidth, config) => - `${label}${maxWidth}${config.fontSize}${config.fontWeight}${config.fontFamily}${config.joinWith}` -); +interface WrapLabelConfig { + fontSize: number; + fontFamily: string; + fontWeight: number; + joinWith: string; +} -const breakString = memoize( - (word, maxWidth, hyphenCharacter = '-', config) => { +export const wrapLabel: (label: string, maxWidth: string, config: WrapLabelConfig) => string = + memoize( + (label: string, maxWidth: string, config: WrapLabelConfig): string => { + if (!label) { + return label; + } + config = Object.assign( + { fontSize: 12, fontWeight: 400, fontFamily: 'Arial', joinWith: '
' }, + config + ); + if (common.lineBreakRegex.test(label)) { + return label; + } + const words = label.split(' '); + const completedLines = []; + let nextLine = ''; + words.forEach((word, index) => { + const wordLength = calculateTextWidth(`${word} `, config); + const nextLineLength = calculateTextWidth(nextLine, config); + if (wordLength > maxWidth) { + const { hyphenatedStrings, remainingWord } = breakString(word, maxWidth, '-', config); + completedLines.push(nextLine, ...hyphenatedStrings); + nextLine = remainingWord; + } else if (nextLineLength + wordLength >= maxWidth) { + completedLines.push(nextLine); + nextLine = word; + } else { + nextLine = [nextLine, word].filter(Boolean).join(' '); + } + const currentWord = index + 1; + const isLastWord = currentWord === words.length; + if (isLastWord) { + completedLines.push(nextLine); + } + }); + return completedLines.filter((line) => line !== '').join(config.joinWith); + }, + (label, maxWidth, config) => + `${label}${maxWidth}${config.fontSize}${config.fontWeight}${config.fontFamily}${config.joinWith}` + ); + +interface BreakStringOutput { + hyphenatedStrings: string[]; + remainingWord: string; +} + +const breakString: ( + word: string, + maxWidth: number, + hyphenCharacter: string, + config: WrapLabelConfig +) => BreakStringOutput = memoize( + ( + word: string, + maxWidth: number, + hyphenCharacter = '-', + config: WrapLabelConfig + ): BreakStringOutput => { config = Object.assign( { fontSize: 12, fontWeight: 400, fontFamily: 'Arial', margin: 0 }, config ); const characters = word.split(''); - const lines = []; + const lines: string[] = []; let currentLine = ''; characters.forEach((character, index) => { const nextLine = `${currentLine}${character}`; @@ -667,6 +690,16 @@ export function calculateTextWidth( return calculateTextDimensions(text, config).width; } +interface TextDimensionConfig { + fontSize?: number; + fontWeight?: number; + fontFamily?: string; +} +interface TextDimensions { + width: number; + height: number; + lineHeight?: number; +} /** * This calculates the dimensions of the given text, font size, font family, font weight, and * margins. @@ -676,15 +709,11 @@ export function calculateTextWidth( * the resulting size * @returns The dimensions for the given text */ -export const calculateTextDimensions = memoize( - function ( - text: string, - config: { - fontSize?: number; - fontWeight?: number; - fontFamily?: string; - } - ) { +export const calculateTextDimensions: ( + text: string, + config: TextDimensionConfig +) => TextDimensions = memoize( + (text: string, config: TextDimensionConfig): TextDimensions => { config = Object.assign({ fontSize: 12, fontWeight: 400, fontFamily: 'Arial' }, config); const { fontSize, fontFamily, fontWeight } = config; if (!text) { @@ -885,6 +914,32 @@ export function getErrorMessage(error: unknown): string { return String(error); } +/** + * Appendselement with the given title, centered. + * + * @param parent - d3 svg object to append title to + * @param cssClass - CSS class for the element containing the title + * @param titleTopMargin - Margin in pixels between title and rest of the graph + * @param title - The title. If empty, returns immediately. + */ +export const insertTitle = ( + parent, + cssClass: string, + titleTopMargin: number, + title?: string +): void => { + if (!title) { + return; + } + const bounds = parent.node().getBBox(); + parent + .append('text') + .text(title) + .attr('x', bounds.x + bounds.width / 2) + .attr('y', -titleTopMargin) + .attr('class', cssClass); +}; + export default { assignWithDepth, wrapLabel, @@ -907,4 +962,5 @@ export default { initIdGenerator: initIdGenerator, directiveSanitizer, sanitizeCss, + insertTitle, }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 85afcb31d..ebb71f65e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,8 +1,5 @@ lockfileVersion: 5.4-inlineSpecifiers -overrides: - d3: ^7.6.1 - importers: .: @@ -25,6 +22,9 @@ importers: '@types/express': specifier: ^4.17.14 version: 4.17.14 + '@types/js-yaml': + specifier: ^4.0.5 + version: 4.0.5 '@types/jsdom': specifier: ^20.0.1 version: 20.0.1 @@ -40,6 +40,9 @@ importers: '@types/prettier': specifier: ^2.7.1 version: 2.7.1 + '@types/rollup-plugin-visualizer': + specifier: ^4.2.1 + version: 4.2.1 '@typescript-eslint/eslint-plugin': specifier: ^5.42.1 version: 5.42.1_2udltptbznfmezdozpdoa2aemq @@ -88,6 +91,9 @@ importers: eslint-plugin-json: specifier: ^3.1.0 version: 3.1.0 + eslint-plugin-lodash: + specifier: ^7.4.0 + version: 7.4.0_eslint@8.27.0 eslint-plugin-markdown: specifier: ^3.0.0 version: 3.0.0_eslint@8.27.0 @@ -97,6 +103,9 @@ importers: eslint-plugin-tsdoc: specifier: ^0.2.17 version: 0.2.17 + eslint-plugin-unicorn: + specifier: ^45.0.0 + version: 45.0.0_eslint@8.27.0 express: specifier: ^4.18.2 version: 4.18.2 @@ -115,6 +124,9 @@ importers: jison: specifier: ^0.4.18 version: 0.4.18 + js-yaml: + specifier: ^4.1.0 + version: 4.1.0 jsdom: specifier: ^20.0.2 version: 20.0.2 @@ -136,6 +148,9 @@ importers: rimraf: specifier: ^3.0.2 version: 3.0.2 + rollup-plugin-visualizer: + specifier: ^5.8.3 + version: 5.8.3_rollup@2.79.1 start-server-and-test: specifier: ^1.14.0 version: 1.14.0 @@ -158,8 +173,8 @@ importers: specifier: ^1.0.4-alpha.15 version: 1.0.4-alpha.15_s3edpouswd4dgoi2en7bdlrp54 vitest: - specifier: ^0.25.1 - version: 0.25.1_iyb77cyw3lw7duusvxyjdsflhu + specifier: ^0.25.3 + version: 0.25.3_iyb77cyw3lw7duusvxyjdsflhu packages/mermaid: dependencies: @@ -167,14 +182,11 @@ importers: specifier: ^6.0.0 version: 6.0.0 d3: - specifier: ^7.6.1 + specifier: ^7.0.0 version: 7.6.1 - dagre: - specifier: ^0.8.5 - version: 0.8.5 - dagre-d3: - specifier: ^0.6.4 - version: 0.6.4 + dagre-d3-es: + specifier: 7.0.4 + version: 7.0.4 dompurify: specifier: 2.4.1 version: 2.4.1 @@ -187,7 +199,7 @@ importers: khroma: specifier: ^2.0.0 version: 2.0.0 - lodash: + lodash-es: specifier: ^4.17.21 version: 4.17.21 moment-mini: @@ -212,9 +224,9 @@ importers: '@types/jsdom': specifier: ^20.0.1 version: 20.0.1 - '@types/lodash': - specifier: ^4.14.188 - version: 4.14.188 + '@types/lodash-es': + specifier: ^4.17.6 + version: 4.17.6 '@types/micromatch': specifier: ^4.0.2 version: 4.0.2 @@ -242,6 +254,9 @@ importers: coveralls: specifier: ^3.1.1 version: 3.1.1 + cspell: + specifier: ^6.14.3 + version: 6.14.3 globby: specifier: ^13.1.2 version: 13.1.2 @@ -315,7 +330,7 @@ importers: specifier: ^2.1.0 version: 2.1.0_cytoscape@3.23.0 d3: - specifier: ^7.6.1 + specifier: ^7.0.0 version: 7.6.1 khroma: specifier: ^2.0.0 @@ -1361,21 +1376,84 @@ packages: '@cspell/dict-vue': 3.0.0 dev: true + /@cspell/cspell-bundled-dicts/6.14.3: + resolution: {integrity: sha512-bgPBduoDi1jkrcLkmAwRG1c6F1iprF2yfBgEDT19dRG1kYuq/fLGNOcSmEp4CbApn8m0MmxsrhEp8O0Q9owQRQ==} + engines: {node: '>=14'} + dependencies: + '@cspell/dict-ada': 4.0.0 + '@cspell/dict-aws': 3.0.0 + '@cspell/dict-bash': 4.1.0 + '@cspell/dict-companies': 3.0.3 + '@cspell/dict-cpp': 4.0.0 + '@cspell/dict-cryptocurrencies': 3.0.1 + '@cspell/dict-csharp': 4.0.1 + '@cspell/dict-css': 4.0.0 + '@cspell/dict-dart': 2.0.0 + '@cspell/dict-django': 4.0.0 + '@cspell/dict-docker': 1.1.3 + '@cspell/dict-dotnet': 4.0.0 + '@cspell/dict-elixir': 4.0.0 + '@cspell/dict-en-gb': 1.1.33 + '@cspell/dict-en_us': 4.1.0 + '@cspell/dict-filetypes': 3.0.0 + '@cspell/dict-fonts': 3.0.0 + '@cspell/dict-fullstack': 3.0.0 + '@cspell/dict-git': 2.0.0 + '@cspell/dict-golang': 5.0.0 + '@cspell/dict-haskell': 4.0.0 + '@cspell/dict-html': 4.0.1 + '@cspell/dict-html-symbol-entities': 4.0.0 + '@cspell/dict-java': 5.0.2 + '@cspell/dict-latex': 3.0.0 + '@cspell/dict-lorem-ipsum': 3.0.0 + '@cspell/dict-lua': 3.0.0 + '@cspell/dict-node': 4.0.1 + '@cspell/dict-npm': 4.0.1 + '@cspell/dict-php': 3.0.3 + '@cspell/dict-powershell': 3.0.0 + '@cspell/dict-public-licenses': 2.0.0 + '@cspell/dict-python': 4.0.0 + '@cspell/dict-r': 2.0.0 + '@cspell/dict-ruby': 3.0.0 + '@cspell/dict-rust': 3.0.0 + '@cspell/dict-scala': 3.0.0 + '@cspell/dict-software-terms': 3.0.5 + '@cspell/dict-sql': 2.0.0 + '@cspell/dict-swift': 2.0.0 + '@cspell/dict-typescript': 3.0.1 + '@cspell/dict-vue': 3.0.0 + dev: true + /@cspell/cspell-pipe/6.14.2: resolution: {integrity: sha512-9H7Z/jy2tGpMW9T/JOk8T3bqvQoHJIz1wddktA5Lq8fnMqlDhM9le2uykhVlLpemLhWpDS2fNzLJ3sHiaPgHBA==} engines: {node: '>=14'} dev: true + /@cspell/cspell-pipe/6.14.3: + resolution: {integrity: sha512-/mLZxJOK3/UFpnR4jrImKY5W4cn5XWjvQPXnFCEzpU0tAAF6GboJgWl30TegqFJjLVCKTNRMOtT1r6kgvb66zw==} + engines: {node: '>=14'} + dev: true + /@cspell/cspell-service-bus/6.14.2: resolution: {integrity: sha512-IOK4MqwDNS2y29eZjdpHrCQ0ouTWZCS2e3EOmlvY+yUpT7e1AX8pVOaar4jLnXg03evAjrFrrmfmhFI6poO6Hg==} engines: {node: '>=14'} dev: true + /@cspell/cspell-service-bus/6.14.3: + resolution: {integrity: sha512-89OWGBzhorhiWcFqFTeHl9Y6WTdd5MGC2XNNCVZLM3VTYaFx4DVkiyxWdkE7gHjYxvNdGSH54/fE18TqLc//dQ==} + engines: {node: '>=14'} + dev: true + /@cspell/cspell-types/6.14.2: resolution: {integrity: sha512-/EZYVglm6+2GlnkFTzuLuQFr7vrttkhG+ZsNO9EDcFYB5N7O2ndNSkTQFxGi8FS8R3RS5CHyS5X6hANnolzvfQ==} engines: {node: '>=14'} dev: true + /@cspell/cspell-types/6.14.3: + resolution: {integrity: sha512-u4Hun0vOQVkk3tJ6VzPjHVmv2dq0D6jYqX8pWLKWRwo38rdoIkdWseN359sWCz96tDM8g5rpSFdmecbWLU7BYg==} + engines: {node: '>=14'} + dev: true + /@cspell/dict-ada/4.0.0: resolution: {integrity: sha512-M0n4ZYmpLOXbDD07Qb/Ekk0K5pX2C+mCuJ2ZxPgbTq9HGlrN43PmqrGJHWcgtVHE3fd1D4VxS85QcQP6r1Y+KQ==} dev: true @@ -1558,6 +1636,11 @@ packages: engines: {node: '>=14.6'} dev: true + /@cspell/strong-weak-map/6.14.3: + resolution: {integrity: sha512-/FTvcywuwfFTMEpRabL8+rqB/ezSjvMp6todO0SwL/agYQmRIuTvTYLh0Ikq430oVnjo7LDgztW0tHq38UlFLw==} + engines: {node: '>=14.6'} + dev: true + /@cspotcode/source-map-support/0.8.1: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} @@ -1760,7 +1843,7 @@ packages: '@types/node': 18.11.9 ansi-escapes: 4.3.2 chalk: 4.1.2 - ci-info: 3.4.0 + ci-info: 3.6.2 exit: 0.1.2 graceful-fs: 4.2.10 jest-changed-files: 29.2.0 @@ -2466,6 +2549,10 @@ packages: '@types/istanbul-lib-report': 3.0.0 dev: true + /@types/js-yaml/4.0.5: + resolution: {integrity: sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==} + dev: true + /@types/jsdom/20.0.1: resolution: {integrity: sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==} dependencies: @@ -2488,6 +2575,12 @@ packages: resolution: {integrity: sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==} dev: true + /@types/lodash-es/4.17.6: + resolution: {integrity: sha512-R+zTeVUKDdfoRxpAryaQNRKk3105Rrgx2CFRClIgRGaqDTdjsm8h6IYA8ir584W3ePzkZfst5xIgDwYrlh9HLg==} + dependencies: + '@types/lodash': 4.14.188 + dev: true + /@types/lodash/4.14.188: resolution: {integrity: sha512-zmEmF5OIM3rb7SbLCFYoQhO4dGt2FRM9AMkxvA3LaADOF1n8in/zGJlWji9fmafLoNyz+FoL6FE0SLtGIArD7w==} dev: true @@ -2584,6 +2677,13 @@ packages: resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} dev: true + /@types/rollup-plugin-visualizer/4.2.1: + resolution: {integrity: sha512-Fk4y0EgmsSbvbayYhtSI9+cGvgw1rcQ9RlbExkQt4ivXRdiEwFKuRpxNuJCr0JktXIvOPUuPR7GSmtyZu0dujQ==} + dependencies: + '@types/node': 18.11.9 + rollup: 2.79.1 + dev: true + /@types/semver/7.3.12: resolution: {integrity: sha512-WwA1MW0++RfXmCr12xeYOOC5baSC9mSb0ZqCquFzKhcoF4TvHu5MKOuXsncgZcpVFhB1pXd5hZmM0ryAoCp12A==} dev: true @@ -3534,7 +3634,7 @@ packages: /axios/0.21.4_debug@4.3.2: resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==} dependencies: - follow-redirects: 1.15.2 + follow-redirects: 1.15.2_debug@4.3.2 transitivePeerDependencies: - debug dev: true @@ -3762,6 +3862,11 @@ packages: ieee754: 1.2.1 dev: true + /builtin-modules/3.3.0: + resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} + engines: {node: '>=6'} + dev: true + /bytes/3.0.0: resolution: {integrity: sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==} engines: {node: '>= 0.8'} @@ -3952,8 +4057,9 @@ packages: engines: {node: '>=6.0'} dev: true - /ci-info/3.4.0: - resolution: {integrity: sha512-t5QdPT5jq3o262DOQ8zA6E1tlH2upmUc4Hlvrbx1pGYJuiiHl7O7rvVNI+l8HTVhd/q3Qc9vqimkNk5yiXsAug==} + /ci-info/3.6.2: + resolution: {integrity: sha512-lVZdhvbEudris15CLytp2u6Y0p5EKfztae9Fqa189MfNmln9F33XuH69v5fvNfiRN5/0eAUz2yJL3mo+nhaRKg==} + engines: {node: '>=8'} dev: true /cjs-module-lexer/1.2.2: @@ -3967,6 +4073,13 @@ packages: jsonlint: 1.6.0 dev: true + /clean-regexp/1.0.0: + resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==} + engines: {node: '>=4'} + dependencies: + escape-string-regexp: 1.0.5 + dev: true + /clean-stack/2.2.0: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} @@ -4099,6 +4212,11 @@ packages: engines: {node: ^12.20.0 || >=14} dev: true + /commander/9.4.1: + resolution: {integrity: sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==} + engines: {node: ^12.20.0 || >=14} + dev: true + /comment-json/4.2.3: resolution: {integrity: sha512-SsxdiOf064DWoZLH799Ata6u7iV658A11PlWtZATDlXPpKGJnbJZ5Z24ybixAi+LUUqJ/GKowAejtC5GFUG7Tw==} engines: {node: '>= 6'} @@ -4307,6 +4425,17 @@ packages: yaml: 1.10.2 dev: true + /cosmiconfig/7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + dependencies: + '@types/parse-json': 4.0.0 + import-fresh: 3.3.0 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + dev: true + /coveralls/3.1.1: resolution: {integrity: sha512-+dxnG2NHncSD1NrqbSM3dn/lE57O6Qf/koe9+I7c+wzkqRmEvcp0kgJdxKInzYzkICKkFMZsX3Vct3++tsF9ww==} engines: {node: '>=6'} @@ -4359,6 +4488,26 @@ packages: gensequence: 4.0.2 dev: true + /cspell-dictionary/6.14.3: + resolution: {integrity: sha512-yIqJEZZj36j1CmmjAiuQOYZM6T62Ih7k35DhAU1hYVARUEEnFN/Uz72UkDj2SAmURVn2On+bAmZ5zCx0JZzf2g==} + engines: {node: '>=14'} + dependencies: + '@cspell/cspell-pipe': 6.14.3 + '@cspell/cspell-types': 6.14.3 + cspell-trie-lib: 6.14.3 + fast-equals: 4.0.3 + gensequence: 4.0.2 + dev: true + + /cspell-gitignore/6.14.3: + resolution: {integrity: sha512-CZTGxx3msF6p1Z0xgLe5LXXvve7DooSuRMBMdGn230usce1nKoxpPoPxgs+zXeCpi+FanykKnoZkdRvjolMpOA==} + engines: {node: '>=14'} + hasBin: true + dependencies: + cspell-glob: 6.14.3 + find-up: 5.0.0 + dev: true + /cspell-glob/6.14.2: resolution: {integrity: sha512-a9o3lBccEcH2676RGge2YqEORovm+II++D53P6hOW/23ltDe1J509MSY6CJdYdPk/VssOExas6akJ6FbKSCBgw==} engines: {node: '>=14'} @@ -4366,6 +4515,13 @@ packages: micromatch: 4.0.5 dev: true + /cspell-glob/6.14.3: + resolution: {integrity: sha512-ISwCK8GqM/dnvtaxA17w1MPmMzFLOqdTz+JWIcR4at47T9qd8bNB0X0P4eqyuqgsbKkWbfnSlsYlEjRHTi4a7A==} + engines: {node: '>=14'} + dependencies: + micromatch: 4.0.5 + dev: true + /cspell-grammar/6.14.2: resolution: {integrity: sha512-Q9+gwp1U/qnECTqxa7WBMPn6sgBfXPIM68jXg8RgNMAuy1CE+m1eTCM9FBNFNpNKJWjaZPvANLOW5/EStN2A/A==} engines: {node: '>=14'} @@ -4375,6 +4531,15 @@ packages: '@cspell/cspell-types': 6.14.2 dev: true + /cspell-grammar/6.14.3: + resolution: {integrity: sha512-Nz8tYUmstyKcFlXbxdw4N8NsQ2ZY/5ztNfouokk47LKaTAS0LyWlLSkZUxN016fMY2h+C+3dI+jaut2H/rtNew==} + engines: {node: '>=14'} + hasBin: true + dependencies: + '@cspell/cspell-pipe': 6.14.3 + '@cspell/cspell-types': 6.14.3 + dev: true + /cspell-io/6.14.2: resolution: {integrity: sha512-QyQ0BBfDvF6B37SlSsmlzRnaGqiIHt7c5NsCNKf3ZfioTWkNI/fiabvSkpNGBAkELP6BPBxjsG+TaS+swZp+Kg==} engines: {node: '>=14'} @@ -4385,6 +4550,16 @@ packages: - encoding dev: true + /cspell-io/6.14.3: + resolution: {integrity: sha512-EbH+qopgWIzr9SZCGDsF4AWYgucN4QzYeAgyXjTbV9RnNIGKOKovMe3vN9nxjOZyPKv2TvmgU+uMXDM61iObRw==} + engines: {node: '>=14'} + dependencies: + '@cspell/cspell-service-bus': 6.14.3 + node-fetch: 2.6.7 + transitivePeerDependencies: + - encoding + dev: true + /cspell-lib/6.14.2: resolution: {integrity: sha512-QNsmWix0oFi1CjzFfNG1xAJVl1OC+6kiWvq0A1S8VD3LJhJVvBqSv1vudpL1oS7H2/2yxk9PUC/MajGLi5i5MQ==} engines: {node: '>=14.6'} @@ -4415,6 +4590,36 @@ packages: - encoding dev: true + /cspell-lib/6.14.3: + resolution: {integrity: sha512-RJT5Tbe0UCMCtqDWRujjxq9u23sc2XylIpDP7MnpLx8wLVgFv2WPzESYNRGZqceqZYwBAPnpqS9h2ANxXSi8UQ==} + engines: {node: '>=14.6'} + dependencies: + '@cspell/cspell-bundled-dicts': 6.14.3 + '@cspell/cspell-pipe': 6.14.3 + '@cspell/cspell-types': 6.14.3 + '@cspell/strong-weak-map': 6.14.3 + clear-module: 4.1.2 + comment-json: 4.2.3 + configstore: 5.0.1 + cosmiconfig: 7.1.0 + cspell-dictionary: 6.14.3 + cspell-glob: 6.14.3 + cspell-grammar: 6.14.3 + cspell-io: 6.14.3 + cspell-trie-lib: 6.14.3 + fast-equals: 4.0.3 + find-up: 5.0.0 + fs-extra: 10.1.0 + gensequence: 4.0.2 + import-fresh: 3.3.0 + resolve-from: 5.0.0 + resolve-global: 1.0.0 + vscode-languageserver-textdocument: 1.0.7 + vscode-uri: 3.0.6 + transitivePeerDependencies: + - encoding + dev: true + /cspell-trie-lib/6.14.2: resolution: {integrity: sha512-+aTRwFUzBPFbJ8zlDwzB1ew/gP7L6kddoXjmqCNeFx9B5DiwN1KPFRo+uBx21JOkoavnviGU//DpyWSU9Cittw==} engines: {node: '>=14'} @@ -4425,6 +4630,40 @@ packages: gensequence: 4.0.2 dev: true + /cspell-trie-lib/6.14.3: + resolution: {integrity: sha512-WVa5gbD9glsZ4c60qPD5RTwojKc5ooxw/Gn+HC9CBdWv5rE1AmM1V3yVWhYx2ZMbJufboBrzmSjJB9qdmUl3oA==} + engines: {node: '>=14'} + dependencies: + '@cspell/cspell-pipe': 6.14.3 + '@cspell/cspell-types': 6.14.3 + fs-extra: 10.1.0 + gensequence: 4.0.2 + dev: true + + /cspell/6.14.3: + resolution: {integrity: sha512-DimVpUiw2iOSvO1daOTtOWjmryVZdFnPmjPhyhWZUqakOEgE2MgoBuk3cFzXqb8GsGXHQh5PqiWr1rqIkQ99qA==} + engines: {node: '>=14'} + hasBin: true + dependencies: + '@cspell/cspell-pipe': 6.14.3 + chalk: 4.1.2 + commander: 9.4.1 + cspell-gitignore: 6.14.3 + cspell-glob: 6.14.3 + cspell-lib: 6.14.3 + fast-json-stable-stringify: 2.1.0 + file-entry-cache: 6.0.1 + fs-extra: 10.1.0 + get-stdin: 8.0.0 + glob: 8.0.3 + imurmurhash: 0.1.4 + semver: 7.3.8 + strip-ansi: 6.0.1 + vscode-uri: 3.0.6 + transitivePeerDependencies: + - encoding + dev: true + /css-tree/1.0.0-alpha.39: resolution: {integrity: sha512-7UvkEYgBAHRG9Nt980lYxjsTrCyHFN53ky3wVsDkiMdVqylqRt+Zc+jm5qw7/qyOvN2dHSYtX0e4MbCCExSvnA==} engines: {node: '>=8.0.0'} @@ -4797,20 +5036,11 @@ packages: d3-zoom: 3.0.0 dev: false - /dagre-d3/0.6.4: - resolution: {integrity: sha512-e/6jXeCP7/ptlAM48clmX4xTZc5Ek6T6kagS7Oz2HrYSdqcLZFLqpAfh7ldbZRFfxCZVyh61NEPR08UQRVxJzQ==} + /dagre-d3-es/7.0.4: + resolution: {integrity: sha512-fQL8ldFR9UYpecz48d1smrXNJ9zGUK38Vl5OzX6Fhn9LR+oQh0GzHRPQylP5kWawmMTKm1QtqcHMVySMJ5CYaQ==} dependencies: d3: 7.6.1 - dagre: 0.8.5 - graphlib: 2.1.8 - lodash: 4.17.21 - dev: false - - /dagre/0.8.5: - resolution: {integrity: sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw==} - dependencies: - graphlib: 2.1.8 - lodash: 4.17.21 + lodash-es: 4.17.21 dev: false /dargs/7.0.0: @@ -5546,6 +5776,16 @@ packages: vscode-json-languageservice: 4.2.1 dev: true + /eslint-plugin-lodash/7.4.0_eslint@8.27.0: + resolution: {integrity: sha512-Tl83UwVXqe1OVeBRKUeWcfg6/pCW1GTRObbdnbEJgYwjxp5Q92MEWQaH9+dmzbRt6kvYU1Mp893E79nJiCSM8A==} + engines: {node: '>=10'} + peerDependencies: + eslint: '>=2' + dependencies: + eslint: 8.27.0 + lodash: 4.17.21 + dev: true + /eslint-plugin-markdown/3.0.0_eslint@8.27.0: resolution: {integrity: sha512-hRs5RUJGbeHDLfS7ELanT0e29Ocyssf/7kBM+p7KluY5AwngGkDf8Oyu4658/NZSGTTq05FZeWbkxXtbVyHPwg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -5570,6 +5810,31 @@ packages: '@microsoft/tsdoc-config': 0.16.2 dev: true + /eslint-plugin-unicorn/45.0.0_eslint@8.27.0: + resolution: {integrity: sha512-iP8cMRxXKHonKioOhnCoCcqVhoqhAp6rB+nsoLjXFDxTHz3btWMAp8xwzjHA0B1K6YV/U/Yvqn1bUXZt8sJPuQ==} + engines: {node: '>=14.18'} + peerDependencies: + eslint: '>=8.28.0' + dependencies: + '@babel/helper-validator-identifier': 7.19.1 + ci-info: 3.6.2 + clean-regexp: 1.0.0 + eslint: 8.27.0 + eslint-utils: 3.0.0_eslint@8.27.0 + esquery: 1.4.0 + indent-string: 4.0.0 + is-builtin-module: 3.2.0 + jsesc: 3.0.2 + lodash: 4.17.21 + pluralize: 8.0.0 + read-pkg-up: 7.0.1 + regexp-tree: 0.1.24 + regjsparser: 0.9.1 + safe-regex: 2.1.1 + semver: 7.3.8 + strip-indent: 3.0.0 + dev: true + /eslint-scope/5.1.1: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} engines: {node: '>=8.0.0'} @@ -6044,7 +6309,7 @@ packages: resolution: {integrity: sha512-XGozTsMPYkm+6b5QL3Z9wQcJjNYxp0CYn3U1gO7dwD6PAqU1SVWZxI9CCg3z+ml3YfqdPnrBehaBrnH2AGKbNA==} dev: true - /follow-redirects/1.15.2: + /follow-redirects/1.15.2_debug@4.3.2: resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==} engines: {node: '>=4.0'} peerDependencies: @@ -6052,6 +6317,8 @@ packages: peerDependenciesMeta: debug: optional: true + dependencies: + debug: 4.3.2 dev: true /foreground-child/2.0.0: @@ -6218,6 +6485,11 @@ packages: engines: {node: '>=0.12.0'} dev: true + /get-stdin/8.0.0: + resolution: {integrity: sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==} + engines: {node: '>=10'} + dev: true + /get-stream/4.1.0: resolution: {integrity: sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==} engines: {node: '>=6'} @@ -6304,6 +6576,17 @@ packages: path-is-absolute: 1.0.1 dev: true + /glob/8.0.3: + resolution: {integrity: sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==} + engines: {node: '>=12'} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 5.1.0 + once: 1.4.0 + dev: true + /global-dirs/0.1.1: resolution: {integrity: sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==} engines: {node: '>=4'} @@ -6628,7 +6911,7 @@ packages: engines: {node: '>=8.0.0'} dependencies: eventemitter3: 4.0.7 - follow-redirects: 1.15.2 + follow-redirects: 1.15.2_debug@4.3.2 requires-port: 1.0.0 transitivePeerDependencies: - debug @@ -6851,11 +7134,18 @@ packages: engines: {node: '>=4'} dev: true + /is-builtin-module/3.2.0: + resolution: {integrity: sha512-phDA4oSGt7vl1n5tJvTWooWWAsXLY+2xCnxNqvKhGEzujg+A43wPlPOyDg3C8XQHN+6k/JTQWJ/j0dQh/qr+Hw==} + engines: {node: '>=6'} + dependencies: + builtin-modules: 3.3.0 + dev: true + /is-ci/3.0.1: resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} hasBin: true dependencies: - ci-info: 3.4.0 + ci-info: 3.6.2 dev: true /is-core-module/2.10.0: @@ -7153,7 +7443,7 @@ packages: '@types/node': 18.11.9 babel-jest: 29.3.1_@babel+core@7.12.3 chalk: 4.1.2 - ci-info: 3.4.0 + ci-info: 3.6.2 deepmerge: 4.2.2 glob: 7.2.3 graceful-fs: 4.2.10 @@ -7439,7 +7729,7 @@ packages: '@jest/types': 29.3.1 '@types/node': 18.11.9 chalk: 4.1.2 - ci-info: 3.4.0 + ci-info: 3.6.2 graceful-fs: 4.2.10 picomatch: 2.3.1 dev: true @@ -7628,12 +7918,23 @@ packages: - utf-8-validate dev: true + /jsesc/0.5.0: + resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} + hasBin: true + dev: true + /jsesc/2.5.2: resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} engines: {node: '>=4'} hasBin: true dev: true + /jsesc/3.0.2: + resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==} + engines: {node: '>=6'} + hasBin: true + dev: true + /json-buffer/3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} dev: true @@ -7902,6 +8203,10 @@ packages: p-locate: 5.0.0 dev: true + /lodash-es/4.17.21: + resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + dev: false + /lodash.merge/4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} dev: true @@ -8945,6 +9250,11 @@ packages: xmlbuilder: 15.1.1 dev: true + /pluralize/8.0.0: + resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} + engines: {node: '>=4'} + dev: true + /png-async/0.9.4: resolution: {integrity: sha512-B//AXX9TkneKfgtOpT1mdUnnhk2BImGD+a98vImsMU8uo1dBeHyW/kM2erWZ/CsYteTPU/xKG+t6T62heHkC3A==} dev: true @@ -9231,11 +9541,23 @@ packages: strip-indent: 3.0.0 dev: true + /regexp-tree/0.1.24: + resolution: {integrity: sha512-s2aEVuLhvnVJW6s/iPgEGK6R+/xngd2jNQ+xy4bXNDKxZKJH6jpPHY6kVeVv1IeLCHgswRj+Kl3ELaDjG6V1iw==} + hasBin: true + dev: true + /regexpp/3.2.0: resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} engines: {node: '>=8'} dev: true + /regjsparser/0.9.1: + resolution: {integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==} + hasBin: true + dependencies: + jsesc: 0.5.0 + dev: true + /remark-parse/10.0.1: resolution: {integrity: sha512-1fUyHr2jLsVOkhbvPRBJ5zTKZZyD6yZzYaWCS6BPBdQ8vEMBCH+9zNCDA6tET/zHCi/jLqjCWtlJZUPk+DbnFw==} dependencies: @@ -9412,6 +9734,22 @@ packages: resolution: {integrity: sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g==} dev: false + /rollup-plugin-visualizer/5.8.3_rollup@2.79.1: + resolution: {integrity: sha512-QGJk4Bqe4AOat5AjipOh8esZH1nck5X2KFpf4VytUdSUuuuSwvIQZjMGgjcxe/zXexltqaXp5Vx1V3LmnQH15Q==} + engines: {node: '>=14'} + hasBin: true + peerDependencies: + rollup: 2.x || 3.x + peerDependenciesMeta: + rollup: + optional: true + dependencies: + open: 8.4.0 + rollup: 2.79.1 + source-map: 0.7.4 + yargs: 17.5.1 + dev: true + /rollup/2.79.1: resolution: {integrity: sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==} engines: {node: '>=10.0.0'} @@ -9451,6 +9789,12 @@ packages: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} dev: true + /safe-regex/2.1.1: + resolution: {integrity: sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==} + dependencies: + regexp-tree: 0.1.24 + dev: true + /safer-buffer/2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -9768,6 +10112,11 @@ packages: engines: {node: '>=0.10.0'} dev: true + /source-map/0.7.4: + resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} + engines: {node: '>= 8'} + dev: true + /sourcemap-codec/1.4.8: resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} dev: true @@ -10865,6 +11214,54 @@ packages: - terser dev: true + /vitest/0.25.3_iyb77cyw3lw7duusvxyjdsflhu: + resolution: {integrity: sha512-/UzHfXIKsELZhL7OaM2xFlRF8HRZgAHtPctacvNK8H4vOcbJJAMEgbWNGSAK7Y9b1NBe5SeM7VTuz2RsTHFJJA==} + engines: {node: '>=v14.16.0'} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@vitest/browser': '*' + '@vitest/ui': '*' + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + dependencies: + '@types/chai': 4.3.3 + '@types/chai-subset': 1.3.3 + '@types/node': 18.11.9 + '@vitest/ui': 0.25.1 + acorn: 8.8.0 + acorn-walk: 8.2.0 + chai: 4.3.6 + debug: 4.3.4 + happy-dom: 6.0.4 + jsdom: 20.0.2 + local-pkg: 0.4.2 + source-map: 0.6.1 + strip-literal: 0.4.2 + tinybench: 2.3.1 + tinypool: 0.3.0 + tinyspy: 1.0.2 + vite: 3.2.3_@types+node@18.11.9 + transitivePeerDependencies: + - less + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + /vm2/3.9.11: resolution: {integrity: sha512-PFG8iJRSjvvBdisowQ7iVF580DXb1uCIiGaXgm7tynMR1uTBlv7UJlB1zdv5KJ+Tmq1f0Upnj3fayoEOPpCBKg==} engines: {node: '>=6.0'}