# How Does mermaid_extract.py Work: Parsing Mermaid Diagrams into Intermediate Representation

> Discover how mermaid_extract.py works. This tool parses Mermaid diagrams into a normalized intermediate representation for flowcharts, sequence, state, and ER diagrams. Get JSON or Markdown output.

- Repository: [Cathryn Lavery/diagram-design](https://github.com/cathrynlavery/diagram-design)
- Tags: how-to-guide
- Published: 2026-08-21

---

**mermaid_extract.py is a command-line tool in the cathrynlavery/diagram-design repository that parses Mermaid diagram source files into a normalized intermediate representation (IR) consisting of nodes and edges, supporting flowcharts, sequence diagrams, state diagrams, and ER diagrams with optional JSON or Markdown output.**

The [`mermaid_extract.py`](https://github.com/cathrynlavery/diagram-design/blob/main/mermaid_extract.py) script serves as the core parsing engine for the diagram-design project, transforming Mermaid syntax into structured data. Located at [`skills/diagram-design/scripts/mermaid_extract.py`](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/scripts/mermaid_extract.py), this self-contained Python module converts both standalone `.mmd` files and Markdown-embedded diagrams into a machine-readable format. Understanding how mermaid_extract.py works reveals a sophisticated pipeline that handles multiple diagram types while maintaining strict security boundaries.

## CLI Entry Point and Argument Parsing

The script operates as a standalone CLI tool invoked via `python3 mermaid_extract.py <file>`.

The **`main`** function (line 92) uses `argparse` to handle command-line arguments including `--diagram`, `--json`, `--max-rows`, and `--out`. After validating the target file path using `Path.is_file`, it delegates to **`load_blocks`** (lines 112-133) to extract Mermaid source blocks from the input file.

## Loading and Preparing Source Blocks

The **`load_blocks`** function distinguishes between three input types to locate raw Mermaid syntax.

For **`.mmd` or `.mermaid` extensions**, the function returns a single `SourceBlock` containing the entire file contents (lines 28-35). For **Markdown files**, it scans for fenced `` ```mermaid `` blocks, capturing the delimiter, collecting content, and wrapping each discovered block in a `SourceBlock` instance (lines 36-61). The `SourceBlock` dataclass (lines 87-93) preserves the block index, raw text, and starting line number for accurate error reporting. If no valid blocks are found or a fence is unterminated, **`_fail`** triggers an error (lines 56-60).

## Diagram Type Detection

Before parsing, **`_prepared_lines`** sanitizes the raw input (lines 81-89).

This preprocessing strips leading YAML front-matter delimited by `---` (lines 66-78), removes Mermaid directives (`%%{…}%%`) and comments (`%% …`), and retains original line numbers for debugging. The cleaned lines pass to **`_kind_and_direction`** (lines 111-127), which scans for the first non-empty statement to identify the diagram type. The parser supports **flowchart**, **sequenceDiagram**, **stateDiagram-v2**, and **erDiagram** types, raising a clear error for unsupported variants (lines 128-132).

## Core Data Structures

The intermediate representation relies on two primary dataclasses defined in the source.

The **`Node`** class (lines 66-78) stores the identifier, display label, shape, parent reference, depth level, container flag, children list, field definitions, and in/out degrees. The **`Edge`** class (lines 80-88) captures the edge identifier, source node, target node, label text, visual style, arrowhead type, and directionality flags. These aggregate into the **`Diagram`** class (lines 94-106), which maintains collections of nodes and edges alongside a private `_nodes_by_id` mapping for O(1) node lookup during parsing.

## Parsing Pipeline

The **`parse_block`** function orchestrates the transformation from text to structured objects.

First, it invokes `_prepared_lines` to sanitize input (lines 93-95). Next, it determines the diagram kind and default direction via `_kind_and_direction` (lines 96-97). Finally, it delegates to a specialized parser based on the detected type: **`_parse_flowchart`**, **`_parse_sequence`**, **`_parse_state`**, or **`_parse_er`** (lines 98-104). Each parser operates on logical statements produced by **`_logical_statements`**, which merges multiline statements and splits on top-level semicolons (lines 111-128).

### Flowchart Parsing

**`_parse_flowchart`** handles complex graph structures including `subgraph` containers and direction changes.

The function discards non-semantic directives such as `style`, `classDef`, and `click` via **`_discard_nonsemantic`** (lines 87-94). Edge detection occurs through **`_edge_operators`** (lines 100-165), which first masks quoted or bracketed content using **`_top_level_mask`** to avoid false positives, then identifies labeled and unlabeled operators with their associated styles. Nodes register via `Diagram.add_node` while edges connect via `Diagram.add_edge`, with parent-child relationships tracked for container hierarchies.

### Sequence Diagram Parsing

**`_parse_sequence`** processes participant declarations, fragments (`alt`, `opt`, `loop`), notes, and messages.

Participant definitions using `participant` or `actor` keywords establish the node set. Message syntax (`A-->B: label`) is parsed using the `message_re` regex (lines 158-176), translating interactions into `Edge` objects with appropriate arrowhead metadata including `cross` and `async` variants.

### State Diagram Parsing

**`_parse_state`** manages nested state declarations and transitions.

The parser supports `state X { … }` syntax for hierarchies and handles direction directives. Transition arrows (`-->`) connect states, with **`_state_endpoint`** generating special pseudo-nodes (`__start_…`, `__end_…`) to represent initial and final states within the IR.

### ER Diagram Parsing

**`_parse_er`** processes entity-relationship models.

Entity blocks defined with `entity {}` syntax become nodes, while relationships (`A --|> B : label`) become edges. The parser normalizes cardinality notations (`--` vs `..`) and stores these values within edge labels for downstream analysis.

## Post-Processing and Analysis

After constructing the initial graph, the script enriches the IR with structural metadata.

**`_finalize_degrees`** (lines 109-116) traverses all edges to compute each node's `in_degree` and `out_degree` counts. The **`_has_cycle`** function (lines 118-144) performs a depth-first search on the adjacency map to detect and flag cyclic graph structures. The **`analyze`** function (lines 149-176) aggregates high-level statistics including node counts, edge counts, container counts, leaf nodes, shape distributions, hub candidates (high-degree nodes), entry/exit points, and orphan nodes.

## Output Generation

The tool provides two distinct output modes controlled by CLI flags.

The **Markdown digest** mode (lines 212-269) generates human-readable summaries with formatted tables for nodes and edges. The `--max-rows` parameter truncates output to prevent information overload in large diagrams. The **JSON** mode via `to_json` (lines 250-268) exports the complete machine-readable representation including the raw `Diagram` objects, analysis statistics, and discarded metadata. Output writes to stdout by default or to a file specified by `--out` (lines 219-227).

## Security and Trust Boundaries

The script implements a strict trust boundary by treating all label and directive content as untrusted data.

The **`clean_label`** function sanitizes input through HTML unescaping and tag removal (lines 4-8). Click handlers and styling directives are counted for analysis but discarded from the IR, ensuring no executable content persists in the output. This design prevents code injection while preserving the structural integrity of the diagram.

## Summary

- **mermaid_extract.py** is located at `skills/diagram-design/scripts/mermaid_extract.py` in the cathrynlavery/diagram-design repository.
- The tool accepts `.mmd`, `.mermaid`, and Markdown files, extracting fenced `mermaid` blocks via `load_blocks`.
- It supports four diagram types: flowchart, sequenceDiagram, stateDiagram-v2, and erDiagram.
- The IR uses `Node` and `Edge` dataclasses aggregated within a `Diagram` object.
- Output formats include human-readable Markdown digests and machine-readable JSON.
- Security measures sanitize all labels and discard executable directives like `click` handlers.

## Frequently Asked Questions

### What diagram types does mermaid_extract.py support?

The parser explicitly supports **flowchart**, **sequenceDiagram**, **stateDiagram-v2**, and **erDiagram** types as detected by `_kind_and_direction` (lines 111-127). Each type delegates to a specialized parser function that understands the specific syntax and semantics of that diagram variety. Unsupported diagram kinds trigger a clear error message during the detection phase.

### How does mermaid_extract.py handle security when parsing untrusted diagrams?

The script treats all content as untrusted data through the `clean_label` function (lines 4-8), which performs HTML unescaping and removes HTML tags from labels. Additionally, the `_discard_nonsemantic` function strips potentially dangerous directives like `click` handlers and `classDef` styling commands, ensuring only structural data enters the intermediate representation.

### Can mermaid_extract.py extract multiple diagrams from a single Markdown file?

Yes. The `load_blocks` function (lines 36-61) scans Markdown files for all fenced `` ```mermaid `` blocks and returns a list of `SourceBlock` objects. Each block maintains its original line number and index, allowing the parser to process multiple diagrams sequentially while preserving error context for each specific block.

### What is the difference between the Markdown digest and JSON output modes?

The **Markdown digest** produces human-readable tables summarizing nodes, edges, and analysis statistics, with optional row limits via `--max-rows` (lines 212-269). The **JSON** mode exports the complete `Diagram` object structure including all nodes, edges, metadata, and analysis results in a machine-parseable format (lines 250-268), suitable for downstream automation or database ingestion.