@hpcc-js/wasm-graphviz
Version:
hpcc-js - WASM Graphviz
281 lines (211 loc) • 8.34 kB
Markdown
---
title: Graphviz
description: WebAssembly wrapper for the Graphviz graph visualization library
outline: deep
---
# -js/wasm-graphviz
This package provides a WebAssembly wrapper around the [Graphviz](https://www.graphviz.org/) library. This allows for the rendering of DOT language graphs directly within a browser or NodeJS type environment.
## Installation
::: code-group
```sh [npm]
npm install -js/wasm-graphviz
```
```sh [yarn]
yarn add -js/wasm-graphviz
```
```sh [pnpm]
pnpm add -js/wasm-graphviz
```
:::
## Quick Start
```typescript
import { Graphviz } from "@hpcc-js/wasm-graphviz";
const graphviz = await Graphviz.load();
const svg = graphviz.dot(`digraph { a -> b; }`);
document.body.innerHTML = svg;
```
## Programmatic Graph Construction
Instead of writing DOT source by hand you can build graphs programmatically using
`createGraph`. The resulting `Graph` object can be rendered directly via
`graph.layout()` or serialised to DOT first with `graph.write()` / `graph.toDot()`.
Attribute names and values follow Graphviz itself; see the official Graphviz
[attribute reference](https://graphviz.org/docs/attrs/) for the supported graph,
node, edge, and cluster attributes.
Existing DOT can also be parsed into a mutable graph with `read`, and any
programmatic graph can be written back to DOT with `write` (`toDot` is an alias).
### Basic usage
```typescript
import { Graphviz } from "@hpcc-js/wasm-graphviz";
const graphviz = await Graphviz.load();
// createGraph returns a Graph; always call delete() when done (or use `using`)
using graph = graphviz.createGraph("G");
graph
.addNode("a")
.addNode("b")
.addEdge("a", "b")
.setNodeAttr("a", "shape", "box")
.setNodeAttr("a", "color", "red")
.setGraphAttr("rankdir", "LR");
// Render directly — no DOT round-trip needed
const svg = graph.layout("svg", "dot");
document.body.innerHTML = svg;
```
### One-shot JSON init objects
You can also create graphs, clusters, nodes, and edges from a single object
that includes their attributes.
```typescript
using graph = graphviz.createGraph({
name: "G",
type: "directed",
attrs: { rankdir: "LR" }
});
{
using cluster = graph.addSubgraph({
name: "cluster_0",
attrs: { label: "Group A", style: "filled", color: "lightgrey" }
});
cluster.addNode({
name: "a",
attrs: { shape: "box", color: "red" },
htmlAttrs: { label: "<B>A</B>" }
});
cluster.addEdge({
tail: "a",
head: "b",
attrs: { label: "hello" }
});
}
```
```typescript
using graph = graphviz.read(`digraph G { a -> b [label="hello"] }`);
graph.setGraphAttr("rankdir", "LR");
const dot = graph.write();
```
### Graph types
```typescript
// "directed" (default) | "undirected" | "strict directed" | "strict undirected"
using graph = graphviz.createGraph("G", "undirected");
graph.addEdge("x", "y");
```
### Mutating a graph between layouts
Nodes, edges, subgraphs, and attributes can all be removed after an initial
`layout()` call, allowing the graph to be updated and re-rendered without
rebuilding from scratch.
```typescript
using graph = graphviz.createGraph("G");
graph.addEdge("a", "b").addEdge("b", "c");
const svg1 = graph.layout(); // initial render
// Remove the middle node (also removes its edges), add a direct edge
graph.removeNode("b").addEdge("a", "c");
const svg2 = graph.layout(); // updated render
```
The full set of mutation methods on `Graph`:
| Method | Effect |
| --------------------------------------- | ------------------------------------------------------------------- |
| `removeNode(name)` | Removes the node and all its edges from the root graph |
| `removeEdge(tail, head, key?)` | Removes a single edge |
| `removeSubgraph(name)` | Dissolves a cluster boundary (nodes/edges remain in the root graph) |
| `removeGraphAttr(attr)` | Resets a graph-level attribute to its default |
| `removeNodeAttr(node, attr)` | Resets a node attribute to its default |
| `removeEdgeAttr(tail, head, key, attr)` | Resets an edge attribute to its default |
`Subgraph` has the same `removeNode` / `removeEdge` pair (scoped to the
subgraph only — root graph is unaffected) plus `removeAttr` /
`removeNodeAttr` / `removeEdgeAttr`.
### Existence checks and graph traversal
`Graph` and `Subgraph` expose methods for querying membership and iterating
the contents of a graph without serialising to DOT.
#### Existence checks
```typescript
using graph = graphviz.createGraph("G");
graph.addNode("alice");
graph.addEdge("alice", "bob");
graph.addSubgraph("cluster_a");
graph.hasNode("alice"); // true
graph.hasNode("charlie"); // false
graph.hasEdge("alice", "bob"); // true (key defaults to "")
graph.hasEdge("bob", "alice"); // false (directed)
graph.hasSubgraph("cluster_a"); // true
```
#### Count queries
```typescript
graph.nodeCount(); // number of nodes
graph.edgeCount(); // number of edges
graph.subgraphCount(); // number of direct subgraphs
```
#### Reading attributes
```typescript
graph.setNodeAttr("alice", "color", "red");
graph.setEdgeAttr("alice", "bob", "", "label", "hello");
graph.setGraphAttr("rankdir", "LR");
graph.getNodeAttr("alice", "color"); // "red"
graph.getEdgeAttr("alice", "bob", "", "label"); // "hello"
graph.getGraphAttr("rankdir"); // "LR"
graph.getNodeAttr("alice", "missing"); // "" (unknown attr)
// Omit the value to reset an attribute to Graphviz's default empty value.
graph.setNodeAttr("alice", "color");
graph.getNodeAttr("alice", "color"); // ""
// Pass a final defaultValue argument to declare Graphviz's default for unset objects.
graph.addNode("charlie");
graph.setNodeAttr("alice", "color", "red", "blue");
graph.getNodeAttr("charlie", "color"); // "blue"
// Or declare defaults directly for graph, node, and edge attributes.
graph.setDefaultNodeAttr("shape", "box");
graph.setDefaultEdgeAttr("color", "red");
// Use HTML-like attributes for Graphviz HTML labels.
graph.setNodeHtmlAttr("alice", "label", "<B>Alice</B>");
```
#### Node / edge / subgraph lists
```typescript
// Returns string[]
const nodes = graph.nodeNames();
const subgraphs = graph.subgraphNames();
// Returns EdgeInfo[] — { tail: string, head: string, key: string }
const allEdges = graph.edges(); // every edge exactly once
const outgoing = graph.outEdges("alice"); // edges leaving alice
const incoming = graph.inEdges("bob"); // edges entering bob
const incident = graph.nodeEdges("alice"); // all edges on alice (in + out)
```
`Subgraph` exposes the same traversal API (scoped to the subgraph's node/edge
set), except `hasSubgraph` / `subgraphCount` / `subgraphNames` which are only
on `Graph`.
### Subgraphs and clusters
Names beginning with `"cluster"` are drawn as bounded rectangles by most
layout engines.
```typescript
using graph = graphviz.createGraph("G");
// Each Subgraph wrapper also needs to be deleted (or use `using`)
{
using cluster = graph.addSubgraph("cluster_0");
cluster
.setAttr("label", "My Cluster")
.setAttr("style", "filled")
.setAttr("color", "lightblue")
.addEdge("a", "b");
}
const svg = graph.layout();
```
### Memory management
Every `Graph` and `Subgraph` wraps a native WASM object. You must release it
when finished to avoid memory leaks:
- **Preferred — `using` keyword** (TypeScript ≥ 5.2 with `"target": "ES2022"` or later):
```typescript
{
using graph = graphviz.createGraph("G");
// ... graph is automatically deleted when the block exits
}
```
- **Manual `delete()`**:
```typescript
const graph = graphviz.createGraph("G");
try {
// ...
} finally {
graph.delete();
}
```
Subgraph wrappers follow the same rule. The underlying subgraph _data_ is
owned by the parent `Graph` and freed automatically when the parent is deleted;
only the thin JS wrapper needs an explicit `delete()` / `using` block.
<!--: ../../docs/graphviz/src/graphviz/README.md-->
## Reference
- [API Documentation](https://hpcc-systems.github.io/hpcc-js-wasm/docs/graphviz/src/graphviz/classes/Graphviz.html)