How Importing Diagrams from Mermaid Syntax Works in Diagram-Design: The 7-Stage Pipeline

Importing diagrams from Mermaid syntax in diagram-design is a deterministic, seven-stage pipeline that validates input, extracts fenced blocks via mermaid_extract.py, normalizes nodes and edges into a safe intermediate representation, and renders editorial templates without executing JavaScript or preserving the original Mermaid layout.

The cathrynlavery/diagram-design repository provides a deterministic toolchain for converting Mermaid source files into editorial-quality diagrams, as advertised in skills/diagram-design/SKILL.md. Importing diagrams from Mermaid syntax relies on a custom parser—skills/diagram-design/scripts/mermaid_extract.py—rather than the official Mermaid renderer, ensuring that every diagram is sanitized, normalized, and rebuilt from scratch.

Stage 1: Locate and Validate Input

The process begins with the import-mermaid command defined in commands/import-mermaid.md. It verifies that the supplied file exists and carries a supported suffix: .mmd, .mermaid, or a Markdown file containing fenced mermaid blocks. If multiple Mermaid blocks exist and the user omits the --diagram flag, the command lists available blocks and prompts for a selection.

Stage 2: Extract Blocks via mermaid_extract.py

The command invokes the core extractor script, mermaid_extract.py (skills/diagram-design/scripts/mermaid_extract.py), as a subprocess. As shown in scripts/verify-mermaid-import.py, the extractor reads the file, pulls out fenced mermaid blocks, or treats the entire file as a single block for pure .mmd/.mermaid sources. This script forms the primary trust boundary: it never evaluates JavaScript, follows URLs, or renders the original layout.

Stage 3: Parse and Normalize the Grammar

Inside mermaid_extract.py, the raw text is tokenized and converted into a Diagram object. The parser respects the four grammars listed in SUPPORTED_KINDS (flowchart, sequenceDiagram, stateDiagram-v2, erDiagram). During this phase the extractor:

  • Strips front-matter and Mermaid directives (%%{…}%%).
  • Normalizes node shapes using classify_shape(), which consults the SHAPE_FOR_DELIMITERS table to map delimiter pairs to canonical shapes.
  • Normalizes edge operators, arrowheads, and styles through _operator_style().
  • Sanitizes labels with clean_label(), treating all content as inert plain text.
  • Discards non-semantic directives such as style overrides and click handlers, incrementing diagram.discarded for auditing.

Stage 4: Build the Intermediate Representation (IR)

After parsing, the extractor constructs an intermediate representation consisting of:

  • Nodes — a Node dataclass carrying id, label, shape, parent, depth, and related metadata.
  • Edges — an Edge dataclass with source, target, label, style, arrowhead, and bidirectional or undirected flags.
  • Metadata — fragments, notes, and discarded counts.

The analyze() function then aggregates statistics such as cycle detection, hub-node identification, and budget warnings. These metrics drive later rendering decisions in the pipeline.

Stage 5: Emit JSON or Markdown Digest

The user controls output serialization through flags. If --json is passed, the tool serializes the full IR via to_json(). Otherwise, it produces a human-readable Markdown digest via digest(), which surfaces diagram metadata, node and edge tables, and any budget warnings. This digest acts as the bridge between extraction and rendering.

Stage 6: Select an Editorial Rendering Template

The import-mermaid command consumes the digest (or JSON) and selects one of the editorial templates stored in skills/diagram-design/assets/. The available templates include template-light.html, template-dark.html, and template-full.html. Each template implements the “four dials” described in references/import-mermaid.md and references/output-spec.md: format, size, detail, and audience.

Stage 7: Render and Deliver the Final Asset

The selected template is populated with the normalized node and edge data. Rendering is layout-free—the original Mermaid layout is intentionally ignored—and produces the requested output format: html, svg, png, or html+png. The result is written to the path specified by --output, and the command prints a summary of the four dials alongside a “fidelity ledger” documenting what was merged, collapsed, or dropped during import.

Security Controls and Resource Limits

Because Mermaid source is treated as untrusted, the extractor enforces strict hard caps to prevent denial-of-service:

  • MAX_SOURCE_BYTES — 4 MiB maximum source size.
  • MAX_NODES — 2,000 nodes per diagram.
  • MAX_EDGES — 5,000 edges per diagram.

Violations trigger a controlled exit with code 2. Additionally, image URLs and click handlers are never fetched or executed; they are stripped and counted only in diagram.discarded.

Extending the Parser for New Diagram Types

Adding support for a new Mermaid grammar requires only two changes in mermaid_extract.py:

  1. Append the grammar name to SUPPORTED_KINDS.
  2. Implement a corresponding _parse_<kind>() function that returns normalized nodes and edges.

This modular design keeps the import pipeline maintainable as the Mermaid ecosystem evolves.

Automated Pipeline Verification

The repository includes scripts/verify-mermaid-import.py to guarantee correctness. This script runs an extensive suite that validates extraction logic, shape and edge vocabularies, front-matter handling, error paths, and documentation wiring. It also verifies the exact subprocess call used to invoke mermaid_extract.py, ensuring the command layer and parser remain synchronized.

Practical Import Examples

Import a simple flowchart and capture the JSON IR:


# 1️⃣ Import a simple flowchart and get a JSON IR

$ diagram-design import-mermaid path/to/flowchart.mmd --json > flow.json

Import a Markdown file containing multiple Mermaid blocks, selecting the second block and exporting as SVG:


# 2️⃣ Import a Markdown file with multiple Mermaid blocks, choosing block 2

$ diagram-design import-mermaid docs/readme.md --diagram 2 --format svg --size doc-wide

Programmatically invoke the extractor from Python:

>>> from pathlib import Path
>>> import json, subprocess, sys
>>> def extract(file):
...     proc = subprocess.run([sys.executable,
...                           "skills/diagram-design/scripts/mermaid_extract.py",
...                           str(file), "--json"], capture_output=True, text=True)
...     return json.loads(proc.stdout)
>>> ir = extract(Path("sample.mmd"))
>>> ir["diagrams"][0]["analysis"]["has_cycle"]
False

Summary

  • Importing diagrams from Mermaid syntax in diagram-design follows a strict seven-stage pipeline from file validation to final render.
  • The core parser, skills/diagram-design/scripts/mermaid_extract.py, tokenizes input into a safe Diagram object without executing JavaScript.
  • Normalization functions such as classify_shape(), _operator_style(), and clean_label() standardize nodes, edges, and labels before IR construction.
  • Hard resource limits (MAX_SOURCE_BYTES, MAX_NODES, MAX_EDGES) protect the pipeline from malformed or malicious input.
  • Output is produced through editorial templates (template-light.html, template-dark.html, template-full.html) that ignore the original Mermaid layout.
  • Extensibility is built in: new grammars require only an entry in SUPPORTED_KINDS and a matching _parse_<kind>() implementation.

Frequently Asked Questions

What file formats does the diagram-design importer accept?

The import-mermaid command accepts files ending in .mmd, .mermaid, or standard Markdown documents that contain fenced mermaid code blocks. If a Markdown file holds multiple blocks and you omit the --diagram flag, the tool lists the blocks and asks you to choose one.

Does the importer preserve the original Mermaid layout?

No. According to the diagram-design source code, the pipeline is intentionally layout-free; it discards the original Mermaid positioning and rebuilds the diagram using normalized node and edge data inserted into editorial templates.

How does the tool handle potentially malicious Mermaid input?

The extractor runs inside a strict trust boundary. It never evaluates JavaScript, follows URLs, or renders the original Mermaid layout. Labels are sanitized through clean_label() and treated as inert plain text. Hard caps on source size (MAX_SOURCE_BYTES = 4 MiB), node count (MAX_NODES = 2000), and edge count (MAX_EDGES = 5000) prevent denial-of-service, with violations returning exit code 2.

Can I add support for additional Mermaid diagram types?

Yes. As implemented in cathrynlavery/diagram-design, you only need to add the grammar name to SUPPORTED_KINDS and implement a corresponding _parse_<kind>() function inside mermaid_extract.py. The existing pipeline will automatically route matching diagrams through your new parser.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →