Replace ```mermaid blocks with rendered PNG image references.
Raises FileNotFoundError if mmdc binary is not found.
Raises subprocess.CalledProcessError if mmdc rendering fails.
Source code in obsidian_export/pipeline/stage3_mermaid.py
| def render_mermaid_blocks(body: str, config: MermaidConfig, tmpdir: Path) -> str:
"""Replace ```mermaid blocks with rendered PNG image references.
Raises FileNotFoundError if mmdc binary is not found.
Raises subprocess.CalledProcessError if mmdc rendering fails.
"""
mmdc = config.mmdc_bin
counter = 0
def replace_block(m: re.Match) -> str:
"""Replace a fenced mermaid code block with a rendered PNG image reference.
Receives a match whose group(1) is the mermaid diagram source. Writes the
source to a temporary file, invokes mmdc to render it as PNG, and returns
a markdown image reference to the output file. Increments the outer
``counter`` for unique filenames.
"""
nonlocal counter
if not mmdc.exists():
raise FileNotFoundError(
f"mmdc binary not found at {mmdc}. Install: npm install --prefix .mmdc @mermaid-js/mermaid-cli"
)
diagram_src = m.group(1)
counter += 1
src_file = tmpdir / f"diagram_{counter}.mmd"
out_file = tmpdir / f"diagram_{counter}.png"
src_file.write_text(diagram_src, encoding="utf-8")
cmd = [
str(mmdc),
"--input",
str(src_file),
"--output",
str(out_file),
"--scale",
str(config.scale),
"--backgroundColor",
"transparent",
]
if config.puppeteer_config and config.puppeteer_config.exists():
cmd.extend(["--puppeteerConfigFile", str(config.puppeteer_config)])
try:
subprocess.run(cmd, check=True, capture_output=True)
except subprocess.CalledProcessError as exc:
stderr = exc.stderr.decode(errors="replace") if exc.stderr else "(no stderr)"
raise MermaidRenderError(
f"mmdc failed (exit {exc.returncode}) rendering diagram {counter}:\n{stderr}"
) from exc
return f""
return _MERMAID_BLOCK_RE.sub(replace_block, body)
|