diff --git a/codecov.yaml b/.github/codecov.yaml similarity index 73% rename from codecov.yaml rename to .github/codecov.yaml index b268d6680..d33925afc 100644 --- a/codecov.yaml +++ b/.github/codecov.yaml @@ -1,6 +1,15 @@ +codecov: + branch: develop + comment: layout: 'reach, diff, flags, files' behavior: default require_changes: false # if true: only post the comment if coverage changes require_base: no # [yes :: must have a base report to post] require_head: yes # [yes :: must have a head report to post] + +coverage: + status: + project: + default: + threshold: 1% diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index f3e440fce..b25ff89cc 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -2,13 +2,13 @@ name: Build Vitepress docs on: pull_request: + merge_group: permissions: contents: read jobs: - # Build job - build: + build-docs: runs-on: ubuntu-latest steps: - name: Checkout diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2a70b5901..eeb557ebb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,6 +2,7 @@ name: Build on: push: {} + merge_group: pull_request: types: - opened @@ -12,7 +13,7 @@ permissions: contents: read jobs: - build: + build-mermaid: runs-on: ubuntu-latest strategy: matrix: diff --git a/.github/workflows/check-readme-in-sync.yml b/.github/workflows/check-readme-in-sync.yml index 13912e5b9..5a8ca319b 100644 --- a/.github/workflows/check-readme-in-sync.yml +++ b/.github/workflows/check-readme-in-sync.yml @@ -14,7 +14,7 @@ permissions: contents: read jobs: - check: + check-readme: runs-on: ubuntu-latest steps: - name: Checkout repository diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 396ff4e6e..9f9f316c4 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -1,15 +1,16 @@ on: - push: {} + push: + merge_group: pull_request: types: - opened - synchronize - ready_for_review -name: Static analysis +name: Static analysis on Test files jobs: - test: + check-tests: runs-on: ubuntu-latest name: check tests if: github.repository_owner == 'mermaid-js' diff --git a/.github/workflows/e2e-applitools.yml b/.github/workflows/e2e-applitools.yml index 92f2f80b1..5b1943142 100644 --- a/.github/workflows/e2e-applitools.yml +++ b/.github/workflows/e2e-applitools.yml @@ -19,7 +19,7 @@ env: USE_APPLI: ${{ secrets.APPLITOOLS_API_KEY && 'true' || '' }} jobs: - test: + e2e-applitools: runs-on: ubuntu-latest strategy: matrix: diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 64637c5fb..9bcb1898c 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -1,12 +1,15 @@ name: E2E -on: [push, pull_request] +on: + push: + pull_request: + merge_group: permissions: contents: read jobs: - build: + e2e: runs-on: ubuntu-latest strategy: fail-fast: false diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index d4bf4afe8..53b7f484d 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -1,7 +1,8 @@ name: Lint on: - push: {} + push: + merge_group: pull_request: types: - opened diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index b556c1b1d..f63e58750 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -19,7 +19,7 @@ concurrency: jobs: # Build job - build: + build-docs: runs-on: ubuntu-latest steps: - name: Checkout @@ -48,11 +48,11 @@ jobs: path: packages/mermaid/src/vitepress/.vitepress/dist # Deployment job - deploy: + deploy-docs: environment: name: github-pages runs-on: ubuntu-latest - needs: build + needs: build-docs steps: - name: Deploy to GitHub Pages id: deployment diff --git a/.github/workflows/release-preview-publish.yml b/.github/workflows/release-preview-publish.yml index 5f4936ab6..221e3836e 100644 --- a/.github/workflows/release-preview-publish.yml +++ b/.github/workflows/release-preview-publish.yml @@ -6,7 +6,7 @@ on: - 'release/**' jobs: - publish: + publish-preview: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6bae6b71f..e25502ee4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,12 +1,12 @@ name: Unit Tests -on: [push, pull_request] +on: [push, pull_request, merge_group] permissions: contents: read jobs: - build: + unit-test: runs-on: ubuntu-latest strategy: matrix: @@ -49,9 +49,3 @@ jobs: name: mermaid-codecov fail_ci_if_error: true verbose: true - # Coveralls is throwing 500. Disabled for now. - # - name: Upload Coverage to Coveralls - # uses: coverallsapp/github-action@v2 - # with: - # github-token: ${{ secrets.GITHUB_TOKEN }} - # flag-name: unit diff --git a/.github/workflows/update-browserlist.yml b/.github/workflows/update-browserlist.yml index 4155ec988..813a400b3 100644 --- a/.github/workflows/update-browserlist.yml +++ b/.github/workflows/update-browserlist.yml @@ -5,7 +5,7 @@ on: workflow_dispatch: jobs: - build: + update-browser-list: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 diff --git a/.prettierignore b/.prettierignore index 2ab91f93e..b50a94e84 100644 --- a/.prettierignore +++ b/.prettierignore @@ -6,3 +6,4 @@ coverage pnpm-lock.yaml stats packages/mermaid/src/docs/.vitepress/components.d.ts +.nyc_output diff --git a/__mocks__/sankeyRenderer.js b/__mocks__/sankeyRenderer.js new file mode 100644 index 000000000..76324c93f --- /dev/null +++ b/__mocks__/sankeyRenderer.js @@ -0,0 +1,13 @@ +/** + * Mocked Sankey diagram renderer + */ + +import { vi } from 'vitest'; + +export const draw = vi.fn().mockImplementation(() => { + return ''; +}); + +export default { + draw, +}; diff --git a/cSpell.json b/cSpell.json index e13677222..690c2bd33 100644 --- a/cSpell.json +++ b/cSpell.json @@ -106,6 +106,7 @@ "rehype", "roledescription", "sandboxed", + "sankey", "setupgraphviewbox", "shiki", "sidharth", diff --git a/cypress/integration/rendering/sankey.spec.ts b/cypress/integration/rendering/sankey.spec.ts new file mode 100644 index 000000000..e334b898b --- /dev/null +++ b/cypress/integration/rendering/sankey.spec.ts @@ -0,0 +1,144 @@ +import { imgSnapshotTest, renderGraph } from '../../helpers/util.js'; + +describe('Sankey Diagram', () => { + it('should render a simple example', () => { + imgSnapshotTest( + ` + sankey-beta + + sourceNode,targetNode,10 + `, + {} + ); + }); + + describe('when given a linkColor', function () { + this.beforeAll(() => { + cy.wrap( + `sankey-beta + a,b,10 + ` + ).as('graph'); + }); + + it('links should use hex color', function () { + renderGraph(this.graph, { sankey: { linkColor: '#636465' } }); + + cy.get('.link path').should((link) => { + expect(link.attr('stroke')).to.equal('#636465'); + }); + }); + + it('links should be the same color as source node', function () { + renderGraph(this.graph, { sankey: { linkColor: 'source' } }); + + cy.get('.link path').then((link) => { + cy.get('.node[id="node-1"] rect').should((node) => + expect(link.attr('stroke')).to.equal(node.attr('fill')) + ); + }); + }); + + it('links should be the same color as target node', function () { + renderGraph(this.graph, { sankey: { linkColor: 'target' } }); + + cy.get('.link path').then((link) => { + cy.get('.node[id="node-2"] rect').should((node) => + expect(link.attr('stroke')).to.equal(node.attr('fill')) + ); + }); + }); + + it('links must be gradient', function () { + renderGraph(this.graph, { sankey: { linkColor: 'gradient' } }); + + cy.get('.link path').should((link) => { + expect(link.attr('stroke')).to.equal('url(#linearGradient-3)'); + }); + }); + }); + + describe('when given a nodeAlignment', function () { + this.beforeAll(() => { + cy.wrap( + ` + sankey-beta + + a,b,8 + b,c,8 + c,d,8 + d,e,8 + + x,c,4 + c,y,4 + ` + ).as('graph'); + }); + + this.afterEach(() => { + cy.get('.node[id="node-1"]').should((node) => { + expect(node.attr('x')).to.equal('0'); + }); + cy.get('.node[id="node-2"]').should((node) => { + expect(node.attr('x')).to.equal('100'); + }); + cy.get('.node[id="node-3"]').should((node) => { + expect(node.attr('x')).to.equal('200'); + }); + cy.get('.node[id="node-4"]').should((node) => { + expect(node.attr('x')).to.equal('300'); + }); + cy.get('.node[id="node-5"]').should((node) => { + expect(node.attr('x')).to.equal('400'); + }); + }); + + it('should justify nodes', function () { + renderGraph(this.graph, { + sankey: { nodeAlignment: 'justify', width: 410, useMaxWidth: false }, + }); + cy.get('.node[id="node-6"]').should((node) => { + expect(node.attr('x')).to.equal('0'); + }); + cy.get('.node[id="node-7"]').should((node) => { + expect(node.attr('x')).to.equal('400'); + }); + }); + + it('should align nodes left', function () { + renderGraph(this.graph, { + sankey: { nodeAlignment: 'left', width: 410, useMaxWidth: false }, + }); + cy.get('.node[id="node-6"]').should((node) => { + expect(node.attr('x')).to.equal('0'); + }); + cy.get('.node[id="node-7"]').should((node) => { + expect(node.attr('x')).to.equal('300'); + }); + }); + + it('should align nodes right', function () { + renderGraph(this.graph, { + sankey: { nodeAlignment: 'right', width: 410, useMaxWidth: false }, + }); + cy.get('.node[id="node-6"]').should((node) => { + expect(node.attr('x')).to.equal('100'); + }); + cy.get('.node[id="node-7"]').should((node) => { + expect(node.attr('x')).to.equal('400'); + }); + }); + + it('should center nodes', function () { + renderGraph(this.graph, { + sankey: { nodeAlignment: 'center', width: 410, useMaxWidth: false }, + }); + cy.get('.node[id="node-6"]').should((node) => { + expect(node.attr('x')).to.equal('100'); + }); + cy.get('.node[id="node-7"]').should((node) => { + expect(node.attr('x')).to.equal('300'); + }); + }); + }); +}); diff --git a/demos/classchart.html b/demos/classchart.html index b20dda2a3..508bb1066 100644 --- a/demos/classchart.html +++ b/demos/classchart.html @@ -154,6 +154,29 @@
+
+    classDiagram
+      A1 --> B1
+      namespace A {
+        class A1 {
+          +foo : string
+        }
+        class A2 {
+          +bar : int
+        }
+      }
+      namespace B {
+        class B1 {
+          +foo : bool
+        }
+        class B2 {
+          +bar : float
+        }
+      }
+      A2 --> B2
+    
+
+ + + diff --git a/docker-compose.yml b/docker-compose.yml index a2773b8a5..c881d0473 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,9 +1,36 @@ version: '3.9' services: mermaid: - image: node:20.3.1-alpine3.18 + image: node:18.16.1-alpine3.18 stdin_open: true tty: true working_dir: /mermaid + mem_limit: '2G' + environment: + - NODE_OPTIONS=--max_old_space_size=2048 volumes: - ./:/mermaid + - root_cache:/root/.cache + - root_local:/root/.local + - root_npm:/root/.npm + ports: + - 9000:9000 + - 3333:3333 + cypress: + image: cypress/included:12.16.0 + stdin_open: true + tty: true + working_dir: /mermaid + mem_limit: '2G' + entrypoint: cypress + environment: + - DISPLAY + volumes: + - ./:/mermaid + - /tmp/.X11-unix:/tmp/.X11-unix + network_mode: host + +volumes: + root_cache: + root_local: + root_npm: diff --git a/docs/config/setup/modules/defaultConfig.md b/docs/config/setup/modules/defaultConfig.md index d95ec4e92..793dfffae 100644 --- a/docs/config/setup/modules/defaultConfig.md +++ b/docs/config/setup/modules/defaultConfig.md @@ -14,7 +14,7 @@ #### Defined in -[defaultConfig.ts:2293](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L2293) +[defaultConfig.ts:2300](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L2300) --- diff --git a/docs/config/theming.md b/docs/config/theming.md index 580afb488..63271a39b 100644 --- a/docs/config/theming.md +++ b/docs/config/theming.md @@ -73,9 +73,9 @@ To make a custom theme, modify `themeVariables` via `init`. You will need to use the [base](#available-themes) theme as it is the only modifiable theme. -| Parameter | Description | Type | Properties | -| -------------- | ------------------------------------ | ------ | --------------------------------------------------------------------------------------------------- | -| themeVariables | Modifiable with the `init` directive | Object | `primaryColor`, `primaryTextColor`, `lineColor` ([see full list](#theme-variables-reference-table)) | +| Parameter | Description | Type | Properties | +| -------------- | ------------------------------------ | ------ | ----------------------------------------------------------------------------------- | +| themeVariables | Modifiable with the `init` directive | Object | `primaryColor`, `primaryTextColor`, `lineColor` ([see full list](#theme-variables)) | Example of modifying `themeVariables` using the `init` directive: diff --git a/docs/ecosystem/integrations.md b/docs/ecosystem/integrations.md index 10041525d..896a23c56 100644 --- a/docs/ecosystem/integrations.md +++ b/docs/ecosystem/integrations.md @@ -161,6 +161,8 @@ They also serve as proof of concept, for the variety of things that can be built - [Nano Mermaid](https://github.com/Yash-Singh1/nano-mermaid) - [CKEditor](https://github.com/ckeditor/ckeditor5) - [CKEditor 5 Mermaid plugin](https://github.com/ckeditor/ckeditor5-mermaid) +- [Standard Notes](https://standardnotes.com/) + - [sn-mermaid](https://github.com/nienow/sn-mermaid) ## Document Generation diff --git a/docs/news/announcements.md b/docs/news/announcements.md index a843ae419..1837b33db 100644 --- a/docs/news/announcements.md +++ b/docs/news/announcements.md @@ -6,8 +6,8 @@ # Announcements -## [subhash-halder contributed quadrant charts so you can show your manager what to select - just like the strategy consultants at BCG do](https://www.mermaidchart.com/blog/posts/subhash-halder-contributed-quadrant-charts-so-you-can-show-your-manager-what-to-select-just-like-the-strategy-consultants-at-bcg-do/) +## [Sequence diagrams, the only good thing UML brought to software development](https://www.mermaidchart.com/blog/posts/sequence-diagrams-the-good-thing-uml-brought-to-software-development/) -8 June 2023 · 7 mins +15 June 2023 · 12 mins -A quadrant chart is a useful diagram that helps users visualize data and identify patterns in a data set. +Sequence diagrams really shine when you’re documenting different parts of a system and the various ways these parts interact with each other. diff --git a/docs/news/blog.md b/docs/news/blog.md index 48b669d79..eb9ec6c47 100644 --- a/docs/news/blog.md +++ b/docs/news/blog.md @@ -6,6 +6,12 @@ # Blog +## [Sequence diagrams, the only good thing UML brought to software development](https://www.mermaidchart.com/blog/posts/sequence-diagrams-the-good-thing-uml-brought-to-software-development/) + +15 June 2023 · 12 mins + +Sequence diagrams really shine when you’re documenting different parts of a system and the various ways these parts interact with each other. + ## [subhash-halder contributed quadrant charts so you can show your manager what to select - just like the strategy consultants at BCG do](https://www.mermaidchart.com/blog/posts/subhash-halder-contributed-quadrant-charts-so-you-can-show-your-manager-what-to-select-just-like-the-strategy-consultants-at-bcg-do/) 8 June 2023 · 7 mins diff --git a/docs/syntax/gantt.md b/docs/syntax/gantt.md index 091cdeabe..8e64a268a 100644 --- a/docs/syntax/gantt.md +++ b/docs/syntax/gantt.md @@ -25,25 +25,25 @@ Mermaid can render Gantt diagrams as SVG, PNG or a MarkDown link that can be pas ```mermaid-example gantt title A Gantt Diagram - dateFormat YYYY-MM-DD + dateFormat YYYY-MM-DD section Section - A task :a1, 2014-01-01, 30d - Another task :after a1 , 20d + A task :a1, 2014-01-01, 30d + Another task :after a1, 20d section Another - Task in sec :2014-01-12 , 12d - another task : 24d + Task in Another :2014-01-12, 12d + another task :24d ``` ```mermaid gantt title A Gantt Diagram - dateFormat YYYY-MM-DD + dateFormat YYYY-MM-DD section Section - A task :a1, 2014-01-01, 30d - Another task :after a1 , 20d + A task :a1, 2014-01-01, 30d + Another task :after a1, 20d section Another - Task in sec :2014-01-12 , 12d - another task : 24d + Task in Another :2014-01-12, 12d + another task :24d ``` ## Syntax @@ -117,17 +117,17 @@ gantt It is possible to set multiple dependencies separated by space: ```mermaid-example - gantt - apple :a, 2017-07-20, 1w - banana :crit, b, 2017-07-23, 1d - cherry :active, c, after b a, 1d +gantt + apple :a, 2017-07-20, 1w + banana :crit, b, 2017-07-23, 1d + cherry :active, c, after b a, 1d ``` ```mermaid - gantt - apple :a, 2017-07-20, 1w - banana :crit, b, 2017-07-23, 1d - cherry :active, c, after b a, 1d +gantt + apple :a, 2017-07-20, 1w + banana :crit, b, 2017-07-23, 1d + cherry :active, c, after b a, 1d ``` ### Title @@ -146,22 +146,22 @@ You can add milestones to the diagrams. Milestones differ from tasks as they rep ```mermaid-example gantt -dateFormat HH:mm -axisFormat %H:%M -Initial milestone : milestone, m1, 17:49,2min -taska2 : 10min -taska3 : 5min -Final milestone : milestone, m2, 18:14, 2min + dateFormat HH:mm + axisFormat %H:%M + Initial milestone : milestone, m1, 17:49, 2m + Task A : 10m + Task B : 5m + Final milestone : milestone, m2, 18:08, 4m ``` ```mermaid gantt -dateFormat HH:mm -axisFormat %H:%M -Initial milestone : milestone, m1, 17:49,2min -taska2 : 10min -taska3 : 5min -Final milestone : milestone, m2, 18:14, 2min + dateFormat HH:mm + axisFormat %H:%M + Initial milestone : milestone, m1, 17:49, 2m + Task A : 10m + Task B : 5m + Final milestone : milestone, m2, 18:08, 4m ``` ## Setting dates @@ -296,29 +296,27 @@ Comments can be entered within a gantt chart, which will be ignored by the parse ```mermaid-example gantt title A Gantt Diagram - %% this is a comment - dateFormat YYYY-MM-DD + %% This is a comment + dateFormat YYYY-MM-DD section Section - A task :a1, 2014-01-01, 30d - Another task :after a1 , 20d + A task :a1, 2014-01-01, 30d + Another task :after a1, 20d section Another - Task in sec :2014-01-12 , 12d - another task : 24d - + Task in Another :2014-01-12, 12d + another task :24d ``` ```mermaid gantt title A Gantt Diagram - %% this is a comment - dateFormat YYYY-MM-DD + %% This is a comment + dateFormat YYYY-MM-DD section Section - A task :a1, 2014-01-01, 30d - Another task :after a1 , 20d + A task :a1, 2014-01-01, 30d + Another task :after a1, 20d section Another - Task in sec :2014-01-12 , 12d - another task : 24d - + Task in Another :2014-01-12, 12d + another task :24d ``` ## Styling @@ -440,7 +438,7 @@ Beginner's tip—a full example using interactive links in an html context: dateFormat YYYY-MM-DD section Clickable - Visit mermaidjs :active, cl1, 2014-01-07, 3d + Visit mermaidjs :active, cl1, 2014-01-07, 3d Print arguments :cl2, after cl1, 3d Print task :cl3, after cl2, 3d diff --git a/docs/syntax/sankey.md b/docs/syntax/sankey.md new file mode 100644 index 000000000..bfbfc3621 --- /dev/null +++ b/docs/syntax/sankey.md @@ -0,0 +1,510 @@ +> **Warning** +> +> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. +> +> ## Please edit the corresponding file in [/packages/mermaid/src/docs/syntax/sankey.md](../../packages/mermaid/src/docs/syntax/sankey.md). + +# Sankey diagrams + +> A sankey diagram is a visualization used to depict a flow from one set of values to another. + +::: warning +This is an experimental diagram. Its syntax are very close to plain CSV, but it is to be extended in the nearest future. +::: + +The things being connected are called nodes and the connections are called links. + +## Example + +This example taken from [observable](https://observablehq.com/@d3/sankey/2?collection=@d3/d3-sankey). It may be rendered a little bit differently, though, in terms of size and colors. + +```mermaid-example +sankey-beta + +Agricultural 'waste',Bio-conversion,124.729 +Bio-conversion,Liquid,0.597 +Bio-conversion,Losses,26.862 +Bio-conversion,Solid,280.322 +Bio-conversion,Gas,81.144 +Biofuel imports,Liquid,35 +Biomass imports,Solid,35 +Coal imports,Coal,11.606 +Coal reserves,Coal,63.965 +Coal,Solid,75.571 +District heating,Industry,10.639 +District heating,Heating and cooling - commercial,22.505 +District heating,Heating and cooling - homes,46.184 +Electricity grid,Over generation / exports,104.453 +Electricity grid,Heating and cooling - homes,113.726 +Electricity grid,H2 conversion,27.14 +Electricity grid,Industry,342.165 +Electricity grid,Road transport,37.797 +Electricity grid,Agriculture,4.412 +Electricity grid,Heating and cooling - commercial,40.858 +Electricity grid,Losses,56.691 +Electricity grid,Rail transport,7.863 +Electricity grid,Lighting & appliances - commercial,90.008 +Electricity grid,Lighting & appliances - homes,93.494 +Gas imports,Ngas,40.719 +Gas reserves,Ngas,82.233 +Gas,Heating and cooling - commercial,0.129 +Gas,Losses,1.401 +Gas,Thermal generation,151.891 +Gas,Agriculture,2.096 +Gas,Industry,48.58 +Geothermal,Electricity grid,7.013 +H2 conversion,H2,20.897 +H2 conversion,Losses,6.242 +H2,Road transport,20.897 +Hydro,Electricity grid,6.995 +Liquid,Industry,121.066 +Liquid,International shipping,128.69 +Liquid,Road transport,135.835 +Liquid,Domestic aviation,14.458 +Liquid,International aviation,206.267 +Liquid,Agriculture,3.64 +Liquid,National navigation,33.218 +Liquid,Rail transport,4.413 +Marine algae,Bio-conversion,4.375 +Ngas,Gas,122.952 +Nuclear,Thermal generation,839.978 +Oil imports,Oil,504.287 +Oil reserves,Oil,107.703 +Oil,Liquid,611.99 +Other waste,Solid,56.587 +Other waste,Bio-conversion,77.81 +Pumped heat,Heating and cooling - homes,193.026 +Pumped heat,Heating and cooling - commercial,70.672 +Solar PV,Electricity grid,59.901 +Solar Thermal,Heating and cooling - homes,19.263 +Solar,Solar Thermal,19.263 +Solar,Solar PV,59.901 +Solid,Agriculture,0.882 +Solid,Thermal generation,400.12 +Solid,Industry,46.477 +Thermal generation,Electricity grid,525.531 +Thermal generation,Losses,787.129 +Thermal generation,District heating,79.329 +Tidal,Electricity grid,9.452 +UK land based bioenergy,Bio-conversion,182.01 +Wave,Electricity grid,19.013 +Wind,Electricity grid,289.366 +``` + +```mermaid +sankey-beta + +Agricultural 'waste',Bio-conversion,124.729 +Bio-conversion,Liquid,0.597 +Bio-conversion,Losses,26.862 +Bio-conversion,Solid,280.322 +Bio-conversion,Gas,81.144 +Biofuel imports,Liquid,35 +Biomass imports,Solid,35 +Coal imports,Coal,11.606 +Coal reserves,Coal,63.965 +Coal,Solid,75.571 +District heating,Industry,10.639 +District heating,Heating and cooling - commercial,22.505 +District heating,Heating and cooling - homes,46.184 +Electricity grid,Over generation / exports,104.453 +Electricity grid,Heating and cooling - homes,113.726 +Electricity grid,H2 conversion,27.14 +Electricity grid,Industry,342.165 +Electricity grid,Road transport,37.797 +Electricity grid,Agriculture,4.412 +Electricity grid,Heating and cooling - commercial,40.858 +Electricity grid,Losses,56.691 +Electricity grid,Rail transport,7.863 +Electricity grid,Lighting & appliances - commercial,90.008 +Electricity grid,Lighting & appliances - homes,93.494 +Gas imports,Ngas,40.719 +Gas reserves,Ngas,82.233 +Gas,Heating and cooling - commercial,0.129 +Gas,Losses,1.401 +Gas,Thermal generation,151.891 +Gas,Agriculture,2.096 +Gas,Industry,48.58 +Geothermal,Electricity grid,7.013 +H2 conversion,H2,20.897 +H2 conversion,Losses,6.242 +H2,Road transport,20.897 +Hydro,Electricity grid,6.995 +Liquid,Industry,121.066 +Liquid,International shipping,128.69 +Liquid,Road transport,135.835 +Liquid,Domestic aviation,14.458 +Liquid,International aviation,206.267 +Liquid,Agriculture,3.64 +Liquid,National navigation,33.218 +Liquid,Rail transport,4.413 +Marine algae,Bio-conversion,4.375 +Ngas,Gas,122.952 +Nuclear,Thermal generation,839.978 +Oil imports,Oil,504.287 +Oil reserves,Oil,107.703 +Oil,Liquid,611.99 +Other waste,Solid,56.587 +Other waste,Bio-conversion,77.81 +Pumped heat,Heating and cooling - homes,193.026 +Pumped heat,Heating and cooling - commercial,70.672 +Solar PV,Electricity grid,59.901 +Solar Thermal,Heating and cooling - homes,19.263 +Solar,Solar Thermal,19.263 +Solar,Solar PV,59.901 +Solid,Agriculture,0.882 +Solid,Thermal generation,400.12 +Solid,Industry,46.477 +Thermal generation,Electricity grid,525.531 +Thermal generation,Losses,787.129 +Thermal generation,District heating,79.329 +Tidal,Electricity grid,9.452 +UK land based bioenergy,Bio-conversion,182.01 +Wave,Electricity grid,19.013 +Wind,Electricity grid,289.366 +``` + +::: details + +```mermaid-example +sankey-beta + +Agricultural 'waste',Bio-conversion,124.729 +Bio-conversion,Liquid,0.597 +Bio-conversion,Losses,26.862 +Bio-conversion,Solid,280.322 +Bio-conversion,Gas,81.144 +Biofuel imports,Liquid,35 +Biomass imports,Solid,35 +Coal imports,Coal,11.606 +Coal reserves,Coal,63.965 +Coal,Solid,75.571 +District heating,Industry,10.639 +District heating,Heating and cooling - commercial,22.505 +District heating,Heating and cooling - homes,46.184 +Electricity grid,Over generation / exports,104.453 +Electricity grid,Heating and cooling - homes,113.726 +Electricity grid,H2 conversion,27.14 +Electricity grid,Industry,342.165 +Electricity grid,Road transport,37.797 +Electricity grid,Agriculture,4.412 +Electricity grid,Heating and cooling - commercial,40.858 +Electricity grid,Losses,56.691 +Electricity grid,Rail transport,7.863 +Electricity grid,Lighting & appliances - commercial,90.008 +Electricity grid,Lighting & appliances - homes,93.494 +Gas imports,Ngas,40.719 +Gas reserves,Ngas,82.233 +Gas,Heating and cooling - commercial,0.129 +Gas,Losses,1.401 +Gas,Thermal generation,151.891 +Gas,Agriculture,2.096 +Gas,Industry,48.58 +Geothermal,Electricity grid,7.013 +H2 conversion,H2,20.897 +H2 conversion,Losses,6.242 +H2,Road transport,20.897 +Hydro,Electricity grid,6.995 +Liquid,Industry,121.066 +Liquid,International shipping,128.69 +Liquid,Road transport,135.835 +Liquid,Domestic aviation,14.458 +Liquid,International aviation,206.267 +Liquid,Agriculture,3.64 +Liquid,National navigation,33.218 +Liquid,Rail transport,4.413 +Marine algae,Bio-conversion,4.375 +Ngas,Gas,122.952 +Nuclear,Thermal generation,839.978 +Oil imports,Oil,504.287 +Oil reserves,Oil,107.703 +Oil,Liquid,611.99 +Other waste,Solid,56.587 +Other waste,Bio-conversion,77.81 +Pumped heat,Heating and cooling - homes,193.026 +Pumped heat,Heating and cooling - commercial,70.672 +Solar PV,Electricity grid,59.901 +Solar Thermal,Heating and cooling - homes,19.263 +Solar,Solar Thermal,19.263 +Solar,Solar PV,59.901 +Solid,Agriculture,0.882 +Solid,Thermal generation,400.12 +Solid,Industry,46.477 +Thermal generation,Electricity grid,525.531 +Thermal generation,Losses,787.129 +Thermal generation,District heating,79.329 +Tidal,Electricity grid,9.452 +UK land based bioenergy,Bio-conversion,182.01 +Wave,Electricity grid,19.013 +Wind,Electricity grid,289.366 +``` + +```mermaid +sankey-beta + +Agricultural 'waste',Bio-conversion,124.729 +Bio-conversion,Liquid,0.597 +Bio-conversion,Losses,26.862 +Bio-conversion,Solid,280.322 +Bio-conversion,Gas,81.144 +Biofuel imports,Liquid,35 +Biomass imports,Solid,35 +Coal imports,Coal,11.606 +Coal reserves,Coal,63.965 +Coal,Solid,75.571 +District heating,Industry,10.639 +District heating,Heating and cooling - commercial,22.505 +District heating,Heating and cooling - homes,46.184 +Electricity grid,Over generation / exports,104.453 +Electricity grid,Heating and cooling - homes,113.726 +Electricity grid,H2 conversion,27.14 +Electricity grid,Industry,342.165 +Electricity grid,Road transport,37.797 +Electricity grid,Agriculture,4.412 +Electricity grid,Heating and cooling - commercial,40.858 +Electricity grid,Losses,56.691 +Electricity grid,Rail transport,7.863 +Electricity grid,Lighting & appliances - commercial,90.008 +Electricity grid,Lighting & appliances - homes,93.494 +Gas imports,Ngas,40.719 +Gas reserves,Ngas,82.233 +Gas,Heating and cooling - commercial,0.129 +Gas,Losses,1.401 +Gas,Thermal generation,151.891 +Gas,Agriculture,2.096 +Gas,Industry,48.58 +Geothermal,Electricity grid,7.013 +H2 conversion,H2,20.897 +H2 conversion,Losses,6.242 +H2,Road transport,20.897 +Hydro,Electricity grid,6.995 +Liquid,Industry,121.066 +Liquid,International shipping,128.69 +Liquid,Road transport,135.835 +Liquid,Domestic aviation,14.458 +Liquid,International aviation,206.267 +Liquid,Agriculture,3.64 +Liquid,National navigation,33.218 +Liquid,Rail transport,4.413 +Marine algae,Bio-conversion,4.375 +Ngas,Gas,122.952 +Nuclear,Thermal generation,839.978 +Oil imports,Oil,504.287 +Oil reserves,Oil,107.703 +Oil,Liquid,611.99 +Other waste,Solid,56.587 +Other waste,Bio-conversion,77.81 +Pumped heat,Heating and cooling - homes,193.026 +Pumped heat,Heating and cooling - commercial,70.672 +Solar PV,Electricity grid,59.901 +Solar Thermal,Heating and cooling - homes,19.263 +Solar,Solar Thermal,19.263 +Solar,Solar PV,59.901 +Solid,Agriculture,0.882 +Solid,Thermal generation,400.12 +Solid,Industry,46.477 +Thermal generation,Electricity grid,525.531 +Thermal generation,Losses,787.129 +Thermal generation,District heating,79.329 +Tidal,Electricity grid,9.452 +UK land based bioenergy,Bio-conversion,182.01 +Wave,Electricity grid,19.013 +Wind,Electricity grid,289.366 +``` + +::: + +## Syntax + +The idea behind syntax is that a user types `sankey-beta` keyword first, then pastes raw CSV below and get the result. + +It implements CSV standard as [described here](https://www.ietf.org/rfc/rfc4180.txt) with subtle **differences**: + +- CSV must contain **3 columns only** +- It is **allowed** to have **empty lines** without comma separators for visual purposes + +### Basic + +It is implied that 3 columns inside CSV should represent `source`, `target` and `value` accordingly: + +```mermaid-example +sankey-beta + +%% source,target,value +Electricity grid,Over generation / exports,104.453 +Electricity grid,Heating and cooling - homes,113.726 +Electricity grid,H2 conversion,27.14 +``` + +```mermaid +sankey-beta + +%% source,target,value +Electricity grid,Over generation / exports,104.453 +Electricity grid,Heating and cooling - homes,113.726 +Electricity grid,H2 conversion,27.14 +``` + +```mermaid-example +sankey-beta + +%% source,target,value +Electricity grid,Over generation / exports,104.453 +Electricity grid,Heating and cooling - homes,113.726 +Electricity grid,H2 conversion,27.14 +``` + +```mermaid +sankey-beta + +%% source,target,value +Electricity grid,Over generation / exports,104.453 +Electricity grid,Heating and cooling - homes,113.726 +Electricity grid,H2 conversion,27.14 +``` + +### Empty Lines + +CSV does not support empty lines without comma delimeters by default. But you can add them if needed: + +```mermaid-example +sankey-beta + +Bio-conversion,Losses,26.862 + +Bio-conversion,Solid,280.322 + +Bio-conversion,Gas,81.144 +``` + +```mermaid +sankey-beta + +Bio-conversion,Losses,26.862 + +Bio-conversion,Solid,280.322 + +Bio-conversion,Gas,81.144 +``` + +```mermaid-example +sankey-beta + +Bio-conversion,Losses,26.862 + +Bio-conversion,Solid,280.322 + +Bio-conversion,Gas,81.144 +``` + +```mermaid +sankey-beta + +Bio-conversion,Losses,26.862 + +Bio-conversion,Solid,280.322 + +Bio-conversion,Gas,81.144 +``` + +### Commas + +If you need to have a comma, wrap it in double quotes: + +```mermaid-example +sankey-beta + +Pumped heat,"Heating and cooling, homes",193.026 +Pumped heat,"Heating and cooling, commercial",70.672 +``` + +```mermaid +sankey-beta + +Pumped heat,"Heating and cooling, homes",193.026 +Pumped heat,"Heating and cooling, commercial",70.672 +``` + +```mermaid-example +sankey-beta + +Pumped heat,"Heating and cooling, homes",193.026 +Pumped heat,"Heating and cooling, commercial",70.672 +``` + +```mermaid +sankey-beta + +Pumped heat,"Heating and cooling, homes",193.026 +Pumped heat,"Heating and cooling, commercial",70.672 +``` + +### Double Quotes + +If you need to have double quote, put a pair of them inside quoted string: + +```mermaid-example +sankey-beta + +Pumped heat,"Heating and cooling, ""homes""",193.026 +Pumped heat,"Heating and cooling, ""commercial""",70.672 +``` + +```mermaid +sankey-beta + +Pumped heat,"Heating and cooling, ""homes""",193.026 +Pumped heat,"Heating and cooling, ""commercial""",70.672 +``` + +```mermaid-example +sankey-beta + +Pumped heat,"Heating and cooling, ""homes""",193.026 +Pumped heat,"Heating and cooling, ""commercial""",70.672 +``` + +```mermaid +sankey-beta + +Pumped heat,"Heating and cooling, ""homes""",193.026 +Pumped heat,"Heating and cooling, ""commercial""",70.672 +``` + +## Configuration + +You can customize link colors, node alignments and diagram dimensions. + +```html + +``` + +### Links Coloring + +You can adjust links' color by setting `linkColor` to one of those: + +- `source` - link will be of a source node color +- `target` - link will be of a target node color +- `gradient` - link color will be smoothly transient between source and target node colors +- hex code of color, like `#a1a1a1` + +### Node Alignment + +Graph layout can be changed by setting `nodeAlignment` to: + +- `justify` +- `center` +- `left` +- `right` diff --git a/docs/syntax/stateDiagram.md b/docs/syntax/stateDiagram.md index 807d6149a..4c28c82b3 100644 --- a/docs/syntax/stateDiagram.md +++ b/docs/syntax/stateDiagram.md @@ -487,7 +487,7 @@ where - 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` +- the fifth _property_ is `stroke` and its _value_ is `yellow` ### Apply classDef styles to states diff --git a/package.json b/package.json index 0d8b25d40..738d1796a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "mermaid-monorepo", "private": true, - "version": "10.2.3", + "version": "10.2.4", "description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.", "type": "module", "packageManager": "pnpm@8.6.5", @@ -22,7 +22,7 @@ "build:watch": "pnpm build:vite --watch", "build": "pnpm run -r clean && pnpm build:types && pnpm build:vite", "dev": "concurrently \"pnpm build:vite --watch\" \"ts-node-esm .vite/server.ts\"", - "dev:coverage": "VITE_COVERAGE=true pnpm dev", + "dev:coverage": "pnpm coverage:cypress:clean && VITE_COVERAGE=true pnpm dev", "release": "pnpm build", "lint": "eslint --cache --cache-strategy content --ignore-path .gitignore . && pnpm lint:jison && prettier --cache --check .", "lint:fix": "eslint --cache --cache-strategy content --fix --ignore-path .gitignore . && prettier --write . && ts-node-esm scripts/fixCSpell.ts", @@ -31,7 +31,8 @@ "cypress": "cypress run", "cypress:open": "cypress open", "e2e": "start-server-and-test dev http://localhost:9000/ cypress", - "e2e:coverage": "VITE_COVERAGE=true pnpm e2e", + "coverage:cypress:clean": "rimraf .nyc_output coverage/cypress", + "e2e:coverage": "pnpm coverage:cypress:clean && VITE_COVERAGE=true pnpm e2e", "coverage:merge": "ts-node-esm scripts/coverage.ts", "coverage": "pnpm test:coverage --run && pnpm e2e:coverage && pnpm coverage:merge", "ci": "vitest run", @@ -77,7 +78,7 @@ "@types/rollup-plugin-visualizer": "^4.2.1", "@typescript-eslint/eslint-plugin": "^5.59.0", "@typescript-eslint/parser": "^5.59.0", - "@vitest/coverage-istanbul": "^0.32.2", + "@vitest/coverage-v8": "^0.32.2", "@vitest/spy": "^0.32.2", "@vitest/ui": "^0.32.2", "concurrently": "^8.0.1", diff --git a/packages/mermaid/package.json b/packages/mermaid/package.json index 8e7e9696d..62efec380 100644 --- a/packages/mermaid/package.json +++ b/packages/mermaid/package.json @@ -1,6 +1,6 @@ { "name": "mermaid", - "version": "10.2.3", + "version": "10.2.4", "description": "Markdown-ish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.", "type": "module", "module": "./dist/mermaid.core.mjs", @@ -53,13 +53,16 @@ }, "dependencies": { "@braintree/sanitize-url": "^6.0.2", + "@types/d3-scale": "^4.0.3", + "@types/d3-scale-chromatic": "^3.0.0", "cytoscape": "^3.23.0", "cytoscape-cose-bilkent": "^4.1.0", "cytoscape-fcose": "^2.1.0", "d3": "^7.4.0", + "d3-sankey": "^0.12.3", "dagre-d3-es": "7.0.10", "dayjs": "^1.11.7", - "dompurify": "3.0.3", + "dompurify": "3.0.4", "elkjs": "^0.8.2", "khroma": "^2.0.0", "lodash-es": "^4.17.21", @@ -73,6 +76,7 @@ "devDependencies": { "@types/cytoscape": "^3.19.9", "@types/d3": "^7.4.0", + "@types/d3-sankey": "^0.12.1", "@types/d3-selection": "^3.0.5", "@types/dompurify": "^3.0.2", "@types/jsdom": "^21.1.1", diff --git a/packages/mermaid/src/assignWithDepth.js b/packages/mermaid/src/assignWithDepth.js index 898481c30..6f2e706ab 100644 --- a/packages/mermaid/src/assignWithDepth.js +++ b/packages/mermaid/src/assignWithDepth.js @@ -20,7 +20,7 @@ * of src to dst in order. * @param {any} dst - The destination of the merge * @param {any} src - The source object(s) to merge into destination - * @param {{ depth: number; clobber: boolean }} [config={ depth: 2, clobber: false }] - Depth: depth + * @param {{ depth: number; clobber: boolean }} [config] - Depth: depth * to traverse within src and dst for merging - clobber: should dissimilar types clobber (default: * { depth: 2, clobber: false }). Default is `{ depth: 2, clobber: false }` * @returns {any} diff --git a/packages/mermaid/src/config.type.ts b/packages/mermaid/src/config.type.ts index 140c200fb..4d40b33c8 100644 --- a/packages/mermaid/src/config.type.ts +++ b/packages/mermaid/src/config.type.ts @@ -32,6 +32,7 @@ export interface MermaidConfig { mindmap?: MindmapDiagramConfig; gitGraph?: GitGraphDiagramConfig; c4?: C4DiagramConfig; + sankey?: SankeyDiagramConfig; dompurifyConfig?: DOMPurify.Config; wrap?: boolean; fontSize?: number; @@ -411,6 +412,26 @@ export interface FlowchartDiagramConfig extends BaseDiagramConfig { wrappingWidth?: number; } +export enum SankeyLinkColor { + source = 'source', + target = 'target', + gradient = 'gradient', +} + +export enum SankeyNodeAlignment { + left = 'left', + right = 'right', + center = 'center', + justify = 'justify', +} + +export interface SankeyDiagramConfig extends BaseDiagramConfig { + width?: number; + height?: number; + linkColor?: SankeyLinkColor | string; + nodeAlignment?: SankeyNodeAlignment; +} + export interface FontConfig { fontSize?: string | number; fontFamily?: string; diff --git a/packages/mermaid/src/defaultConfig.ts b/packages/mermaid/src/defaultConfig.ts index c9407828f..6e24b6391 100644 --- a/packages/mermaid/src/defaultConfig.ts +++ b/packages/mermaid/src/defaultConfig.ts @@ -1,5 +1,5 @@ import theme from './themes/index.js'; -import { MermaidConfig } from './config.type.js'; +import { MermaidConfig, SankeyLinkColor, SankeyNodeAlignment } from './config.type.js'; /** * **Configuration methods in Mermaid version 8.6.0 have been updated, to learn more[[click * here](8.6.0_docs.md)].** @@ -2270,6 +2270,13 @@ const config: Partial = { padding: 10, maxNodeWidth: 200, }, + sankey: { + width: 800, + height: 400, + linkColor: SankeyLinkColor.gradient, + nodeAlignment: SankeyNodeAlignment.justify, + useMaxWidth: false, + }, fontSize: 16, }; diff --git a/packages/mermaid/src/diagram-api/diagram-orchestration.ts b/packages/mermaid/src/diagram-api/diagram-orchestration.ts index 0253be45d..9c03e27f3 100644 --- a/packages/mermaid/src/diagram-api/diagram-orchestration.ts +++ b/packages/mermaid/src/diagram-api/diagram-orchestration.ts @@ -18,6 +18,7 @@ import errorDiagram from '../diagrams/error/errorDiagram.js'; import flowchartElk from '../diagrams/flowchart/elk/detector.js'; import timeline from '../diagrams/timeline/detector.js'; import mindmap from '../diagrams/mindmap/detector.js'; +import sankey from '../diagrams/sankey/sankeyDetector.js'; import { registerLazyLoadedDiagrams } from './detectType.js'; import { registerDiagram } from './diagramAPI.js'; @@ -79,6 +80,7 @@ export const addDiagrams = () => { stateV2, state, journey, - quadrantChart + quadrantChart, + sankey ); }; diff --git a/packages/mermaid/src/diagrams/class/classDb.ts b/packages/mermaid/src/diagrams/class/classDb.ts index 11a1eb37d..7b74aa819 100644 --- a/packages/mermaid/src/diagrams/class/classDb.ts +++ b/packages/mermaid/src/diagrams/class/classDb.ts @@ -448,9 +448,8 @@ const getNamespaces = function (): NamespaceMap { export const addClassesToNamespace = function (id: string, classNames: string[]) { if (namespaces[id] !== undefined) { classNames.map((className) => { + classes[className].parent = id; namespaces[id].classes[className] = classes[className]; - delete classes[className]; - classCounter--; }); } }; diff --git a/packages/mermaid/src/diagrams/class/classDiagram.spec.ts b/packages/mermaid/src/diagrams/class/classDiagram.spec.ts index a43ed2fcd..2182e8c34 100644 --- a/packages/mermaid/src/diagrams/class/classDiagram.spec.ts +++ b/packages/mermaid/src/diagrams/class/classDiagram.spec.ts @@ -1373,9 +1373,54 @@ class Class2 parser.parse(str); const testNamespace = parser.yy.getNamespace('Namespace1'); + const testClasses = parser.yy.getClasses(); expect(Object.keys(testNamespace.classes).length).toBe(2); expect(Object.keys(testNamespace.children).length).toBe(0); expect(testNamespace.classes['Class1'].id).toBe('Class1'); + expect(Object.keys(testClasses).length).toBe(2); + }); + + it('should add relations between classes of different namespaces', function () { + const str = `classDiagram + A1 --> B1 + namespace A { + class A1 { + +foo : string + } + class A2 { + +bar : int + } + } + namespace B { + class B1 { + +foo : bool + } + class B2 { + +bar : float + } + } + A2 --> B2`; + + parser.parse(str); + const testNamespaceA = parser.yy.getNamespace('A'); + const testNamespaceB = parser.yy.getNamespace('B'); + const testClasses = parser.yy.getClasses(); + const testRelations = parser.yy.getRelations(); + expect(Object.keys(testNamespaceA.classes).length).toBe(2); + expect(testNamespaceA.classes['A1'].members[0]).toBe('+foo : string'); + expect(testNamespaceA.classes['A2'].members[0]).toBe('+bar : int'); + expect(Object.keys(testNamespaceB.classes).length).toBe(2); + expect(testNamespaceB.classes['B1'].members[0]).toBe('+foo : bool'); + expect(testNamespaceB.classes['B2'].members[0]).toBe('+bar : float'); + expect(Object.keys(testClasses).length).toBe(4); + expect(testClasses['A1'].parent).toBe('A'); + expect(testClasses['A2'].parent).toBe('A'); + expect(testClasses['B1'].parent).toBe('B'); + expect(testClasses['B2'].parent).toBe('B'); + expect(testRelations[0].id1).toBe('A1'); + expect(testRelations[0].id2).toBe('B1'); + expect(testRelations[1].id1).toBe('A2'); + expect(testRelations[1].id2).toBe('B2'); }); }); diff --git a/packages/mermaid/src/diagrams/class/classRenderer-v2.ts b/packages/mermaid/src/diagrams/class/classRenderer-v2.ts index e0cfea641..6197fe8ac 100644 --- a/packages/mermaid/src/diagrams/class/classRenderer-v2.ts +++ b/packages/mermaid/src/diagrams/class/classRenderer-v2.ts @@ -93,52 +93,51 @@ export const addClasses = function ( log.info(classes); // Iterate through each item in the vertex object (containing all the vertices found) in the graph definition - keys.forEach(function (id) { - const vertex = classes[id]; + keys + .filter((id) => classes[id].parent == parent) + .forEach(function (id) { + const vertex = classes[id]; - /** - * Variable for storing the classes for the vertex - */ - let cssClassStr = ''; - if (vertex.cssClasses.length > 0) { - cssClassStr = cssClassStr + ' ' + vertex.cssClasses.join(' '); - } + /** + * Variable for storing the classes for the vertex + */ + const cssClassStr = vertex.cssClasses.join(' '); - const styles = { labelStyle: '', style: '' }; //getStylesFromArray(vertex.styles); + const styles = { labelStyle: '', style: '' }; //getStylesFromArray(vertex.styles); - // Use vertex id as text in the box if no text is provided by the graph definition - const vertexText = vertex.label ?? vertex.id; - const radius = 0; - const shape = 'class_box'; + // Use vertex id as text in the box if no text is provided by the graph definition + const vertexText = vertex.label ?? vertex.id; + const radius = 0; + const shape = 'class_box'; - // Add the node - const node = { - labelStyle: styles.labelStyle, - shape: shape, - labelText: sanitizeText(vertexText), - classData: vertex, - rx: radius, - ry: radius, - class: cssClassStr, - style: styles.style, - id: vertex.id, - domId: vertex.domId, - tooltip: diagObj.db.getTooltip(vertex.id, parent) || '', - haveCallback: vertex.haveCallback, - link: vertex.link, - width: vertex.type === 'group' ? 500 : undefined, - type: vertex.type, - // TODO V10: Flowchart ? Keeping flowchart for backwards compatibility. Remove in next major release - padding: getConfig().flowchart?.padding ?? getConfig().class?.padding, - }; - g.setNode(vertex.id, node); + // Add the node + const node = { + labelStyle: styles.labelStyle, + shape: shape, + labelText: sanitizeText(vertexText), + classData: vertex, + rx: radius, + ry: radius, + class: cssClassStr, + style: styles.style, + id: vertex.id, + domId: vertex.domId, + tooltip: diagObj.db.getTooltip(vertex.id, parent) || '', + haveCallback: vertex.haveCallback, + link: vertex.link, + width: vertex.type === 'group' ? 500 : undefined, + type: vertex.type, + // TODO V10: Flowchart ? Keeping flowchart for backwards compatibility. Remove in next major release + padding: getConfig().flowchart?.padding ?? getConfig().class?.padding, + }; + g.setNode(vertex.id, node); - if (parent) { - g.setParent(vertex.id, parent); - } + if (parent) { + g.setParent(vertex.id, parent); + } - log.info('setNode', node); - }); + log.info('setNode', node); + }); }; /** diff --git a/packages/mermaid/src/diagrams/class/classTypes.ts b/packages/mermaid/src/diagrams/class/classTypes.ts index cf6f20f0b..f3b8dc8cf 100644 --- a/packages/mermaid/src/diagrams/class/classTypes.ts +++ b/packages/mermaid/src/diagrams/class/classTypes.ts @@ -7,6 +7,7 @@ export interface ClassNode { members: string[]; annotations: string[]; domId: string; + parent?: string; link?: string; linkTarget?: string; haveCallback?: boolean; diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow-style.spec.js b/packages/mermaid/src/diagrams/flowchart/parser/flow-style.spec.js index e05c48627..3feaa2469 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow-style.spec.js +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow-style.spec.js @@ -338,4 +338,20 @@ describe('[Style] when parsing', () => { expect(edges[0].type).toBe('arrow_point'); }); + + it('should handle multiple vertices with style', function () { + const res = flow.parser.parse(` + graph TD + classDef C1 stroke-dasharray:4 + classDef C2 stroke-dasharray:6 + A & B:::C1 & D:::C1 --> E:::C2 + `); + + const vert = flow.parser.yy.getVertices(); + + expect(vert['A'].classes.length).toBe(0); + expect(vert['B'].classes[0]).toBe('C1'); + expect(vert['D'].classes[0]).toBe('C1'); + expect(vert['E'].classes[0]).toBe('C2'); + }); }); diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison index 51427118f..70fb49162 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison @@ -359,7 +359,7 @@ statement separator: NEWLINE | SEMI | EOF ; - + verticeStatement: verticeStatement link node { /* console.warn('vs',$1.stmt,$3); */ yy.addLink($1.stmt,$3,$2); $$ = { stmt: $3, nodes: $3.concat($1.nodes) } } | verticeStatement link node spaceList @@ -368,12 +368,16 @@ verticeStatement: verticeStatement link node |node { /*console.warn('noda', $1);*/ $$ = {stmt: $1, nodes:$1 }} ; -node: vertex +node: styledVertex { /* console.warn('nod', $1); */ $$ = [$1];} - | node spaceList AMP spaceList vertex + | node spaceList AMP spaceList styledVertex { $$ = $1.concat($5); /* console.warn('pip', $1[0], $5, $$); */ } + ; + +styledVertex: vertex + { /* console.warn('nod', $1); */ $$ = $1;} | vertex STYLE_SEPARATOR idString - {$$ = [$1];yy.setClass($1,$3)} + {$$ = $1;yy.setClass($1,$3)} ; vertex: idString SQS text SQE diff --git a/packages/mermaid/src/diagrams/sankey/parser/energy.csv b/packages/mermaid/src/diagrams/sankey/parser/energy.csv new file mode 100644 index 000000000..d9a8fac9a --- /dev/null +++ b/packages/mermaid/src/diagrams/sankey/parser/energy.csv @@ -0,0 +1,99 @@ +%% There are leading and trailing spaces, do not crop + Agricultural 'waste',Bio-conversion,124.729 +%% line with a comment + +%% Normal line +Bio-conversion,Liquid,0.597 + +%% Line with unquoted sankey keyword +sankey,target,10 + +%% Quoted sankey keyword +"sankey",target,10 + +%% Another normal line +Bio-conversion,Losses,26.862 + +%% Line with integer amount +Bio-conversion,Solid,280 + +%% Some blank lines in the middle of CSV + + +%% Another normal line +Bio-conversion,Gas,81.144 + +%% Quoted line +"Biofuel imports",Liquid,35 + +%% Quoted line with escaped quotes inside +"""Biomass imports""",Solid,35 + +%% Lines containing commas inside +%% Quoted and unquoted values should be equal in terms of graph +"District heating","Heating and cooling, commercial",22.505 +District heating,"Heating and cooling, homes",46.184 + +%% A bunch of lines, normal CSV +Coal imports,Coal,11.606 +Coal reserves,Coal,63.965 +Coal,Solid,75.571 +District heating,Industry,10.639 +Electricity grid,Over generation / exports,104.453 +Electricity grid,Heating and cooling - homes,113.726 +Electricity grid,H2 conversion,27.14 +Electricity grid,Industry,342.165 +Electricity grid,Road transport,37.797 +Electricity grid,Agriculture,4.412 +Electricity grid,Heating and cooling - commercial,40.858 +Electricity grid,Losses,56.691 +Electricity grid,Rail transport,7.863 +Electricity grid,Lighting & appliances - commercial,90.008 +Electricity grid,Lighting & appliances - homes,93.494 +Gas imports,Ngas,40.719 +Gas reserves,Ngas,82.233 +Gas,Heating and cooling - commercial,0.129 +Gas,Losses,1.401 +Gas,Thermal generation,151.891 +Gas,Agriculture,2.096 +Gas,Industry,48.58 +Geothermal,Electricity grid,7.013 +H2 conversion,H2,20.897 +H2 conversion,Losses,6.242 +H2,Road transport,20.897 +Hydro,Electricity grid,6.995 +Liquid,Industry,121.066 +Liquid,International shipping,128.69 +Liquid,Road transport,135.835 +Liquid,Domestic aviation,14.458 +Liquid,International aviation,206.267 +Liquid,Agriculture,3.64 +Liquid,National navigation,33.218 +Liquid,Rail transport,4.413 +Marine algae,Bio-conversion,4.375 +Ngas,Gas,122.952 +Nuclear,Thermal generation,839.978 +Oil imports,Oil,504.287 +Oil reserves,Oil,107.703 +Oil,Liquid,611.99 +Other waste,Solid,56.587 +Other waste,Bio-conversion,77.81 +Pumped heat,Heating and cooling - homes,193.026 +Pumped heat,Heating and cooling - commercial,70.672 +Solar PV,Electricity grid,59.901 +Solar Thermal,Heating and cooling - homes,19.263 +Solar,Solar Thermal,19.263 +Solar,Solar PV,59.901 +Solid,Agriculture,0.882 +Solid,Thermal generation,400.12 +Solid,Industry,46.477 +Thermal generation,Electricity grid,525.531 +Thermal generation,Losses,787.129 +Thermal generation,District heating,79.329 +Tidal,Electricity grid,9.452 +UK land based bioenergy,Bio-conversion,182.01 +"""Wave""",Electricity grid,19.013 +"""Wind""",Electricity grid,289.366 + +%% lines at the end, do not remove + diff --git a/packages/mermaid/src/diagrams/sankey/parser/sankey.jison b/packages/mermaid/src/diagrams/sankey/parser/sankey.jison new file mode 100644 index 000000000..b11f8a87b --- /dev/null +++ b/packages/mermaid/src/diagrams/sankey/parser/sankey.jison @@ -0,0 +1,69 @@ +/** mermaid */ + +//--------------------------------------------------------- +// We support csv format as defined here: +// https://www.ietf.org/rfc/rfc4180.txt +// There are some minor changes for compliance with jison +// We also parse only 3 columns: source,target,value +// And allow blank lines for visual purposes +//--------------------------------------------------------- + +%lex + +%options case-insensitive +%options easy_keword_rules + +%x escaped_text +%x csv + +// as per section 6.1 of RFC 2234 [2] +COMMA \u002C +CR \u000D +LF \u000A +CRLF \u000D\u000A +ESCAPED_QUOTE \u0022 +DQUOTE \u0022 +TEXTDATA [\u0020-\u0021\u0023-\u002B\u002D-\u007E] + +%% + +"sankey-beta" { this.pushState('csv'); return 'SANKEY'; } +<> { return 'EOF' } // match end of file +({CRLF}|{LF}) { return 'NEWLINE' } +{COMMA} { return 'COMMA' } +{DQUOTE} { this.pushState('escaped_text'); return 'DQUOTE'; } +{TEXTDATA}* { return 'NON_ESCAPED_TEXT' } +{DQUOTE}(?!{DQUOTE}) {this.popState('escaped_text'); return 'DQUOTE'; } // unescaped DQUOTE closes string +({TEXTDATA}|{COMMA}|{CR}|{LF}|{DQUOTE}{DQUOTE})* { return 'ESCAPED_TEXT'; } + +/lex + +%start start + +%% // language grammar + +start: SANKEY NEWLINE csv opt_eof; + +csv: record csv_tail; +csv_tail: NEWLINE csv | ; +opt_eof: EOF | ; + +record + : field\[source] COMMA field\[target] COMMA field\[value] { + const source = yy.findOrCreateNode($source.trim().replaceAll('""', '"')); + const target = yy.findOrCreateNode($target.trim().replaceAll('""', '"')); + const value = parseFloat($value.trim()); + yy.addLink(source,target,value); + } // parse only 3 fields, this is not part of CSV standard + ; + +field + : escaped { $$=$escaped; } + | non_escaped { $$=$non_escaped; } + ; + +escaped: DQUOTE ESCAPED_TEXT DQUOTE { $$=$ESCAPED_TEXT; }; + +non_escaped: NON_ESCAPED_TEXT { $$=$NON_ESCAPED_TEXT; }; + + diff --git a/packages/mermaid/src/diagrams/sankey/parser/sankey.spec.ts b/packages/mermaid/src/diagrams/sankey/parser/sankey.spec.ts new file mode 100644 index 000000000..4517ca01f --- /dev/null +++ b/packages/mermaid/src/diagrams/sankey/parser/sankey.spec.ts @@ -0,0 +1,24 @@ +// @ts-ignore: jison doesn't export types +import sankey from './sankey.jison'; +import db from '../sankeyDB.js'; +import { cleanupComments } from '../../../diagram-api/comments.js'; +import { prepareTextForParsing } from '../sankeyUtils.js'; +import * as fs from 'fs'; +import * as path from 'path'; + +describe('Sankey diagram', function () { + describe('when parsing an info graph it', function () { + beforeEach(function () { + sankey.parser.yy = db; + sankey.parser.yy.clear(); + }); + + it('parses csv', async () => { + const csv = path.resolve(__dirname, './energy.csv'); + const data = fs.readFileSync(csv, 'utf8'); + const graphDefinition = prepareTextForParsing(cleanupComments('sankey-beta\n\n ' + data)); + + sankey.parser.parse(graphDefinition); + }); + }); +}); diff --git a/packages/mermaid/src/diagrams/sankey/sankeyDB.ts b/packages/mermaid/src/diagrams/sankey/sankeyDB.ts new file mode 100644 index 000000000..1921e1b85 --- /dev/null +++ b/packages/mermaid/src/diagrams/sankey/sankeyDB.ts @@ -0,0 +1,81 @@ +import * as configApi from '../../config.js'; +import common from '../common/common.js'; +import { + setAccTitle, + getAccTitle, + getAccDescription, + setAccDescription, + setDiagramTitle, + getDiagramTitle, + clear as commonClear, +} from '../../commonDb.js'; + +// Sankey diagram represented by nodes and links between those nodes +let links: SankeyLink[] = []; +// Array of nodes guarantees their order +let nodes: SankeyNode[] = []; +// We also have to track nodes uniqueness (by ID) +let nodesMap: Record = {}; + +const clear = (): void => { + links = []; + nodes = []; + nodesMap = {}; + commonClear(); +}; + +class SankeyLink { + constructor(public source: SankeyNode, public target: SankeyNode, public value: number = 0) {} +} + +/** + * @param source - Node where the link starts + * @param target - Node where the link ends + * @param value - number, float or integer, describes the amount to be passed + */ +const addLink = (source: SankeyNode, target: SankeyNode, value: number): void => { + links.push(new SankeyLink(source, target, value)); +}; + +class SankeyNode { + constructor(public ID: string) {} +} + +const findOrCreateNode = (ID: string): SankeyNode => { + ID = common.sanitizeText(ID, configApi.getConfig()); + + if (!nodesMap[ID]) { + nodesMap[ID] = new SankeyNode(ID); + nodes.push(nodesMap[ID]); + } + return nodesMap[ID]; +}; + +const getNodes = () => nodes; +const getLinks = () => links; + +const getGraph = () => ({ + nodes: nodes.map((node) => ({ id: node.ID })), + links: links.map((link) => ({ + source: link.source.ID, + target: link.target.ID, + value: link.value, + })), +}); + +export default { + nodesMap, + getConfig: () => configApi.getConfig().sankey, + getNodes, + getLinks, + getGraph, + addLink, + findOrCreateNode, + getAccTitle, + setAccTitle, + getAccDescription, + setAccDescription, + getDiagramTitle, + setDiagramTitle, + clear, +}; diff --git a/packages/mermaid/src/diagrams/sankey/sankeyDetector.ts b/packages/mermaid/src/diagrams/sankey/sankeyDetector.ts new file mode 100644 index 000000000..73c4d1428 --- /dev/null +++ b/packages/mermaid/src/diagrams/sankey/sankeyDetector.ts @@ -0,0 +1,20 @@ +import type { DiagramDetector, ExternalDiagramDefinition } from '../../diagram-api/types.js'; + +const id = 'sankey'; + +const detector: DiagramDetector = (txt) => { + return /^\s*sankey-beta/.test(txt); +}; + +const loader = async () => { + const { diagram } = await import('./sankeyDiagram.js'); + return { id, diagram }; +}; + +const plugin: ExternalDiagramDefinition = { + id, + detector, + loader, +}; + +export default plugin; diff --git a/packages/mermaid/src/diagrams/sankey/sankeyDiagram.ts b/packages/mermaid/src/diagrams/sankey/sankeyDiagram.ts new file mode 100644 index 000000000..d5b62122e --- /dev/null +++ b/packages/mermaid/src/diagrams/sankey/sankeyDiagram.ts @@ -0,0 +1,15 @@ +import { DiagramDefinition } from '../../diagram-api/types.js'; +// @ts-ignore: jison doesn't export types +import parser from './parser/sankey.jison'; +import db from './sankeyDB.js'; +import renderer from './sankeyRenderer.js'; +import { prepareTextForParsing } from './sankeyUtils.js'; + +const originalParse = parser.parse.bind(parser); +parser.parse = (text: string) => originalParse(prepareTextForParsing(text)); + +export const diagram: DiagramDefinition = { + parser, + db, + renderer, +}; diff --git a/packages/mermaid/src/diagrams/sankey/sankeyRenderer.ts b/packages/mermaid/src/diagrams/sankey/sankeyRenderer.ts new file mode 100644 index 000000000..e6e18f113 --- /dev/null +++ b/packages/mermaid/src/diagrams/sankey/sankeyRenderer.ts @@ -0,0 +1,205 @@ +import { Diagram } from '../../Diagram.js'; +import * as configApi from '../../config.js'; + +import { + select as d3select, + scaleOrdinal as d3scaleOrdinal, + schemeTableau10 as d3schemeTableau10, +} from 'd3'; + +import { + sankey as d3Sankey, + sankeyLinkHorizontal as d3SankeyLinkHorizontal, + sankeyLeft as d3SankeyLeft, + sankeyRight as d3SankeyRight, + sankeyCenter as d3SankeyCenter, + sankeyJustify as d3SankeyJustify, + SankeyNode as d3SankeyNode, +} from 'd3-sankey'; +import { configureSvgSize } from '../../setupGraphViewbox.js'; +import { Uid } from '../../rendering-util/uid.js'; +import { SankeyLinkColor, SankeyNodeAlignment } from '../../config.type.js'; + +// Map config options to alignment functions +const alignmentsMap: Record< + SankeyNodeAlignment, + (node: d3SankeyNode, n: number) => number +> = { + [SankeyNodeAlignment.left]: d3SankeyLeft, + [SankeyNodeAlignment.right]: d3SankeyRight, + [SankeyNodeAlignment.center]: d3SankeyCenter, + [SankeyNodeAlignment.justify]: d3SankeyJustify, +}; + +/** + * Draws Sankey diagram. + * + * @param text - The text of the diagram + * @param id - The id of the diagram which will be used as a DOM element id¨ + * @param _version - Mermaid version from package.json + * @param diagObj - A standard diagram containing the db and the text and type etc of the diagram + */ +export const draw = function (text: string, id: string, _version: string, diagObj: Diagram): void { + // Get Sankey config + const { securityLevel, sankey: conf } = configApi.getConfig(); + const defaultSankeyConfig = configApi!.defaultConfig!.sankey!; + + // TODO: + // This code repeats for every diagram + // Figure out what is happening there, probably it should be separated + // The main thing is svg object that is a d3 wrapper for svg operations + // + let sandboxElement: any; + if (securityLevel === 'sandbox') { + sandboxElement = d3select('#i' + id); + } + const root = + securityLevel === 'sandbox' + ? d3select(sandboxElement.nodes()[0].contentDocument.body) + : d3select('body'); + // @ts-ignore TODO root.select is not callable + const svg = securityLevel === 'sandbox' ? root.select(`[id="${id}"]`) : d3select(`[id="${id}"]`); + + // Establish svg dimensions and get width and height + // + const width = conf?.width || defaultSankeyConfig.width!; + const height = conf?.height || defaultSankeyConfig.width!; + const useMaxWidth = conf?.useMaxWidth || defaultSankeyConfig.useMaxWidth!; + const nodeAlignment = conf?.nodeAlignment || defaultSankeyConfig.nodeAlignment!; + + // FIX: using max width prevents height from being set, is it intended? + // to add height directly one can use `svg.attr('height', height)` + // + // @ts-ignore TODO: svg type vs selection mismatch + configureSvgSize(svg, height, width, useMaxWidth); + + // Prepare data for construction based on diagObj.db + // This must be a mutable object with `nodes` and `links` properties: + // + // { + // "nodes": [ { "id": "Alice" }, { "id": "Bob" }, { "id": "Carol" } ], + // "links": [ { "source": "Alice", "target": "Bob", "value": 23 }, { "source": "Bob", "target": "Carol", "value": 43 } ] + // } + // + // @ts-ignore TODO: db should be coerced to sankey DB type + const graph = diagObj.db.getGraph(); + + // Get alignment function + const nodeAlign = alignmentsMap[nodeAlignment]; + + // Construct and configure a Sankey generator + // That will be a function that calculates nodes and links dimensions + // + const nodeWidth = 10; + const sankey = d3Sankey() + .nodeId((d: any) => d.id) // we use 'id' property to identify node + .nodeWidth(nodeWidth) + .nodePadding(10) + .nodeAlign(nodeAlign) + .extent([ + [0, 0], + [width, height], + ]); + + // Compute the Sankey layout: calculate nodes and links positions + // Our `graph` object will be mutated by this and enriched with other properties + // + sankey(graph); + + // Get color scheme for the graph + const colorScheme = d3scaleOrdinal(d3schemeTableau10); + + // Create rectangles for nodes + svg + .append('g') + .attr('class', 'nodes') + .selectAll('.node') + .data(graph.nodes) + .join('g') + .attr('class', 'node') + .attr('id', (d: any) => (d.uid = Uid.next('node-')).id) + .attr('transform', function (d: any) { + return 'translate(' + d.x0 + ',' + d.y0 + ')'; + }) + .attr('x', (d: any) => d.x0) + .attr('y', (d: any) => d.y0) + .append('rect') + .attr('height', (d: any) => { + return d.y1 - d.y0; + }) + .attr('width', (d: any) => d.x1 - d.x0) + .attr('fill', (d: any) => colorScheme(d.id)); + + // Create labels for nodes + svg + .append('g') + .attr('class', 'node-labels') + .attr('font-family', 'sans-serif') + .attr('font-size', 14) + .selectAll('text') + .data(graph.nodes) + .join('text') + .attr('x', (d: any) => (d.x0 < width / 2 ? d.x1 + 6 : d.x0 - 6)) + .attr('y', (d: any) => (d.y1 + d.y0) / 2) + .attr('dy', '0.35em') + .attr('text-anchor', (d: any) => (d.x0 < width / 2 ? 'start' : 'end')) + .text((d: any) => d.id); + + // Creates the paths that represent the links. + const link = svg + .append('g') + .attr('class', 'links') + .attr('fill', 'none') + .attr('stroke-opacity', 0.5) + .selectAll('.link') + .data(graph.links) + .join('g') + .attr('class', 'link') + .style('mix-blend-mode', 'multiply'); + + const linkColor = conf?.linkColor || SankeyLinkColor.gradient; + + if (linkColor === SankeyLinkColor.gradient) { + const gradient = link + .append('linearGradient') + .attr('id', (d: any) => (d.uid = Uid.next('linearGradient-')).id) + .attr('gradientUnits', 'userSpaceOnUse') + .attr('x1', (d: any) => d.source.x1) + .attr('x2', (d: any) => d.target.x0); + + gradient + .append('stop') + .attr('offset', '0%') + .attr('stop-color', (d: any) => colorScheme(d.source.id)); + + gradient + .append('stop') + .attr('offset', '100%') + .attr('stop-color', (d: any) => colorScheme(d.target.id)); + } + + let coloring: any; + switch (linkColor) { + case SankeyLinkColor.gradient: + coloring = (d: any) => d.uid; + break; + case SankeyLinkColor.source: + coloring = (d: any) => colorScheme(d.source.id); + break; + case SankeyLinkColor.target: + coloring = (d: any) => colorScheme(d.target.id); + break; + default: + coloring = linkColor; + } + + link + .append('path') + .attr('d', d3SankeyLinkHorizontal()) + .attr('stroke', coloring) + .attr('stroke-width', (d: any) => Math.max(1, d.width)); +}; + +export default { + draw, +}; diff --git a/packages/mermaid/src/diagrams/sankey/sankeyUtils.ts b/packages/mermaid/src/diagrams/sankey/sankeyUtils.ts new file mode 100644 index 000000000..45ecf21dd --- /dev/null +++ b/packages/mermaid/src/diagrams/sankey/sankeyUtils.ts @@ -0,0 +1,8 @@ +export const prepareTextForParsing = (text: string): string => { + const textToParse = text + .replaceAll(/^[^\S\n\r]+|[^\S\n\r]+$/g, '') // remove all trailing spaces for each row + .replaceAll(/([\n\r])+/g, '\n') // remove empty lines duplicated + .trim(); + + return textToParse; +}; diff --git a/packages/mermaid/src/diagrams/state/stateRenderer-v2.js b/packages/mermaid/src/diagrams/state/stateRenderer-v2.js index 20ae0d112..592cb43cc 100644 --- a/packages/mermaid/src/diagrams/state/stateRenderer-v2.js +++ b/packages/mermaid/src/diagrams/state/stateRenderer-v2.js @@ -358,7 +358,7 @@ const setupDoc = (g, parentParsedItem, doc, diagramStates, diagramDb, altFlag) = * Look through all of the documents (docs) in the parsedItems * Because is a _document_ direction, the default direction is not necessarily the same as the overall default _diagram_ direction. * @param {object[]} parsedItem - the parsed statement item to look through - * @param [defaultDir=DEFAULT_NESTED_DOC_DIR] - the direction to use if none is found + * @param [defaultDir] - the direction to use if none is found * @returns {string} */ const getDir = (parsedItem, defaultDir = DEFAULT_NESTED_DOC_DIR) => { diff --git a/packages/mermaid/src/docs/.vitepress/config.ts b/packages/mermaid/src/docs/.vitepress/config.ts index 8fceb810b..2b2914ca3 100644 --- a/packages/mermaid/src/docs/.vitepress/config.ts +++ b/packages/mermaid/src/docs/.vitepress/config.ts @@ -138,6 +138,7 @@ function sidebarSyntax() { { text: 'Mindmaps 🔥', link: '/syntax/mindmap' }, { text: 'Timeline 🔥', link: '/syntax/timeline' }, { text: 'Zenuml 🔥', link: '/syntax/zenuml' }, + { text: 'Sankey 🔥', link: '/syntax/sankey' }, { text: 'Other Examples', link: '/syntax/examples' }, ], }, diff --git a/packages/mermaid/src/docs/.vitepress/scripts/fetch-avatars.ts b/packages/mermaid/src/docs/.vitepress/scripts/fetch-avatars.ts index bbea31bc1..47f371fee 100644 --- a/packages/mermaid/src/docs/.vitepress/scripts/fetch-avatars.ts +++ b/packages/mermaid/src/docs/.vitepress/scripts/fetch-avatars.ts @@ -18,7 +18,7 @@ async function download(url: string, fileName: URL) { const image = await fetch(url); await writeFile(fileName, Buffer.from(await image.arrayBuffer())); } catch (error) { - console.error(error); + console.error('failed to load', url, error); } } @@ -26,10 +26,13 @@ async function fetchAvatars() { await mkdir(fileURLToPath(new URL(getAvatarPath('none'))).replace('none.png', ''), { recursive: true, }); + contributors = JSON.parse(await readFile(pathContributors, { encoding: 'utf-8' })); - for (const name of contributors) { - await download(`https://github.com/${name}.png?size=100`, getAvatarPath(name)); - } + let avatars = contributors.map((name) => { + download(`https://github.com/${name}.png?size=100`, getAvatarPath(name)); + }); + + await Promise.all(avatars); } fetchAvatars(); diff --git a/packages/mermaid/src/docs/.vitepress/scripts/fetch-contributors.ts b/packages/mermaid/src/docs/.vitepress/scripts/fetch-contributors.ts index fd5409d0f..a810fc30f 100644 --- a/packages/mermaid/src/docs/.vitepress/scripts/fetch-contributors.ts +++ b/packages/mermaid/src/docs/.vitepress/scripts/fetch-contributors.ts @@ -10,23 +10,27 @@ interface Contributor { async function fetchContributors() { const collaborators: string[] = []; - let page = 1; - let data: Contributor[] = []; - do { - const response = await fetch( - `https://api.github.com/repos/mermaid-js/mermaid/contributors?per_page=100&page=${page}`, - { - method: 'GET', - headers: { - 'content-type': 'application/json', - }, - } - ); - data = await response.json(); - collaborators.push(...data.map((i) => i.login)); - console.log(`Fetched page ${page}`); - page++; - } while (data.length === 100); + try { + let page = 1; + let data: Contributor[] = []; + do { + const response = await fetch( + `https://api.github.com/repos/mermaid-js/mermaid/contributors?per_page=100&page=${page}`, + { + method: 'GET', + headers: { + 'content-type': 'application/json', + }, + } + ); + data = await response.json(); + collaborators.push(...data.map((i) => i.login)); + console.log(`Fetched page ${page}`); + page++; + } while (data.length === 100); + } catch (e) { + /* contributors fetching failure must not hinder docs development */ + } return collaborators.filter((name) => !name.includes('[bot]')); } diff --git a/packages/mermaid/src/docs/config/theming.md b/packages/mermaid/src/docs/config/theming.md index 0e4571d15..0e0853283 100644 --- a/packages/mermaid/src/docs/config/theming.md +++ b/packages/mermaid/src/docs/config/theming.md @@ -55,9 +55,9 @@ To make a custom theme, modify `themeVariables` via `init`. You will need to use the [base](#available-themes) theme as it is the only modifiable theme. -| Parameter | Description | Type | Properties | -| -------------- | ------------------------------------ | ------ | --------------------------------------------------------------------------------------------------- | -| themeVariables | Modifiable with the `init` directive | Object | `primaryColor`, `primaryTextColor`, `lineColor` ([see full list](#theme-variables-reference-table)) | +| Parameter | Description | Type | Properties | +| -------------- | ------------------------------------ | ------ | ----------------------------------------------------------------------------------- | +| themeVariables | Modifiable with the `init` directive | Object | `primaryColor`, `primaryTextColor`, `lineColor` ([see full list](#theme-variables)) | Example of modifying `themeVariables` using the `init` directive: diff --git a/packages/mermaid/src/docs/ecosystem/integrations.md b/packages/mermaid/src/docs/ecosystem/integrations.md index 336ae22f5..e64cf1a13 100644 --- a/packages/mermaid/src/docs/ecosystem/integrations.md +++ b/packages/mermaid/src/docs/ecosystem/integrations.md @@ -155,6 +155,8 @@ They also serve as proof of concept, for the variety of things that can be built - [Nano Mermaid](https://github.com/Yash-Singh1/nano-mermaid) - [CKEditor](https://github.com/ckeditor/ckeditor5) - [CKEditor 5 Mermaid plugin](https://github.com/ckeditor/ckeditor5-mermaid) +- [Standard Notes](https://standardnotes.com/) + - [sn-mermaid](https://github.com/nienow/sn-mermaid) ## Document Generation diff --git a/packages/mermaid/src/docs/news/announcements.md b/packages/mermaid/src/docs/news/announcements.md index b752d3d57..83dafaab6 100644 --- a/packages/mermaid/src/docs/news/announcements.md +++ b/packages/mermaid/src/docs/news/announcements.md @@ -1,7 +1,7 @@ # Announcements -## [subhash-halder contributed quadrant charts so you can show your manager what to select - just like the strategy consultants at BCG do](https://www.mermaidchart.com/blog/posts/subhash-halder-contributed-quadrant-charts-so-you-can-show-your-manager-what-to-select-just-like-the-strategy-consultants-at-bcg-do/) +## [Sequence diagrams, the only good thing UML brought to software development](https://www.mermaidchart.com/blog/posts/sequence-diagrams-the-good-thing-uml-brought-to-software-development/) -8 June 2023 · 7 mins +15 June 2023 · 12 mins -A quadrant chart is a useful diagram that helps users visualize data and identify patterns in a data set. +Sequence diagrams really shine when you’re documenting different parts of a system and the various ways these parts interact with each other. diff --git a/packages/mermaid/src/docs/news/blog.md b/packages/mermaid/src/docs/news/blog.md index e62a327b4..61d53656d 100644 --- a/packages/mermaid/src/docs/news/blog.md +++ b/packages/mermaid/src/docs/news/blog.md @@ -1,5 +1,11 @@ # Blog +## [Sequence diagrams, the only good thing UML brought to software development](https://www.mermaidchart.com/blog/posts/sequence-diagrams-the-good-thing-uml-brought-to-software-development/) + +15 June 2023 · 12 mins + +Sequence diagrams really shine when you’re documenting different parts of a system and the various ways these parts interact with each other. + ## [subhash-halder contributed quadrant charts so you can show your manager what to select - just like the strategy consultants at BCG do](https://www.mermaidchart.com/blog/posts/subhash-halder-contributed-quadrant-charts-so-you-can-show-your-manager-what-to-select-just-like-the-strategy-consultants-at-bcg-do/) 8 June 2023 · 7 mins diff --git a/packages/mermaid/src/docs/syntax/gantt.md b/packages/mermaid/src/docs/syntax/gantt.md index cecaf52cb..710b39e52 100644 --- a/packages/mermaid/src/docs/syntax/gantt.md +++ b/packages/mermaid/src/docs/syntax/gantt.md @@ -19,13 +19,13 @@ Mermaid can render Gantt diagrams as SVG, PNG or a MarkDown link that can be pas ```mermaid-example gantt title A Gantt Diagram - dateFormat YYYY-MM-DD + dateFormat YYYY-MM-DD section Section - A task :a1, 2014-01-01, 30d - Another task :after a1 , 20d + A task :a1, 2014-01-01, 30d + Another task :after a1, 20d section Another - Task in sec :2014-01-12 , 12d - another task : 24d + Task in Another :2014-01-12, 12d + another task :24d ``` ## Syntax @@ -66,10 +66,10 @@ gantt It is possible to set multiple dependencies separated by space: ```mermaid-example - gantt - apple :a, 2017-07-20, 1w - banana :crit, b, 2017-07-23, 1d - cherry :active, c, after b a, 1d +gantt + apple :a, 2017-07-20, 1w + banana :crit, b, 2017-07-23, 1d + cherry :active, c, after b a, 1d ``` ### Title @@ -88,12 +88,12 @@ You can add milestones to the diagrams. Milestones differ from tasks as they rep ```mermaid-example gantt -dateFormat HH:mm -axisFormat %H:%M -Initial milestone : milestone, m1, 17:49,2min -taska2 : 10min -taska3 : 5min -Final milestone : milestone, m2, 18:14, 2min + dateFormat HH:mm + axisFormat %H:%M + Initial milestone : milestone, m1, 17:49, 2m + Task A : 10m + Task B : 5m + Final milestone : milestone, m2, 18:08, 4m ``` ## Setting dates @@ -214,15 +214,14 @@ Comments can be entered within a gantt chart, which will be ignored by the parse ```mermaid gantt title A Gantt Diagram - %% this is a comment - dateFormat YYYY-MM-DD + %% This is a comment + dateFormat YYYY-MM-DD section Section - A task :a1, 2014-01-01, 30d - Another task :after a1 , 20d + A task :a1, 2014-01-01, 30d + Another task :after a1, 20d section Another - Task in sec :2014-01-12 , 12d - another task : 24d - + Task in Another :2014-01-12, 12d + another task :24d ``` ## Styling @@ -350,7 +349,7 @@ Beginner's tip—a full example using interactive links in an html context: dateFormat YYYY-MM-DD section Clickable - Visit mermaidjs :active, cl1, 2014-01-07, 3d + Visit mermaidjs :active, cl1, 2014-01-07, 3d Print arguments :cl2, after cl1, 3d Print task :cl3, after cl2, 3d diff --git a/packages/mermaid/src/docs/syntax/sankey.md b/packages/mermaid/src/docs/syntax/sankey.md new file mode 100644 index 000000000..32f3663b9 --- /dev/null +++ b/packages/mermaid/src/docs/syntax/sankey.md @@ -0,0 +1,292 @@ +# Sankey diagrams + +> A sankey diagram is a visualization used to depict a flow from one set of values to another. + +::: warning +This is an experimental diagram. Its syntax are very close to plain CSV, but it is to be extended in the nearest future. +::: + +The things being connected are called nodes and the connections are called links. + +## Example + +This example taken from [observable](https://observablehq.com/@d3/sankey/2?collection=@d3/d3-sankey). It may be rendered a little bit differently, though, in terms of size and colors. + +```mermaid +sankey-beta + +Agricultural 'waste',Bio-conversion,124.729 +Bio-conversion,Liquid,0.597 +Bio-conversion,Losses,26.862 +Bio-conversion,Solid,280.322 +Bio-conversion,Gas,81.144 +Biofuel imports,Liquid,35 +Biomass imports,Solid,35 +Coal imports,Coal,11.606 +Coal reserves,Coal,63.965 +Coal,Solid,75.571 +District heating,Industry,10.639 +District heating,Heating and cooling - commercial,22.505 +District heating,Heating and cooling - homes,46.184 +Electricity grid,Over generation / exports,104.453 +Electricity grid,Heating and cooling - homes,113.726 +Electricity grid,H2 conversion,27.14 +Electricity grid,Industry,342.165 +Electricity grid,Road transport,37.797 +Electricity grid,Agriculture,4.412 +Electricity grid,Heating and cooling - commercial,40.858 +Electricity grid,Losses,56.691 +Electricity grid,Rail transport,7.863 +Electricity grid,Lighting & appliances - commercial,90.008 +Electricity grid,Lighting & appliances - homes,93.494 +Gas imports,Ngas,40.719 +Gas reserves,Ngas,82.233 +Gas,Heating and cooling - commercial,0.129 +Gas,Losses,1.401 +Gas,Thermal generation,151.891 +Gas,Agriculture,2.096 +Gas,Industry,48.58 +Geothermal,Electricity grid,7.013 +H2 conversion,H2,20.897 +H2 conversion,Losses,6.242 +H2,Road transport,20.897 +Hydro,Electricity grid,6.995 +Liquid,Industry,121.066 +Liquid,International shipping,128.69 +Liquid,Road transport,135.835 +Liquid,Domestic aviation,14.458 +Liquid,International aviation,206.267 +Liquid,Agriculture,3.64 +Liquid,National navigation,33.218 +Liquid,Rail transport,4.413 +Marine algae,Bio-conversion,4.375 +Ngas,Gas,122.952 +Nuclear,Thermal generation,839.978 +Oil imports,Oil,504.287 +Oil reserves,Oil,107.703 +Oil,Liquid,611.99 +Other waste,Solid,56.587 +Other waste,Bio-conversion,77.81 +Pumped heat,Heating and cooling - homes,193.026 +Pumped heat,Heating and cooling - commercial,70.672 +Solar PV,Electricity grid,59.901 +Solar Thermal,Heating and cooling - homes,19.263 +Solar,Solar Thermal,19.263 +Solar,Solar PV,59.901 +Solid,Agriculture,0.882 +Solid,Thermal generation,400.12 +Solid,Industry,46.477 +Thermal generation,Electricity grid,525.531 +Thermal generation,Losses,787.129 +Thermal generation,District heating,79.329 +Tidal,Electricity grid,9.452 +UK land based bioenergy,Bio-conversion,182.01 +Wave,Electricity grid,19.013 +Wind,Electricity grid,289.366 +``` + +::: details + +```mermaid-example +sankey-beta + +Agricultural 'waste',Bio-conversion,124.729 +Bio-conversion,Liquid,0.597 +Bio-conversion,Losses,26.862 +Bio-conversion,Solid,280.322 +Bio-conversion,Gas,81.144 +Biofuel imports,Liquid,35 +Biomass imports,Solid,35 +Coal imports,Coal,11.606 +Coal reserves,Coal,63.965 +Coal,Solid,75.571 +District heating,Industry,10.639 +District heating,Heating and cooling - commercial,22.505 +District heating,Heating and cooling - homes,46.184 +Electricity grid,Over generation / exports,104.453 +Electricity grid,Heating and cooling - homes,113.726 +Electricity grid,H2 conversion,27.14 +Electricity grid,Industry,342.165 +Electricity grid,Road transport,37.797 +Electricity grid,Agriculture,4.412 +Electricity grid,Heating and cooling - commercial,40.858 +Electricity grid,Losses,56.691 +Electricity grid,Rail transport,7.863 +Electricity grid,Lighting & appliances - commercial,90.008 +Electricity grid,Lighting & appliances - homes,93.494 +Gas imports,Ngas,40.719 +Gas reserves,Ngas,82.233 +Gas,Heating and cooling - commercial,0.129 +Gas,Losses,1.401 +Gas,Thermal generation,151.891 +Gas,Agriculture,2.096 +Gas,Industry,48.58 +Geothermal,Electricity grid,7.013 +H2 conversion,H2,20.897 +H2 conversion,Losses,6.242 +H2,Road transport,20.897 +Hydro,Electricity grid,6.995 +Liquid,Industry,121.066 +Liquid,International shipping,128.69 +Liquid,Road transport,135.835 +Liquid,Domestic aviation,14.458 +Liquid,International aviation,206.267 +Liquid,Agriculture,3.64 +Liquid,National navigation,33.218 +Liquid,Rail transport,4.413 +Marine algae,Bio-conversion,4.375 +Ngas,Gas,122.952 +Nuclear,Thermal generation,839.978 +Oil imports,Oil,504.287 +Oil reserves,Oil,107.703 +Oil,Liquid,611.99 +Other waste,Solid,56.587 +Other waste,Bio-conversion,77.81 +Pumped heat,Heating and cooling - homes,193.026 +Pumped heat,Heating and cooling - commercial,70.672 +Solar PV,Electricity grid,59.901 +Solar Thermal,Heating and cooling - homes,19.263 +Solar,Solar Thermal,19.263 +Solar,Solar PV,59.901 +Solid,Agriculture,0.882 +Solid,Thermal generation,400.12 +Solid,Industry,46.477 +Thermal generation,Electricity grid,525.531 +Thermal generation,Losses,787.129 +Thermal generation,District heating,79.329 +Tidal,Electricity grid,9.452 +UK land based bioenergy,Bio-conversion,182.01 +Wave,Electricity grid,19.013 +Wind,Electricity grid,289.366 +``` + +::: + +## Syntax + +The idea behind syntax is that a user types `sankey-beta` keyword first, then pastes raw CSV below and get the result. + +It implements CSV standard as [described here](https://www.ietf.org/rfc/rfc4180.txt) with subtle **differences**: + +- CSV must contain **3 columns only** +- It is **allowed** to have **empty lines** without comma separators for visual purposes + +### Basic + +It is implied that 3 columns inside CSV should represent `source`, `target` and `value` accordingly: + +```mermaid-example +sankey-beta + +%% source,target,value +Electricity grid,Over generation / exports,104.453 +Electricity grid,Heating and cooling - homes,113.726 +Electricity grid,H2 conversion,27.14 +``` + +```mermaid +sankey-beta + +%% source,target,value +Electricity grid,Over generation / exports,104.453 +Electricity grid,Heating and cooling - homes,113.726 +Electricity grid,H2 conversion,27.14 +``` + +### Empty Lines + +CSV does not support empty lines without comma delimeters by default. But you can add them if needed: + +```mermaid-example +sankey-beta + +Bio-conversion,Losses,26.862 + +Bio-conversion,Solid,280.322 + +Bio-conversion,Gas,81.144 +``` + +```mermaid +sankey-beta + +Bio-conversion,Losses,26.862 + +Bio-conversion,Solid,280.322 + +Bio-conversion,Gas,81.144 +``` + +### Commas + +If you need to have a comma, wrap it in double quotes: + +```mermaid-example +sankey-beta + +Pumped heat,"Heating and cooling, homes",193.026 +Pumped heat,"Heating and cooling, commercial",70.672 +``` + +```mermaid +sankey-beta + +Pumped heat,"Heating and cooling, homes",193.026 +Pumped heat,"Heating and cooling, commercial",70.672 +``` + +### Double Quotes + +If you need to have double quote, put a pair of them inside quoted string: + +```mermaid-example +sankey-beta + +Pumped heat,"Heating and cooling, ""homes""",193.026 +Pumped heat,"Heating and cooling, ""commercial""",70.672 +``` + +```mermaid +sankey-beta + +Pumped heat,"Heating and cooling, ""homes""",193.026 +Pumped heat,"Heating and cooling, ""commercial""",70.672 +``` + +## Configuration + +You can customize link colors, node alignments and diagram dimensions. + +```html + +``` + +### Links Coloring + +You can adjust links' color by setting `linkColor` to one of those: + +- `source` - link will be of a source node color +- `target` - link will be of a target node color +- `gradient` - link color will be smoothly transient between source and target node colors +- hex code of color, like `#a1a1a1` + +### Node Alignment + +Graph layout can be changed by setting `nodeAlignment` to: + +- `justify` +- `center` +- `left` +- `right` diff --git a/packages/mermaid/src/docs/syntax/stateDiagram.md b/packages/mermaid/src/docs/syntax/stateDiagram.md index 248146993..f35796b13 100644 --- a/packages/mermaid/src/docs/syntax/stateDiagram.md +++ b/packages/mermaid/src/docs/syntax/stateDiagram.md @@ -304,7 +304,7 @@ where - 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` +- the fifth _property_ is `stroke` and its _value_ is `yellow` ### Apply classDef styles to states diff --git a/packages/mermaid/src/rendering-util/uid.ts b/packages/mermaid/src/rendering-util/uid.ts new file mode 100644 index 000000000..9f581faa7 --- /dev/null +++ b/packages/mermaid/src/rendering-util/uid.ts @@ -0,0 +1,18 @@ +export class Uid { + private static count = 0; + id: string; + href: string; + + public static next(name: string): Uid { + return new Uid(name + ++Uid.count); + } + + constructor(id: string) { + this.id = id; + this.href = `#${id}`; + } + + toString(): string { + return 'url(' + this.href + ')'; + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2f1ea7a43..9d28e9e50 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -62,7 +62,7 @@ importers: '@typescript-eslint/parser': specifier: ^5.59.0 version: 5.59.0(eslint@8.39.0)(typescript@5.1.3) - '@vitest/coverage-istanbul': + '@vitest/coverage-v8': specifier: ^0.32.2 version: 0.32.2(vitest@0.32.2) '@vitest/spy': @@ -194,6 +194,12 @@ importers: '@braintree/sanitize-url': specifier: ^6.0.2 version: 6.0.2 + '@types/d3-scale': + specifier: ^4.0.3 + version: 4.0.3 + '@types/d3-scale-chromatic': + specifier: ^3.0.0 + version: 3.0.0 cytoscape: specifier: ^3.23.0 version: 3.23.0 @@ -206,6 +212,9 @@ importers: d3: specifier: ^7.4.0 version: 7.8.2 + d3-sankey: + specifier: ^0.12.3 + version: 0.12.3 dagre-d3-es: specifier: 7.0.10 version: 7.0.10 @@ -213,8 +222,8 @@ importers: specifier: ^1.11.7 version: 1.11.7 dompurify: - specifier: 3.0.3 - version: 3.0.3 + specifier: 3.0.4 + version: 3.0.4 elkjs: specifier: ^0.8.2 version: 0.8.2 @@ -249,6 +258,9 @@ importers: '@types/d3': specifier: ^7.4.0 version: 7.4.0 + '@types/d3-sankey': + specifier: ^0.12.1 + version: 0.12.1 '@types/d3-selection': specifier: ^3.0.5 version: 3.0.5 @@ -3482,7 +3494,7 @@ packages: slash: 3.0.0 string-length: 4.0.2 strip-ansi: 6.0.1 - v8-to-istanbul: 9.0.1 + v8-to-istanbul: 9.1.0 transitivePeerDependencies: - supports-color dev: true @@ -3984,6 +3996,10 @@ packages: '@types/d3-color': 3.1.0 dev: true + /@types/d3-path@1.0.9: + resolution: {integrity: sha512-NaIeSIBiFgSC6IGUBjZWcscUJEq7vpVu7KthHN8eieTV9d9MqkSOZLH4chq1PmcKy06PNe3axLeKmRIyxJ+PZQ==} + dev: true + /@types/d3-path@3.0.0: resolution: {integrity: sha512-0g/A+mZXgFkQxN3HniRDbXMN79K3CdTpLsevj+PXiTcb2hVyvkZUBg37StmgCQkaD84cUJ4uaDAWq7UJOQy2Tg==} dev: true @@ -4000,20 +4016,30 @@ packages: resolution: {integrity: sha512-IIE6YTekGczpLYo/HehAy3JGF1ty7+usI97LqraNa8IiDur+L44d0VOjAvFQWJVdZOJHukUJw+ZdZBlgeUsHOQ==} dev: true - /@types/d3-scale-chromatic@3.0.0: - resolution: {integrity: sha512-dsoJGEIShosKVRBZB0Vo3C8nqSDqVGujJU6tPznsBJxNJNwMF8utmS83nvCBKQYPpjCzaaHcrf66iTRpZosLPw==} + /@types/d3-sankey@0.12.1: + resolution: {integrity: sha512-10X6l6lXB42udBNX9/fDN+kJuooifSMk7+x4U9815eobavldqis4wDdFQUQjMazh+qlzsUZsGzXKxfWFUVt+3w==} + dependencies: + '@types/d3-shape': 1.3.8 dev: true - /@types/d3-scale@4.0.2: - resolution: {integrity: sha512-Yk4htunhPAwN0XGlIwArRomOjdoBFXC3+kCxK2Ubg7I9shQlVSJy/pG/Ht5ASN+gdMIalpk8TJ5xV74jFsetLA==} + /@types/d3-scale-chromatic@3.0.0: + resolution: {integrity: sha512-dsoJGEIShosKVRBZB0Vo3C8nqSDqVGujJU6tPznsBJxNJNwMF8utmS83nvCBKQYPpjCzaaHcrf66iTRpZosLPw==} + + /@types/d3-scale@4.0.3: + resolution: {integrity: sha512-PATBiMCpvHJSMtZAMEhc2WyL+hnzarKzI6wAHYjhsonjWJYGq5BXTzQjv4l8m2jO183/4wZ90rKvSeT7o72xNQ==} dependencies: '@types/d3-time': 3.0.0 - dev: true /@types/d3-selection@3.0.5: resolution: {integrity: sha512-xCB0z3Hi8eFIqyja3vW8iV01+OHGYR2di/+e+AiOcXIOrY82lcvWW8Ke1DYE/EUVMsBl4Db9RppSBS3X1U6J0w==} dev: true + /@types/d3-shape@1.3.8: + resolution: {integrity: sha512-gqfnMz6Fd5H6GOLYixOZP/xlrMtJms9BaS+6oWxTKHNqPGZ93BkWWupQSCYm6YHqx6h9wjRupuJb90bun6ZaYg==} + dependencies: + '@types/d3-path': 1.0.9 + dev: true + /@types/d3-shape@3.1.0: resolution: {integrity: sha512-jYIYxFFA9vrJ8Hd4Se83YI6XF+gzDL1aC5DCsldai4XYYiVNdhtpGbA/GM6iyQ8ayhSp3a148LY34hy7A4TxZA==} dependencies: @@ -4026,7 +4052,6 @@ packages: /@types/d3-time@3.0.0: resolution: {integrity: sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==} - dev: true /@types/d3-timer@3.0.0: resolution: {integrity: sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g==} @@ -4069,7 +4094,7 @@ packages: '@types/d3-polygon': 3.0.0 '@types/d3-quadtree': 3.0.2 '@types/d3-random': 3.0.1 - '@types/d3-scale': 4.0.2 + '@types/d3-scale': 4.0.3 '@types/d3-scale-chromatic': 3.0.0 '@types/d3-selection': 3.0.5 '@types/d3-shape': 3.1.0 @@ -4879,17 +4904,22 @@ packages: vue: 3.3.4 dev: true - /@vitest/coverage-istanbul@0.32.2(vitest@0.32.2): - resolution: {integrity: sha512-B5VSvfzwTsDt9HjFmQ4sZ2tQHivmHJpAoG/BJwNNQeBtSCSdY1L6tfCjwZLo7ryOmZEDA3ck/DAmHCUZqa+MWA==} + /@vitest/coverage-v8@0.32.2(vitest@0.32.2): + resolution: {integrity: sha512-/+V3nB3fyeuuSeKxCfi6XmWjDIxpky7AWSkGVfaMjAk7di8igBwRsThLjultwIZdTDH1RAxpjmCXEfSqsMFZOA==} peerDependencies: vitest: '>=0.32.0 <1' dependencies: + '@ampproject/remapping': 2.2.1 + '@bcoe/v8-coverage': 0.2.3 istanbul-lib-coverage: 3.2.0 - istanbul-lib-instrument: 5.2.1 istanbul-lib-report: 3.0.0 istanbul-lib-source-maps: 4.0.1 istanbul-reports: 3.1.5 + magic-string: 0.30.0 + picocolors: 1.0.0 + std-env: 3.3.2 test-exclude: 6.0.0 + v8-to-istanbul: 9.1.0 vitest: 0.32.2(@vitest/ui@0.32.2)(jsdom@22.0.0) transitivePeerDependencies: - supports-color @@ -7139,6 +7169,12 @@ packages: lodash: 4.17.21 dev: false + /d3-array@2.12.1: + resolution: {integrity: sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==} + dependencies: + internmap: 1.0.1 + dev: false + /d3-array@3.2.0: resolution: {integrity: sha512-3yXFQo0oG3QCxbF06rMPFyGRMGJNS7NvsV1+2joOjbBE+9xvWQ8+GcMJAjRCzw06zQ3/arXeJgbPYcjUCuC+3g==} engines: {node: '>=12'} @@ -7256,6 +7292,10 @@ packages: d3-color: 3.1.0 dev: false + /d3-path@1.0.9: + resolution: {integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==} + dev: false + /d3-path@3.0.1: resolution: {integrity: sha512-gq6gZom9AFZby0YLduxT1qmrp4xpBA1YZr19OI717WIdKE2OM5ETq5qrHLb301IgxhLwcuxvGZVLeeWc/k1I6w==} engines: {node: '>=12'} @@ -7276,6 +7316,13 @@ packages: engines: {node: '>=12'} dev: false + /d3-sankey@0.12.3: + resolution: {integrity: sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==} + dependencies: + d3-array: 2.12.1 + d3-shape: 1.3.7 + dev: false + /d3-scale-chromatic@3.0.0: resolution: {integrity: sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==} engines: {node: '>=12'} @@ -7300,6 +7347,12 @@ packages: engines: {node: '>=12'} dev: false + /d3-shape@1.3.7: + resolution: {integrity: sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==} + dependencies: + d3-path: 1.0.9 + dev: false + /d3-shape@3.1.0: resolution: {integrity: sha512-tGDh1Muf8kWjEDT/LswZJ8WF85yDZLvVJpYU9Nq+8+yW1Z5enxrmXOhTArlkaElU+CTn0OTVNli+/i+HP45QEQ==} engines: {node: '>=12'} @@ -7704,8 +7757,8 @@ packages: domelementtype: 2.3.0 dev: true - /dompurify@3.0.3: - resolution: {integrity: sha512-axQ9zieHLnAnHh0sfAamKYiqXMJAVwu+LM/alQ7WDagoWessyWvMSFyW65CqF3owufNu8HBcE4cM2Vflu7YWcQ==} + /dompurify@3.0.4: + resolution: {integrity: sha512-ae0mA+Qiqp6C29pqZX3fQgK+F91+F7wobM/v8DRzDqJdZJELXiFUx4PP4pK/mzUS0xkiSEx3Ncd9gr69jg3YsQ==} dev: false /domutils@3.0.1: @@ -9535,6 +9588,10 @@ packages: side-channel: 1.0.4 dev: true + /internmap@1.0.1: + resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==} + dev: false + /internmap@2.0.3: resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} engines: {node: '>=12'} @@ -14700,8 +14757,8 @@ packages: /v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - /v8-to-istanbul@9.0.1: - resolution: {integrity: sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==} + /v8-to-istanbul@9.1.0: + resolution: {integrity: sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==} engines: {node: '>=10.12.0'} dependencies: '@jridgewell/trace-mapping': 0.3.17 diff --git a/run b/run index 6afe76eee..d9f669cab 100755 --- a/run +++ b/run @@ -1,40 +1,93 @@ #!/bin/bash RUN="docker-compose run --rm" + +ansi() { echo -e "\e[${1}m${*:2}\e[0m"; } +bold() { ansi 1 "$@"; } +# italic() { ansi 3 "$@"; } +underline() { ansi 4 "$@"; } +# strikethrough() { ansi 9 "$@"; } +# red() { ansi 31 "$@"; } + +name=$(basename $0) command=$1 args=${@:2} case $command in sh) -$RUN mermaid sh $args +$RUN mermaid sh -c "npx $args" ;; -install) -$RUN mermaid sh -c "npx pnpm install" +pnpm) +$RUN mermaid sh -c "npx pnpm $args" ;; -test) -$RUN mermaid sh -c "npx pnpm test" +dev) +$RUN --service-ports mermaid sh -c "npx pnpm run dev" ;; -lint) -$RUN mermaid sh -c "npx pnpm -w run lint:fix" +docs:dev) +$RUN --service-ports mermaid sh -c "cd packages/mermaid/src/docs && npx pnpm prefetch && npx vitepress --port 3333 --host" +;; + +cypress) +$RUN cypress $args ;; help) + +# Alignment of help message must be as it is, it will be nice looking when printed +usage=$( cat <