Docsify Mermaid 10
Using Docsify and Mermaid 10 can be tricky since Mermaid 10 renders asynchronously (mermaid.render
returns a Promise).
This post shows how to work around the mismatch between Docsify’s markdown.renderer.code
and Mermaid’s render
function on Mermaid v9 and v10.
Table of Contents
Working example available at: github.com/HugoDF/docsify-mermaid-10.
A summary of the problem
To extend Docsify markdown rendering, a $docsify.markdown.renderer.code
function can be provided. However $docsify.markdown.renderer.code
expects a function that returns a string, not a Promise.
This causes problems due to Mermaid v9+ rendering. On Mermaid v9, the flowchart-elk
diagram is rendered asynchronously (mermaid.render
returns a promise). This behaviour of mermaid.render
returning a Promise is true for all diagrams in Mermaid v10.
A naive implementation when mermaid.render
returns a Promise would be to extract the svg
property as follows:
window.$docsify = {
markdown: {
renderer: {
code(code, lang) {
if (lang === 'mermaid') {
return mermaid.render(`mermaid-svg`, code).then(({ svg }) => svg);
}
return this.origin.code.apply(this, arguments);
},
},
},
};
When we load this in the browser, the block is rendered as follows, this proves the point that $docsify.markdown.renderer.code
does not support a function that returns a Promise:
[object Promise]
We’ve now covered the issue caused by the mismatch of interface between mermaid.render
(which returns a Promise) and $docsify.markdown.renderer.code
(which expects a string). Next we’ll see how to work around this.
A solution
Since we control the return value of $docsify.markdown.renderer.code
and the code in that function runs with access to the page’s DOM, we can solve this issue as follows:
- Docsify starts running
markdown.renderer.code
- Start Mermaid rendering (out of band, we won’t wait for this to complete before doing 3.)
- Return a string with an element which can be referenced by id (
MERMAID_CONTAINER
) or other attribute later to Docsify - Docsify will render the returned element
- When Mermaid rendering completes (started in 2.)
- Inject the generated SVG into the
MERMAID_CONTAINER
element
- Inject the generated SVG into the
As a timeline, this looks as follows:
gantt title Timeline of Docsify vs Mermaid rendering axisFormat todayMarker off tickInterval 100millisecond section Docsify Run markdown.renderer.code() : rendercode, 2000, 100ms Render renderer.code() output : after rendercode, 140ms Section Mermaid Mermaid render() : mmd, 2000, 200ms Inject render() output SVG : after mmd, 90ms
We can code this up as follows:
let svgCounter = 0;
window.$docsify = {
markdown: {
renderer: {
code(code, lang) {
if (lang === 'mermaid') {
const svgName = `mermaid-svg-${svgCounter++}`;
const MERMAID_CONTAINER_ID = `${svgName}-container`;
// 2. start mermaid rendering
mermaid.render(svgName, code).then(({ svg }) => {
// 5. On Mermaid render completion, inject the SVG
// into the element created from 3.
const containerElement = document.querySelector(
`#${MERMAID_CONTAINER_ID}`
);
if (containerElement) {
containerElement.innerHTML = svg;
} else {
console.error(`Error: #${MERMAID_CONTAINER_ID} not found`);
}
});
// 3. Return an element that can be referenced by mermaid rendering
return `<div class="mermaid" id="${MERMAID_CONTAINER_ID}"></div>`;
}
return this.origin.code.apply(this, arguments);
},
},
},
};
Demos
We can see the code in action at example.codewithhugo.com/docsify-mermaid-10/#/?id=demos.
The source code is available at: github.com/HugoDF/docsify-mermaid-10.
Get The Jest Handbook (100 pages)
Take your JavaScript testing to the next level by learning the ins and outs of Jest, the top JavaScript testing library.
orJoin 1000s of developers learning about Enterprise-grade Node.js & JavaScript