Skip to content

Commit 3499974

Browse files
cgreenoclaude
andcommitted
feat(graph): tolerant graph/flowchart type detection
The diagram type was matched with an exact string switch on the first line, so any leading/trailing whitespace, a missing direction, or the reverse directions RL/BT caused an "unsupported graph type" error. Parse the declaration with strings.Fields instead: - ignore surrounding/repeated whitespace (e.g. indented ```mermaid blocks) - default to top-down when no direction is given (matches mermaid) - accept TD/TB/BT/LR/RL, mapping reverse directions onto their axis Adds a table-driven test covering these cases. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 823db56 commit 3499974

2 files changed

Lines changed: 67 additions & 11 deletions

File tree

cmd/parse.go

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ type graphNodeSpec struct {
3838
}
3939

4040
type textEdge struct {
41-
parent textNode
42-
child textNode
43-
label string
41+
parent textNode
42+
child textNode
43+
label string
4444
isBidirectional bool
4545
}
4646

@@ -371,14 +371,28 @@ func mermaidFileToMap(mermaid, styleType string) (*graphProperties, error) {
371371
return &properties, errors.New("missing graph definition")
372372
}
373373

374-
// First line should either say "graph TD" or "graph LR"
375-
switch lines[0] {
376-
case "graph LR", "flowchart LR":
377-
properties.graphDirection = "LR"
378-
case "graph TD", "flowchart TD", "graph TB", "flowchart TB":
379-
properties.graphDirection = "TD"
380-
default:
381-
return &properties, fmt.Errorf("unsupported graph type '%s'. Supported types: graph TD, graph TB, graph LR, flowchart TD, flowchart TB, flowchart LR", lines[0])
374+
// The first line declares the diagram: "graph" or "flowchart" followed by an
375+
// optional direction (e.g. "flowchart LR", "graph TD", or a bare "graph").
376+
// strings.Fields collapses any surrounding or repeated whitespace, so
377+
// indented or trailing-padded declarations parse correctly.
378+
fields := strings.Fields(lines[0])
379+
if len(fields) == 0 || (fields[0] != "graph" && fields[0] != "flowchart") {
380+
return &properties, fmt.Errorf("unsupported graph type '%s'. Supported types: 'graph' or 'flowchart' with an optional direction (TD, TB, BT, LR, RL)", strings.TrimSpace(lines[0]))
381+
}
382+
383+
// Mermaid defaults to top-down when no direction is given. The renderer only
384+
// lays out along the horizontal (LR) or vertical (TD) axis, so the reverse
385+
// directions RL and BT map onto their respective axes.
386+
properties.graphDirection = "TD"
387+
if len(fields) >= 2 {
388+
switch fields[1] {
389+
case "LR", "RL":
390+
properties.graphDirection = "LR"
391+
case "TD", "TB", "BT":
392+
properties.graphDirection = "TD"
393+
default:
394+
return &properties, fmt.Errorf("unsupported graph direction '%s'. Supported directions: TD, TB, BT, LR, RL", fields[1])
395+
}
382396
}
383397
lines = lines[1:]
384398

cmd/parse_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,3 +174,45 @@ func TestMermaidFileToMapUsesLatestExplicitLabel(t *testing.T) {
174174
t.Fatal("expected A label to remain explicit")
175175
}
176176
}
177+
178+
// TestGraphTypeDetection verifies that the diagram declaration line is parsed
179+
// tolerantly: surrounding whitespace, a missing direction (defaults to
180+
// top-down), and the reverse directions RL/BT are all accepted.
181+
func TestGraphTypeDetection(t *testing.T) {
182+
tests := []struct {
183+
name string
184+
input string
185+
wantDir string
186+
wantErr bool
187+
}{
188+
{"plain graph TD", "graph TD\nA --> B", "TD", false},
189+
{"flowchart LR", "flowchart LR\nA --> B", "LR", false},
190+
{"leading whitespace", " flowchart LR\n A --> B", "LR", false},
191+
{"trailing whitespace", "graph LR \nA --> B", "LR", false},
192+
{"indented graph TD", " graph TD\n A --> B", "TD", false},
193+
{"bare graph defaults to TD", "graph\nA --> B", "TD", false},
194+
{"TB maps to TD", "flowchart TB\nA --> B", "TD", false},
195+
{"RL maps to LR axis", "graph RL\nA --> B", "LR", false},
196+
{"BT maps to TD axis", "flowchart BT\nA --> B", "TD", false},
197+
{"unknown type errors", "sequenceDiagram\nA->>B: x", "", true},
198+
{"unknown direction errors", "graph SIDEWAYS\nA --> B", "", true},
199+
}
200+
201+
for _, tt := range tests {
202+
t.Run(tt.name, func(t *testing.T) {
203+
props, err := mermaidFileToMap(tt.input, "cli")
204+
if tt.wantErr {
205+
if err == nil {
206+
t.Fatalf("expected error, got direction %q", props.graphDirection)
207+
}
208+
return
209+
}
210+
if err != nil {
211+
t.Fatalf("unexpected error: %v", err)
212+
}
213+
if props.graphDirection != tt.wantDir {
214+
t.Errorf("direction = %q, want %q", props.graphDirection, tt.wantDir)
215+
}
216+
})
217+
}
218+
}

0 commit comments

Comments
 (0)