Repository: https://github.com/neongreen/mermaid-ast
Description: Parse and render Mermaid diagrams to/from AST
License: MIT
Approach: Uses vendored JISON parsers from mermaid.js
- ✅ Complete AST-based parser - Supports all 18 mermaid diagram types
- ✅ Round-trip guarantee -
render(parse(text))produces equivalent diagrams - ✅ Uses official JISON parsers - Vendored from mermaid.js for compatibility
- ✅ Well-tested - Unit tests, round-trip tests, golden JSON tests
- ✅ Programmatic API - Wrapper classes for building/mutating diagrams
- ✅ TypeScript - Full type definitions
| File | Lines | Purpose |
|---|---|---|
types/class.ts |
91 | Type definitions for AST |
parser/class-parser.ts |
352 | Parser using vendored JISON |
renderer/class-renderer.ts |
257 | Render AST back to mermaid syntax |
tests/unit/class-parser.test.ts |
285 | Unit tests |
Total: ~900 lines for complete class diagram support
| File | Lines | Purpose |
|---|---|---|
types.ts |
~200 | Type definitions |
class-extractor.ts |
~560 | Custom parser (no JISON) |
| Tests | ~700+ | Unit + integration tests |
Total: ~1500 lines
export interface ClassDiagramAST {
type: 'classDiagram';
direction: ClassDirection; // 'TB' | 'BT' | 'LR' | 'RL'
classes: Map<string, ClassDefinition>;
relations: ClassRelation[];
namespaces: Map<string, Namespace>;
notes: ClassNote[];
classDefs: Map<string, ClassDefStyle>;
accTitle?: string;
accDescription?: string;
}
export interface ClassDefinition {
id: string;
label?: string;
members: ClassMember[];
annotations: string[]; // ["interface", "abstract", "service"]
cssClasses: string[];
styles: string[];
link?: string;
linkTarget?: string;
tooltip?: string;
callback?: string;
callbackArgs?: string;
}
export interface ClassRelation {
id1: string;
id2: string;
relation: {
type1: RelationType;
type2: RelationType;
lineType: LineType;
};
relationTitle1?: string;
relationTitle2?: string;
title?: string;
}export interface ClassDiagramResult {
classes: ParsedClass[];
relations: ParsedRelation[];
namespaces: ParsedNamespace[];
notes: ParsedNote[];
}
export interface ParsedClass {
name: string;
isGeneric: boolean;
typeParams: string[];
stereotype: Stereotype;
body: ClassBody;
namespace?: string;
startLine: number;
endLine: number;
}
export interface ParsedRelation {
from: string;
to: string;
type: RelationType;
sourceCardinality?: string;
targetCardinality?: string;
label?: string;
}| Feature | mermaid-ast | Our Implementation |
|---|---|---|
| Parser Engine | Vendored JISON from mermaid.js | Custom regex-based parser |
| Classes Storage | Map<string, ClassDefinition> |
ParsedClass[] |
| Namespaces | Map with class IDs list |
Array with class list |
| Direction | Stored in AST | ❌ Not tracked |
| Accessibility | accTitle, accDescription |
❌ Not tracked |
| CSS/Styles | Full support | ❌ Not tracked |
| Line Numbers | ❌ Not tracked | ✅ Tracked |
| Generics | Via JISON parser | Custom parsing |
| Rendering | ✅ Can render back | ❌ No rendering |
Instead of writing our own parser, we could use the vendored JISON parser like they do:
Advantages:
- ✅ Guaranteed mermaid.js compatibility
- ✅ Handles all edge cases (they spent years on this)
- ✅ Automatic updates when we sync parsers
- ✅ Less code to maintain
How they do it (simplified):
import classParser from '../vendored/parsers/class.js';
// Create yy object with callbacks
const yy = {
addClass(id: string) { /* add to AST */ },
addRelation(rel) { /* add to AST */ },
addMember(id, member) { /* parse and add */ },
// ... etc
};
classParser.yy = yy;
classParser.parse(input);Their type structure is cleaner:
- Uses
Mapfor O(1) lookups vs our array searches - Separates concerns better (classes, relations, namespaces, notes)
annotationsarray vs our singlestereotypeenum
From their implementation:
- Direction -
direction TB/LR/RL/BT - Accessibility -
accTitle,accDescription - CSS Classes -
cssClass,classDef - Inline styles -
styledirective - Click/Link handlers -
click,link,callback - Text labels -
class C1["Display Text"]
They have 3 test types:
- Unit tests - Parse/render specific features
- Round-trip tests -
render(parse(render(parse(x)))) === render(parse(x)) - Golden JSON tests - Snapshot testing of AST structure
Pros:
- Maximum compatibility with mermaid.js
- Less code to maintain
- Handles all edge cases
- Easy to update (sync script provided)
Cons:
- Dependency on vendored code
- Slightly larger bundle
- Less control over parsing
Implementation:
- Copy their vendored JISON parser
- Adapt their
createClassYYto build our AST types - Keep our type structure but align with theirs where sensible
- Add line number tracking (they don't have this)
Pros:
- Full control
- Smaller bundle (no JISON)
- Already working
Cons:
- Must manually track mermaid.js changes
- More edge cases to handle
- More code to maintain
Implementation:
- Add direction parsing
- Add text label support
class C1["Label"] - Add CSS class syntax
class C1:::styleClass - Improve generic parsing (nested, composite)
Use their JISON parser but keep our AST structure:
- Parse using JISON → their AST
- Transform to our AST structure
- Keep our line number tracking
| Criterion | Option A (JISON) | Option B (Custom) | Option C (Hybrid) |
|---|---|---|---|
| Compatibility | ⭐⭐⭐ | ⭐ | ⭐⭐⭐ |
| Maintainability | ⭐⭐⭐ | ⭐ | ⭐⭐ |
| Bundle Size | ⭐ | ⭐⭐⭐ | ⭐ |
| Control | ⭐ | ⭐⭐⭐ | ⭐⭐ |
| Line Tracking | Custom add | ✅ | ✅ |
| Effort | Medium | High | High |
- Review their parser tests - Extract test cases we're missing
- Compare AST structures - Identify type alignment opportunities
- Evaluate JISON adoption - Proof of concept with their parser
- Extract golden test fixtures - Use their
.mmdfiles as additional test cases
speckey/reference-projects/mermaid-ast/packages/mermaid-ast/
├── src/
│ ├── types/class.ts # AST type definitions
│ ├── parser/class-parser.ts # JISON parser wrapper
│ ├── renderer/class-renderer.ts # Render AST → mermaid
│ └── vendored/
│ ├── parsers/class.js # Compiled JISON parser
│ └── grammars/class.jison # JISON grammar
├── tests/
│ ├── unit/class-parser.test.ts # Unit tests
│ ├── roundtrip/class-roundtrip.test.ts # Round-trip tests
│ ├── golden/class/*.mmd # Test fixtures
│ └── fixtures/class.json # Golden AST
└── examples/class-diagram.ts # Usage examples
mermaid-ast is highly relevant and usable. Their JISON-based approach is battle-tested and provides guaranteed compatibility with mermaid.js. We should seriously consider adopting their parser while keeping our line tracking feature.
Next steps: Create a proof-of-concept using their JISON parser with our AST types to evaluate feasibility.