mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-28 11:49:37 +02:00
Compare commits
112 Commits
3659_heigh
...
v9.2.2
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9bb0ed2040 | ||
![]() |
2f9d6e0aff | ||
![]() |
ecc51d7cb8 | ||
![]() |
c309e3e3d6 | ||
![]() |
f52df3037f | ||
![]() |
649ab17806 | ||
![]() |
89da6ea31a | ||
![]() |
c7f7ff39ce | ||
![]() |
8e63a072e4 | ||
![]() |
b03ac389fa | ||
![]() |
d2511f6a8c | ||
![]() |
5b53cee673 | ||
![]() |
1b2dce99c9 | ||
![]() |
ba7f83019f | ||
![]() |
745abb81dc | ||
![]() |
e64e98fbfc | ||
![]() |
605f288554 | ||
![]() |
6d2552ea6e | ||
![]() |
20b4358c0e | ||
![]() |
7ca525622b | ||
![]() |
aab8f9273f | ||
![]() |
166dca4924 | ||
![]() |
75d276e19e | ||
![]() |
2243af1871 | ||
![]() |
41dbf0fa96 | ||
![]() |
1a5e7315c0 | ||
![]() |
da6bb9498a | ||
![]() |
1e417833f4 | ||
![]() |
97a7f1fd71 | ||
![]() |
c86d46eb8a | ||
![]() |
361dd6a96e | ||
![]() |
17adec38af | ||
![]() |
638b9d9aae | ||
![]() |
c8f6994895 | ||
![]() |
1117a80500 | ||
![]() |
5d83ec6fa2 | ||
![]() |
bab5937426 | ||
![]() |
099a26977a | ||
![]() |
0b834485a8 | ||
![]() |
41f21d4f72 | ||
![]() |
312e5f3d96 | ||
![]() |
6ef3e7f536 | ||
![]() |
4f5228aec4 | ||
![]() |
b9daa35558 | ||
![]() |
21304a9677 | ||
![]() |
3f6613ea9f | ||
![]() |
c8635c0b9b | ||
![]() |
e78ac9b92a | ||
![]() |
7a47fcfcbc | ||
![]() |
d4c19ffa59 | ||
![]() |
120940f9f4 | ||
![]() |
48b1f489fc | ||
![]() |
e62dd255bc | ||
![]() |
13110c4ed9 | ||
![]() |
327fcbf902 | ||
![]() |
81924f72c8 | ||
![]() |
5d048ce21e | ||
![]() |
1a0309fb87 | ||
![]() |
e793aae0ec | ||
![]() |
f41e34e61a | ||
![]() |
89d3d297b7 | ||
![]() |
41249fd064 | ||
![]() |
3a2669e634 | ||
![]() |
895a5eb78a | ||
![]() |
4601c90904 | ||
![]() |
8ad8d39fe4 | ||
![]() |
56a8068a7f | ||
![]() |
d17aa6ecdd | ||
![]() |
6f27363862 | ||
![]() |
5192608f7c | ||
![]() |
77f5e0d5f3 | ||
![]() |
4c311ea4b1 | ||
![]() |
bbb3712284 | ||
![]() |
1388e201e5 | ||
![]() |
125312c114 | ||
![]() |
ea314cd24a | ||
![]() |
8230c8f8b4 | ||
![]() |
d115fbc6da | ||
![]() |
2ae8bf2987 | ||
![]() |
e86d7894f5 | ||
![]() |
752a6b2cb0 | ||
![]() |
97a842e651 | ||
![]() |
c83e29c6e3 | ||
![]() |
a4af3704ba | ||
![]() |
775fb80c43 | ||
![]() |
aec1d80966 | ||
![]() |
58a53c0fa8 | ||
![]() |
b486177ca7 | ||
![]() |
957e64fe8a | ||
![]() |
7881d1e457 | ||
![]() |
16be835c9b | ||
![]() |
59cf085af5 | ||
![]() |
fc8db95597 | ||
![]() |
cc10e62ebd | ||
![]() |
bed95c77a9 | ||
![]() |
d348f2847c | ||
![]() |
4d46ea9801 | ||
![]() |
eec97d10af | ||
![]() |
ebef4a1416 | ||
![]() |
24605784f2 | ||
![]() |
3240f8ae56 | ||
![]() |
50f44c5cb0 | ||
![]() |
cc2f4f779a | ||
![]() |
00ce0fc034 | ||
![]() |
551b37f969 | ||
![]() |
bc5ef5fb7d | ||
![]() |
c20b8a0664 | ||
![]() |
23e590e09b | ||
![]() |
60e4585e20 | ||
![]() |
ba436cc37a | ||
![]() |
960ea450e9 | ||
![]() |
c5d859e52e |
43
.github/ISSUE_TEMPLATE/bug_report.md
vendored
43
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,43 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: 'Status: Triage, Type: Bug / Error'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Code Sample**
|
||||
If applicable, add the code sample or a link to the [live editor](https://mermaid.live).
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
69
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
69
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
name: Bug Report
|
||||
description: Create a report to help us improve
|
||||
labels:
|
||||
- 'Status: Triage'
|
||||
- 'Type: Bug / Error'
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |-
|
||||
## Security vulnerabilities
|
||||
Please refer our [Security Policy](https://github.com/mermaid-js/.github/blob/main/SECURITY.md) and report to keep vulnerabilities confidential so we can release fixes first.
|
||||
|
||||
## Before you submit...
|
||||
We like to help you, but in order to do that should you make a few things first:
|
||||
|
||||
- Use a clear and concise title
|
||||
- Fill out the text fields with as much detail as possible.
|
||||
- Never be shy to give us screenshots and/or code samples. It will help!
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Description
|
||||
description: Give a clear and concise description of what the bug is.
|
||||
placeholder: When I do ... does ... happen.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: Give a step-by-step example on how to reproduce the bug.
|
||||
placeholder: |-
|
||||
1. Do this
|
||||
2. Do that
|
||||
3. ...
|
||||
4. Bug!
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Screenshots
|
||||
description: If applicable, add screenshots to help explain your issue.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Code Sample
|
||||
description: |-
|
||||
If applicable, add the code sample or a link to the [Live Editor](https://mermaid.live).
|
||||
Any text pasted here will be rendered as a Code block.
|
||||
render: text
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Setup
|
||||
description: |-
|
||||
Please fill out the below info.
|
||||
Note that you only need to fill out one and not both sections.
|
||||
value: |-
|
||||
**Desktop**
|
||||
|
||||
- OS and Version: [Windows, Linux, Mac, ...]
|
||||
- Browser and Version: [Chrome, Edge, Firefox]
|
||||
|
||||
**Smartphone**
|
||||
|
||||
- Device: [Samsung, iPhone, ...]
|
||||
- OS and Version: [Android, iOS, ...]
|
||||
- Browser and Version: [Chrome, Safari, ...]
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional Context
|
||||
description: Anything else to add?
|
17
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
17
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: GitHub Discussions
|
||||
url: https://github.com/mermaid-js/mermaid/discussions
|
||||
about: Ask the Community questions or share your own graphs in our discussions.
|
||||
- name: Security Vulnerability
|
||||
url: https://github.com/mermaid-js/.github/blob/main/SECURITY.md
|
||||
about: Report security issues confidentially.
|
||||
- name: Slack
|
||||
url: https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE
|
||||
about: Join our Community on Slack for Help and a casual chat.
|
||||
- name: Documentation
|
||||
url: https://mermaid-js.github.io
|
||||
about: Read our documentation for all that Mermaid.js can offer.
|
||||
- name: Live Editor
|
||||
url: https://mermaid.live
|
||||
about: Try the live editor to preview graphs in no time.
|
42
.github/ISSUE_TEMPLATE/diagram_proposal.yml
vendored
Normal file
42
.github/ISSUE_TEMPLATE/diagram_proposal.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
name: Diagram Proposal
|
||||
description: Suggest a new Diagram Type to add to Mermaid.
|
||||
labels:
|
||||
- 'Status: Triage'
|
||||
- 'Type: Enhancement'
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |-
|
||||
## Before you submit...
|
||||
First of all, thank you for proposing a new Diagram to us.
|
||||
We are always happy about new ideas to improve Mermaid.js wherever possible.
|
||||
|
||||
To get the fastest and best response possible, make sure you do the following:
|
||||
|
||||
- Use a clear and concise title
|
||||
- Fill out the text fields with as much detail as possible.
|
||||
- Never be shy to give us screenshots and/or code samples. It will help!
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Proposal
|
||||
description: A clear and concise description of what should be added to Mermaid.js.
|
||||
placeholder: Mermaid.js should add ... because ...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Use Cases
|
||||
description: If applicable, give some use cases for where this diagram would be useful.
|
||||
placeholder: The Diagram could be used for ...
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Screenshots
|
||||
description: If applicable, add screenshots to show possible examples of how the diagram may look like.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Code Sample
|
||||
description: |-
|
||||
If applicable, add a code sample for how to implement this new diagram.
|
||||
The text will automatically be rendered as JavaScript code.
|
||||
render: javascript
|
19
.github/ISSUE_TEMPLATE/feature_request.md
vendored
19
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,19 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: 'Status: Triage, Type: Enhancement'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
16
.github/ISSUE_TEMPLATE/question.md
vendored
16
.github/ISSUE_TEMPLATE/question.md
vendored
@@ -1,16 +0,0 @@
|
||||
---
|
||||
name: Question
|
||||
about: Get some help from the community.
|
||||
title: ''
|
||||
labels: 'Help wanted!, Type: Other'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
## Help us help you!
|
||||
|
||||
You want an answer. Here are some ways to get it quicker:
|
||||
|
||||
- Use a clear and concise title.
|
||||
- Try to pose a clear and concise question.
|
||||
- Include as much, or as little, code as necessary.
|
||||
- Don't be shy to give us some screenshots, if it helps!
|
34
.github/ISSUE_TEMPLATE/syntax_proposal.yml
vendored
Normal file
34
.github/ISSUE_TEMPLATE/syntax_proposal.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
name: Syntax Proposal
|
||||
description: Suggest a new Syntax to add to Mermaid.js.
|
||||
labels:
|
||||
- 'Status: Triage'
|
||||
- 'Type: Enhancement'
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |-
|
||||
## Before you submit...
|
||||
First of all, thank you for proposing a new Syntax to us.
|
||||
We are always happy about new ideas to improve Mermaid.js wherever possible.
|
||||
|
||||
To get the fastest and best response possible, make sure you do the following:
|
||||
|
||||
- Use a clear and concise title
|
||||
- Fill out the text fields with as much detail as possible. Examples are always welcome.
|
||||
- Never be shy to give us screenshots and/or code samples. It will help!
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Proposal
|
||||
description: A clear and concise description of what Syntax should be added to Mermaid.js.
|
||||
placeholder: Mermaid.js should add ... because ...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Example
|
||||
description: If applicable, provide an example of the new Syntax.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Screenshots
|
||||
description: If applicable, add screenshots to show possible examples of how the theme may look like.
|
42
.github/ISSUE_TEMPLATE/theme_proposal.yml
vendored
Normal file
42
.github/ISSUE_TEMPLATE/theme_proposal.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
name: Theme Proposal
|
||||
description: Suggest a new theme to add to Mermaid.js.
|
||||
labels:
|
||||
- 'Status: Triage'
|
||||
- 'Type: Enhancement'
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |-
|
||||
## Before you submit...
|
||||
First of all, thank you for proposing a new Theme to us.
|
||||
We are always happy about new ideas to improve Mermaid.js wherever possible.
|
||||
|
||||
To get the fastest and best response possible, make sure you do the following:
|
||||
|
||||
- Use a clear and concise title
|
||||
- Fill out the text fields with as much detail as possible. Examples are always welcome!
|
||||
- Never be shy to give us screenshots and/or code samples. It will help!
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Proposal
|
||||
description: A clear and concise description of what theme should be added to Mermaid.js.
|
||||
placeholder: Mermaid.js should add ... because ...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Colors
|
||||
description: |-
|
||||
A detailed list of the different colour values to use.
|
||||
A list of currently used variable names can be found [here](https://mermaid-js.github.io/mermaid/#/theming?id=theme-variables-reference-table)
|
||||
placeholder: |-
|
||||
- background: #f4f4f4
|
||||
- primaryColor: #fff4dd
|
||||
- ...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Screenshots
|
||||
description: If applicable, add screenshots to show possible examples of how the theme may look like.
|
4
.github/workflows/docs.yml
vendored
4
.github/workflows/docs.yml
vendored
@@ -16,9 +16,9 @@ jobs:
|
||||
name: 'Docs: Spellcheck'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v2
|
||||
name: Check out the code
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v1
|
||||
name: Setup node
|
||||
with:
|
||||
node-version: '16'
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -32,3 +32,6 @@ cypress/snapshots/
|
||||
.eslintcache
|
||||
.tsbuildinfo
|
||||
tsconfig.tsbuildinfo
|
||||
knsv*.html
|
||||
|
||||
local*.html
|
||||
|
2
.npmrc
Normal file
2
.npmrc
Normal file
@@ -0,0 +1,2 @@
|
||||
auto-install-peers=true
|
||||
strict-peer-dependencies=false
|
@@ -6,6 +6,7 @@ import pkg from '../package.json' assert { type: 'json' };
|
||||
|
||||
const { dependencies } = pkg;
|
||||
const watch = process.argv.includes('--watch');
|
||||
const mermaidOnly = process.argv.includes('--mermaid');
|
||||
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
||||
|
||||
type OutputOptions = Exclude<
|
||||
@@ -22,23 +23,13 @@ const packageOptions = {
|
||||
'mermaid-mindmap': {
|
||||
name: 'mermaid-mindmap',
|
||||
packageName: 'mermaid-mindmap',
|
||||
file: 'diagram-definition.ts',
|
||||
},
|
||||
'mermaid-mindmap-detector': {
|
||||
name: 'mermaid-mindmap-detector',
|
||||
packageName: 'mermaid-mindmap',
|
||||
file: 'detector.ts',
|
||||
},
|
||||
'mermaid-example-diagram': {
|
||||
name: 'mermaid-example-diagram',
|
||||
packageName: 'mermaid-example-diagram',
|
||||
file: 'diagram-definition.ts',
|
||||
},
|
||||
'mermaid-example-diagram-detector': {
|
||||
name: 'mermaid-example-diagram-detector',
|
||||
packageName: 'mermaid-example-diagram',
|
||||
file: 'detector.ts',
|
||||
},
|
||||
// 'mermaid-example-diagram-detector': {
|
||||
// name: 'mermaid-example-diagram-detector',
|
||||
// packageName: 'mermaid-example-diagram',
|
||||
// file: 'detector.ts',
|
||||
// },
|
||||
};
|
||||
|
||||
interface BuildOptions {
|
||||
@@ -110,7 +101,7 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
|
||||
include: [
|
||||
'packages/mermaid-mindmap/src/**',
|
||||
'packages/mermaid/src/**',
|
||||
'packages/mermaid-example-diagram/src/**',
|
||||
// 'packages/mermaid-example-diagram/src/**',
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -129,14 +120,19 @@ const buildPackage = async (entryName: keyof typeof packageOptions) => {
|
||||
const main = async () => {
|
||||
const packageNames = Object.keys(packageOptions) as (keyof typeof packageOptions)[];
|
||||
for (const pkg of packageNames) {
|
||||
if (mermaidOnly && pkg !== 'mermaid') {
|
||||
continue;
|
||||
}
|
||||
await buildPackage(pkg);
|
||||
}
|
||||
};
|
||||
|
||||
if (watch) {
|
||||
build(getBuildConfig({ minify: false, watch, entryName: 'mermaid' }));
|
||||
build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-mindmap' }));
|
||||
build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-example-diagram' }));
|
||||
build(getBuildConfig({ minify: false, watch, core: true, entryName: 'mermaid' }));
|
||||
if (!mermaidOnly) {
|
||||
build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-mindmap' }));
|
||||
// build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-example-diagram' }));
|
||||
}
|
||||
} else {
|
||||
void main();
|
||||
}
|
||||
|
@@ -1,7 +1,15 @@
|
||||
import express from 'express';
|
||||
import express, { NextFunction, Request, Response } from 'express';
|
||||
import { createServer as createViteServer } from 'vite';
|
||||
// import { getBuildConfig } from './build';
|
||||
|
||||
const cors = (req: Request, res: Response, next: NextFunction) => {
|
||||
res.header('Access-Control-Allow-Origin', '*');
|
||||
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
|
||||
res.header('Access-Control-Allow-Headers', 'Content-Type');
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
async function createServer() {
|
||||
const app = express();
|
||||
|
||||
@@ -12,6 +20,7 @@ async function createServer() {
|
||||
appType: 'custom', // don't include Vite's default HTML handling middlewares
|
||||
});
|
||||
|
||||
app.use(cors);
|
||||
app.use(express.static('./packages/mermaid/dist'));
|
||||
app.use(express.static('./packages/mermaid-example-diagram/dist'));
|
||||
app.use(express.static('./packages/mermaid-mindmap/dist'));
|
||||
|
@@ -15,5 +15,5 @@ module.exports = defineConfig({
|
||||
// { deviceName: 'Pixel 2', screenOrientation: 'portrait' },
|
||||
],
|
||||
// set batch name to the configuration
|
||||
batchName: `Mermaid ${process.env.APPLI_BRANCH ?? "'no APPLI_BRANCH set'"}`,
|
||||
// batchName: `Mermaid ${process.env.APPLI_BRANCH ?? "'no APPLI_BRANCH set'"}`,
|
||||
});
|
||||
|
@@ -2,6 +2,8 @@ const utf8ToB64 = (str) => {
|
||||
return window.btoa(unescape(encodeURIComponent(str)));
|
||||
};
|
||||
|
||||
const batchId = 'mermid-batch' + new Date().getTime();
|
||||
|
||||
export const mermaidUrl = (graphStr, options, api) => {
|
||||
const obj = {
|
||||
code: graphStr,
|
||||
@@ -50,9 +52,12 @@ export const imgSnapshotTest = (graphStr, _options, api = false, validation) =>
|
||||
const name = (options.name || cy.state('runnable').fullTitle()).replace(/\s+/g, '-');
|
||||
|
||||
if (useAppli) {
|
||||
cy.log('Opening eyes ' + Cypress.spec.name + ' --- ' + name);
|
||||
cy.eyesOpen({
|
||||
appName: 'Mermaid',
|
||||
testName: name,
|
||||
batchName: Cypress.spec.name,
|
||||
batchId: batchId + Cypress.spec.name,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -64,7 +69,9 @@ export const imgSnapshotTest = (graphStr, _options, api = false, validation) =>
|
||||
// Default name to test title
|
||||
|
||||
if (useAppli) {
|
||||
cy.log('Check eyes' + Cypress.spec.name);
|
||||
cy.eyesCheckWindow('Click!');
|
||||
cy.log('Closing eyes: ' + Cypress.spec.name);
|
||||
cy.eyesClose();
|
||||
} else {
|
||||
cy.matchImageSnapshot(name);
|
||||
@@ -100,9 +107,12 @@ export const urlSnapshotTest = (url, _options, api = false, validation) => {
|
||||
const name = (options.name || cy.state('runnable').fullTitle()).replace(/\s+/g, '-');
|
||||
|
||||
if (useAppli) {
|
||||
cy.log('Opening eyes 2' + Cypress.spec.name);
|
||||
cy.eyesOpen({
|
||||
appName: 'Mermaid',
|
||||
testName: name,
|
||||
batchName: Cypress.spec.name,
|
||||
batchId: batchId + Cypress.spec.name,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -112,7 +122,9 @@ export const urlSnapshotTest = (url, _options, api = false, validation) => {
|
||||
// Default name to test title
|
||||
|
||||
if (useAppli) {
|
||||
cy.log('Check eyes 2' + Cypress.spec.name);
|
||||
cy.eyesCheckWindow('Click!');
|
||||
cy.log('Closing eyes 2' + Cypress.spec.name);
|
||||
cy.eyesClose();
|
||||
} else {
|
||||
cy.matchImageSnapshot(name);
|
||||
|
13
cypress/integration/other/external-diagrams.spec.js
Normal file
13
cypress/integration/other/external-diagrams.spec.js
Normal file
@@ -0,0 +1,13 @@
|
||||
describe('mermaid', () => {
|
||||
describe('registerDiagram', () => {
|
||||
it('should work on @mermaid-js/mermaid-mindmap and mermaid-example-diagram', () => {
|
||||
const url = 'http://localhost:9000/external-diagrams-mindmap.html';
|
||||
cy.visit(url);
|
||||
|
||||
cy.get('svg', {
|
||||
// may be a bit slower than normal, since vite might need to re-compile mermaid/mermaid-mindmap/mermaid-example-diagram
|
||||
timeout: 10000,
|
||||
}).matchImageSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
49
cypress/platform/external-diagrams-mindmap.html
Normal file
49
cypress/platform/external-diagrams-mindmap.html
Normal file
@@ -0,0 +1,49 @@
|
||||
<html>
|
||||
<body>
|
||||
<h1>Should correctly load a third-party diagram using registerDiagram</h1>
|
||||
<pre id="diagram" class="mermaid">
|
||||
mindmap
|
||||
root
|
||||
A
|
||||
B
|
||||
C
|
||||
D
|
||||
E
|
||||
A2
|
||||
B2
|
||||
C2
|
||||
D2
|
||||
E2
|
||||
child1((Circle))
|
||||
grandchild 1
|
||||
grandchild 2
|
||||
child2(Round rectangle)
|
||||
grandchild 3
|
||||
grandchild 4
|
||||
child3[Square]
|
||||
grandchild 5
|
||||
::icon(mdi mdi-fire)
|
||||
gc6((grand<br/>child 6))
|
||||
::icon(mdi mdi-fire)
|
||||
gc7((grand<br/>grand<br/>child 8))
|
||||
</pre>
|
||||
<!-- <pre id="diagram" class="mermaid2">
|
||||
example-diagram
|
||||
</pre> -->
|
||||
|
||||
<!-- <div id="cy"></div> -->
|
||||
<!-- <script src="http://localhost:9000/packages/mermaid-mindmap/dist/mermaid-mindmap-detector.js"></script> -->
|
||||
<!-- <script src="./mermaid-example-diagram-detector.js"></script> -->
|
||||
<!-- <script src="//cdn.jsdelivr.net/npm/mermaid@9.1.7/dist/mermaid.min.js"></script> -->
|
||||
<!-- <script type="module" src="./external-diagrams-mindmap.mjs" /> -->
|
||||
<script type="module">
|
||||
import mindmap from '../../packages/mermaid-mindmap/src/detector';
|
||||
// import example from '../../packages/mermaid-example-diagram/src/detector';
|
||||
import mermaid from '../../packages/mermaid/src/mermaid';
|
||||
|
||||
await mermaid.registerExternalDiagrams([mindmap]);
|
||||
await mermaid.initialize({ logLevel: 0 });
|
||||
await mermaid.initThrowsErrorsAsync();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@@ -56,24 +56,25 @@
|
||||
<body>
|
||||
<div>Security check</div>
|
||||
<pre id="diagram" class="mermaid">
|
||||
flowchart TD
|
||||
A --> B
|
||||
B --> C
|
||||
A --> C
|
||||
classDiagram
|
||||
direction LR
|
||||
class Student {
|
||||
-idCard : IdCard
|
||||
}
|
||||
class IdCard{
|
||||
-id : int
|
||||
-name : string
|
||||
}
|
||||
class Bike{
|
||||
-id : int
|
||||
-name : string
|
||||
}
|
||||
Student "1" --o "1" IdCard : carries
|
||||
Student "1" --o "1" Bike : rides
|
||||
</pre>
|
||||
<pre id="diagram" class="mermaid2">
|
||||
mindmap
|
||||
root
|
||||
A
|
||||
B
|
||||
C
|
||||
D
|
||||
E
|
||||
A2
|
||||
B2
|
||||
C2
|
||||
D2
|
||||
E2
|
||||
child1((Circle))
|
||||
grandchild 1
|
||||
grandchild 2
|
||||
@@ -87,14 +88,8 @@ mindmap
|
||||
::icon(mdi mdi-fire)
|
||||
gc7((grand<br/>grand<br/>child 8))
|
||||
</pre>
|
||||
<pre id="diagram" class="mermaid">
|
||||
gantt
|
||||
title Style today marker (vertical line should be 5px wide and half-transparent blue)
|
||||
dateFormat YYYY-MM-DD
|
||||
axisFormat %d
|
||||
todayMarker stroke-width:5px,stroke:#00f,opacity:0.5
|
||||
section Section1
|
||||
Today: 1, -1h
|
||||
<pre id="diagram" class="mermaid2">
|
||||
example-diagram
|
||||
</pre>
|
||||
|
||||
<!-- <div id="cy"></div> -->
|
||||
@@ -111,18 +106,13 @@ mindmap
|
||||
theme: 'forest',
|
||||
startOnLoad: true,
|
||||
logLevel: 0,
|
||||
flowchart: {
|
||||
useMaxWidth: false,
|
||||
htmlLabels: true,
|
||||
},
|
||||
gantt: {
|
||||
useMaxWidth: false,
|
||||
},
|
||||
useMaxWidth: false,
|
||||
// basePath: './packages/',
|
||||
// themeVariables: { darkMode: true },
|
||||
lazyLoadedDiagrams: [
|
||||
'./mermaid-mindmap-detector.esm.mjs',
|
||||
'./mermaid-example-diagram-detector.esm.mjs',
|
||||
],
|
||||
// lazyLoadedDiagrams: ['../../mermaid-mindmap/registry.ts'],
|
||||
});
|
||||
function callback() {
|
||||
alert('It worked');
|
||||
|
@@ -14,16 +14,14 @@
|
||||
mermaid.init({ startOnLoad: false });
|
||||
|
||||
mermaid.mermaidAPI.initialize({ securityLevel: 'strict' });
|
||||
(async () => {
|
||||
try {
|
||||
console.log('rendering');
|
||||
await mermaid.mermaidAPI.render('graphDiv', `>`);
|
||||
} catch (e) {}
|
||||
try {
|
||||
console.log('rendering');
|
||||
mermaid.mermaidAPI.render('graphDiv', `>`);
|
||||
} catch (e) {}
|
||||
|
||||
await mermaid.mermaidAPI.render('graphDiv', `graph LR\n a --> b`, (html) => {
|
||||
document.getElementById('graph').innerHTML = html;
|
||||
});
|
||||
})();
|
||||
mermaid.mermaidAPI.render('graphDiv', `graph LR\n a --> b`, (html) => {
|
||||
document.getElementById('graph').innerHTML = html;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@@ -19,10 +19,9 @@
|
||||
function rerender(text) {
|
||||
const graphText = `graph TD
|
||||
A[${text}] -->|Get money| B(Go shopping)`;
|
||||
mermaid.mermaidAPI.render('id', graphText).then((svg) => {
|
||||
console.log('\x1b[35m%s\x1b[0m', '>> graph', svg);
|
||||
document.getElementById('graph').innerHTML = svg;
|
||||
});
|
||||
const graph = mermaid.mermaidAPI.render('id', graphText);
|
||||
console.log('\x1b[35m%s\x1b[0m', '>> graph', graph);
|
||||
document.getElementById('graph').innerHTML = graph;
|
||||
}
|
||||
</script>
|
||||
<button id="rerender" onclick="rerender('Saturday')">Rerender</button>
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import mermaid2 from '../../packages/mermaid/src/mermaid';
|
||||
import mindmap from '../../packages/mermaid-mindmap/src/detector';
|
||||
|
||||
function b64ToUtf8(str) {
|
||||
return decodeURIComponent(escape(window.atob(str)));
|
||||
@@ -9,7 +10,7 @@ function b64ToUtf8(str) {
|
||||
* configuration for mermaid rendering and calls init for rendering the mermaid diagrams on the
|
||||
* page.
|
||||
*/
|
||||
const contentLoaded = function () {
|
||||
const contentLoaded = async function () {
|
||||
let pos = document.location.href.indexOf('?graph=');
|
||||
if (pos > 0) {
|
||||
pos = pos + 7;
|
||||
@@ -36,8 +37,7 @@ const contentLoaded = function () {
|
||||
document.getElementsByTagName('body')[0].appendChild(div);
|
||||
}
|
||||
|
||||
graphObj.mermaid.lazyLoadedDiagrams = ['/mermaid-mindmap-detector.esm.mjs'];
|
||||
|
||||
await mermaid2.registerExternalDiagrams([mindmap]);
|
||||
mermaid2.initialize(graphObj.mermaid);
|
||||
mermaid2.init();
|
||||
}
|
||||
|
@@ -264,6 +264,20 @@ flowchart LR
|
||||
A --- B
|
||||
```
|
||||
|
||||
### An invisisble link
|
||||
|
||||
This can be a usefull tool in some instances where you want to alter the default positining of a node.
|
||||
|
||||
```mermaid-example
|
||||
flowchart LR
|
||||
A ~~~ B
|
||||
```
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A ~~~ B
|
||||
```
|
||||
|
||||
### Text on links
|
||||
|
||||
```mermaid-example
|
||||
@@ -315,13 +329,13 @@ flowchart LR
|
||||
### Dotted link
|
||||
|
||||
```mermaid-example
|
||||
flowchart LR
|
||||
A-.->B
|
||||
flowchart LR;
|
||||
A-.->B;
|
||||
```
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A-.->B
|
||||
flowchart LR;
|
||||
A-.->B;
|
||||
```
|
||||
|
||||
### Dotted link with text
|
||||
@@ -866,13 +880,13 @@ A shorter form of adding a class is to attach the classname to the node using th
|
||||
```mermaid-example
|
||||
flowchart LR
|
||||
A:::someclass --> B
|
||||
classDef someclass fill:#f96
|
||||
classDef someclass fill:#f96;
|
||||
```
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A:::someclass --> B
|
||||
classDef someclass fill:#f96
|
||||
classDef someclass fill:#f96;
|
||||
```
|
||||
|
||||
### Css classes
|
||||
@@ -895,14 +909,14 @@ below:
|
||||
**Example definition**
|
||||
|
||||
```mermaid-example
|
||||
flowchart LR
|
||||
flowchart LR;
|
||||
A-->B[AAA<span>BBB</span>]
|
||||
B-->D
|
||||
class A cssClass
|
||||
```
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
flowchart LR;
|
||||
A-->B[AAA<span>BBB</span>]
|
||||
B-->D
|
||||
class A cssClass
|
||||
@@ -924,7 +938,7 @@ The icons are accessed via the syntax fa:#icon class name#.
|
||||
flowchart TD
|
||||
B["fab:fa-twitter for peace"]
|
||||
B-->C[fa:fa-ban forbidden]
|
||||
B-->D(fa:fa-spinner)
|
||||
B-->D(fa:fa-spinner);
|
||||
B-->E(A fa:fa-camera-retro perhaps?)
|
||||
```
|
||||
|
||||
@@ -932,7 +946,7 @@ flowchart TD
|
||||
flowchart TD
|
||||
B["fab:fa-twitter for peace"]
|
||||
B-->C[fa:fa-ban forbidden]
|
||||
B-->D(fa:fa-spinner)
|
||||
B-->D(fa:fa-spinner);
|
||||
B-->E(A fa:fa-camera-retro perhaps?)
|
||||
```
|
||||
|
||||
|
@@ -50,13 +50,36 @@
|
||||
<div id="app"></div>
|
||||
<script type="module">
|
||||
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@9/dist/mermaid.esm.min.mjs';
|
||||
import mindmap from 'https://cdn.jsdelivr.net/npm/@mermaid-js/mermaid-mindmap@9/dist/mermaid-mindmap.esm.mjs';
|
||||
await mermaid.registerExternalDiagrams([mindmap]);
|
||||
|
||||
window.mermaid = mermaid;
|
||||
const isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
|
||||
const conf = {
|
||||
logLevel: 4,
|
||||
startOnLoad: true,
|
||||
themeCSS: '.label { font-family: Source Sans Pro,Helvetica Neue,Arial,sans-serif; }',
|
||||
};
|
||||
if (isDarkMode) conf.theme = 'dark';
|
||||
|
||||
async function loadMermaid() {
|
||||
mermaid.parseError = (e) => {
|
||||
console.log('parse error', e); // eslint-disable-line
|
||||
};
|
||||
await mermaid.registerExternalDiagrams([mindmap]);
|
||||
mermaid.initialize(conf);
|
||||
console.log('mermaid initialized'); // eslint-disable-line
|
||||
}
|
||||
|
||||
await loadMermaid();
|
||||
</script>
|
||||
<script>
|
||||
let initEditor = exports.default;
|
||||
let parser = new DOMParser();
|
||||
let currentCodeExample = 0;
|
||||
let colorize = [];
|
||||
let num = 0;
|
||||
|
||||
function colorizeEverything(html) {
|
||||
initEditor(monaco);
|
||||
@@ -97,14 +120,12 @@
|
||||
renderer: {
|
||||
code: function (code, lang) {
|
||||
if (lang === 'mermaid-example') {
|
||||
console.log('An example'); // eslint-disable-line
|
||||
currentCodeExample++;
|
||||
colorize.push(currentCodeExample);
|
||||
return '<pre id="code' + currentCodeExample + '">' + escapeHTML(code) + '</pre>';
|
||||
} else if (lang === 'mermaid') {
|
||||
// TODO: This will need to be updated when render is async.
|
||||
return (
|
||||
'<pre class="mermaid">' + mermaid.render('mermaid-svg-' + num++, code) + '</pre>'
|
||||
);
|
||||
return '<pre class="mermaid">' + code + '</pre>';
|
||||
}
|
||||
return this.origin.code.apply(this, arguments);
|
||||
},
|
||||
@@ -123,6 +144,10 @@
|
||||
const editHtml = '[:memo: Edit this Page](' + url + ')\n';
|
||||
return editHtml + html;
|
||||
});
|
||||
// Invoked on each page load after new HTML has been appended to the DOM
|
||||
hook.doneEach(function () {
|
||||
window.mermaid.init();
|
||||
});
|
||||
|
||||
hook.afterEach(function (html, next) {
|
||||
next(html);
|
||||
@@ -138,28 +163,17 @@
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
|
||||
const conf = {
|
||||
logLevel: 4,
|
||||
startOnLoad: false,
|
||||
themeCSS: '.label { font-family: Source Sans Pro,Helvetica Neue,Arial,sans-serif; }',
|
||||
};
|
||||
if (isDarkMode) conf.theme = 'dark';
|
||||
mermaid.initialize(conf);
|
||||
</script>
|
||||
<script>
|
||||
window.onhashchange = function (a) {
|
||||
//code
|
||||
if (location) {
|
||||
ga('send', 'pageview', location.hash);
|
||||
}
|
||||
// if (location && ga) {
|
||||
// ga('send', 'pageview', location.hash);
|
||||
// }
|
||||
};
|
||||
</script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify/lib/docsify.min.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/search.min.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/ga.min.js"></script>
|
||||
<!-- <script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/ga.min.js"></script> -->
|
||||
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-coffeescript.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@@ -26,7 +26,6 @@ mindmap
|
||||
Tools
|
||||
Pen and paper
|
||||
Mermaid
|
||||
|
||||
```
|
||||
|
||||
```mermaid
|
||||
@@ -47,14 +46,13 @@ mindmap
|
||||
Tools
|
||||
Pen and paper
|
||||
Mermaid
|
||||
|
||||
```
|
||||
|
||||
## Syntax
|
||||
|
||||
The syntax for creating Mindmaps is simple and relies on indentation for setting the levels in the hierarchy.
|
||||
|
||||
In the following example you can see how there are 3 different levels. One with starting at the left of the text and another level with two rows starting at the same column, defining the node A. At the end there is one more level where the text is indented further then the previous lines defining the nodes B and C.
|
||||
In the following example you can see how there are 3 different levels of indentation. The leftmost indentation is the root of the mindmap. There can only be one root and if you by misstake add two of them on the same level there will be a syntax error. Rows with larger indentation will be connected as children to the previous row with lower indentation. Based on that you can see in the example how the nodes B and C both are children to node A whci in turn is a child of the node Root.
|
||||
|
||||
mindmap
|
||||
Root
|
||||
@@ -62,7 +60,7 @@ In the following example you can see how there are 3 different levels. One with
|
||||
B
|
||||
C
|
||||
|
||||
In summary is a simple text outline where there are one node at the root level called `Root` which has one child `A`. `A` in turn has two children `B`and `C`. In the diagram below we can see this rendered as a mindmap.
|
||||
In the diagram below you can see the example rendered as a mindmap.
|
||||
|
||||
```mermaid-example
|
||||
mindmap
|
||||
@@ -220,7 +218,7 @@ The actual indentation does not really matter only compared with the previous ro
|
||||
B
|
||||
C
|
||||
|
||||
This outline is unclear as `B` clearly is a child of `A` but when we move on to `C` the clarity is lost. `C` is not a child of `B` with a higher indentation nor does it have the same indentation as `B`. The only thing that is clear is that the first node with smaller indentation, indicating a parent, is A. Then Mermaid relies on this known truth and compensates for the unclear indentation and selects `A` as a parent of `C` leading till the same diagram with `B` and `C` as siblings.
|
||||
This outline is unclear as `B` clearly is a child of `A` but when we move on to `C` the clarity is lost. `C` is not a child of `B` with a higher indentation nor does it have the same indentation as `B`. The only thing that is clear is that the first node with smaller indentation, indicating a parent, is A. Mermaid will rely on this known truth and compensates for the unclear indentation and selects `A` as a parent of `C` leading till the same diagram with `B` and `C` as siblings.
|
||||
|
||||
```mermaid-example
|
||||
mindmap
|
||||
|
19
package.json
19
package.json
@@ -1,21 +1,10 @@
|
||||
{
|
||||
"name": "mermaid-monorepo",
|
||||
"private": true,
|
||||
"version": "9.2.0-rc2",
|
||||
"version": "9.2.0-rc4",
|
||||
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
||||
"main": "dist/mermaid.core.mjs",
|
||||
"module": "dist/mermaid.core.mjs",
|
||||
"types": "dist/mermaid.d.ts",
|
||||
"type": "module",
|
||||
"packageManager": "pnpm@7.13.2",
|
||||
"exports": {
|
||||
".": {
|
||||
"require": "./dist/mermaid.min.js",
|
||||
"import": "./dist/mermaid.core.mjs",
|
||||
"types": "./dist/mermaid.d.ts"
|
||||
},
|
||||
"./*": "./*"
|
||||
},
|
||||
"keywords": [
|
||||
"diagram",
|
||||
"markdown",
|
||||
@@ -26,8 +15,9 @@
|
||||
"git graph"
|
||||
],
|
||||
"scripts": {
|
||||
"build:mermaid": "ts-node-esm --transpileOnly --project=.vite/tsconfig.json .vite/build.ts --mermaid",
|
||||
"build:vite": "ts-node-esm --transpileOnly --project=.vite/tsconfig.json .vite/build.ts",
|
||||
"build:types": "concurrently \"tsc -p ./packages/mermaid/tsconfig.json --emitDeclarationOnly\" \"tsc -p ./packages/mermaid-mindmap/tsconfig.json --emitDeclarationOnly\"",
|
||||
"build:types": "tsc -p ./packages/mermaid/tsconfig.json --emitDeclarationOnly && tsc -p ./packages/mermaid-mindmap/tsconfig.json --emitDeclarationOnly",
|
||||
"build:watch": "pnpm build:vite --watch",
|
||||
"build": "pnpm run -r clean && concurrently \"pnpm build:vite\" \"pnpm build:types\"",
|
||||
"dev": "concurrently \"pnpm build:vite --watch\" \"ts-node-esm .vite/server.ts\"",
|
||||
@@ -146,9 +136,6 @@
|
||||
"resolutions": {
|
||||
"d3": "^7.0.0"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"sideEffects": [
|
||||
"**/*.css",
|
||||
"**/*.scss"
|
||||
|
3
packages/mermaid-example-diagram/Readme.md
Normal file
3
packages/mermaid-example-diagram/Readme.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Do not refer this package. It is not ready.
|
||||
|
||||
### Refer mermaid-mindmap instead.
|
@@ -12,3 +12,5 @@ export const diagram = {
|
||||
styles,
|
||||
injectUtils,
|
||||
};
|
||||
|
||||
export { detector, id } from './detector';
|
||||
|
@@ -1,14 +1,14 @@
|
||||
{
|
||||
"name": "@mermaid-js/mermaid-mindmap",
|
||||
"version": "9.2.0-rc2",
|
||||
"version": "9.2.2-rc.2",
|
||||
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
||||
"main": "dist/mermaid-mindmap.core.mjs",
|
||||
"module": "dist/mermaid-mindmap.core.mjs",
|
||||
"types": "dist/detector.d.ts",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"require": "./dist/mermaid-mindmap.min.js",
|
||||
"import": "./dist/mermaid-mindmap.core.mjs"
|
||||
"import": "./dist/mermaid-mindmap.core.mjs",
|
||||
"types": "./dist/detector.d.ts"
|
||||
},
|
||||
"./*": "./*"
|
||||
},
|
||||
@@ -58,6 +58,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"concurrently": "^7.4.0",
|
||||
"mermaid": "workspace:*",
|
||||
"rimraf": "^3.0.2"
|
||||
},
|
||||
"resolutions": {
|
||||
|
@@ -1,10 +1,20 @@
|
||||
export const id = 'mindmap';
|
||||
import type { ExternalDiagramDefinition } from 'mermaid';
|
||||
|
||||
export const detector = (txt: string) => {
|
||||
const id = 'mindmap';
|
||||
|
||||
const detector = (txt: string) => {
|
||||
return txt.match(/^\s*mindmap/) !== null;
|
||||
};
|
||||
|
||||
export const loadDiagram = async () => {
|
||||
const loader = async () => {
|
||||
const { diagram } = await import('./diagram-definition');
|
||||
return { id, diagram };
|
||||
};
|
||||
|
||||
const plugin: ExternalDiagramDefinition = {
|
||||
id,
|
||||
detector,
|
||||
loader,
|
||||
};
|
||||
|
||||
export default plugin;
|
||||
|
@@ -1,3 +1,5 @@
|
||||
import type { MermaidConfig } from 'mermaid';
|
||||
|
||||
const warning = (s: string) => {
|
||||
// Todo remove debug code
|
||||
console.error('Log function was called before initialization', s); // eslint-disable-line
|
||||
@@ -24,7 +26,7 @@ export const log: Record<keyof typeof LEVELS, typeof console.log> = {
|
||||
};
|
||||
|
||||
export let setLogLevel: (level: keyof typeof LEVELS | number | string) => void;
|
||||
export let getConfig: () => object;
|
||||
export let getConfig: () => MermaidConfig;
|
||||
export let sanitizeText: (str: string) => string;
|
||||
// eslint-disable @typescript-eslint/no-explicit-any
|
||||
export let setupGraphViewbox: (
|
||||
|
@@ -1,16 +1,28 @@
|
||||
/** Created by knut on 15-01-14. */
|
||||
/** Created by knut on 23-07-2022. */
|
||||
import { sanitizeText, getConfig, log } from './mermaidUtils';
|
||||
import type { DetailedError } from 'mermaid';
|
||||
|
||||
let nodes = [];
|
||||
interface Node {
|
||||
id: number;
|
||||
nodeId: string;
|
||||
level: number;
|
||||
descr: string;
|
||||
type: number;
|
||||
children: Node[];
|
||||
width: number;
|
||||
padding: number;
|
||||
icon?: string;
|
||||
class?: string;
|
||||
}
|
||||
|
||||
let nodes: Node[] = [];
|
||||
let cnt = 0;
|
||||
let elements = {};
|
||||
export const clear = () => {
|
||||
nodes = [];
|
||||
cnt = 0;
|
||||
elements = {};
|
||||
};
|
||||
|
||||
const getParent = function (level) {
|
||||
const getParent = function (level: number) {
|
||||
for (let i = nodes.length - 1; i >= 0; i--) {
|
||||
if (nodes[i].level < level) {
|
||||
return nodes[i];
|
||||
@@ -23,28 +35,21 @@ const getParent = function (level) {
|
||||
export const getMindmap = () => {
|
||||
return nodes.length > 0 ? nodes[0] : null;
|
||||
};
|
||||
export const addNode = (level, id, descr, type) => {
|
||||
|
||||
export const addNode = (level: number, id: string, descr: string, type: number) => {
|
||||
log.info('addNode', level, id, descr, type);
|
||||
const conf = getConfig();
|
||||
const node = {
|
||||
const padding = conf.mindmap?.padding ?? 15;
|
||||
const node: Node = {
|
||||
id: cnt++,
|
||||
nodeId: sanitizeText(id),
|
||||
level,
|
||||
descr: sanitizeText(descr),
|
||||
type,
|
||||
children: [],
|
||||
width: getConfig().mindmap.maxNodeWidth,
|
||||
width: getConfig().mindmap?.maxNodeWidth ?? 200,
|
||||
padding: type === nodeType.ROUNDED_RECT || type === nodeType.RECT ? 2 * padding : padding,
|
||||
};
|
||||
switch (node.type) {
|
||||
case nodeType.ROUNDED_RECT:
|
||||
node.padding = 2 * conf.mindmap.padding;
|
||||
break;
|
||||
case nodeType.RECT:
|
||||
node.padding = 2 * conf.mindmap.padding;
|
||||
break;
|
||||
default:
|
||||
node.padding = conf.mindmap.padding;
|
||||
}
|
||||
const parent = getParent(level);
|
||||
if (parent) {
|
||||
parent.children.push(node);
|
||||
@@ -56,9 +61,10 @@ export const addNode = (level, id, descr, type) => {
|
||||
nodes.push(node);
|
||||
} else {
|
||||
// Syntax error ... there can only bee one root
|
||||
let error = new Error(
|
||||
const error = new Error(
|
||||
'There can be only one root. No parent could be found for ("' + node.descr + '")'
|
||||
);
|
||||
// @ts-ignore TODO: Add mermaid error
|
||||
error.hash = {
|
||||
text: 'branch ' + name,
|
||||
token: 'branch ' + name,
|
||||
@@ -81,7 +87,7 @@ export const nodeType = {
|
||||
BANG: 5,
|
||||
};
|
||||
|
||||
export const getType = (startStr, endStr) => {
|
||||
export const getType = (startStr: string, endStr: string): number => {
|
||||
log.debug('In get type', startStr, endStr);
|
||||
switch (startStr) {
|
||||
case '[':
|
||||
@@ -99,11 +105,7 @@ export const getType = (startStr, endStr) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const setElementForId = (id, element) => {
|
||||
elements[id] = element;
|
||||
};
|
||||
|
||||
export const decorateNode = (decoration) => {
|
||||
export const decorateNode = (decoration: { icon: string; class: string }) => {
|
||||
const node = nodes[nodes.length - 1];
|
||||
if (decoration && decoration.icon) {
|
||||
node.icon = sanitizeText(decoration.icon);
|
||||
@@ -113,7 +115,7 @@ export const decorateNode = (decoration) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const type2Str = (type) => {
|
||||
export const type2Str = (type: number) => {
|
||||
switch (type) {
|
||||
case nodeType.DEFAULT:
|
||||
return 'no-border';
|
||||
@@ -132,13 +134,13 @@ export const type2Str = (type) => {
|
||||
}
|
||||
};
|
||||
|
||||
export let parseError;
|
||||
export const setErrorHandler = (handler) => {
|
||||
export type ParseErrorFunction = (err: string | DetailedError, hash?: any) => void;
|
||||
export let parseError: ParseErrorFunction;
|
||||
export const setErrorHandler = (handler: ParseErrorFunction) => {
|
||||
parseError = handler;
|
||||
};
|
||||
|
||||
// Expose logger to grammar
|
||||
export const getLogger = () => log;
|
||||
|
||||
export const getNodeById = (id) => nodes[id];
|
||||
export const getElementById = (id) => elements[id];
|
||||
export const getNodeById = (id: number): Node => nodes[id];
|
@@ -1,10 +1,9 @@
|
||||
/** Created by knut on 14-12-11. */
|
||||
/** Created by knut on 23-07-2022. */
|
||||
import { select } from 'd3';
|
||||
import { log, getConfig, setupGraphViewbox } from './mermaidUtils';
|
||||
import svgDraw from './svgDraw';
|
||||
import svgDraw, { getElementById, clearElementRefs } from './svgDraw';
|
||||
import cytoscape from 'cytoscape';
|
||||
import coseBilkent from 'cytoscape-cose-bilkent';
|
||||
import * as db from './mindmapDb';
|
||||
|
||||
// Inject the layout algorithm into cytoscape
|
||||
cytoscape.use(coseBilkent);
|
||||
@@ -34,7 +33,7 @@ function drawNodes(svg, mindmap, section, conf) {
|
||||
* @param cy
|
||||
*/
|
||||
function drawEdges(edgesEl, cy) {
|
||||
cy.edges().map((edge, id) => {
|
||||
cy?.edges().map((edge, id) => {
|
||||
const data = edge.data();
|
||||
if (edge[0]._private.bodyBounds) {
|
||||
const bounds = edge[0]._private.rscratch;
|
||||
@@ -100,9 +99,10 @@ function addNodes(mindmap, cy, conf, level) {
|
||||
*/
|
||||
function layoutMindmap(node, conf) {
|
||||
return new Promise((resolve) => {
|
||||
if (node.children.length === 0) {
|
||||
return node;
|
||||
}
|
||||
// if (node.children.length === 0) {
|
||||
// resolve(node);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// Add temporary render element
|
||||
const renderEl = select('body').append('div').attr('id', 'cy').attr('style', 'display:none');
|
||||
@@ -154,7 +154,7 @@ function positionNodes(cy) {
|
||||
data.x = node.position().x;
|
||||
data.y = node.position().y;
|
||||
svgDraw.positionNode(data);
|
||||
const el = db.getElementById(data.nodeId);
|
||||
const el = getElementById(data.nodeId);
|
||||
log.info('Id:', id, 'Position: (', node.position().x, ', ', node.position().y, ')', data);
|
||||
el.attr(
|
||||
'transform',
|
||||
@@ -178,6 +178,7 @@ export const draw = async (text, id, version, diagObj) => {
|
||||
|
||||
// This is done only for throwing the error if the text is not valid.
|
||||
diagObj.db.clear();
|
||||
clearElementRefs();
|
||||
// Parse the graph definition
|
||||
diagObj.parser.parse(text);
|
||||
|
||||
|
@@ -22,12 +22,10 @@ const genSections = (options) => {
|
||||
}
|
||||
.section-${i - 1} text {
|
||||
fill: ${options['cScaleLabel' + i]};
|
||||
// fill: ${options['gitInv' + i]};
|
||||
}
|
||||
.node-icon-${i - 1} {
|
||||
font-size: 40px;
|
||||
color: ${options['cScaleLabel' + i]};
|
||||
// color: ${options['gitInv' + i]};
|
||||
}
|
||||
.section-edge-${i - 1}{
|
||||
stroke: ${options['cScale' + i]};
|
||||
@@ -36,7 +34,7 @@ const genSections = (options) => {
|
||||
stroke-width: ${sw};
|
||||
}
|
||||
.section-${i - 1} line {
|
||||
stroke: ${options['lineColor' + i]} ;
|
||||
stroke: ${options['cScaleInv' + i]} ;
|
||||
stroke-width: 3;
|
||||
}
|
||||
|
||||
|
@@ -259,7 +259,7 @@ export const drawNode = function (elem, node, fullSection, conf) {
|
||||
// if (typeof node.x !== 'undefined' && typeof node.y !== 'undefined') {
|
||||
// nodeElem.attr('transform', 'translate(' + node.x + ',' + node.y + ')');
|
||||
// }
|
||||
db.setElementForId(node.id, nodeElem);
|
||||
setElementById(node.id, nodeElem);
|
||||
return node.height;
|
||||
};
|
||||
|
||||
@@ -286,7 +286,7 @@ export const drawEdge = function drawEdge(edgesElem, mindmap, parent, depth, ful
|
||||
};
|
||||
|
||||
export const positionNode = function (node) {
|
||||
const nodeElem = db.getElementById(node.id);
|
||||
const nodeElem = getElementById(node.id);
|
||||
|
||||
const x = node.x || 0;
|
||||
const y = node.y || 0;
|
||||
@@ -294,4 +294,18 @@ export const positionNode = function (node) {
|
||||
nodeElem.attr('transform', 'translate(' + x + ',' + y + ')');
|
||||
};
|
||||
|
||||
let elements = {};
|
||||
|
||||
const setElementById = (id, element) => {
|
||||
elements[id] = element;
|
||||
};
|
||||
|
||||
export const getElementById = (id) => {
|
||||
return elements[id];
|
||||
};
|
||||
|
||||
export const clearElementRefs = () => {
|
||||
elements = {};
|
||||
};
|
||||
|
||||
export default { drawNode, positionNode, drawEdge };
|
||||
|
1
packages/mermaid/README.md
Symbolic link
1
packages/mermaid/README.md
Symbolic link
@@ -0,0 +1 @@
|
||||
../../README.md
|
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "mermaid",
|
||||
"version": "9.2.0-rc2",
|
||||
"version": "9.2.2-rc.2",
|
||||
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
||||
"main": "./dist/mermaid.core.mjs",
|
||||
"main": "./dist/mermaid.min.js",
|
||||
"module": "./dist/mermaid.core.mjs",
|
||||
"types": "./dist/mermaid.d.ts",
|
||||
"type": "module",
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
".": {
|
||||
"require": "./dist/mermaid.min.js",
|
||||
@@ -72,7 +72,8 @@
|
||||
"lodash": "^4.17.21",
|
||||
"moment-mini": "^2.24.0",
|
||||
"non-layered-tidy-tree-layout": "^2.0.2",
|
||||
"stylis": "^4.1.2"
|
||||
"stylis": "^4.1.2",
|
||||
"uuid": "^9.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@applitools/eyes-cypress": "^3.25.7",
|
||||
@@ -86,6 +87,7 @@
|
||||
"@types/lodash": "^4.14.185",
|
||||
"@types/prettier": "^2.7.0",
|
||||
"@types/stylis": "^4.0.2",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@typescript-eslint/eslint-plugin": "^5.37.0",
|
||||
"@typescript-eslint/parser": "^5.37.0",
|
||||
"concurrently": "^7.4.0",
|
||||
@@ -125,7 +127,8 @@
|
||||
"d3": "^7.0.0"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
"dist",
|
||||
"README.md"
|
||||
],
|
||||
"sideEffects": [
|
||||
"**/*.css",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import * as configApi from './config';
|
||||
import { log } from './logger';
|
||||
import { getDiagram, registerDiagram } from './diagram-api/diagramAPI';
|
||||
import { DiagramNotFoundError, getDiagram, registerDiagram } from './diagram-api/diagramAPI';
|
||||
import { detectType, getDiagramLoader } from './diagram-api/detectType';
|
||||
import { isDetailedError } from './utils';
|
||||
export class Diagram {
|
||||
@@ -81,25 +81,35 @@ export class Diagram {
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
export const getDiagramFromText = async (txt: string, parseError?: Function): Promise<Diagram> => {
|
||||
export const getDiagramFromText = (
|
||||
txt: string,
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
parseError?: Function
|
||||
): Diagram | Promise<Diagram> => {
|
||||
const type = detectType(txt, configApi.getConfig());
|
||||
try {
|
||||
// Trying to find the diagram
|
||||
getDiagram(type);
|
||||
return new Diagram(txt, parseError);
|
||||
} catch (error) {
|
||||
if (!(error instanceof DiagramNotFoundError)) {
|
||||
log.error(error);
|
||||
throw error;
|
||||
}
|
||||
const loader = getDiagramLoader(type);
|
||||
if (!loader) {
|
||||
throw new Error(`Diagram ${type} not found.`);
|
||||
throw new Error(`Loader for ${type} not found.`);
|
||||
}
|
||||
// Diagram not available, loading it
|
||||
const { diagram } = await loader();
|
||||
registerDiagram(type, diagram, undefined, diagram.injectUtils);
|
||||
// new diagram will try getDiagram again and if fails then it is a valid throw
|
||||
// TODO: Uncomment for v10
|
||||
// // Diagram not available, loading it
|
||||
// const { diagram } = await loader();
|
||||
// registerDiagram(type, diagram, undefined, diagram.injectUtils);
|
||||
// // new diagram will try getDiagram again and if fails then it is a valid throw
|
||||
return loader().then(({ diagram }) => {
|
||||
registerDiagram(type, diagram, undefined);
|
||||
return new Diagram(txt, parseError);
|
||||
});
|
||||
}
|
||||
// If either of the above worked, we have the diagram
|
||||
// logic and can continue
|
||||
return new Diagram(txt, parseError);
|
||||
};
|
||||
|
||||
export default Diagram;
|
||||
|
@@ -25,6 +25,7 @@ function parse(text: string, parseError?: Function): boolean {
|
||||
// original version cannot be modified since it was frozen with `Object.freeze()`
|
||||
export const mermaidAPI = {
|
||||
render: vi.fn(),
|
||||
renderAsync: vi.fn(),
|
||||
parse,
|
||||
parseDirective: vi.fn(),
|
||||
initialize: vi.fn(),
|
||||
|
@@ -40,7 +40,8 @@ export const updateCurrentConfig = (siteCfg: MermaidConfig, _directives: any[])
|
||||
}
|
||||
|
||||
currentConfig = cfg;
|
||||
return cfg;
|
||||
checkConfig(currentConfig);
|
||||
return currentConfig;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -68,7 +69,7 @@ export const setSiteConfig = (conf: MermaidConfig): MermaidConfig => {
|
||||
siteConfig.themeVariables = theme[conf.theme].getThemeVariables(conf.themeVariables);
|
||||
}
|
||||
|
||||
currentConfig = updateCurrentConfig(siteConfig, directives);
|
||||
updateCurrentConfig(siteConfig, directives);
|
||||
return siteConfig;
|
||||
};
|
||||
|
||||
@@ -117,6 +118,7 @@ export const setConfig = (conf: MermaidConfig): MermaidConfig => {
|
||||
// conf[key] = manipulator ? manipulator(conf[key]) : conf[key];
|
||||
// });
|
||||
|
||||
checkConfig(conf);
|
||||
assignWithDepth(currentConfig, conf);
|
||||
|
||||
return getConfig();
|
||||
@@ -224,3 +226,25 @@ export const reset = (config = siteConfig): void => {
|
||||
directives = [];
|
||||
updateCurrentConfig(config, directives);
|
||||
};
|
||||
|
||||
enum ConfigWarning {
|
||||
'LAZY_LOAD_DEPRECATED' = 'The configuration options lazyLoadedDiagrams and loadExternalDiagramsAtStartup are deprecated. Please use registerExternalDiagrams instead.',
|
||||
}
|
||||
type ConfigWarningStrings = keyof typeof ConfigWarning;
|
||||
const issuedWarnings: { [key in ConfigWarningStrings]?: boolean } = {};
|
||||
const issueWarning = (warning: ConfigWarningStrings) => {
|
||||
if (issuedWarnings[warning]) {
|
||||
return;
|
||||
}
|
||||
log.warn(ConfigWarning[warning]);
|
||||
issuedWarnings[warning] = true;
|
||||
};
|
||||
|
||||
const checkConfig = (config: MermaidConfig) => {
|
||||
if (!config) {
|
||||
return;
|
||||
}
|
||||
if (config.lazyLoadedDiagrams || config.loadExternalDiagramsAtStartup) {
|
||||
issueWarning('LAZY_LOAD_DEPRECATED');
|
||||
}
|
||||
};
|
||||
|
@@ -3,7 +3,10 @@
|
||||
import DOMPurify from 'dompurify';
|
||||
|
||||
export interface MermaidConfig {
|
||||
/** @deprecated use mermaid.registerLazyDiagrams instead */
|
||||
lazyLoadedDiagrams?: string[];
|
||||
/** @deprecated use mermaid.registerLazyDiagrams instead */
|
||||
loadExternalDiagramsAtStartup?: boolean;
|
||||
theme?: string;
|
||||
themeVariables?: any;
|
||||
themeCSS?: string;
|
||||
|
@@ -438,6 +438,9 @@ export const insertEdge = function (elem, e, edge, clusterDb, diagramType, graph
|
||||
case 'thick':
|
||||
strokeClasses = 'edge-thickness-thick';
|
||||
break;
|
||||
case 'invisible':
|
||||
strokeClasses = 'edge-thickness-thick';
|
||||
break;
|
||||
default:
|
||||
strokeClasses = '';
|
||||
}
|
||||
|
@@ -115,7 +115,6 @@ const config: Partial<MermaidConfig> = {
|
||||
* Default value: ['secure', 'securityLevel', 'startOnLoad', 'maxTextSize']
|
||||
*/
|
||||
secure: ['secure', 'securityLevel', 'startOnLoad', 'maxTextSize'],
|
||||
lazyLoadedDiagrams: [],
|
||||
/**
|
||||
* This option controls if the generated ids of nodes in the SVG are generated randomly or based
|
||||
* on a seed. If set to false, the IDs are generated based on the current date and thus are not
|
||||
|
@@ -22,34 +22,51 @@ export interface Detectors {
|
||||
[key: string]: DiagramDetector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the given diagram with Mermaid.
|
||||
*
|
||||
* Can be used for third-party custom diagrams.
|
||||
*
|
||||
* @param id - A unique ID for the given diagram.
|
||||
* @param diagram - The diagram definition.
|
||||
* @param detector - Function that returns `true` if a given mermaid text is this diagram definition.
|
||||
*/
|
||||
export const registerDiagram = (
|
||||
id: string,
|
||||
diagram: DiagramDefinition,
|
||||
detector?: DiagramDetector,
|
||||
callback?: (
|
||||
_log: any,
|
||||
_setLogLevel: any,
|
||||
_getConfig: any,
|
||||
_sanitizeText: any,
|
||||
_setupGraphViewbox: any
|
||||
) => void
|
||||
detector?: DiagramDetector
|
||||
) => {
|
||||
log.debug(`Registering diagram ${id}`);
|
||||
if (diagrams[id]) {
|
||||
throw new Error(`Diagram ${id} already registered.`);
|
||||
log.warn(`Diagram ${id} already registered.`);
|
||||
// The error throw is commented out to as it breaks pages where you have multiple diagrams,
|
||||
// it can happen that rendering of the same type of diagram is initiated while the previous
|
||||
// one is still being imported. import deals with this and only one diagram is imported in
|
||||
// the end.
|
||||
// throw new Error(`Diagram ${id} already registered.`);
|
||||
}
|
||||
diagrams[id] = diagram;
|
||||
if (detector) {
|
||||
addDetector(id, detector);
|
||||
}
|
||||
addStylesForDiagram(id, diagram.styles);
|
||||
if (typeof callback !== 'undefined') {
|
||||
callback(log, setLogLevel, getConfig, sanitizeText, setupGraphViewbox);
|
||||
|
||||
if (diagram.injectUtils) {
|
||||
diagram.injectUtils(log, setLogLevel, getConfig, sanitizeText, setupGraphViewbox);
|
||||
}
|
||||
log.debug(`Registered diagram ${id}. ${Object.keys(diagrams).join(', ')} diagrams registered.`);
|
||||
};
|
||||
|
||||
export const getDiagram = (name: string): DiagramDefinition => {
|
||||
log.debug(`Getting diagram ${name}. ${Object.keys(diagrams).join(', ')} diagrams registered.`);
|
||||
if (name in diagrams) {
|
||||
return diagrams[name];
|
||||
}
|
||||
throw new Error(`Diagram ${name} not found.`);
|
||||
throw new DiagramNotFoundError(name);
|
||||
};
|
||||
|
||||
export class DiagramNotFoundError extends Error {
|
||||
constructor(message: string) {
|
||||
super(`Diagram ${message} not found.`);
|
||||
}
|
||||
}
|
||||
|
@@ -14,7 +14,13 @@ export interface DiagramDefinition {
|
||||
parser: any;
|
||||
styles: any;
|
||||
init?: (config: MermaidConfig) => void;
|
||||
injectUtils?: (utils: InjectUtils) => void;
|
||||
injectUtils?: (
|
||||
_log: InjectUtils['_log'],
|
||||
_setLogLevel: InjectUtils['_setLogLevel'],
|
||||
_getConfig: InjectUtils['_getConfig'],
|
||||
_sanitizeText: InjectUtils['_sanitizeText'],
|
||||
_setupGraphViewbox: InjectUtils['_setupGraphViewbox']
|
||||
) => void;
|
||||
}
|
||||
|
||||
export interface DetectorRecord {
|
||||
@@ -22,5 +28,11 @@ export interface DetectorRecord {
|
||||
loader?: DiagramLoader;
|
||||
}
|
||||
|
||||
export interface ExternalDiagramDefinition {
|
||||
id: string;
|
||||
detector: DiagramDetector;
|
||||
loader: DiagramLoader;
|
||||
}
|
||||
|
||||
export type DiagramDetector = (text: string, config?: MermaidConfig) => boolean;
|
||||
export type DiagramLoader = (() => Promise<{ id: string; diagram: DiagramDefinition }>) | null;
|
||||
export type DiagramLoader = () => Promise<{ id: string; diagram: DiagramDefinition }>;
|
||||
|
@@ -456,8 +456,8 @@ export const defaultStyle = function () {
|
||||
export const addSubGraph = function (_id, list, _title) {
|
||||
// console.log('addSubGraph', _id, list, _title);
|
||||
let id = _id.trim();
|
||||
let title = _title;
|
||||
if (_id === _title && _title.match(/\s/)) {
|
||||
let title = _title.trim();
|
||||
if (id === title && title.match(/\s/)) {
|
||||
id = undefined;
|
||||
}
|
||||
/** @param a */
|
||||
@@ -674,6 +674,10 @@ const destructEndLink = (_str) => {
|
||||
stroke = 'thick';
|
||||
}
|
||||
|
||||
if (line[0] === '~') {
|
||||
stroke = 'invisible';
|
||||
}
|
||||
|
||||
let dots = countChar('.', line);
|
||||
|
||||
if (dots) {
|
||||
|
@@ -280,6 +280,11 @@ export const addEdges = function (edges, g, diagObj) {
|
||||
edgeData.pattern = 'solid';
|
||||
edgeData.style = 'stroke-width: 3.5px;fill:none;';
|
||||
break;
|
||||
case 'invisible':
|
||||
edgeData.thickness = 'invisible';
|
||||
edgeData.pattern = 'solid';
|
||||
edgeData.style = 'stroke-width: 0;fill:none;';
|
||||
break;
|
||||
}
|
||||
if (typeof edge.style !== 'undefined') {
|
||||
const styles = getStylesFromArray(edge.style);
|
||||
|
@@ -120,6 +120,7 @@ that id.
|
||||
\s*[xo<]?\-\-+[-xo>]\s* return 'LINK';
|
||||
\s*[xo<]?\=\=+[=xo>]\s* return 'LINK';
|
||||
\s*[xo<]?\-?\.+\-[xo>]?\s* return 'LINK';
|
||||
\s*\~\~[\~]+\s* return 'LINK';
|
||||
\s*[xo<]?\-\-\s* return 'START_LINK';
|
||||
\s*[xo<]?\=\=\s* return 'START_LINK';
|
||||
\s*[xo<]?\-\.\s* return 'START_LINK';
|
||||
|
@@ -35,7 +35,7 @@ import { exec } from 'child_process';
|
||||
import { globby } from 'globby';
|
||||
import { JSDOM } from 'jsdom';
|
||||
import type { Code, Root } from 'mdast';
|
||||
import { posix, dirname } from 'path';
|
||||
import { join, dirname } from 'path';
|
||||
import prettier from 'prettier';
|
||||
import { remark } from 'remark';
|
||||
// @ts-ignore No typescript declaration file
|
||||
@@ -210,28 +210,30 @@ const transformHtml = (filename: string) => {
|
||||
copyTransformedContents(filename, !verifyOnly, formattedHTML);
|
||||
};
|
||||
|
||||
const getFilesFromGlobs = async (globs: string[]): Promise<string[]> => {
|
||||
return await globby(globs, { dot: true });
|
||||
};
|
||||
|
||||
/** Main method (entry point) */
|
||||
(async () => {
|
||||
if (verifyOnly) {
|
||||
console.log('Verifying that all files are in sync with the source files');
|
||||
}
|
||||
const sourceDirGlob = posix.join('.', SOURCE_DOCS_DIR, '**');
|
||||
const action = verifyOnly ? 'Verifying' : 'Transforming';
|
||||
const sourceDirGlob = join('.', SOURCE_DOCS_DIR, '**');
|
||||
const includeFilesStartingWithDot = true;
|
||||
|
||||
const mdFiles = await getFilesFromGlobs([posix.join(sourceDirGlob, '*.md')]);
|
||||
console.log(`${action} ${mdFiles.length} markdown files...`);
|
||||
console.log('Transforming markdown files...');
|
||||
const mdFiles = await globby([join(sourceDirGlob, '*.md')], {
|
||||
dot: includeFilesStartingWithDot,
|
||||
});
|
||||
mdFiles.forEach(transformMarkdown);
|
||||
|
||||
const htmlFiles = await getFilesFromGlobs([posix.join(sourceDirGlob, '*.html')]);
|
||||
console.log(`${action} ${htmlFiles.length} html files...`);
|
||||
console.log('Transforming html files...');
|
||||
const htmlFiles = await globby([join(sourceDirGlob, '*.html')], {
|
||||
dot: includeFilesStartingWithDot,
|
||||
});
|
||||
htmlFiles.forEach(transformHtml);
|
||||
|
||||
const otherFiles = await getFilesFromGlobs([sourceDirGlob, '!**/*.md', '!**/*.html']);
|
||||
console.log(`${action} ${otherFiles.length} other files...`);
|
||||
console.log('Transforming all other files...');
|
||||
const otherFiles = await globby([sourceDirGlob, '!**/*.md', '!**/*.html'], {
|
||||
dot: includeFilesStartingWithDot,
|
||||
});
|
||||
otherFiles.forEach((file: string) => {
|
||||
copyTransformedContents(file, !verifyOnly); // no transformation
|
||||
});
|
||||
@@ -242,7 +244,7 @@ const getFilesFromGlobs = async (globs: string[]): Promise<string[]> => {
|
||||
process.exit(1);
|
||||
}
|
||||
if (git) {
|
||||
console.log(`Adding changes in ${FINAL_DOCS_DIR} folder to git`);
|
||||
console.log('Adding changes in ${FINAL_DOCS_DIR} folder to git');
|
||||
exec('git add docs');
|
||||
}
|
||||
}
|
||||
|
@@ -167,6 +167,15 @@ flowchart LR
|
||||
A --- B
|
||||
```
|
||||
|
||||
### An invisisble link
|
||||
|
||||
This can be a usefull tool in some instances where you want to alter the default positining of a node.
|
||||
|
||||
```mermaid-example
|
||||
flowchart LR
|
||||
A ~~~ B
|
||||
```
|
||||
|
||||
### Text on links
|
||||
|
||||
```mermaid-example
|
||||
@@ -198,8 +207,8 @@ flowchart LR
|
||||
### Dotted link
|
||||
|
||||
```mermaid-example
|
||||
flowchart LR
|
||||
A-.->B
|
||||
flowchart LR;
|
||||
A-.->B;
|
||||
```
|
||||
|
||||
### Dotted link with text
|
||||
@@ -587,7 +596,7 @@ A shorter form of adding a class is to attach the classname to the node using th
|
||||
```mermaid-example
|
||||
flowchart LR
|
||||
A:::someclass --> B
|
||||
classDef someclass fill:#f96
|
||||
classDef someclass fill:#f96;
|
||||
```
|
||||
|
||||
### Css classes
|
||||
@@ -610,7 +619,7 @@ below:
|
||||
**Example definition**
|
||||
|
||||
```mermaid-example
|
||||
flowchart LR
|
||||
flowchart LR;
|
||||
A-->B[AAA<span>BBB</span>]
|
||||
B-->D
|
||||
class A cssClass
|
||||
@@ -634,7 +643,7 @@ The icons are accessed via the syntax fa:#icon class name#.
|
||||
flowchart TD
|
||||
B["fab:fa-twitter for peace"]
|
||||
B-->C[fa:fa-ban forbidden]
|
||||
B-->D(fa:fa-spinner)
|
||||
B-->D(fa:fa-spinner);
|
||||
B-->E(A fa:fa-camera-retro perhaps?)
|
||||
```
|
||||
|
||||
|
@@ -50,13 +50,36 @@
|
||||
<div id="app"></div>
|
||||
<script type="module">
|
||||
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@<MERMAID_VERSION>/dist/mermaid.esm.min.mjs';
|
||||
import mindmap from 'https://cdn.jsdelivr.net/npm/@mermaid-js/mermaid-mindmap@<MERMAID_VERSION>/dist/mermaid-mindmap.esm.mjs';
|
||||
await mermaid.registerExternalDiagrams([mindmap]);
|
||||
|
||||
window.mermaid = mermaid;
|
||||
const isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
|
||||
const conf = {
|
||||
logLevel: 4,
|
||||
startOnLoad: true,
|
||||
themeCSS: '.label { font-family: Source Sans Pro,Helvetica Neue,Arial,sans-serif; }',
|
||||
};
|
||||
if (isDarkMode) conf.theme = 'dark';
|
||||
|
||||
async function loadMermaid() {
|
||||
mermaid.parseError = (e) => {
|
||||
console.log('parse error', e); // eslint-disable-line
|
||||
};
|
||||
await mermaid.registerExternalDiagrams([mindmap]);
|
||||
mermaid.initialize(conf);
|
||||
console.log('mermaid initialized'); // eslint-disable-line
|
||||
}
|
||||
|
||||
await loadMermaid();
|
||||
</script>
|
||||
<script>
|
||||
let initEditor = exports.default;
|
||||
let parser = new DOMParser();
|
||||
let currentCodeExample = 0;
|
||||
let colorize = [];
|
||||
let num = 0;
|
||||
|
||||
function colorizeEverything(html) {
|
||||
initEditor(monaco);
|
||||
@@ -97,14 +120,12 @@
|
||||
renderer: {
|
||||
code: function (code, lang) {
|
||||
if (lang === 'mermaid-example') {
|
||||
console.log('An example'); // eslint-disable-line
|
||||
currentCodeExample++;
|
||||
colorize.push(currentCodeExample);
|
||||
return '<pre id="code' + currentCodeExample + '">' + escapeHTML(code) + '</pre>';
|
||||
} else if (lang === 'mermaid') {
|
||||
// TODO: This will need to be updated when render is async.
|
||||
return (
|
||||
'<pre class="mermaid">' + mermaid.render('mermaid-svg-' + num++, code) + '</pre>'
|
||||
);
|
||||
return '<pre class="mermaid">' + code + '</pre>';
|
||||
}
|
||||
return this.origin.code.apply(this, arguments);
|
||||
},
|
||||
@@ -123,6 +144,10 @@
|
||||
const editHtml = '[:memo: Edit this Page](' + url + ')\n';
|
||||
return editHtml + html;
|
||||
});
|
||||
// Invoked on each page load after new HTML has been appended to the DOM
|
||||
hook.doneEach(function () {
|
||||
window.mermaid.init();
|
||||
});
|
||||
|
||||
hook.afterEach(function (html, next) {
|
||||
next(html);
|
||||
@@ -138,28 +163,17 @@
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
|
||||
const conf = {
|
||||
logLevel: 4,
|
||||
startOnLoad: false,
|
||||
themeCSS: '.label { font-family: Source Sans Pro,Helvetica Neue,Arial,sans-serif; }',
|
||||
};
|
||||
if (isDarkMode) conf.theme = 'dark';
|
||||
mermaid.initialize(conf);
|
||||
</script>
|
||||
<script>
|
||||
window.onhashchange = function (a) {
|
||||
//code
|
||||
if (location) {
|
||||
ga('send', 'pageview', location.hash);
|
||||
}
|
||||
// if (location && ga) {
|
||||
// ga('send', 'pageview', location.hash);
|
||||
// }
|
||||
};
|
||||
</script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify/lib/docsify.min.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/search.min.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/ga.min.js"></script>
|
||||
<!-- <script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/ga.min.js"></script> -->
|
||||
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-coffeescript.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@@ -24,14 +24,13 @@ mindmap
|
||||
Tools
|
||||
Pen and paper
|
||||
Mermaid
|
||||
|
||||
```
|
||||
|
||||
## Syntax
|
||||
|
||||
The syntax for creating Mindmaps is simple and relies on indentation for setting the levels in the hierarchy.
|
||||
|
||||
In the following example you can see how there are 3 different levels. One with starting at the left of the text and another level with two rows starting at the same column, defining the node A. At the end there is one more level where the text is indented further then the previous lines defining the nodes B and C.
|
||||
In the following example you can see how there are 3 different levels of indentation. The leftmost indentation is the root of the mindmap. There can only be one root and if you by misstake add two of them on the same level there will be a syntax error. Rows with larger indentation will be connected as children to the previous row with lower indentation. Based on that you can see in the example how the nodes B and C both are children to node A whci in turn is a child of the node Root.
|
||||
|
||||
```
|
||||
mindmap
|
||||
@@ -41,7 +40,7 @@ mindmap
|
||||
C
|
||||
```
|
||||
|
||||
In summary is a simple text outline where there are one node at the root level called `Root` which has one child `A`. `A` in turn has two children `B`and `C`. In the diagram below we can see this rendered as a mindmap.
|
||||
In the diagram below you can see the example rendered as a mindmap.
|
||||
|
||||
```mermaid
|
||||
mindmap
|
||||
@@ -145,7 +144,7 @@ mindmap
|
||||
C
|
||||
```
|
||||
|
||||
This outline is unclear as `B` clearly is a child of `A` but when we move on to `C` the clarity is lost. `C` is not a child of `B` with a higher indentation nor does it have the same indentation as `B`. The only thing that is clear is that the first node with smaller indentation, indicating a parent, is A. Then Mermaid relies on this known truth and compensates for the unclear indentation and selects `A` as a parent of `C` leading till the same diagram with `B` and `C` as siblings.
|
||||
This outline is unclear as `B` clearly is a child of `A` but when we move on to `C` the clarity is lost. `C` is not a child of `B` with a higher indentation nor does it have the same indentation as `B`. The only thing that is clear is that the first node with smaller indentation, indicating a parent, is A. Mermaid will rely on this known truth and compensates for the unclear indentation and selects `A` as a parent of `C` leading till the same diagram with `B` and `C` as siblings.
|
||||
|
||||
```mermaid
|
||||
mindmap
|
||||
|
@@ -30,6 +30,8 @@ export const log: Record<keyof typeof LEVELS, typeof console.log> = {
|
||||
* @param {LogLevel} [level="fatal"] The level to set the logging to. Default is `"fatal"`
|
||||
*/
|
||||
export const setLogLevel = function (level: keyof typeof LEVELS | number | string = 'fatal') {
|
||||
// TODO: Even if we call initialize with loglevel 0, many initial logs are skipped as LL is set to 5 initially.
|
||||
|
||||
let numericLevel: number = LEVELS.fatal;
|
||||
if (typeof level === 'string') {
|
||||
level = level.toLowerCase();
|
||||
@@ -39,12 +41,14 @@ export const setLogLevel = function (level: keyof typeof LEVELS | number | strin
|
||||
} else if (typeof level === 'number') {
|
||||
numericLevel = level;
|
||||
}
|
||||
|
||||
log.trace = () => {};
|
||||
log.debug = () => {};
|
||||
log.info = () => {};
|
||||
log.warn = () => {};
|
||||
log.error = () => {};
|
||||
log.fatal = () => {};
|
||||
|
||||
if (numericLevel <= LEVELS.fatal) {
|
||||
log.fatal = console.error
|
||||
? console.error.bind(console, format('FATAL'), 'color: orange')
|
||||
|
@@ -54,6 +54,88 @@ describe('when using mermaid and ', function () {
|
||||
expect(mermaidAPI.render).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
describe('when using #registerExternalDiagrams', function () {
|
||||
it('should throw error (but still render) if registerExternalDiagrams fails', async () => {
|
||||
const node = document.createElement('div');
|
||||
node.appendChild(document.createTextNode('graph TD;\na;'));
|
||||
|
||||
await expect(
|
||||
mermaid.registerExternalDiagrams(
|
||||
[
|
||||
{
|
||||
id: 'dummy',
|
||||
detector: (text) => /dummy/.test(text),
|
||||
loader: () => Promise.reject('error'),
|
||||
},
|
||||
],
|
||||
{ lazyLoad: false }
|
||||
)
|
||||
).rejects.toThrow('Failed to load 1 external diagrams');
|
||||
|
||||
expect(() => mermaid.initThrowsErrorsAsync(undefined, node)).not.toThrow();
|
||||
// should still render, even if lazyLoadedDiagrams fails
|
||||
expect(mermaidAPI.renderAsync).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should defer diagram load based on parameter', async () => {
|
||||
let loaded = false;
|
||||
const dummyDiagram = {
|
||||
db: {},
|
||||
renderer: () => {
|
||||
// do nothing
|
||||
},
|
||||
parser: () => {
|
||||
// do nothing
|
||||
},
|
||||
styles: () => {
|
||||
// do nothing
|
||||
},
|
||||
};
|
||||
await expect(
|
||||
mermaid.registerExternalDiagrams(
|
||||
[
|
||||
{
|
||||
id: 'dummy',
|
||||
detector: (text) => /dummy/.test(text),
|
||||
loader: () => {
|
||||
loaded = true;
|
||||
return Promise.resolve({
|
||||
id: 'dummy',
|
||||
diagram: dummyDiagram,
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
{ lazyLoad: true }
|
||||
)
|
||||
).resolves.toBe(undefined);
|
||||
expect(loaded).toBe(false);
|
||||
await expect(
|
||||
mermaid.registerExternalDiagrams(
|
||||
[
|
||||
{
|
||||
id: 'dummy2',
|
||||
detector: (text) => /dummy2/.test(text),
|
||||
loader: () => {
|
||||
loaded = true;
|
||||
return Promise.resolve({
|
||||
id: 'dummy2',
|
||||
diagram: dummyDiagram,
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
{ lazyLoad: false }
|
||||
)
|
||||
).resolves.toBe(undefined);
|
||||
expect(loaded).toBe(true);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// we modify mermaid config in some tests, so we need to make sure to reset them
|
||||
mermaidAPI.reset();
|
||||
});
|
||||
});
|
||||
|
||||
describe('checking validity of input ', function () {
|
||||
it('should throw for an invalid definition', function () {
|
||||
|
@@ -2,13 +2,18 @@
|
||||
* Web page integration module for the mermaid framework. It uses the mermaidAPI for mermaid
|
||||
* functionality and to render the diagrams to svg code!
|
||||
*/
|
||||
import { MermaidConfig } from './config.type';
|
||||
import type { MermaidConfig } from './config.type';
|
||||
import { log } from './logger';
|
||||
import utils from './utils';
|
||||
import { mermaidAPI } from './mermaidAPI';
|
||||
import { addDetector } from './diagram-api/detectType';
|
||||
import { isDetailedError } from './utils';
|
||||
import { isDetailedError, type DetailedError } from './utils';
|
||||
import { registerDiagram } from './diagram-api/diagramAPI';
|
||||
import { ExternalDiagramDefinition } from './diagram-api/types';
|
||||
|
||||
export type { MermaidConfig, DetailedError, ExternalDiagramDefinition };
|
||||
|
||||
let externalDiagramsRegistered = false;
|
||||
/**
|
||||
* ## init
|
||||
*
|
||||
@@ -45,17 +50,12 @@ const init = async function (
|
||||
callback?: Function
|
||||
) {
|
||||
try {
|
||||
const conf = mermaidAPI.getConfig();
|
||||
if (conf?.lazyLoadedDiagrams && conf.lazyLoadedDiagrams.length > 0) {
|
||||
// Load all lazy loaded diagrams in parallel
|
||||
await Promise.allSettled(
|
||||
conf.lazyLoadedDiagrams.map(async (diagram: string) => {
|
||||
const { id, detector, loadDiagram } = await import(diagram);
|
||||
addDetector(id, detector, loadDiagram);
|
||||
})
|
||||
);
|
||||
// Not really sure if we need to check this, or simply call initThrowsErrorsAsync directly.
|
||||
if (externalDiagramsRegistered) {
|
||||
await initThrowsErrorsAsync(config, nodes, callback);
|
||||
} else {
|
||||
initThrowsErrors(config, nodes, callback);
|
||||
}
|
||||
await initThrowsErrors(config, nodes, callback);
|
||||
} catch (e) {
|
||||
log.warn('Syntax Error rendering');
|
||||
if (isDetailedError(e)) {
|
||||
@@ -67,7 +67,33 @@ const init = async function (
|
||||
}
|
||||
};
|
||||
|
||||
const initThrowsErrors = async function (
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
const handleError = (error: unknown, errors: DetailedError[], parseError?: Function) => {
|
||||
log.warn(error);
|
||||
if (isDetailedError(error)) {
|
||||
// handle case where error string and hash were
|
||||
// wrapped in object like`const error = { str, hash };`
|
||||
if (parseError) {
|
||||
parseError(error.str, error.hash);
|
||||
}
|
||||
errors.push({ ...error, message: error.str, error });
|
||||
} else {
|
||||
// assume it is just error string and pass it on
|
||||
if (parseError) {
|
||||
parseError(error);
|
||||
}
|
||||
if (error instanceof Error) {
|
||||
errors.push({
|
||||
str: error.message,
|
||||
message: error.message,
|
||||
hash: error.name,
|
||||
error,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const initThrowsErrors = function (
|
||||
config?: MermaidConfig,
|
||||
// eslint-disable-next-line no-undef
|
||||
nodes?: string | HTMLElement | NodeListOf<HTMLElement>,
|
||||
@@ -75,7 +101,6 @@ const initThrowsErrors = async function (
|
||||
callback?: Function
|
||||
) {
|
||||
const conf = mermaidAPI.getConfig();
|
||||
// console.log('Starting rendering diagrams (init) - mermaid.init', conf);
|
||||
if (config) {
|
||||
// This is a legacy way of setting config. It is not documented and should be removed in the future.
|
||||
// @ts-ignore: TODO Fix ts errors
|
||||
@@ -107,7 +132,7 @@ const initThrowsErrors = async function (
|
||||
const idGenerator = new utils.initIdGenerator(conf.deterministicIds, conf.deterministicIDSeed);
|
||||
|
||||
let txt: string;
|
||||
const errors = [];
|
||||
const errors: DetailedError[] = [];
|
||||
|
||||
// element is the current div with mermaid class
|
||||
for (const element of Array.from(nodesToProcess)) {
|
||||
@@ -134,7 +159,7 @@ const initThrowsErrors = async function (
|
||||
log.debug('Detected early reinit: ', init);
|
||||
}
|
||||
try {
|
||||
await mermaidAPI.render(
|
||||
mermaidAPI.render(
|
||||
id,
|
||||
txt,
|
||||
(svgCode: string, bindFunctions?: (el: Element) => void) => {
|
||||
@@ -147,13 +172,7 @@ const initThrowsErrors = async function (
|
||||
element
|
||||
);
|
||||
} catch (error) {
|
||||
log.warn('Catching Error (bootstrap)', error);
|
||||
// @ts-ignore: TODO Fix ts errors
|
||||
const mermaidError = { error, str: error.str, hash: error.hash, message: error.str };
|
||||
if (typeof mermaid.parseError === 'function') {
|
||||
mermaid.parseError(mermaidError);
|
||||
}
|
||||
errors.push(mermaidError);
|
||||
handleError(error, errors, mermaid.parseError);
|
||||
}
|
||||
}
|
||||
if (errors.length > 0) {
|
||||
@@ -162,8 +181,169 @@ const initThrowsErrors = async function (
|
||||
}
|
||||
};
|
||||
|
||||
const initialize = async function (config: MermaidConfig) {
|
||||
await mermaidAPI.initialize(config);
|
||||
/**
|
||||
* This is an internal function and should not be made public, as it will likely change.
|
||||
* @internal
|
||||
* @param diagrams - Array of {@link ExternalDiagramDefinition}.
|
||||
*/
|
||||
const registerLazyLoadedDiagrams = (diagrams: ExternalDiagramDefinition[]) => {
|
||||
for (const { id, detector, loader } of diagrams) {
|
||||
addDetector(id, detector, loader);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This is an internal function and should not be made public, as it will likely change.
|
||||
* @internal
|
||||
* @param diagrams - Array of {@link ExternalDiagramDefinition}.
|
||||
*/
|
||||
const loadExternalDiagrams = async (diagrams: ExternalDiagramDefinition[]) => {
|
||||
log.debug(`Loading ${diagrams.length} external diagrams`);
|
||||
// Load all lazy loaded diagrams in parallel
|
||||
const results = await Promise.allSettled(
|
||||
diagrams.map(async ({ id, detector, loader }) => {
|
||||
const { diagram } = await loader();
|
||||
registerDiagram(id, diagram, detector);
|
||||
})
|
||||
);
|
||||
const failed = results.filter((result) => result.status === 'rejected');
|
||||
if (failed.length > 0) {
|
||||
log.error(`Failed to load ${failed.length} external diagrams`);
|
||||
for (const res of failed) {
|
||||
log.error(res);
|
||||
}
|
||||
throw new Error(`Failed to load ${failed.length} external diagrams`);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Equivalent to {@link init()}, except an error will be thrown on error.
|
||||
*
|
||||
* @alpha
|
||||
* @deprecated This is an internal function and will very likely be modified in v10, or earlier.
|
||||
* We recommend staying with {@link initThrowsErrors} if you don't need `lazyLoadedDiagrams`.
|
||||
*
|
||||
* @param config - **Deprecated** Mermaid sequenceConfig.
|
||||
* @param nodes - One of:
|
||||
* - A DOM Node
|
||||
* - An array of DOM nodes (as would come from a jQuery selector)
|
||||
* - A W3C selector, a la `.mermaid` (default)
|
||||
* @param callback - Function that is called with the id of each generated mermaid diagram.
|
||||
* @returns Resolves on success, otherwise the {@link Promise} will be rejected.
|
||||
*/
|
||||
const initThrowsErrorsAsync = async function (
|
||||
config?: MermaidConfig,
|
||||
// eslint-disable-next-line no-undef
|
||||
nodes?: string | HTMLElement | NodeListOf<HTMLElement>,
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
callback?: Function
|
||||
) {
|
||||
const conf = mermaidAPI.getConfig();
|
||||
|
||||
if (config) {
|
||||
// This is a legacy way of setting config. It is not documented and should be removed in the future.
|
||||
// @ts-ignore: TODO Fix ts errors
|
||||
mermaid.sequenceConfig = config;
|
||||
}
|
||||
|
||||
// if last argument is a function this is the callback function
|
||||
log.debug(`${!callback ? 'No ' : ''}Callback function found`);
|
||||
let nodesToProcess: ArrayLike<HTMLElement>;
|
||||
if (typeof nodes === 'undefined') {
|
||||
nodesToProcess = document.querySelectorAll('.mermaid');
|
||||
} else if (typeof nodes === 'string') {
|
||||
nodesToProcess = document.querySelectorAll(nodes);
|
||||
} else if (nodes instanceof HTMLElement) {
|
||||
nodesToProcess = [nodes];
|
||||
} else if (nodes instanceof NodeList) {
|
||||
nodesToProcess = nodes;
|
||||
} else {
|
||||
throw new Error('Invalid argument nodes for mermaid.init');
|
||||
}
|
||||
|
||||
log.debug(`Found ${nodesToProcess.length} diagrams`);
|
||||
if (typeof config?.startOnLoad !== 'undefined') {
|
||||
log.debug('Start On Load: ' + config?.startOnLoad);
|
||||
mermaidAPI.updateSiteConfig({ startOnLoad: config?.startOnLoad });
|
||||
}
|
||||
|
||||
// generate the id of the diagram
|
||||
const idGenerator = new utils.initIdGenerator(conf.deterministicIds, conf.deterministicIDSeed);
|
||||
|
||||
let txt: string;
|
||||
const errors: DetailedError[] = [];
|
||||
|
||||
// element is the current div with mermaid class
|
||||
for (const element of Array.from(nodesToProcess)) {
|
||||
log.info('Rendering diagram: ' + element.id);
|
||||
/*! Check if previously processed */
|
||||
if (element.getAttribute('data-processed')) {
|
||||
continue;
|
||||
}
|
||||
element.setAttribute('data-processed', 'true');
|
||||
|
||||
const id = `mermaid-${idGenerator.next()}`;
|
||||
|
||||
// Fetch the graph definition including tags
|
||||
txt = element.innerHTML;
|
||||
|
||||
// transforms the html to pure text
|
||||
txt = utils
|
||||
.entityDecode(txt)
|
||||
.trim()
|
||||
.replace(/<br\s*\/?>/gi, '<br/>');
|
||||
|
||||
const init = utils.detectInit(txt);
|
||||
if (init) {
|
||||
log.debug('Detected early reinit: ', init);
|
||||
}
|
||||
try {
|
||||
await mermaidAPI.renderAsync(
|
||||
id,
|
||||
txt,
|
||||
(svgCode: string, bindFunctions?: (el: Element) => void) => {
|
||||
element.innerHTML = svgCode;
|
||||
if (typeof callback !== 'undefined') {
|
||||
callback(id);
|
||||
}
|
||||
if (bindFunctions) bindFunctions(element);
|
||||
},
|
||||
element
|
||||
);
|
||||
} catch (error) {
|
||||
handleError(error, errors, mermaid.parseError);
|
||||
}
|
||||
}
|
||||
if (errors.length > 0) {
|
||||
// TODO: We should be throwing an error object.
|
||||
throw errors[0];
|
||||
}
|
||||
};
|
||||
|
||||
const initialize = function (config: MermaidConfig) {
|
||||
mermaidAPI.initialize(config);
|
||||
};
|
||||
|
||||
/**
|
||||
* Used to register external diagram types.
|
||||
* @param diagrams - Array of {@link ExternalDiagramDefinition}.
|
||||
* @param opts
|
||||
* @param opts.lazyLoad - If true, the diagram will be loaded on demand.
|
||||
*/
|
||||
const registerExternalDiagrams = async (
|
||||
diagrams: ExternalDiagramDefinition[],
|
||||
{
|
||||
lazyLoad = true,
|
||||
}: {
|
||||
lazyLoad?: boolean;
|
||||
} = {}
|
||||
) => {
|
||||
if (lazyLoad) {
|
||||
registerLazyLoadedDiagrams(diagrams);
|
||||
} else {
|
||||
await loadExternalDiagrams(diagrams);
|
||||
}
|
||||
externalDiagramsRegistered = true;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -199,7 +379,7 @@ if (typeof document !== 'undefined') {
|
||||
* This is provided for environments where the mermaid object can't directly have a new member added
|
||||
* to it (eg. dart interop wrapper). (Initially there is no parseError member of mermaid).
|
||||
*
|
||||
* @param {function (err, hash)} newParseErrorHandler New parseError() callback.
|
||||
* @param newParseErrorHandler New parseError() callback.
|
||||
*/
|
||||
const setParseErrorHandler = function (newParseErrorHandler: (err: any, hash: any) => void) {
|
||||
mermaid.parseError = newParseErrorHandler;
|
||||
@@ -209,6 +389,125 @@ const parse = (txt: string) => {
|
||||
return mermaidAPI.parse(txt, mermaid.parseError);
|
||||
};
|
||||
|
||||
const executionQueue: (() => Promise<unknown>)[] = [];
|
||||
let executionQueueRunning = false;
|
||||
const executeQueue = async () => {
|
||||
if (executionQueueRunning) {
|
||||
return;
|
||||
}
|
||||
executionQueueRunning = true;
|
||||
while (executionQueue.length > 0) {
|
||||
const f = executionQueue.shift();
|
||||
if (f) {
|
||||
try {
|
||||
await f();
|
||||
} catch (e) {
|
||||
log.error('Error executing queue', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
executionQueueRunning = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param txt
|
||||
* @deprecated This is an internal function and should not be used. Will be removed in v10.
|
||||
*/
|
||||
const parseAsync = (txt: string): Promise<boolean> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// This promise will resolve when the mermaidAPI.render call is done.
|
||||
// It will be queued first and will be executed when it is first in line
|
||||
const performCall = () =>
|
||||
new Promise((res, rej) => {
|
||||
mermaidAPI.parseAsync(txt, mermaid.parseError).then(
|
||||
(r) => {
|
||||
// This resolves for the promise for the queue handling
|
||||
res(r);
|
||||
// This fulfills the promise sent to the value back to the original caller
|
||||
resolve(r);
|
||||
},
|
||||
(e) => {
|
||||
log.error('Error parsing', e);
|
||||
rej(e);
|
||||
reject(e);
|
||||
}
|
||||
);
|
||||
});
|
||||
executionQueue.push(performCall);
|
||||
executeQueue();
|
||||
});
|
||||
};
|
||||
|
||||
// const asynco = (id: string, delay: number) =>
|
||||
// new Promise((res) => {
|
||||
// setTimeout(() => {
|
||||
// // This resolves for the promise for the queue handling
|
||||
// res(id);
|
||||
// }, delay);
|
||||
// });
|
||||
|
||||
/**
|
||||
* @param txt
|
||||
* @param id
|
||||
* @param delay
|
||||
* @deprecated This is an internal function and should not be used. Will be removed in v10.
|
||||
*/
|
||||
// const test1 = (id: string, delay: number) => {
|
||||
// const p = new Promise((resolve, reject) => {
|
||||
// // This promise will resolve when the mermaidAPI.render call is done.
|
||||
// // It will be queued first and will be executed when it is first in line
|
||||
// const performCall = () =>
|
||||
// new Promise((res) => {
|
||||
// asynco(id, delay).then((r) => {
|
||||
// // This resolves for the promise for the queue handling
|
||||
// res(r);
|
||||
// // This fullfills the promise sent to the value back to the original caller
|
||||
// resolve(r + ' result to caller');
|
||||
// });
|
||||
// });
|
||||
// executionQueue.push(performCall);
|
||||
// });
|
||||
// return p;
|
||||
// };
|
||||
|
||||
/**
|
||||
* @param txt
|
||||
* @param id
|
||||
* @param text
|
||||
* @param cb
|
||||
* @param container
|
||||
* @deprecated This is an internal function and should not be used. Will be removed in v10.
|
||||
*/
|
||||
const renderAsync = (
|
||||
id: string,
|
||||
text: string,
|
||||
cb?: (svgCode: string, bindFunctions?: (element: Element) => void) => void,
|
||||
container?: Element
|
||||
): Promise<string> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// This promise will resolve when the mermaidAPI.render call is done.
|
||||
// It will be queued first and will be executed when it is first in line
|
||||
const performCall = () =>
|
||||
new Promise((res, rej) => {
|
||||
mermaidAPI.renderAsync(id, text, cb, container).then(
|
||||
(r) => {
|
||||
// This resolves for the promise for the queue handling
|
||||
res(r);
|
||||
// This fullfills the promise sent to the value back to the original caller
|
||||
resolve(r);
|
||||
},
|
||||
(e) => {
|
||||
log.error('Error parsing', e);
|
||||
rej(e);
|
||||
reject(e);
|
||||
}
|
||||
);
|
||||
});
|
||||
executionQueue.push(performCall);
|
||||
executeQueue();
|
||||
});
|
||||
};
|
||||
|
||||
const mermaid: {
|
||||
startOnLoad: boolean;
|
||||
diagrams: any;
|
||||
@@ -216,9 +515,13 @@ const mermaid: {
|
||||
parseError?: Function;
|
||||
mermaidAPI: typeof mermaidAPI;
|
||||
parse: typeof parse;
|
||||
parseAsync: typeof parseAsync;
|
||||
render: typeof mermaidAPI.render;
|
||||
renderAsync: typeof renderAsync;
|
||||
init: typeof init;
|
||||
initThrowsErrors: typeof initThrowsErrors;
|
||||
initThrowsErrorsAsync: typeof initThrowsErrorsAsync;
|
||||
registerExternalDiagrams: typeof registerExternalDiagrams;
|
||||
initialize: typeof initialize;
|
||||
contentLoaded: typeof contentLoaded;
|
||||
setParseErrorHandler: typeof setParseErrorHandler;
|
||||
@@ -227,9 +530,13 @@ const mermaid: {
|
||||
diagrams: {},
|
||||
mermaidAPI,
|
||||
parse,
|
||||
parseAsync,
|
||||
render: mermaidAPI.render,
|
||||
renderAsync,
|
||||
init,
|
||||
initThrowsErrors,
|
||||
initThrowsErrorsAsync,
|
||||
registerExternalDiagrams,
|
||||
initialize,
|
||||
parseError: undefined,
|
||||
contentLoaded,
|
||||
|
@@ -44,6 +44,18 @@ function parse(text: string, parseError?: Function): boolean {
|
||||
return diagram.parse(text, parseError);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param text
|
||||
* @param parseError
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
async function parseAsync(text: string, parseError?: Function): Promise<boolean> {
|
||||
addDiagrams();
|
||||
const diagram = await getDiagramFromText(text, parseError);
|
||||
return diagram.parse(text, parseError);
|
||||
}
|
||||
|
||||
export const encodeEntities = function (text: string): string {
|
||||
let txt = text;
|
||||
|
||||
@@ -103,19 +115,295 @@ export const decodeEntities = function (text: string): string {
|
||||
*
|
||||
* @param {string} id The id of the element to be rendered
|
||||
* @param {string} text The graph definition
|
||||
* @param {(svgCode: string, bindFunctions?: (element: Element) => void) => void} cb Callback which
|
||||
* @param cb - Optional callback which
|
||||
* is called after rendering is finished with the svg code as inparam.
|
||||
* @param {Element} container Selector to element in which a div with the graph temporarily will be
|
||||
* inserted. If one is provided a hidden div will be inserted in the body of the page instead. The
|
||||
* element will be removed when rendering is completed.
|
||||
* @returns {void}
|
||||
* @returns Returns the rendered element as a string containing the SVG definition.
|
||||
*/
|
||||
const render = async function (
|
||||
const render = function (
|
||||
id: string,
|
||||
text: string,
|
||||
cb: (svgCode: string, bindFunctions?: (element: Element) => void) => void,
|
||||
cb?: (svgCode: string, bindFunctions?: (element: Element) => void) => void,
|
||||
container?: Element
|
||||
): Promise<void> {
|
||||
): string {
|
||||
addDiagrams();
|
||||
configApi.reset();
|
||||
text = text.replace(/\r\n?/g, '\n'); // parser problems on CRLF ignore all CR and leave LF;;
|
||||
const graphInit = utils.detectInit(text);
|
||||
if (graphInit) {
|
||||
directiveSanitizer(graphInit);
|
||||
configApi.addDirective(graphInit);
|
||||
}
|
||||
const cnf = configApi.getConfig();
|
||||
|
||||
log.debug(cnf);
|
||||
|
||||
// Check the maximum allowed text size
|
||||
if (text.length > cnf.maxTextSize!) {
|
||||
text = 'graph TB;a[Maximum text size in diagram exceeded];style a fill:#faa';
|
||||
}
|
||||
|
||||
let root: any = select('body');
|
||||
|
||||
// In regular execution the container will be the div with a mermaid class
|
||||
if (typeof container !== 'undefined') {
|
||||
// A container was provided by the caller
|
||||
if (container) {
|
||||
container.innerHTML = '';
|
||||
}
|
||||
|
||||
if (cnf.securityLevel === 'sandbox') {
|
||||
// IF we are in sandboxed mode, we do everyting mermaid related
|
||||
// in a sandboxed div
|
||||
const iframe = select(container)
|
||||
.append('iframe')
|
||||
.attr('id', 'i' + id)
|
||||
.attr('style', 'width: 100%; height: 100%;')
|
||||
.attr('sandbox', '');
|
||||
// const iframeBody = ;
|
||||
root = select(iframe.nodes()[0]!.contentDocument!.body);
|
||||
root.node().style.margin = 0;
|
||||
} else {
|
||||
root = select(container);
|
||||
}
|
||||
|
||||
root
|
||||
.append('div')
|
||||
.attr('id', 'd' + id)
|
||||
.attr('style', 'font-family: ' + cnf.fontFamily)
|
||||
.append('svg')
|
||||
.attr('id', id)
|
||||
.attr('width', '100%')
|
||||
.attr('xmlns', 'http://www.w3.org/2000/svg')
|
||||
.attr('xmlns:xlink', 'http://www.w3.org/1999/xlink')
|
||||
.append('g');
|
||||
} else {
|
||||
// No container was provided
|
||||
// If there is an existing element with the id, we remove it
|
||||
// this likely a previously rendered diagram
|
||||
const existingSvg = document.getElementById(id);
|
||||
if (existingSvg) {
|
||||
existingSvg.remove();
|
||||
}
|
||||
|
||||
// Remove previous tpm element if it exists
|
||||
let element;
|
||||
if (cnf.securityLevel === 'sandbox') {
|
||||
element = document.querySelector('#i' + id);
|
||||
} else {
|
||||
element = document.querySelector('#d' + id);
|
||||
}
|
||||
|
||||
if (element) {
|
||||
element.remove();
|
||||
}
|
||||
|
||||
// Add the tmp div used for rendering with the id `d${id}`
|
||||
// d+id it will contain a svg with the id "id"
|
||||
|
||||
if (cnf.securityLevel === 'sandbox') {
|
||||
// IF we are in sandboxed mode, we do everyting mermaid related
|
||||
// in a sandboxed div
|
||||
const iframe = select('body')
|
||||
.append('iframe')
|
||||
.attr('id', 'i' + id)
|
||||
.attr('style', 'width: 100%; height: 100%;')
|
||||
.attr('sandbox', '');
|
||||
|
||||
root = select(iframe.nodes()[0]!.contentDocument!.body);
|
||||
root.node().style.margin = 0;
|
||||
} else {
|
||||
root = select('body');
|
||||
}
|
||||
|
||||
// This is the temporary div
|
||||
root
|
||||
.append('div')
|
||||
.attr('id', 'd' + id)
|
||||
// this is the seed of the svg to be rendered
|
||||
.append('svg')
|
||||
.attr('id', id)
|
||||
.attr('width', '100%')
|
||||
.attr('xmlns', 'http://www.w3.org/2000/svg')
|
||||
.append('g');
|
||||
}
|
||||
|
||||
text = encodeEntities(text);
|
||||
|
||||
// Important that we do not create the diagram until after the directives have been included
|
||||
let diag;
|
||||
let parseEncounteredException;
|
||||
try {
|
||||
// diag = new Diagram(text);
|
||||
diag = getDiagramFromText(text);
|
||||
if ('then' in diag) {
|
||||
throw new Error('Diagram is a promise');
|
||||
}
|
||||
} catch (error) {
|
||||
diag = new Diagram('error');
|
||||
parseEncounteredException = error;
|
||||
}
|
||||
// Get the tmp element containing the the svg
|
||||
const element = root.select('#d' + id).node();
|
||||
const graphType = diag.type;
|
||||
|
||||
// insert inline style into svg
|
||||
const svg = element.firstChild;
|
||||
const firstChild = svg.firstChild;
|
||||
|
||||
let userStyles = '';
|
||||
// user provided theme CSS
|
||||
// If you add more configuration driven data into the user styles make sure that the value is
|
||||
// sanitized bye the santiizeCSS function
|
||||
if (cnf.themeCSS !== undefined) {
|
||||
userStyles += `\n${cnf.themeCSS}`;
|
||||
}
|
||||
// user provided theme CSS
|
||||
if (cnf.fontFamily !== undefined) {
|
||||
userStyles += `\n:root { --mermaid-font-family: ${cnf.fontFamily}}`;
|
||||
}
|
||||
// user provided theme CSS
|
||||
if (cnf.altFontFamily !== undefined) {
|
||||
userStyles += `\n:root { --mermaid-alt-font-family: ${cnf.altFontFamily}}`;
|
||||
}
|
||||
|
||||
// classDef
|
||||
if (graphType === 'flowchart' || graphType === 'flowchart-v2' || graphType === 'graph') {
|
||||
const classes: any = flowRenderer.getClasses(text, diag);
|
||||
const htmlLabels = cnf.htmlLabels || cnf.flowchart?.htmlLabels;
|
||||
for (const className in classes) {
|
||||
if (htmlLabels) {
|
||||
userStyles += `\n.${className} > * { ${classes[className].styles.join(
|
||||
' !important; '
|
||||
)} !important; }`;
|
||||
userStyles += `\n.${className} span { ${classes[className].styles.join(
|
||||
' !important; '
|
||||
)} !important; }`;
|
||||
} else {
|
||||
userStyles += `\n.${className} path { ${classes[className].styles.join(
|
||||
' !important; '
|
||||
)} !important; }`;
|
||||
userStyles += `\n.${className} rect { ${classes[className].styles.join(
|
||||
' !important; '
|
||||
)} !important; }`;
|
||||
userStyles += `\n.${className} polygon { ${classes[className].styles.join(
|
||||
' !important; '
|
||||
)} !important; }`;
|
||||
userStyles += `\n.${className} ellipse { ${classes[className].styles.join(
|
||||
' !important; '
|
||||
)} !important; }`;
|
||||
userStyles += `\n.${className} circle { ${classes[className].styles.join(
|
||||
' !important; '
|
||||
)} !important; }`;
|
||||
if (classes[className].textStyles) {
|
||||
userStyles += `\n.${className} tspan { ${classes[className].textStyles.join(
|
||||
' !important; '
|
||||
)} !important; }`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const stylis = (selector: string, styles: string) =>
|
||||
serialize(compile(`${selector}{${styles}}`), stringify);
|
||||
const rules = stylis(`#${id}`, getStyles(graphType, userStyles, cnf.themeVariables));
|
||||
|
||||
const style1 = document.createElement('style');
|
||||
style1.innerHTML = `#${id} ` + rules;
|
||||
svg.insertBefore(style1, firstChild);
|
||||
|
||||
try {
|
||||
diag.renderer.draw(text, id, pkg.version, diag);
|
||||
} catch (e) {
|
||||
errorRenderer.draw(text, id, pkg.version);
|
||||
throw e;
|
||||
}
|
||||
|
||||
root
|
||||
.select(`[id="${id}"]`)
|
||||
.selectAll('foreignobject > *')
|
||||
.attr('xmlns', 'http://www.w3.org/1999/xhtml');
|
||||
|
||||
// Fix for when the base tag is used
|
||||
let svgCode = root.select('#d' + id).node().innerHTML;
|
||||
|
||||
log.debug('cnf.arrowMarkerAbsolute', cnf.arrowMarkerAbsolute);
|
||||
if (!evaluate(cnf.arrowMarkerAbsolute) && cnf.securityLevel !== 'sandbox') {
|
||||
svgCode = svgCode.replace(/marker-end="url\(.*?#/g, 'marker-end="url(#', 'g');
|
||||
}
|
||||
|
||||
svgCode = decodeEntities(svgCode);
|
||||
|
||||
// Fix for when the br tag is used
|
||||
svgCode = svgCode.replace(/<br>/g, '<br/>');
|
||||
|
||||
if (cnf.securityLevel === 'sandbox') {
|
||||
const svgEl = root.select('#d' + id + ' svg').node();
|
||||
const width = '100%';
|
||||
let height = '100%';
|
||||
if (svgEl) {
|
||||
height = svgEl.viewBox.baseVal.height + 'px';
|
||||
}
|
||||
svgCode = `<iframe style="width:${width};height:${height};border:0;margin:0;" src="data:text/html;base64,${btoa(
|
||||
'<body style="margin:0">' + svgCode + '</body>'
|
||||
)}" sandbox="allow-top-navigation-by-user-activation allow-popups">
|
||||
The “iframe” tag is not supported by your browser.
|
||||
</iframe>`;
|
||||
} else {
|
||||
if (cnf.securityLevel !== 'loose') {
|
||||
svgCode = DOMPurify.sanitize(svgCode, {
|
||||
ADD_TAGS: ['foreignobject'],
|
||||
ADD_ATTR: ['dominant-baseline'],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof cb !== 'undefined') {
|
||||
switch (graphType) {
|
||||
case 'flowchart':
|
||||
case 'flowchart-v2':
|
||||
cb(svgCode, flowDb.bindFunctions);
|
||||
break;
|
||||
case 'gantt':
|
||||
cb(svgCode, ganttDb.bindFunctions);
|
||||
break;
|
||||
case 'class':
|
||||
case 'classDiagram':
|
||||
cb(svgCode, classDb.bindFunctions);
|
||||
break;
|
||||
default:
|
||||
cb(svgCode);
|
||||
}
|
||||
} else {
|
||||
log.debug('CB = undefined!');
|
||||
}
|
||||
attachFunctions();
|
||||
|
||||
const tmpElementSelector = cnf.securityLevel === 'sandbox' ? '#i' + id : '#d' + id;
|
||||
const node = select(tmpElementSelector).node();
|
||||
if (node && 'remove' in node) {
|
||||
node.remove();
|
||||
}
|
||||
|
||||
if (parseEncounteredException) {
|
||||
throw parseEncounteredException;
|
||||
}
|
||||
|
||||
return svgCode;
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated This is an internal function and should not be used. Will be removed in v10.
|
||||
*/
|
||||
|
||||
const renderAsync = async function (
|
||||
id: string,
|
||||
text: string,
|
||||
cb?: (svgCode: string, bindFunctions?: (element: Element) => void) => void,
|
||||
container?: Element
|
||||
): Promise<string> {
|
||||
addDiagrams();
|
||||
configApi.reset();
|
||||
text = text.replace(/\r\n?/g, '\n'); // parser problems on CRLF ignore all CR and leave LF;;
|
||||
@@ -302,7 +590,7 @@ const render = async function (
|
||||
try {
|
||||
await diag.renderer.draw(text, id, pkg.version, diag);
|
||||
} catch (e) {
|
||||
await errorRenderer.draw(text, id, pkg.version);
|
||||
errorRenderer.draw(text, id, pkg.version);
|
||||
throw e;
|
||||
}
|
||||
|
||||
@@ -453,12 +741,13 @@ const handleDirective = function (p: any, directive: any, type: string): void {
|
||||
};
|
||||
|
||||
/** @param {MermaidConfig} options */
|
||||
async function initialize(options: MermaidConfig) {
|
||||
function initialize(options: MermaidConfig = {}) {
|
||||
// Handle legacy location of font-family configuration
|
||||
if (options?.fontFamily) {
|
||||
if (!options.themeVariables?.fontFamily) {
|
||||
options.themeVariables = { fontFamily: options.fontFamily };
|
||||
if (options.fontFamily) {
|
||||
if (!options.themeVariables) {
|
||||
options.themeVariables = {};
|
||||
}
|
||||
options.themeVariables.fontFamily = options.fontFamily;
|
||||
}
|
||||
|
||||
// Set default options
|
||||
@@ -482,7 +771,9 @@ async function initialize(options: MermaidConfig) {
|
||||
|
||||
export const mermaidAPI = Object.freeze({
|
||||
render,
|
||||
renderAsync,
|
||||
parse,
|
||||
parseAsync,
|
||||
parseDirective,
|
||||
initialize,
|
||||
getConfig: configApi.getConfig,
|
||||
@@ -501,6 +792,7 @@ export const mermaidAPI = Object.freeze({
|
||||
setLogLevel(configApi.getConfig().logLevel);
|
||||
configApi.reset(configApi.getConfig());
|
||||
export default mermaidAPI;
|
||||
|
||||
/**
|
||||
* ## mermaidAPI configuration defaults
|
||||
*
|
||||
|
@@ -26,7 +26,6 @@ export const calculateSvgSizeAttrs = function (height, width, useMaxWidth) {
|
||||
attrs.set('width', '100%');
|
||||
attrs.set('style', `max-width: ${width}px;`);
|
||||
} else {
|
||||
attrs.set('height', height);
|
||||
attrs.set('width', width);
|
||||
}
|
||||
return attrs;
|
||||
|
@@ -147,11 +147,18 @@ class Theme {
|
||||
this['cScaleInv' + i] = this['cScaleInv' + i] || adjust(this['cScale' + i], { h: 180 });
|
||||
}
|
||||
|
||||
// Setup teh label color for the set
|
||||
this.scaleLabelColor = this.scaleLabelColor || (this.darkMode ? 'black' : this.labelTextColor);
|
||||
// Setup the label color for the set
|
||||
this.scaleLabelColor =
|
||||
this.scaleLabelColor !== 'calculated' && this.scaleLabelColor
|
||||
? this.scaleLabelColor
|
||||
: this.labelTextColor;
|
||||
|
||||
for (let i = 0; i < this.THEME_COLOR_LIMIT; i++) {
|
||||
this['cScaleLabel' + i] = this['cScaleLabel' + i] || this.scaleLabelColor;
|
||||
if (this.labelTextColor !== 'calculated') {
|
||||
this.cScaleLabel0 = this.cScaleLabel0 || invert(this.labelTextColor);
|
||||
this.cScaleLabel3 = this.cScaleLabel3 || invert(this.labelTextColor);
|
||||
for (let i = 0; i < this.THEME_COLOR_LIMIT; i++) {
|
||||
this['cScaleLabel' + i] = this['cScaleLabel' + i] || this.labelTextColor;
|
||||
}
|
||||
}
|
||||
|
||||
/* Flowchart variables */
|
||||
|
@@ -121,7 +121,10 @@ class Theme {
|
||||
}
|
||||
|
||||
// Setup teh label color for the set
|
||||
this.scaleLabelColor = this.scaleLabelColor || (this.darkMode ? 'black' : this.labelTextColor);
|
||||
this.scaleLabelColor =
|
||||
this.scaleLabelColor !== 'calculated' && this.scaleLabelColor
|
||||
? this.scaleLabelColor
|
||||
: this.labelTextColor;
|
||||
|
||||
for (let i = 0; i < this.THEME_COLOR_LIMIT; i++) {
|
||||
this['cScaleLabel' + i] = this['cScaleLabel' + i] || this.scaleLabelColor;
|
||||
|
@@ -825,6 +825,8 @@ export const sanitizeCss = (str) => {
|
||||
export interface DetailedError {
|
||||
str: string;
|
||||
hash: any;
|
||||
error?: any;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
/** @param error */
|
||||
|
1435
pnpm-lock.yaml
generated
1435
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,4 @@
|
||||
packages:
|
||||
# all packages in direct subdirs of packages/
|
||||
- 'packages/*'
|
||||
# - 'tests/*'
|
||||
|
23
tests/webpack/package.json
Normal file
23
tests/webpack/package.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "webpack",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"private": true,
|
||||
"module": "commonjs",
|
||||
"scripts": {
|
||||
"build": "webpack",
|
||||
"serve": "webpack serve"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"webpack": "^5.74.0",
|
||||
"webpack-cli": "^4.10.0",
|
||||
"webpack-dev-server": "^4.11.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"mermaid": "workspace:*",
|
||||
"@mermaid-js/mermaid-mindmap": "workspace:*"
|
||||
}
|
||||
}
|
11
tests/webpack/public/index.html
Normal file
11
tests/webpack/public/index.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Getting Started</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="graphDiv"></div>
|
||||
<script src="./main.js"></script>
|
||||
</body>
|
||||
</html>
|
38
tests/webpack/src/index.js
Normal file
38
tests/webpack/src/index.js
Normal file
@@ -0,0 +1,38 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
/* eslint-disable no-console */
|
||||
const mermaid = require('mermaid');
|
||||
import mindmap from '@mermaid-js/mermaid-mindmap';
|
||||
|
||||
const render = async (graph) => {
|
||||
const svg = await mermaid.renderAsync('dummy', graph);
|
||||
console.log(svg);
|
||||
document.getElementById('graphDiv').innerHTML = svg;
|
||||
};
|
||||
|
||||
const load = async () => {
|
||||
await mermaid.registerExternalDiagrams([mindmap]);
|
||||
await render('info');
|
||||
|
||||
setTimeout(async () => {
|
||||
await render(`mindmap
|
||||
root((mindmap))
|
||||
Origins
|
||||
Long history
|
||||
::icon(fa fa-book)
|
||||
Popularisation
|
||||
British popular psychology author Tony Buzan
|
||||
Research
|
||||
On effectivness<br/>and features
|
||||
On Automatic creation
|
||||
Uses
|
||||
Creative techniques
|
||||
Strategic planning
|
||||
Argument mapping
|
||||
Tools
|
||||
Pen and paper
|
||||
Mermaid
|
||||
`);
|
||||
}, 2500);
|
||||
};
|
||||
|
||||
window.addEventListener('load', load, false);
|
10
tests/webpack/webpack.config.js
Normal file
10
tests/webpack/webpack.config.js
Normal file
@@ -0,0 +1,10 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
entry: './src/index.js',
|
||||
output: {
|
||||
filename: 'main.js',
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
},
|
||||
};
|
Reference in New Issue
Block a user