Importers
@oh-just-another/importers converts popular diagram source formats into a
@oh-just-another/scene Scene. Five formats are supported: Mermaid
flowcharts, Graphviz dot, drawio XML, .excalidraw JSON (import and
export), and JSON Canvas (.canvas).
Each format has a one-shot helper (importMermaid / importDot /
importDrawio) that parses, lays out, and materialises a Scene in a single
call. Under the hood the pipeline is two-stage: a per-format parseX produces an
intermediate GraphDocument, then graphToScene runs layout (via
@dagrejs/dagre) and builds the Scene. Use
the split when you need to inspect or transform nodes/edges before materialising.
Mermaidβ
importMermaid(source: string): Scene parses a Mermaid flowchart.
import { importMermaid } from "@oh-just-another/importers";
const scene = importMermaid(`flowchart TD
A[Start] --> B{Decide}
B -->|yes| C(Done)
B -->|no| A`);
Supported subset: direction headers (TD / TB / BT / LR / RL, plus the
graph alias), bracket shapes ([ ], ( ), (( )), { }), directed (-->),
undirected (---), labelled (-->|label|) and chained edges. Comments and
class / classDef / style / subgraph are silently ignored.
exportMermaid(scene: Scene): string is the inverse β it writes a
flowchart TD where rectangle β id[label], ellipse β id((label)),
polygon β id{label} (labels from a text element centred over the shape),
links become A --> B / A -->|label| B, and non-graph elements
(brush / image / template / β¦) turn into %% skipped: <type> comments.
importMermaid(exportMermaid(scene)) round-trips the node + edge structure.
Graphviz (dot)β
importDot(source: string): Scene parses Graphviz dot.
import { importDot } from "@oh-just-another/importers";
const scene = importDot(`digraph G {
rankdir=LR
a [shape=box, label="A"]
a -> b -> c
}`);
Supported subset: digraph / graph with the strict modifier, rankdir,
directed (->) / undirected (--) edges and chains, node attrs (label,
shape, fillcolor, color), edge label, and //, /* */, # comments.
drawioβ
importDrawio(source: string): Scene parses the uncompressed <mxGraphModel>
XML payload. Unlike the other two, drawio files keep their original coordinates,
so graphToScene skips layout entirely when every node already has a position.
import { readFile } from "node:fs/promises";
import { importDrawio } from "@oh-just-another/importers";
const xml = await readFile("diagram.drawio", "utf8");
const scene = importDrawio(xml);
Vertices, edges, and shapes inferred from style (ellipse, rhombus/diamond,
rounded, rectangle) are supported. Subgraphs, groups, swimlanes, custom mxgraph
shapes, splines, and fonts are not.
.excalidrawβ
importExcalidraw(json: string): Scene imports a .excalidraw JSON document;
exportExcalidraw(scene: Scene): string writes one back (format version 2) β
the pair round-trips shapes, geometry and connectors.
import { importExcalidraw, exportExcalidraw } from "@oh-just-another/importers";
const scene = importExcalidraw(await readFile("sketch.excalidraw", "utf8"));
await writeFile("sketch.excalidraw", exportExcalidraw(scene));
Rectangles, ellipses, diamonds (β 4-point polygons), text, freedraw (β brush),
images with embedded data, frames, and arrows/lines (β straight Links with
anchor or point endpoints) are mapped both ways. Nested groups flatten to the
outermost group on import; templates, block arrows, paths, link labels and
externally-hosted images are not exported. Unknown element types are skipped,
never fatal.
JSON Canvasβ
importJsonCanvas(json: string): Scene imports a JSON Canvas
(jsoncanvas.org) document: text nodes β text
elements, file / link nodes β text elements showing the path / URL,
group nodes β frames, edges β straight Links anchored on fromSide /
toSide (edge label and preset colours "1"β"6" carried over).
import { importJsonCanvas } from "@oh-just-another/importers";
const scene = importJsonCanvas(await readFile("board.canvas", "utf8"));
Two-stage pipelineβ
import { parseMermaid, graphToScene } from "@oh-just-another/importers";
const graph = parseMermaid("flowchart LR\n A --> B");
// inspect / mutate graph.nodes and graph.edges hereβ¦
const scene = graphToScene(graph);
The per-format parsers are hand-rolled and intentionally minimal β hosts needing
full fidelity can pre-process with the official tool and hand the result to
graphToScene as a GraphDocument. The .excalidraw and JSON Canvas
importers skip the GraphDocument step and build the Scene directly β those
formats already carry coordinates and styles.