diff --git a/CHANGELOG.md b/CHANGELOG.md index fb7ec57f0..c38a87d48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1483,6 +1483,7 @@ raw template {%- endblock in_prompt -%} """ + exporter_attr = AttrExporter() output_attr, _ = exporter_attr.from_notebook_node(nb) assert "raw template" in output_attr diff --git a/share/templates/lab/mermaidjs.html.j2 b/share/templates/lab/mermaidjs.html.j2 index f75e33ffb..0f7158894 100644 --- a/share/templates/lab/mermaidjs.html.j2 +++ b/share/templates/lab/mermaidjs.html.j2 @@ -84,7 +84,8 @@ url="https://cdnjs.cloudflare.com/ajax/libs/mermaid/10.7.0/mermaid.esm.min.mjs" let results = null; let output = null; try { - const { svg } = await mermaid.render(id, raw, el); + let { svg } = await mermaid.render(id, raw, el); + svg = cleanMermaidSvg(svg); results = makeMermaidImage(svg); output = document.createElement("figure"); results.map(output.appendChild, output); @@ -99,6 +100,38 @@ url="https://cdnjs.cloudflare.com/ajax/libs/mermaid/10.7.0/mermaid.esm.min.mjs" parent.appendChild(output); } + + /** + * Post-process to ensure mermaid diagrams contain only valid SVG and XHTML. + */ + function cleanMermaidSvg(svg) { + return svg.replace(RE_VOID_ELEMENT, replaceVoidElement); + } + + + /** + * A regular expression for all void elements, which may include attributes and + * a slash. + * + * @see https://developer.mozilla.org/en-US/docs/Glossary/Void_element + * + * Of these, only `
` is generated by Mermaid in place of `\n`, + * but _any_ "malformed" tag will break the SVG rendering entirely. + */ + const RE_VOID_ELEMENT = + /<\s*(area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)\s*([^>]*?)\s*>/gi; + + /** + * Ensure a void element is closed with a slash, preserving any attributes. + */ + function replaceVoidElement(match, tag, rest) { + rest = rest.trim(); + if (!rest.endsWith('/')) { + rest = `${rest} /`; + } + return `<${tag} ${rest}>`; + } + void Promise.all([...diagrams].map(renderOneMarmaid)); });