Setting up Mermaid for quick diagrams

yarn init -y
yarn add -D @mermaid-js/mermaid-cli markserv remark-cli remark-mermaid
mkdir src 
touch src/README.md src/remark-mermaid-alt.js
touch .remarkrc

.remarkrc

{
"plugins": {
"./src/remark-mermaid-alt.js": {
"destination": "./images/mermaid",
"linkRoot": "./images/mermaid/"
}
}
}

src/remark-mermaid-alt.js

const crypto = require( 'crypto' );
const fs = require( 'fs-extra' );
const path = require( 'path' );

const visit = require( 'unist-util-visit' );
const { execSync } = require( 'child_process' );
const mmdc = require.resolve( '@mermaid-js/mermaid-cli/index.bundle.js' );

const PLUGIN_NAME = 'remark-mermaid-alt';

async function generateSVG( value, file, options ) {
const { destination = './images', mermaidArgs = {} } = options;

// Unique paths.
const unique = crypto
.createHmac( 'sha1', PLUGIN_NAME )
.update( value )
.digest( 'hex' );
const inputFile = `${ unique }.mmd`;
const mmdPath = path.join( destination, inputFile );
const outputFile = `${ unique }.svg`;
const svgPath = path.join( destination, outputFile );

// Args for CLI.
mermaidArgs.input = mmdPath;
mermaidArgs.output = svgPath;
const args = []
.concat(
...Object.keys( mermaidArgs ).map( ( k ) => [
`--${ k }`,
mermaidArgs[ k ],
] )
)
.join( ' ' );

// Write temp file.
fs.outputFileSync( mmdPath, value, { encoding: 'utf8' } );

// Execute mermaid.
execSync( `${ mmdc } ${ args }` );

// Clean up.
fs.removeSync( mmdPath );

return `${ outputFile }`;
}

async function transformMermaidNode( node, file, index, parent, options ) {
const { lang, value, position } = node;

try {
const { linkRoot = './' } = options;
const svgFile = await generateSVG( value, file, options );
const message = `${ lang } code block replaced with rendered mermaid SVG`;
file.info( message, position, PLUGIN_NAME );

const newNode = {
type: 'image',
title: '`mermaid` image',
url: `${ linkRoot }${ svgFile }`,
};
parent.children.splice( index, 1, newNode );
} catch ( error ) {
file.fail( error, position, PLUGIN_NAME );
}

return node;
}

/**
* Remark plugin that converts mermaid codeblocks into SVG files.
*
* @param {Object} options
*/

function mermaid( options = {} ) {
/**
* Look for all code nodes that have the language mermaid,
* generate SVGs and update the url.
*
* @param {Node} ast The Markdown Tree
* @param {Object} file The virtual file.
* @return {Promise<void>} The altered tree.
*/

return async function ( ast, file ) {
const promises = []; // keep track of promises since visit isn't async
visit( ast, 'code', ( node, index, parent ) => {
// If this codeblock is not mermaid, bail.
if ( node.lang !== 'mermaid' ) {
return node;
}
promises.push(
transformMermaidNode( node, file, index, parent, options )
);
} );
await Promise.all( promises );
};
}

module.exports = mermaid;

Build and Serve

src/README.md

# Readme for Project XYZ
yarn build
yarn serve

Optional Mermaid Config:

.remarkrc

{
"plugins": {
"./src/remark-mermaid-alt.js": {
"destination": "./images/mermaid",
"linkRoot": "./images/mermaid/",
"mermaidArgs": {
"configFile": ".mermaidrc"
}
}
}
}
touch .mermaidrc

.mermaidrc (example)

{
"sequence":{
"diagramMarginY" :10,
"diagramMarginX" :50,
"actorMargin" :50,
"width" :150,
"height" :65,
"boxMargin" :10,
"boxTextMargin" :5,
"noteMargin" :10,
"messageMargin" :35,
"messageAlign" :"center",
"mirrorActors" :false,
"bottomMarginAdj" :1,
"useMaxWidth" :true,
"rightAngles" :false,
"showSequenceNumbers" :false
}
}