Files
computer-playbook/roles/web-app-docs/files/python/infinito_docs/extensions/mermaid.py
T
kevinveenbirkenbachandClaude Opus 5 d206f2593e feat(web-app-docs): render mermaid fences as diagrams
Every role README carries a ```mermaid flowchart and the documentation
showed it as source. A post-transform now replaces those literal blocks with
<pre class="mermaid"> in html builds, which the runtime picks up; every other
builder keeps the fence as it was.

sphinxcontrib-mermaid injects an inline init script, which this deployment's
CSP refuses, so the runtime and its initialisation are two linked files
instead: mermaid-init.js calls mermaid.initialize with securityLevel strict
and follows the reader's colour scheme. The diagram source is html-escaped
before it is embedded, or an & or < in a node label would break the page.

The runtime is fetched at image build time through ADD --checksum, so a
changed upstream file fails the build rather than shipping. Version and
digest are not ARG defaults, which test_arg_without_default forbids; they
travel meta/services.yml -> vars/main.yml -> compose.yml.j2 the way
DOCS_IMAGE and DOCS_VERSION already do, and an update block registers the
pin with the npm version watcher instead of exempting it.

Verified by building a page with sphinx: the diagram arrives as
<pre class="mermaid"> with its source escaped, a bash fence stays a
highlighted block, and both scripts are linked. A missing mermaid.min.js does
not fail that build, so a local run without the image stays green.

scripts/lint/mermaid.sh keeps validating diagram syntax through mmdc, which
npm installs unpinned. It therefore checks against a newer mermaid than the
11.4.1 readers get, and a diagram using syntax from in between would pass the
lint and render blank.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-26 01:02:30 +02:00

41 lines
1.2 KiB
Python

"""Render ```` ```mermaid ```` fences as diagrams instead of code blocks.
The fence is replaced by a ``<pre class="mermaid">`` that the self-hosted
``mermaid.min.js`` picks up, so no directive and no inline script are needed
and the fence keeps rendering as source in every non-HTML builder.
"""
from __future__ import annotations
import html
from docutils import nodes
from sphinx.transforms.post_transforms import SphinxPostTransform
LANGUAGE = "mermaid"
class MermaidFences(SphinxPostTransform):
"""Turn every mermaid literal block of an HTML page into a raw diagram."""
default_priority = 400
formats = ("html",)
def run(self, **kwargs) -> None:
for node in list(self.document.findall(nodes.literal_block)):
if node.get("language") != LANGUAGE:
continue
diagram = nodes.raw(
"",
f'<pre class="mermaid">{html.escape(node.astext())}</pre>',
format="html",
)
node.replace_self(diagram)
def setup(app):
app.add_post_transform(MermaidFences)
app.add_js_file("js/mermaid.min.js")
app.add_js_file("js/mermaid-init.js")
return {"version": "1.0", "parallel_read_safe": True}