@homedev/ariel
v0.1.8
Published
A tiny Mermaid graph parser and writer with extensions.
Readme
Ariel - Tiny Mermaid Graph Parser & Writer
Ariel is a lightweight TypeScript library for parsing and building Mermaid diagrams. It provides a fluent API for programmatic graph manipulation with support for nodes, connections, groups (subgraphs), and CSS classes.
Installation
npm install @homedev/ariel
# or
bun add @homedev/arielQuick Start
Basic Usage - Parsing
import { Ariel } from '@homedev/ariel'
// Create a parser and parse Mermaid syntax
const ariel = Ariel
.createParser()
.parse(`
graph TD
A["Node A"]
B["Node B"]
A-->B
`)
// Listen for computed events
ariel.on('node', (node) => {
console.log(`Processing node: ${node.name}`)
})
// Compute the graph structure
ariel.compute()
// Serialize back to Mermaid format
const output = ariel.toString()
console.log(output)Building Graphs Programmatically
const ariel = new Ariel()
ariel.type = 'graph TD'
// Add nodes
ariel.addNodes([
{ name: 'start', label: 'Start', shape: 'circle' },
{ name: 'process', label: 'Process', shape: 'square' },
{ name: 'end', label: 'End', shape: 'circle' }
])
// Connect nodes
ariel.connectNodes('start', 'process', 'proceed')
ariel.connectNodes('process', 'end', 'done')
// Compute and output
ariel.compute()
console.log(ariel.toString())Core Concepts
Parser Events
The parser emits events during parsing:
const parser = Ariel.createParser()
parser
.on('node', (node, state) => {
console.log(`Found node: ${node.name}`)
})
.on('group', (groupName, nodes, state) => {
console.log(`Found group: ${groupName} with ${nodes.length} nodes`)
})
.on('class', (className, nodes, state) => {
console.log(`Assigning class ${className} to ${nodes.length} nodes`)
})
.on('line', (line, state) => {
// Custom line parsing - return true to skip default parsing
if (line.startsWith('custom:')) {
console.log(`Custom line: ${line}`)
return true
}
return false
})
const ariel = parser.parse(mermaidText)Graph Events
After parsing, listen to graph computation events:
ariel
.on('node', (node) => {
// Modify node properties
if (!node.style) {
node.style = 'fill:#f0f'
}
})
.on('group', (group) => {
// Customize group styling
group.style = 'fill:#0f0'
})
.on('class', (cls) => {
// Modify class properties
if (cls.name === 'important') {
cls.style = 'stroke:#f00,stroke-width:2px'
}
})
ariel.compute()API Reference
Ariel
Main class for building and manipulating graphs.
Properties
type: string- Graph type (e.g., "graph TD")nodes: ArielNode[]- Array of all nodesgroups: ArielGroupWithRefs[]- Array of all groups/subgraphsclasses: ArielClassWithRefs[]- Array of all style classesconnections: ArielConnection[]- Array of all connections
Methods
static createParser(): ArielParser- Create a new parser instanceaddNodes(nodes: ArielNode[]): this- Add nodes to the graphconnectNodes(from: string, to: string, label?: string): this- Create a connection between two nodeson(event: string, callback: Function): this- Listen for eventscompute(): void- Compute the graph structure (must call beforetoString())toString(): string- Serialize to Mermaid format
ArielNode
Represents a node in the graph.
interface ArielNode {
name: string // Unique node identifier
label?: string // Display label
shape?: string // Node shape (circle, square, diamond, etc.)
link?: string // URL link
style?: string // Inline CSS style
class?: string // CSS class name
group?: string // Group/subgraph name
from?: (string | Connection)[] // Incoming connections
to?: (string | Connection)[] // Outgoing connections
}ArielConnection
Represents a connection between two nodes.
interface ArielConnection {
from: ArielNode
to: ArielNode
label?: string
}Supported Shapes
circle/round- Circlesquare/rect/rectangle- Squarediamond/decision- Diamondhexagon/hex- Hexagoncylinder/database- Cylinderstadium- Stadiumsubroutine- Subroutinetrapezoid- Trapezoidparallelogram- Parallelogram
Examples
Complete Workflow
import { Ariel } from '@homedev/ariel'
// Parse existing Mermaid diagram
const mermaidText = `
graph TD
subgraph Team
Alice["Alice"]
Bob["Bob"]
end
subgraph Tasks
T1["Task 1"]
T2["Task 2"]
end
Alice-->|assigns|T1
Bob-->|works on|T2
class Alice,Bob person
class T1,T2 task
classDef person fill:#f9f,stroke:#f0f
classDef task fill:#fbf,stroke:#faf
`
const ariel = Ariel.createParser().parse(mermaidText)
// Customize nodes during computation
ariel
.on('node', (node) => {
node.style = 'stroke-width:2px'
})
.on('group', (group) => {
if (group.name === 'Team') {
group.style = 'fill:#eee'
}
})
ariel.compute()
// Get the Mermaid output
const output = ariel.toString()
console.log(output)Custom Node Creation
const ariel = Ariel.createParser()
.on('line', (line, state) => {
// Custom syntax: actor: name
if (line.startsWith('actor:')) {
const name = line.substring(6).trim()
state.result.nodes.push({
name,
shape: 'circle',
style: 'fill:#ffb6c1'
})
return true // Skip default parsing
}
return false
})
.parse(`
graph TD
actor: Alice
actor: Bob
`)
ariel.compute()
console.log(ariel.toString())TypeScript Support
Ariel is fully typed and includes comprehensive type definitions for all public APIs:
import {
Ariel,
ArielNode,
ArielConnection,
ArielParser
} from '@homedev/ariel'
const node: ArielNode = {
name: 'mynode',
label: 'My Node',
shape: 'circle'
}
const ariel: Ariel = new Ariel()
ariel.addNodes([node])Performance
Ariel is optimized for small to medium graphs (typically under 1000 nodes). It uses:
- Efficient event emitter pattern (Node.js EventEmitter)
- Synchronous processing (no async overhead)
- Minimal dependencies
- Tree-shaking friendly
Testing
Run the test suite with:
bun testDevelopment & Publishing
Build Structure
Ariel uses a hierarchical build structure for clean npm publishing:
project/
├── package.json (development - contains devDependencies, scripts)
├── src/ (source TypeScript files)
├── tests/ (test files)
└── release/ (auto-generated during build)
├── package.json (publishing version - clean, no devDeps)
└── dist/
├── index.js (compiled & minified code)
└── index.d.ts (bundled type definitions)Build Process
The build pipeline consists of several steps:
# Full build (recommended)
bun run build
# Individual steps:
bun run build:js # Compile TypeScript to JavaScript
bun run build:types # Generate .d.ts files
bun run build:bundle-types # Merge type definitions
bun run prepare-publish # Generate clean release/package.jsonWhy separate package.json files?
- Root package.json: Contains all development tools (devDependencies) and build scripts
- release/package.json: Clean version for npm publishing (no devDeps, no scripts)
- This ensures published packages are lightweight and don't include unnecessary development tooling
Publishing to npm
# Build and publish (uses prepublishOnly hook)
bun push
# Manual alternative
bun run build
npm publish ./releaseWhen you run npm publish (or bun push):
- The
prepublishOnlyhook triggersbun run build - TypeScript is compiled to
release/dist/ - Type definitions are bundled to
release/dist/index.d.ts - A clean
release/package.jsonis generated - npm publishes the
release/folder as the package
File Organization
- src/: Source TypeScript files (all exported in index.ts)
- release/: Publishing artifacts (generated, git-ignored)
- .temp/: Temporary TypeScript output (generated, cleaned up automatically)
- config/: ESLint configuration
- tests/: Unit tests for all modules
The library includes comprehensive unit tests for:
- Parser functionality
- Graph building and manipulation
- Node styling and shaping
- Connection management
- Event emissions
- Serialization
License
MIT
