Cache mermaid graph rendering (#1023)

* Cache mermaid graph rendering

* Bump mermaid

* Update naming
This commit is contained in:
Jonatan Kłosko 2022-02-22 12:02:53 +01:00 committed by GitHub
parent ca0128d028
commit f699575b45
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 81 additions and 29 deletions

View file

@ -1,3 +1,6 @@
import { md5Base64 } from "../../lib/utils";
import CacheLRU from "../../lib/cache_lru";
let idCount = 0;
let getId = () => `mermaid-graph-${idCount++}`;
@ -5,6 +8,32 @@ let mermaidInitialized = false;
const fontAwesomeVersion = "5.15.4";
const cache = new CacheLRU(25);
/**
* Renders SVG graph from mermaid definition.
*/
export function renderMermaid(definition) {
const hash = md5Base64(definition);
const svg = cache.get(hash);
if (svg) {
return Promise.resolve(svg);
}
return importMermaid().then((mermaid) => {
injectFontAwesomeIfNeeded(definition);
try {
const svg = mermaid.render(getId(), definition);
cache.set(hash, svg);
return svg;
} catch (e) {
return `<div class="error-box whitespace-pre-wrap"><span class="font-semibold">Mermaid</span>\n${e.message}</div>`;
}
});
}
function importMermaid() {
return import(
/* webpackChunkName: "mermaid" */
@ -18,10 +47,13 @@ function importMermaid() {
});
}
const maybeInjectFontAwesome = (value) => {
function injectFontAwesomeIfNeeded(definition) {
const fontAwesomeUrl = `https://cdnjs.cloudflare.com/ajax/libs/font-awesome/${fontAwesomeVersion}/css/all.min.css`;
// Graphs may include Font Awesome icons via fa: prefix, so we
// load the icon set if needed
if (
value.includes("fa:") &&
definition.includes("fa:") &&
!document.querySelector(`link[href="${fontAwesomeUrl}"]`)
) {
const link = document.createElement("link");
@ -30,17 +62,4 @@ const maybeInjectFontAwesome = (value) => {
link.href = fontAwesomeUrl;
document.head.appendChild(link);
}
};
export function renderMermaid(value) {
return importMermaid().then((mermaid) => {
// Inject font-awesome when fa: prefix is used
maybeInjectFontAwesome(value);
try {
return mermaid.render(getId(), value);
} catch (e) {
return `<div class="error-box whitespace-pre-wrap"><span class="font-semibold">Mermaid</span>\n${e.message}</div>`;
}
});
}

View file

@ -0,0 +1,33 @@
/**
* A Map-based LRU cache.
*/
export default class CacheLRU {
constructor(size) {
this.size = size;
this.cache = new Map();
}
get(key) {
if (this.cache.has(key)) {
const value = this.cache.get(key);
// Map keys are stored and iterated in insertion order,
// so we reinsert on every access
this.cache.delete(key);
this.cache.set(key, value);
return value;
} else {
return undefined;
}
}
set(key, value) {
if (this.cache.has(key)) {
this.cache.delete(key);
} else if (this.cache.size === this.size) {
const oldestKey = this.cache.keys().next().value;
this.cache.delete(oldestKey);
}
this.cache.set(key, value);
}
}

View file

@ -5007,9 +5007,9 @@
}
},
"node_modules/dompurify": {
"version": "2.3.4",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.4.tgz",
"integrity": "sha512-6BVcgOAVFXjI0JTjEvZy901Rghm+7fDQOrNIcxB4+gdhj6Kwp6T9VBhBY/AbagKHJocRkDYGd6wvI+p4/10xtQ=="
"version": "2.3.5",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.5.tgz",
"integrity": "sha512-kD+f8qEaa42+mjdOpKeztu9Mfx5bv9gVLO6K9jRx4uGvh6Wv06Srn4jr1wPNY2OOUGGSKHNFN+A8MA3v0E0QAQ=="
},
"node_modules/domutils": {
"version": "2.8.0",
@ -8810,15 +8810,15 @@
}
},
"node_modules/mermaid": {
"version": "8.13.9",
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-8.13.9.tgz",
"integrity": "sha512-kMH676xEomSe/gzxMpDx91L+z9L+9iB3lvtPFA8aeOPRNNrfd3ZDvDCGFnuqQaJvPRCxs3Me2JDaVVNOZjojrg==",
"version": "8.14.0",
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-8.14.0.tgz",
"integrity": "sha512-ITSHjwVaby1Li738sxhF48sLTxcNyUAoWfoqyztL1f7J6JOLpHOuQPNLBb6lxGPUA0u7xP9IRULgvod0dKu35A==",
"dependencies": {
"@braintree/sanitize-url": "^3.1.0",
"d3": "^7.0.0",
"dagre": "^0.8.5",
"dagre-d3": "^0.6.4",
"dompurify": "2.3.4",
"dompurify": "2.3.5",
"graphlib": "^2.1.8",
"khroma": "^1.4.1",
"moment-mini": "^2.24.0",
@ -16445,9 +16445,9 @@
}
},
"dompurify": {
"version": "2.3.4",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.4.tgz",
"integrity": "sha512-6BVcgOAVFXjI0JTjEvZy901Rghm+7fDQOrNIcxB4+gdhj6Kwp6T9VBhBY/AbagKHJocRkDYGd6wvI+p4/10xtQ=="
"version": "2.3.5",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.5.tgz",
"integrity": "sha512-kD+f8qEaa42+mjdOpKeztu9Mfx5bv9gVLO6K9jRx4uGvh6Wv06Srn4jr1wPNY2OOUGGSKHNFN+A8MA3v0E0QAQ=="
},
"domutils": {
"version": "2.8.0",
@ -19230,15 +19230,15 @@
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="
},
"mermaid": {
"version": "8.13.9",
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-8.13.9.tgz",
"integrity": "sha512-kMH676xEomSe/gzxMpDx91L+z9L+9iB3lvtPFA8aeOPRNNrfd3ZDvDCGFnuqQaJvPRCxs3Me2JDaVVNOZjojrg==",
"version": "8.14.0",
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-8.14.0.tgz",
"integrity": "sha512-ITSHjwVaby1Li738sxhF48sLTxcNyUAoWfoqyztL1f7J6JOLpHOuQPNLBb6lxGPUA0u7xP9IRULgvod0dKu35A==",
"requires": {
"@braintree/sanitize-url": "^3.1.0",
"d3": "^7.0.0",
"dagre": "^0.8.5",
"dagre-d3": "^0.6.4",
"dompurify": "2.3.4",
"dompurify": "2.3.5",
"graphlib": "^2.1.8",
"khroma": "^1.4.1",
"moment-mini": "^2.24.0",