Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: mermaid-js/mermaid
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v9.2.1
Choose a base ref
...
head repository: mermaid-js/mermaid
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v9.2.2
Choose a head ref

Commits on Oct 23, 2022

  1. fix(mermaid): fix DiagramDefinition types

    The `injectUtils` function takes the utils as multiple parameters,
    not an object.
    aloisklink committed Oct 23, 2022

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    41249fd View commit details
  2. refactor(mermaid): remove registerDiagram cb func

    Remove the callback function parameter from registerDiagram.
    Instead, we can just load the callback function from the `injectUtils`
    diagram definition, if it exists.
    aloisklink committed Oct 23, 2022

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    89d3d29 View commit details
  3. feat: add mermaidAPI.registerDiagram()

    Exposes the registerDiagram() function publically as
    `mermaid.mermaidAPI.registerDiagram` so that users can add their
    own diagrams at bundle-time.
    
    This is instead of using the lazyLoadedDiagrams config setting.
    aloisklink committed Oct 23, 2022

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    f41e34e View commit details

Commits on Nov 4, 2022

  1. Basic webpack

    sidharthv96 committed Nov 4, 2022

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    1e41783 View commit details

Commits on Nov 6, 2022

  1. fix(mermaid): fix mermaid.render types

    The cb param of mermaid.render should be optional,
    and mermaid.render returns an SVG string instead of `void`.
    aloisklink committed Nov 6, 2022

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    da6bb94 View commit details
  2. fix(mermaid): default mermaid back to CommonJS

    Default mermaid back to being a CommonJS module.
    
    Improrting Mermaid as CommonJS (e.g. using `require("mermaid")`)
    is normally broken (since v8), due to it's dependency on d3,
    which is now ESM only.
    
    However, it looks like some software
    (e.g. TypeScript, in the docusaurus project)
    could still handle the CommonJS version of Mermaid.
    
    This commit now means that older versions of Node/build-tools
    should now default to using the CommonJS version of Mermaid.
    
    Newer tools should still see that the `"module"` field points to ESM,
    or use the `exports["."]["import"]` field to load ESM.
    aloisklink committed Nov 6, 2022

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    1a5e731 View commit details

Commits on Nov 7, 2022

  1. Merge pull request #3768 from aloisklink/fix/fix-mermaid.render-types

    [9.2] fix(mermaid): fix `mermaid.render` types
    sidharthv96 authored Nov 7, 2022

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    41dbf0f View commit details
  2. Merge pull request #3767 from aloisklink/fix/convert-package-back-to-…

    …commonjs
    
    [9.2] fix(mermaid): default mermaid back to CommonJS
    sidharthv96 authored Nov 7, 2022

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    2243af1 View commit details
  3. Merge branch 'release_9.2.1_bugfixes' into sidv/webpackTest

    * release_9.2.1_bugfixes:
      fix(mermaid): default mermaid back to CommonJS
      fix(mermaid): fix mermaid.render types
    sidharthv96 committed Nov 7, 2022

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    75d276e View commit details

Commits on Nov 8, 2022

  1. webpack test

    sidharthv96 committed Nov 8, 2022

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    166dca4 View commit details
  2. Merge branch 'feat/3701-expose-registerDiagram' into sidv/fixLL

    * feat/3701-expose-registerDiagram:
      feat: add `mermaidAPI.registerDiagram()`
      refactor(mermaid): remove registerDiagram cb func
      fix(mermaid): fix DiagramDefinition types
    sidharthv96 committed Nov 8, 2022

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    aab8f92 View commit details
  3. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    7ca5256 View commit details
  4. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    20b4358 View commit details
  5. fix: Filenames

    sidharthv96 committed Nov 8, 2022

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    6d2552e View commit details
  6. fix Lint

    sidharthv96 committed Nov 8, 2022
    Copy the full SHA
    605f288 View commit details
  7. Bump pnpm

    sidharthv96 committed Nov 8, 2022
    Copy the full SHA
    e64e98f View commit details
  8. Fix pnpm

    sidharthv96 committed Nov 8, 2022
    Copy the full SHA
    745abb8 View commit details
  9. Merge branch 'release/9.2.1' of https://github.com/mermaid-js/mermaid

    …into sidv/fixLL
    
    * 'release/9.2.1' of https://github.com/mermaid-js/mermaid:
      Fixing applitools batches
    sidharthv96 committed Nov 8, 2022
    Copy the full SHA
    1b2dce9 View commit details
  10. Fix pnpm-lock

    sidharthv96 committed Nov 8, 2022
    Copy the full SHA
    5b53cee View commit details
  11. fix pnpm lock

    sidharthv96 committed Nov 8, 2022
    Copy the full SHA
    d2511f6 View commit details
  12. Copy the full SHA
    b03ac38 View commit details
  13. Cleanup package.json

    sidharthv96 committed Nov 8, 2022
    Copy the full SHA
    8e63a07 View commit details
  14. Copy the full SHA
    c7f7ff3 View commit details

Commits on Nov 9, 2022

  1. Apply suggestions from code review

    Co-authored-by: Alois Klink <alois@aloisklink.com>
    sidharthv96 and aloisklink authored Nov 9, 2022
    Copy the full SHA
    89da6ea View commit details
  2. Copy the full SHA
    649ab17 View commit details
  3. fix versions

    sidharthv96 committed Nov 9, 2022
    Copy the full SHA
    f52df30 View commit details
  4. Merge branch 'sidv/fixLL' of https://github.com/mermaid-js/mermaid in…

    …to sidv/fixLL
    
    * 'sidv/fixLL' of https://github.com/mermaid-js/mermaid:
      Apply suggestions from code review
    sidharthv96 committed Nov 9, 2022
    Copy the full SHA
    c309e3e View commit details
  5. Copy the full SHA
    ecc51d7 View commit details
  6. Merge pull request #3774 from mermaid-js/sidv/fixLL

    Fix lazy loading in webpack
    knsv authored Nov 9, 2022
    Copy the full SHA
    2f9d6e0 View commit details
  7. Copy the full SHA
    9bb0ed2 View commit details
3 changes: 2 additions & 1 deletion .npmrc
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
auto-install-peers=true
auto-install-peers=true
strict-peer-dependencies=false
24 changes: 7 additions & 17 deletions .vite/build.ts
Original file line number Diff line number Diff line change
@@ -23,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 {
@@ -111,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/**',
],
};
}
@@ -141,7 +131,7 @@ if (watch) {
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' }));
// build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-example-diagram' }));
}
} else {
void main();
13 changes: 13 additions & 0 deletions cypress/integration/other/external-diagrams.spec.js
Original file line number Diff line number Diff line change
@@ -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 changes: 49 additions & 0 deletions cypress/platform/external-diagrams-mindmap.html
Original file line number Diff line number Diff line change
@@ -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>
6 changes: 3 additions & 3 deletions cypress/platform/viewer.js
Original file line number Diff line number Diff line change
@@ -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();
}
22 changes: 10 additions & 12 deletions docs/index.html
Original file line number Diff line number Diff line change
@@ -49,31 +49,29 @@
<body>
<div id="app"></div>
<script type="module">
// import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@9/dist/mermaid.esm.min.mjs';
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@9.2.0/dist/mermaid.esm.min.mjs';
// import mermaid from 'http://localhost:9000/mermaid.esm.mjs';
console.log(mermaid); // eslint-disable-line
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; }',
lazyLoadedDiagrams: [
'https://cdn.jsdelivr.net/npm/@mermaid-js/mermaid-mindmap@9.2.0/dist/mermaid-mindmap-detector.esm.mjs',
// 'http://localhost:9000/mermaid-mindmap-detector.esm.mjs',
],
};
if (isDarkMode) conf.theme = 'dark';

async function loadMermaid() {
await mermaid.initialize(conf);
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
}
mermaid.parseError = (e) => {
console.log('parse error', e); // eslint-disable-line
};

await loadMermaid();
</script>
<script>
14 changes: 0 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
@@ -3,19 +3,8 @@
"private": true,
"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",
@@ -147,9 +136,6 @@
"resolutions": {
"d3": "^7.0.0"
},
"files": [
"dist"
],
"sideEffects": [
"**/*.css",
"**/*.scss"
3 changes: 3 additions & 0 deletions packages/mermaid-example-diagram/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
### Do not refer this package. It is not ready.

### Refer mermaid-mindmap instead.
2 changes: 2 additions & 0 deletions packages/mermaid-example-diagram/src/diagram-definition.ts
Original file line number Diff line number Diff line change
@@ -12,3 +12,5 @@ export const diagram = {
styles,
injectUtils,
};

export { detector, id } from './detector';
8 changes: 4 additions & 4 deletions packages/mermaid-mindmap/package.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
{
"name": "@mermaid-js/mermaid-mindmap",
"version": "9.2.0",
"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"
},
"./*": "./*"
},
16 changes: 13 additions & 3 deletions packages/mermaid-mindmap/src/detector.ts
Original file line number Diff line number Diff line change
@@ -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 change: 1 addition & 0 deletions packages/mermaid/README.md
9 changes: 5 additions & 4 deletions packages/mermaid/package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"name": "mermaid",
"version": "9.2.0",
"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",
@@ -127,7 +127,8 @@
"d3": "^7.0.0"
},
"files": [
"dist"
"dist",
"README.md"
],
"sideEffects": [
"**/*.css",
3 changes: 1 addition & 2 deletions packages/mermaid/src/Diagram.ts
Original file line number Diff line number Diff line change
@@ -106,11 +106,10 @@ export const getDiagramFromText = (
// 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, diagram.injectUtils);
registerDiagram(type, diagram, undefined);
return new Diagram(txt, parseError);
});
}
// return new Diagram(txt, parseError);
};

export default Diagram;
28 changes: 26 additions & 2 deletions packages/mermaid/src/config.ts
Original file line number Diff line number Diff line change
@@ -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');
}
};
2 changes: 2 additions & 0 deletions packages/mermaid/src/config.type.ts
Original file line number Diff line number Diff line change
@@ -3,7 +3,9 @@
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;
1 change: 0 additions & 1 deletion packages/mermaid/src/defaultConfig.ts
Original file line number Diff line number Diff line change
@@ -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
23 changes: 13 additions & 10 deletions packages/mermaid/src/diagram-api/diagramAPI.ts
Original file line number Diff line number Diff line change
@@ -22,17 +22,19 @@ 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]) {
@@ -48,8 +50,9 @@ export const registerDiagram = (
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.`);
};
16 changes: 14 additions & 2 deletions packages/mermaid/src/diagram-api/types.ts
Original file line number Diff line number Diff line change
@@ -14,13 +14,25 @@ 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 {
detector: DiagramDetector;
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 }>;
22 changes: 10 additions & 12 deletions packages/mermaid/src/docs/index.html
Original file line number Diff line number Diff line change
@@ -49,31 +49,29 @@
<body>
<div id="app"></div>
<script type="module">
// import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@<MERMAID_VERSION>/dist/mermaid.esm.min.mjs';
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@9.2.0-rc6/dist/mermaid.esm.min.mjs';
// import mermaid from 'http://localhost:9000/mermaid.esm.mjs';
console.log(mermaid); // eslint-disable-line
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; }',
lazyLoadedDiagrams: [
'https://cdn.jsdelivr.net/npm/@mermaid-js/mermaid-mindmap@9.2.0-rc3/dist/mermaid-mindmap-detector.esm.mjs',
// 'http://localhost:9000/mermaid-mindmap-detector.esm.mjs',
],
};
if (isDarkMode) conf.theme = 'dark';

async function loadMermaid() {
await mermaid.initialize(conf);
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
}
mermaid.parseError = (e) => {
console.log('parse error', e); // eslint-disable-line
};

await loadMermaid();
</script>
<script>
81 changes: 70 additions & 11 deletions packages/mermaid/src/mermaid.spec.ts
Original file line number Diff line number Diff line change
@@ -54,24 +54,83 @@ describe('when using mermaid and ', function () {
expect(mermaidAPI.render).toHaveBeenCalled();
});
});
describe('when using #initThrowsErrorsAsync', function () {
it('should throw error (but still render) if lazyLoadedDiagram fails', async () => {
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;'));

mermaidAPI.setConfig({
lazyLoadedDiagrams: ['this-file-does-not-exist.mjs'],
});
await expect(mermaid.initThrowsErrorsAsync(undefined, node)).rejects.toThrowError(
// this error message is probably different on every platform
// this one is just for vite-note (node/jest/browser may be different)
'Failed to load this-file-does-not-exist.mjs'
);

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();
113 changes: 56 additions & 57 deletions packages/mermaid/src/mermaid.ts
Original file line number Diff line number Diff line change
@@ -9,8 +9,11 @@ import { mermaidAPI } from './mermaidAPI';
import { addDetector } from './diagram-api/detectType';
import { isDetailedError, type DetailedError } from './utils';
import { registerDiagram } from './diagram-api/diagramAPI';
import { ExternalDiagramDefinition } from './diagram-api/types';

export type { MermaidConfig, DetailedError };
export type { MermaidConfig, DetailedError, ExternalDiagramDefinition };

let externalDiagramsRegistered = false;
/**
* ## init
*
@@ -47,8 +50,8 @@ const init = async function (
callback?: Function
) {
try {
const conf = mermaidAPI.getConfig();
if (conf?.lazyLoadedDiagrams && conf.lazyLoadedDiagrams.length > 0) {
// 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);
@@ -89,6 +92,7 @@ const handleError = (error: unknown, errors: DetailedError[], parseError?: Funct
}
}
};

const initThrowsErrors = function (
config?: MermaidConfig,
// eslint-disable-next-line no-undef
@@ -177,45 +181,39 @@ const initThrowsErrors = function (
}
};

let lazyLoadingPromise: Promise<PromiseSettledResult<void>[]> | undefined = undefined;
/**
* This is an internal function and should not be made public, as it will likely change.
* @internal
* @param conf - Mermaid config.
* @returns An array of {@link PromiseSettledResult}, showing the status of imports.
* @param diagrams - Array of {@link ExternalDiagramDefinition}.
*/
const registerLazyLoadedDiagrams = async (conf: MermaidConfig) => {
// Only lazy load once
// TODO: This is a hack. We should either throw error when new diagrams are added, or load them anyway.
if (lazyLoadingPromise === undefined) {
// Load all lazy loaded diagrams in parallel
lazyLoadingPromise = Promise.allSettled(
(conf?.lazyLoadedDiagrams ?? []).map(async (diagram: string) => {
const { id, detector, loadDiagram } = await import(diagram);
addDetector(id, detector, loadDiagram);
})
);
const registerLazyLoadedDiagrams = (diagrams: ExternalDiagramDefinition[]) => {
for (const { id, detector, loader } of diagrams) {
addDetector(id, detector, loader);
}
return await lazyLoadingPromise;
};

let loadingPromise: Promise<unknown> | undefined = undefined;

const loadExternalDiagrams = async (conf: MermaidConfig) => {
// Only lazy load once
// TODO: This is a hack. We should either throw error when new diagrams are added, or load them anyway.
if (loadingPromise === undefined) {
log.debug(`Loading ${conf?.lazyLoadedDiagrams?.length} external diagrams`);
// Load all lazy loaded diagrams in parallel
loadingPromise = Promise.allSettled(
(conf?.lazyLoadedDiagrams ?? []).map(async (url: string) => {
const { id, detector, loadDiagram } = await import(url);
const { diagram } = await loadDiagram();
registerDiagram(id, diagram, detector, diagram.injectUtils);
})
);
/**
* 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`);
}
await loadingPromise;
};

/**
@@ -242,13 +240,6 @@ const initThrowsErrorsAsync = async function (
) {
const conf = mermaidAPI.getConfig();

const registerLazyLoadedDiagramsErrors: Error[] = [];
for (const registerResult of await registerLazyLoadedDiagrams(conf)) {
if (registerResult.status == 'rejected') {
registerLazyLoadedDiagramsErrors.push(registerResult.reason);
}
}

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
@@ -323,10 +314,9 @@ const initThrowsErrorsAsync = async function (
handleError(error, errors, mermaid.parseError);
}
}
const allErrors = [...registerLazyLoadedDiagramsErrors, ...errors];
if (allErrors.length > 0) {
if (errors.length > 0) {
// TODO: We should be throwing an error object.
throw allErrors[0];
throw errors[0];
}
};

@@ -335,16 +325,25 @@ const initialize = function (config: MermaidConfig) {
};

/**
* @param config
* @deprecated This is an internal function and should not be used. Will be removed in v10.
* 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 initializeAsync = async function (config: MermaidConfig) {
if (config.loadExternalDiagramsAtStartup) {
await loadExternalDiagrams(config);
const registerExternalDiagrams = async (
diagrams: ExternalDiagramDefinition[],
{
lazyLoad = true,
}: {
lazyLoad?: boolean;
} = {}
) => {
if (lazyLoad) {
registerLazyLoadedDiagrams(diagrams);
} else {
await registerLazyLoadedDiagrams(config);
await loadExternalDiagrams(diagrams);
}
mermaidAPI.initialize(config);
externalDiagramsRegistered = true;
};

/**
@@ -414,7 +413,7 @@ const executeQueue = async () => {
* @param txt
* @deprecated This is an internal function and should not be used. Will be removed in v10.
*/
const parseAsync = (txt: string) => {
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
@@ -424,7 +423,7 @@ const parseAsync = (txt: string) => {
(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
// This fulfills the promise sent to the value back to the original caller
resolve(r);
},
(e) => {
@@ -482,9 +481,9 @@ const parseAsync = (txt: string) => {
const renderAsync = (
id: string,
text: string,
cb: (svgCode: string, bindFunctions?: (element: Element) => void) => void,
cb?: (svgCode: string, bindFunctions?: (element: Element) => void) => void,
container?: Element
): Promise<void> => {
): 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
@@ -522,8 +521,8 @@ const mermaid: {
init: typeof init;
initThrowsErrors: typeof initThrowsErrors;
initThrowsErrorsAsync: typeof initThrowsErrorsAsync;
registerExternalDiagrams: typeof registerExternalDiagrams;
initialize: typeof initialize;
initializeAsync: typeof initializeAsync;
contentLoaded: typeof contentLoaded;
setParseErrorHandler: typeof setParseErrorHandler;
} = {
@@ -537,8 +536,8 @@ const mermaid: {
init,
initThrowsErrors,
initThrowsErrorsAsync,
registerExternalDiagrams,
initialize,
initializeAsync,
parseError: undefined,
contentLoaded,
setParseErrorHandler,
12 changes: 6 additions & 6 deletions packages/mermaid/src/mermaidAPI.ts
Original file line number Diff line number Diff line change
@@ -115,19 +115,19 @@ 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 = function (
id: string,
text: string,
cb: (svgCode: string, bindFunctions?: (element: Element) => void) => void,
cb?: (svgCode: string, bindFunctions?: (element: Element) => void) => void,
container?: Element
): void {
): string {
addDiagrams();
configApi.reset();
text = text.replace(/\r\n?/g, '\n'); // parser problems on CRLF ignore all CR and leave LF;;
@@ -401,9 +401,9 @@ const render = function (
const renderAsync = async 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> {
): Promise<string> {
addDiagrams();
configApi.reset();
text = text.replace(/\r\n?/g, '\n'); // parser problems on CRLF ignore all CR and leave LF;;
1 change: 1 addition & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
packages:
# all packages in direct subdirs of packages/
- 'packages/*'
# - 'tests/*'
23 changes: 23 additions & 0 deletions tests/webpack/package.json
Original file line number Diff line number Diff line change
@@ -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 changes: 11 additions & 0 deletions tests/webpack/public/index.html
Original file line number Diff line number Diff line change
@@ -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 changes: 38 additions & 0 deletions tests/webpack/src/index.js
Original file line number Diff line number Diff line change
@@ -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 changes: 10 additions & 0 deletions tests/webpack/webpack.config.js
Original file line number Diff line number Diff line change
@@ -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'),
},
};