zombie-mermaid 2.2.1 → 2.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -0
- package/README.md +7 -2
- package/dist/ascii.cjs +9 -9
- package/dist/ascii.cjs.map +1 -1
- package/dist/ascii.js +4069 -3969
- package/dist/ascii.js.map +1 -1
- package/dist/cli.js +7605 -7381
- package/dist/index.cjs +52 -52
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +7613 -7459
- package/dist/index.js.map +1 -1
- package/dist/mcp.cjs +14142 -13717
- package/dist/mcp.cjs.map +1 -1
- package/dist/mcp.js +14142 -13717
- package/dist/mcp.js.map +1 -1
- package/package.json +27 -5
- package/packages/ascii-renderer/LICENSE +22 -0
- package/{src/ascii → packages/ascii-renderer/src}/class-diagram.ts +277 -57
- package/{src/ascii → packages/ascii-renderer/src}/er-diagram.ts +2 -2
- package/packages/ascii-renderer/src/flowchart.ts +94 -0
- package/{src/ascii → packages/ascii-renderer/src}/grid.ts +23 -0
- package/{src/ascii → packages/ascii-renderer/src}/index.ts +13 -84
- package/{src/ascii → packages/ascii-renderer/src}/registry.ts +23 -12
- package/{src/ascii → packages/ascii-renderer/src}/sequence.ts +55 -8
- package/{src/ascii → packages/ascii-renderer/src}/xychart.ts +6 -3
- package/packages/core/LICENSE +22 -0
- package/packages/core/src/click-directive.ts +106 -0
- package/packages/core/src/color-utils.ts +205 -0
- package/packages/core/src/diagram-type.ts +35 -0
- package/packages/core/src/direction-override.ts +46 -0
- package/packages/core/src/direction.ts +56 -0
- package/packages/core/src/index.ts +34 -0
- package/packages/core/src/init-directive.ts +248 -0
- package/packages/core/src/multiline-utils.ts +275 -0
- package/packages/core/src/statements.ts +197 -0
- package/packages/core/src/style-directives.ts +214 -0
- package/packages/core/src/text-metrics.ts +498 -0
- package/packages/core/src/theme.ts +696 -0
- package/packages/core/src/types.ts +670 -0
- package/packages/mcp/LICENSE +22 -0
- package/{src/mcp → packages/mcp/src}/server.ts +34 -2
- package/{src/mcp → packages/mcp/src}/tools/check-sequence-activations.ts +8 -5
- package/packages/mcp/src/tools/fix-sequence-activations.ts +77 -0
- package/{src/mcp → packages/mcp/src}/tools/render-ascii.ts +2 -2
- package/{src/mcp → packages/mcp/src}/tools/render-svg.ts +1 -1
- package/packages/mermaid-parser/LICENSE +22 -0
- package/{src → packages/mermaid-parser/src}/class/parser.ts +86 -8
- package/{src → packages/mermaid-parser/src}/er/parser.ts +59 -26
- package/packages/mermaid-parser/src/index.ts +48 -0
- package/{src → packages/mermaid-parser/src}/sequence/activation-check.ts +4 -3
- package/packages/mermaid-parser/src/sequence/activation-fix.ts +112 -0
- package/{src → packages/mermaid-parser/src}/sequence/parser.ts +74 -16
- package/{src → packages/mermaid-parser/src}/xychart/parser.ts +84 -6
- package/packages/svg-renderer/LICENSE +22 -0
- package/{src → packages/svg-renderer/src}/class/layout.ts +8 -4
- package/{src → packages/svg-renderer/src}/class/renderer.ts +7 -6
- package/packages/svg-renderer/src/edge-curves.ts +204 -0
- package/packages/svg-renderer/src/elk-instance.ts +292 -0
- package/{src → packages/svg-renderer/src}/er/layout.ts +6 -4
- package/{src → packages/svg-renderer/src}/er/renderer.ts +4 -4
- package/packages/svg-renderer/src/index.ts +58 -0
- package/packages/svg-renderer/src/layout-engine/constants.ts +19 -0
- package/packages/svg-renderer/src/layout-engine/edge-bundling.ts +379 -0
- package/packages/svg-renderer/src/layout-engine/elk-adapter-utils.ts +81 -0
- package/packages/svg-renderer/src/layout-engine/elk-graph-builder.ts +240 -0
- package/packages/svg-renderer/src/layout-engine/from-elk.ts +685 -0
- package/packages/svg-renderer/src/layout-engine/layer-alignment.ts +174 -0
- package/packages/svg-renderer/src/layout-engine/to-elk.ts +695 -0
- package/packages/svg-renderer/src/layout-engine.ts +74 -0
- package/packages/svg-renderer/src/layout.ts +8 -0
- package/packages/svg-renderer/src/renderer.ts +1485 -0
- package/packages/svg-renderer/src/resolve-colors.ts +339 -0
- package/{src → packages/svg-renderer/src}/sequence/layout.ts +2 -6
- package/{src → packages/svg-renderer/src}/sequence/renderer.ts +4 -4
- package/packages/svg-renderer/src/shape-clipping.ts +197 -0
- package/packages/svg-renderer/src/styles.ts +118 -0
- package/{src → packages/svg-renderer/src}/xychart/layout.ts +2 -2
- package/{src → packages/svg-renderer/src}/xychart/renderer.ts +6 -6
- package/src/__tests__/ascii-ansi.test.ts +6 -2
- package/src/__tests__/ascii-canvas-write.test.ts +2 -2
- package/src/__tests__/ascii-charset-border-junctions.test.ts +1 -1
- package/src/__tests__/ascii-cjk-width.test.ts +1 -1
- package/src/__tests__/ascii-class-box-occupancy.test.ts +147 -45
- package/src/__tests__/ascii-class-column-width-488-489.test.ts +1 -1
- package/src/__tests__/ascii-class-cross-level-jog-corruption.test.ts +187 -0
- package/src/__tests__/ascii-class-detour-label-routing-487.test.ts +1 -1
- package/src/__tests__/ascii-class-diagram-compartments.test.ts +1 -1
- package/src/__tests__/ascii-class-label-box-collision.test.ts +1 -1
- package/src/__tests__/ascii-class-label-row-collision-531.test.ts +1 -1
- package/src/__tests__/ascii-class-label-territory-row-awareness.test.ts +1 -1
- package/src/__tests__/ascii-class-padding.test.ts +1 -1
- package/src/__tests__/ascii-class-reciprocal-relationships-448.test.ts +86 -1
- package/src/__tests__/ascii-combining-mark-width.test.ts +2 -2
- package/src/__tests__/ascii-coords-overlay.test.ts +2 -2
- package/src/__tests__/ascii-decision-lr-box-start.test.ts +2 -2
- package/src/__tests__/ascii-display-width-unit.test.ts +1 -1
- package/src/__tests__/ascii-draw-arrows-coverage.test.ts +1 -1
- package/src/__tests__/ascii-draw-arrows-single-point-path.test.ts +12 -5
- package/src/__tests__/ascii-draw-bundles.test.ts +5 -5
- package/src/__tests__/ascii-edge-bundling-rank-violation-454.test.ts +1 -1
- package/src/__tests__/ascii-edge-bundling-routing.test.ts +9 -5
- package/src/__tests__/ascii-edge-cross-style-overlap.test.ts +10 -6
- package/src/__tests__/ascii-edge-ending-glyphs.test.ts +1 -1
- package/src/__tests__/ascii-edge-label-diagonal-fallback-418.test.ts +2 -2
- package/src/__tests__/ascii-edge-routing-fixes.test.ts +1 -1
- package/src/__tests__/ascii-edge-routing-single-point-path.test.ts +8 -4
- package/src/__tests__/ascii-edge-styles.test.ts +1 -1
- package/src/__tests__/ascii-emoji-cluster-width.test.ts +2 -2
- package/src/__tests__/ascii-er-box-occupancy.test.ts +1 -1
- package/src/__tests__/ascii-er-cardinality.test.ts +1 -1
- package/src/__tests__/ascii-er-corner-glyphs.test.ts +1 -1
- package/src/__tests__/ascii-er-jog-stray-line.test.ts +1 -1
- package/src/__tests__/ascii-er-label-padding.test.ts +1 -1
- package/src/__tests__/ascii-er-padding.test.ts +1 -1
- package/src/__tests__/ascii-er-relationship-label-corruption-350.test.ts +1 -1
- package/src/__tests__/ascii-er-relationship-overwrite.test.ts +1 -1
- package/src/__tests__/ascii-er-stray-connectors.test.ts +7 -4
- package/src/__tests__/ascii-er-unrelated-stem-separation-411.test.ts +1 -1
- package/src/__tests__/ascii-er-vertical-one-marker.test.ts +1 -1
- package/src/__tests__/ascii-grid-deep-chain.test.ts +4 -4
- package/src/__tests__/ascii-hyperlinks.test.ts +9 -6
- package/src/__tests__/ascii-label-line-terminal-fallback.test.ts +8 -4
- package/src/__tests__/ascii-lane-search.test.ts +1 -1
- package/src/__tests__/ascii-multibox-cjk-width.test.ts +1 -1
- package/src/__tests__/ascii-multiline.test.ts +6 -3
- package/src/__tests__/ascii-nested-subgraph-sibling-order-444.test.ts +8 -5
- package/src/__tests__/ascii-package-boundary.test.ts +47 -37
- package/src/__tests__/ascii-padding-edge-cases.test.ts +1 -1
- package/src/__tests__/ascii-parallel-edges-329.test.ts +8 -5
- package/src/__tests__/ascii-pathfinder-route-edge.test.ts +14 -4
- package/src/__tests__/ascii-reverse-direction-edges-629.test.ts +9 -5
- package/src/__tests__/ascii-sequence-alt-else-label.test.ts +1 -1
- package/src/__tests__/ascii-sequence-block-wall-clearance.test.ts +1 -1
- package/src/__tests__/ascii-sequence-box-group.test.ts +1 -1
- package/src/__tests__/ascii-sequence-cjk-width.test.ts +1 -1
- package/src/__tests__/ascii-sequence-create-destroy.test.ts +1 -1
- package/src/__tests__/ascii-sequence-form-invariants.test.ts +48 -50
- package/src/__tests__/ascii-sequence-mermaid-parity.test.ts +1 -1
- package/src/__tests__/ascii-sequence-notes.test.ts +1 -1
- package/src/__tests__/ascii-sequence-padding.test.ts +1 -1
- package/src/__tests__/ascii-sequence-self-arrow.test.ts +1 -1
- package/src/__tests__/ascii-shape-diamond.test.ts +3 -3
- package/src/__tests__/ascii-shape-rectangle.test.ts +5 -5
- package/src/__tests__/ascii-shape-rounded.test.ts +3 -3
- package/src/__tests__/ascii-shapes-circle.test.ts +3 -3
- package/src/__tests__/ascii-shapes-hexagon.test.ts +3 -3
- package/src/__tests__/ascii-shapes-special.test.ts +10 -5
- package/src/__tests__/ascii-shapes-stadium.test.ts +3 -3
- package/src/__tests__/ascii-shapes-state.test.ts +15 -5
- package/src/__tests__/ascii-state-bidirectional-label-swap-530.test.ts +1 -1
- package/src/__tests__/ascii-subgraph-bbox.test.ts +8 -4
- package/src/__tests__/ascii-subgraph-direction-honored-445.test.ts +1 -1
- package/src/__tests__/ascii-subgraph-label-border-clip.test.ts +88 -25
- package/src/__tests__/ascii-subgraph-later-edgeless-301.test.ts +8 -4
- package/src/__tests__/ascii-subgraph-title-padding.test.ts +1 -1
- package/src/__tests__/ascii-territory-unit.test.ts +2 -2
- package/src/__tests__/ascii-validate.test.ts +1 -1
- package/src/__tests__/ascii.test.ts +5 -2
- package/src/__tests__/box-color.test.ts +1 -1
- package/src/__tests__/class-arrow-directions.test.ts +1 -1
- package/src/__tests__/class-click-interactions.test.ts +3 -6
- package/src/__tests__/class-integration.test.ts +1 -1
- package/src/__tests__/class-notes.test.ts +3 -3
- package/src/__tests__/class-parser-error-messages.test.ts +162 -0
- package/src/__tests__/class-parser.test.ts +6 -6
- package/src/__tests__/class-styling.test.ts +2 -2
- package/src/__tests__/cli-render.test.ts +16 -1
- package/src/__tests__/direction-override.test.ts +1 -1
- package/src/__tests__/draw-lines.test.ts +2 -2
- package/src/__tests__/edge-cell-styles.test.ts +5 -2
- package/src/__tests__/elk-layout-cache.test.ts +6 -4
- package/src/__tests__/er-click-unsupported.test.ts +5 -8
- package/src/__tests__/er-parser-error-messages.test.ts +82 -0
- package/src/__tests__/er-parser.test.ts +3 -6
- package/src/__tests__/expanded-shapes.test.ts +1 -1
- package/src/__tests__/grid-occupancy.test.ts +1 -1
- package/src/__tests__/mcp-check-sequence-activations-tool.test.ts +1 -1
- package/src/__tests__/mcp-fix-sequence-activations-tool.test.ts +134 -0
- package/src/__tests__/mcp-render-ascii-tool.test.ts +5 -4
- package/src/__tests__/mcp-render-svg-theme-guard.test.ts +3 -3
- package/src/__tests__/mcp-render-svg-tool.test.ts +1 -1
- package/src/__tests__/mcp-server.test.ts +30 -1
- package/src/__tests__/multiline-labels.test.ts +29 -18
- package/src/__tests__/parser.test.ts +117 -5
- package/src/__tests__/pathfinder.test.ts +11 -4
- package/src/__tests__/render-options-overrides.test.ts +28 -21
- package/src/__tests__/render-options-per-type.test.ts +13 -9
- package/src/__tests__/resolve-colors.test.ts +1 -1
- package/src/__tests__/self-loop-edges.test.ts +112 -0
- package/src/__tests__/sequence-activation-check.test.ts +7 -8
- package/src/__tests__/sequence-activation-fix.test.ts +91 -0
- package/src/__tests__/sequence-layout.test.ts +4 -7
- package/src/__tests__/sequence-parser-syntax-errors.test.ts +179 -0
- package/src/__tests__/sequence-parser.test.ts +3 -6
- package/src/__tests__/statements.test.ts +53 -35
- package/src/__tests__/testdata/ascii/subgraph_direction_override.txt +21 -21
- package/src/__tests__/theme-palette-contrast.test.ts +84 -0
- package/src/__tests__/workspace-package-boundaries.test.ts +199 -25
- package/src/__tests__/xychart-ascii.test.ts +1 -1
- package/src/__tests__/xychart-colors.test.ts +3 -2
- package/src/__tests__/xychart-layout.test.ts +1 -1
- package/src/__tests__/xychart-parser-errors.test.ts +95 -0
- package/src/browser.ts +8 -2
- package/src/cli/mcp.ts +1 -1
- package/src/cli/render.ts +59 -25
- package/src/cli/web.ts +1 -1
- package/src/diagram-registry.ts +190 -25
- package/src/index.ts +34 -131
- package/src/parser.ts +102 -40
- /package/{src/ascii → packages/ascii-renderer/src}/ansi.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/canvas.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/converter.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/coords.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/display-width.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/draw-arrows.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/draw-boxes.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/draw-bundles.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/draw-lines.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/draw-subgraphs.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/draw.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/edge-bundling.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/edge-cell-styles.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/edge-routing.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/grid-occupancy.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/hyperlinks.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/lane-search.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/multiline-utils.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/pathfinder.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/shapes/circle.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/shapes/corners.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/shapes/diamond.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/shapes/hexagon.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/shapes/index.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/shapes/rectangle.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/shapes/rounded.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/shapes/special.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/shapes/stadium.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/shapes/state.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/shapes/types.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/territory.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/types.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/validate.ts +0 -0
- /package/{src/mcp → packages/mcp/src}/index.ts +0 -0
- /package/{src → packages/mermaid-parser/src}/class/format.ts +0 -0
- /package/{src → packages/mermaid-parser/src}/class/types.ts +0 -0
- /package/{src → packages/mermaid-parser/src}/er/types.ts +0 -0
- /package/{src → packages/mermaid-parser/src}/expanded-shapes.ts +0 -0
- /package/{src → packages/mermaid-parser/src}/sequence/box-color.ts +0 -0
- /package/{src → packages/mermaid-parser/src}/sequence/types.ts +0 -0
- /package/{src → packages/mermaid-parser/src}/xychart/colors.ts +0 -0
- /package/{src → packages/mermaid-parser/src}/xychart/types.ts +0 -0
|
@@ -13,8 +13,9 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { describe, it, expect } from 'vitest'
|
|
15
15
|
import { renderMermaidSVG } from '../index.ts'
|
|
16
|
-
import { layoutSequenceDiagram } from '
|
|
17
|
-
import { parseSequenceDiagram } from '
|
|
16
|
+
import { layoutSequenceDiagram } from '@zombie-mermaid/svg-renderer'
|
|
17
|
+
import { parseSequenceDiagram } from '@zombie-mermaid/mermaid-parser'
|
|
18
|
+
import { splitStatements } from '@zombie-mermaid/core'
|
|
18
19
|
|
|
19
20
|
describe('RenderOptions.fontSizes – flowchart', () => {
|
|
20
21
|
it('overriding nodeLabel changes the emitted node font-size', () => {
|
|
@@ -87,7 +88,9 @@ describe('RenderOptions.fontSizes – sequence diagram', () => {
|
|
|
87
88
|
|
|
88
89
|
describe('RenderOptions.sequence – layout overrides', () => {
|
|
89
90
|
it('overriding actorHeight changes actor box height and overall diagram height', () => {
|
|
90
|
-
const lines =
|
|
91
|
+
const lines = splitStatements(
|
|
92
|
+
['sequenceDiagram', 'Alice->>Bob: Hi'].join('\n'),
|
|
93
|
+
)
|
|
91
94
|
const diagram = parseSequenceDiagram(lines)
|
|
92
95
|
|
|
93
96
|
const withDefault = layoutSequenceDiagram(diagram)
|
|
@@ -115,11 +118,9 @@ describe('RenderOptions.sequence – layout overrides', () => {
|
|
|
115
118
|
})
|
|
116
119
|
|
|
117
120
|
it('overriding messageRowHeight increases vertical spacing between messages', () => {
|
|
118
|
-
const lines =
|
|
119
|
-
'sequenceDiagram',
|
|
120
|
-
|
|
121
|
-
'Bob->>Alice: Two',
|
|
122
|
-
].map((l) => l.trim())
|
|
121
|
+
const lines = splitStatements(
|
|
122
|
+
['sequenceDiagram', 'Alice->>Bob: One', 'Bob->>Alice: Two'].join('\n'),
|
|
123
|
+
)
|
|
123
124
|
const diagram = parseSequenceDiagram(lines)
|
|
124
125
|
|
|
125
126
|
const withDefault = layoutSequenceDiagram(diagram)
|
|
@@ -135,7 +136,9 @@ describe('RenderOptions.sequence – layout overrides', () => {
|
|
|
135
136
|
})
|
|
136
137
|
|
|
137
138
|
it('overriding headerGap increases the gap between actors and the first message', () => {
|
|
138
|
-
const lines =
|
|
139
|
+
const lines = splitStatements(
|
|
140
|
+
['sequenceDiagram', 'Alice->>Bob: Hi'].join('\n'),
|
|
141
|
+
)
|
|
139
142
|
const diagram = parseSequenceDiagram(lines)
|
|
140
143
|
|
|
141
144
|
const withDefault = layoutSequenceDiagram(diagram)
|
|
@@ -149,12 +152,14 @@ describe('RenderOptions.sequence – layout overrides', () => {
|
|
|
149
152
|
})
|
|
150
153
|
|
|
151
154
|
it('overriding noteStackGap changes spacing between consecutively stacked notes', () => {
|
|
152
|
-
const lines =
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
155
|
+
const lines = splitStatements(
|
|
156
|
+
[
|
|
157
|
+
'sequenceDiagram',
|
|
158
|
+
'Alice->>Bob: Hi',
|
|
159
|
+
'Note right of Bob: First note',
|
|
160
|
+
'Note right of Bob: Second note',
|
|
161
|
+
].join('\n'),
|
|
162
|
+
)
|
|
158
163
|
const diagram = parseSequenceDiagram(lines)
|
|
159
164
|
|
|
160
165
|
const withDefault = layoutSequenceDiagram(diagram)
|
|
@@ -172,11 +177,11 @@ describe('RenderOptions.sequence – layout overrides', () => {
|
|
|
172
177
|
})
|
|
173
178
|
|
|
174
179
|
it('overriding noteOffsetAfterMessage changes the gap between a message and the note after it', () => {
|
|
175
|
-
const lines =
|
|
176
|
-
'sequenceDiagram',
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
+
const lines = splitStatements(
|
|
181
|
+
['sequenceDiagram', 'Alice->>Bob: Hi', 'Note right of Bob: A note'].join(
|
|
182
|
+
'\n',
|
|
183
|
+
),
|
|
184
|
+
)
|
|
180
185
|
const diagram = parseSequenceDiagram(lines)
|
|
181
186
|
|
|
182
187
|
const withDefault = layoutSequenceDiagram(diagram)
|
|
@@ -192,7 +197,9 @@ describe('RenderOptions.sequence – layout overrides', () => {
|
|
|
192
197
|
})
|
|
193
198
|
|
|
194
199
|
it('partial sequence overrides only change the specified field', () => {
|
|
195
|
-
const lines =
|
|
200
|
+
const lines = splitStatements(
|
|
201
|
+
['sequenceDiagram', 'Alice->>Bob: Hi'].join('\n'),
|
|
202
|
+
)
|
|
196
203
|
const diagram = parseSequenceDiagram(lines)
|
|
197
204
|
|
|
198
205
|
const withDefault = layoutSequenceDiagram(diagram)
|
|
@@ -25,16 +25,20 @@
|
|
|
25
25
|
*/
|
|
26
26
|
import { describe, it, expect } from 'vitest'
|
|
27
27
|
import { renderMermaidSVG } from '../index.ts'
|
|
28
|
-
import {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
import {
|
|
29
|
+
layoutGraphSync,
|
|
30
|
+
layoutSequenceDiagram,
|
|
31
|
+
layoutClassDiagramSync,
|
|
32
|
+
layoutErDiagramSync,
|
|
33
|
+
layoutXYChart,
|
|
34
|
+
} from '@zombie-mermaid/svg-renderer'
|
|
33
35
|
import { parseMermaid } from '../parser.ts'
|
|
34
|
-
import {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
import {
|
|
37
|
+
parseSequenceDiagram,
|
|
38
|
+
parseClassDiagram,
|
|
39
|
+
parseErDiagram,
|
|
40
|
+
parseXYChart,
|
|
41
|
+
} from '@zombie-mermaid/mermaid-parser'
|
|
38
42
|
import { splitStatements } from '@zombie-mermaid/core'
|
|
39
43
|
import type {
|
|
40
44
|
FlowchartRenderOptions,
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
evaluateCssColorValue,
|
|
6
6
|
resolveCssColors,
|
|
7
7
|
} from '@zombie-mermaid/svg-renderer'
|
|
8
|
-
import { diagramColorsToAsciiTheme } from '
|
|
8
|
+
import { diagramColorsToAsciiTheme } from '../../packages/ascii-renderer/src/ansi.ts'
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Escape regex metacharacters so a value can be interpolated into a
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Self-loop edge rendering (issue #537 follow-up).
|
|
3
|
+
*
|
|
4
|
+
* packages/svg-renderer/src/layout-engine/ (ELK-based flowchart/state
|
|
5
|
+
* layout) previously had no self-loop-specific routing at all: ELK has no
|
|
6
|
+
* native concept of a self-loop, so a `B --> B` edge came back as a
|
|
7
|
+
* degenerate 4-point right-angle polyline sitting on top of the node
|
|
8
|
+
* instead of a loop beside it — see docs/research/537-self-loop-regression-check.md
|
|
9
|
+
* for the before/after comparison against current upstream Mermaid.
|
|
10
|
+
*
|
|
11
|
+
* These tests assert the synthesized replacement: a rounded loop bulging
|
|
12
|
+
* out from the node's right side, discretized into enough points to read
|
|
13
|
+
* as smooth, with the label offset clear of the node.
|
|
14
|
+
*/
|
|
15
|
+
import { describe, it, expect } from 'vitest'
|
|
16
|
+
import { parseMermaid } from '../parser.ts'
|
|
17
|
+
import { layoutGraphSync } from '@zombie-mermaid/svg-renderer'
|
|
18
|
+
|
|
19
|
+
interface Point {
|
|
20
|
+
x: number
|
|
21
|
+
y: number
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** True when the segment p1->p2 runs purely horizontal or vertical. */
|
|
25
|
+
function isAxisAligned(p1: Point, p2: Point, tolerance = 0.5): boolean {
|
|
26
|
+
return Math.abs(p2.x - p1.x) < tolerance || Math.abs(p2.y - p1.y) < tolerance
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
describe('self-loop edge routing (flowchart)', () => {
|
|
30
|
+
it('routes a self-loop as a rounded loop beside the node, not a 4-point right-angle bracket', () => {
|
|
31
|
+
const graph = parseMermaid(`flowchart LR
|
|
32
|
+
A --->|foo| B
|
|
33
|
+
B --->|bar| B`)
|
|
34
|
+
|
|
35
|
+
const positioned = layoutGraphSync(graph)
|
|
36
|
+
const nodeB = positioned.nodes.find((n) => n.id === 'B')
|
|
37
|
+
const selfLoop = positioned.edges.find(
|
|
38
|
+
(e) => e.source === 'B' && e.target === 'B',
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
expect(nodeB).toBeDefined()
|
|
42
|
+
expect(selfLoop).toBeDefined()
|
|
43
|
+
const points = selfLoop!.points
|
|
44
|
+
|
|
45
|
+
// The old ELK-routed bracket was exactly 4 points. A real loop needs
|
|
46
|
+
// many more to read as rounded.
|
|
47
|
+
expect(points.length).toBeGreaterThan(4)
|
|
48
|
+
|
|
49
|
+
// The bracket was pure right angles throughout. A rounded loop must
|
|
50
|
+
// have a majority of genuinely diagonal segments.
|
|
51
|
+
let diagonalSegments = 0
|
|
52
|
+
for (let i = 1; i < points.length; i++) {
|
|
53
|
+
if (!isAxisAligned(points[i - 1]!, points[i]!)) diagonalSegments++
|
|
54
|
+
}
|
|
55
|
+
expect(diagonalSegments).toBeGreaterThan(points.length / 2)
|
|
56
|
+
|
|
57
|
+
// The loop bulges out to the side of the node — every point sits at or
|
|
58
|
+
// beyond the node's right edge — rather than sitting on top of it (the
|
|
59
|
+
// old bracket's points landed inside/just above the node's bounding box).
|
|
60
|
+
for (const p of points) {
|
|
61
|
+
expect(p.x).toBeGreaterThanOrEqual(nodeB!.x + nodeB!.width - 0.5)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Both ends of the loop attach to the node's right edge.
|
|
65
|
+
expect(points[0]!.x).toBeCloseTo(nodeB!.x + nodeB!.width, 0)
|
|
66
|
+
expect(points[points.length - 1]!.x).toBeCloseTo(nodeB!.x + nodeB!.width, 0)
|
|
67
|
+
|
|
68
|
+
// The label sits clear of the node, out past the loop's apex — not on
|
|
69
|
+
// top of the node the way the old bracket's cramped shape forced it to.
|
|
70
|
+
expect(selfLoop!.labelPosition).toBeDefined()
|
|
71
|
+
expect(selfLoop!.labelPosition!.x).toBeGreaterThan(nodeB!.x + nodeB!.width)
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
it('stacks multiple self-loops on the same node instead of overlapping', () => {
|
|
75
|
+
const graph = parseMermaid(`flowchart LR
|
|
76
|
+
B -->|one| B
|
|
77
|
+
B -->|two| B`)
|
|
78
|
+
|
|
79
|
+
const positioned = layoutGraphSync(graph)
|
|
80
|
+
const selfLoops = positioned.edges.filter(
|
|
81
|
+
(e) => e.source === 'B' && e.target === 'B',
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
expect(selfLoops).toHaveLength(2)
|
|
85
|
+
const [first, second] = selfLoops as [
|
|
86
|
+
(typeof selfLoops)[0],
|
|
87
|
+
(typeof selfLoops)[0],
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
// The second loop reaches further right than the first, so they nest
|
|
91
|
+
// rather than draw identically on top of each other.
|
|
92
|
+
const apexX = (points: Point[]) => Math.max(...points.map((p) => p.x))
|
|
93
|
+
expect(apexX(second.points)).toBeGreaterThan(apexX(first.points))
|
|
94
|
+
})
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
describe('self-loop edge routing (state diagram)', () => {
|
|
98
|
+
it('routes a state-diagram self-transition as a rounded loop', () => {
|
|
99
|
+
const graph = parseMermaid(`stateDiagram-v2
|
|
100
|
+
[*] --> Idle
|
|
101
|
+
Idle --> Idle: retry`)
|
|
102
|
+
|
|
103
|
+
const positioned = layoutGraphSync(graph)
|
|
104
|
+
const selfLoop = positioned.edges.find(
|
|
105
|
+
(e) => e.source === 'Idle' && e.target === 'Idle',
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
expect(selfLoop).toBeDefined()
|
|
109
|
+
expect(selfLoop!.points.length).toBeGreaterThan(4)
|
|
110
|
+
expect(selfLoop!.label).toBe('retry')
|
|
111
|
+
})
|
|
112
|
+
})
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tests for the sequence diagram activation/deactivation balance check
|
|
3
|
-
* (src/sequence/activation-check.ts).
|
|
3
|
+
* (packages/mermaid-parser/src/sequence/activation-check.ts).
|
|
4
4
|
*
|
|
5
5
|
* Covers: balanced diagrams (both the standalone activate/deactivate form
|
|
6
6
|
* and the +/- arrow shorthand, including nesting), a dangling activation
|
|
@@ -8,16 +8,15 @@
|
|
|
8
8
|
* that mix both issue types or both syntactic forms together.
|
|
9
9
|
*/
|
|
10
10
|
import { describe, it, expect } from 'vitest'
|
|
11
|
-
import {
|
|
12
|
-
|
|
11
|
+
import {
|
|
12
|
+
parseSequenceDiagram,
|
|
13
|
+
checkActivationBalance,
|
|
14
|
+
} from '@zombie-mermaid/mermaid-parser'
|
|
15
|
+
import { splitStatements } from '@zombie-mermaid/core'
|
|
13
16
|
|
|
14
17
|
/** Helper to parse — preprocesses text the same way index.ts does. */
|
|
15
18
|
function parse(text: string) {
|
|
16
|
-
|
|
17
|
-
.split('\n')
|
|
18
|
-
.map((l) => l.trim())
|
|
19
|
-
.filter((l) => l.length > 0 && !l.startsWith('%%'))
|
|
20
|
-
return parseSequenceDiagram(lines)
|
|
19
|
+
return parseSequenceDiagram(splitStatements(text))
|
|
21
20
|
}
|
|
22
21
|
|
|
23
22
|
describe('checkActivationBalance — balanced diagrams', () => {
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the sequence diagram activation/deactivation auto-fix
|
|
3
|
+
* (packages/mermaid-parser/src/sequence/activation-fix.ts). Operates on raw
|
|
4
|
+
* Mermaid source text (unlike checkActivationBalance, which takes an
|
|
5
|
+
* already-parsed diagram), since the fix needs to append to the original
|
|
6
|
+
* source rather than just report against the parsed model.
|
|
7
|
+
*
|
|
8
|
+
* Covers: an already-balanced diagram (no-op), a single dangling
|
|
9
|
+
* activation (auto-fixed), multiple dangling activations across actors, an
|
|
10
|
+
* unmatched deactivation (deliberately NOT auto-fixed — see module header),
|
|
11
|
+
* and a mix of both issue types together.
|
|
12
|
+
*/
|
|
13
|
+
import { describe, it, expect } from 'vitest'
|
|
14
|
+
import { fixActivationBalance } from '@zombie-mermaid/mermaid-parser'
|
|
15
|
+
|
|
16
|
+
describe('fixActivationBalance', () => {
|
|
17
|
+
it('returns the diagram unchanged when already balanced', () => {
|
|
18
|
+
const source = 'sequenceDiagram\n A->>+B: hello\n B-->>-A: world'
|
|
19
|
+
const result = fixActivationBalance(source)
|
|
20
|
+
expect(result.ok).toBe(true)
|
|
21
|
+
expect(result.fixedDiagram).toBe(source)
|
|
22
|
+
expect(result.fixesApplied).toEqual([])
|
|
23
|
+
expect(result.remainingIssues).toEqual([])
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('appends a deactivate statement for a single dangling activation', () => {
|
|
27
|
+
const source = 'sequenceDiagram\n activate A\n A->>B: hello'
|
|
28
|
+
const result = fixActivationBalance(source)
|
|
29
|
+
expect(result.ok).toBe(true)
|
|
30
|
+
expect(result.originalDiagram).toBe(source)
|
|
31
|
+
expect(result.fixedDiagram).toBe(`${source}\n deactivate A`)
|
|
32
|
+
expect(result.fixesApplied).toEqual([
|
|
33
|
+
expect.stringContaining('Appended "deactivate A"'),
|
|
34
|
+
])
|
|
35
|
+
expect(result.remainingIssues).toEqual([])
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('makes the fixed diagram re-check as balanced', () => {
|
|
39
|
+
const source = 'sequenceDiagram\n activate A\n A->>B: hello'
|
|
40
|
+
const { fixedDiagram } = fixActivationBalance(source)
|
|
41
|
+
const reChecked = fixActivationBalance(fixedDiagram)
|
|
42
|
+
expect(reChecked.ok).toBe(true)
|
|
43
|
+
expect(reChecked.fixesApplied).toEqual([])
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('closes multiple dangling activations for different actors', () => {
|
|
47
|
+
const source = 'sequenceDiagram\n activate A\n activate B\n A->>B: hello'
|
|
48
|
+
const result = fixActivationBalance(source)
|
|
49
|
+
expect(result.ok).toBe(true)
|
|
50
|
+
expect(result.fixesApplied).toHaveLength(2)
|
|
51
|
+
expect(result.fixedDiagram).toContain(' deactivate A')
|
|
52
|
+
expect(result.fixedDiagram).toContain(' deactivate B')
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('does not fix an unmatched deactivation and reports it back', () => {
|
|
56
|
+
const source = 'sequenceDiagram\n A->>B: hello\n deactivate A'
|
|
57
|
+
const result = fixActivationBalance(source)
|
|
58
|
+
expect(result.ok).toBe(false)
|
|
59
|
+
expect(result.fixedDiagram).toBe(source)
|
|
60
|
+
expect(result.fixesApplied).toEqual([])
|
|
61
|
+
expect(result.remainingIssues).toEqual([
|
|
62
|
+
expect.objectContaining({
|
|
63
|
+
code: 'UNMATCHED_DEACTIVATION',
|
|
64
|
+
actorId: 'A',
|
|
65
|
+
}),
|
|
66
|
+
])
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('fixes the dangling half of a mixed-issue diagram, leaving the unmatched half reported', () => {
|
|
70
|
+
const source =
|
|
71
|
+
'sequenceDiagram\n activate A\n A->>B: hello\n deactivate B'
|
|
72
|
+
const result = fixActivationBalance(source)
|
|
73
|
+
expect(result.ok).toBe(false)
|
|
74
|
+
expect(result.fixesApplied).toEqual([
|
|
75
|
+
expect.stringContaining('Appended "deactivate A"'),
|
|
76
|
+
])
|
|
77
|
+
expect(result.fixedDiagram).toContain(' deactivate A')
|
|
78
|
+
expect(result.remainingIssues).toEqual([
|
|
79
|
+
expect.objectContaining({
|
|
80
|
+
code: 'UNMATCHED_DEACTIVATION',
|
|
81
|
+
actorId: 'B',
|
|
82
|
+
}),
|
|
83
|
+
])
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('throws a clear error for a non-sequence diagram', () => {
|
|
87
|
+
expect(() => fixActivationBalance('graph LR\n A --> B')).toThrow(
|
|
88
|
+
/only supports sequence diagrams/,
|
|
89
|
+
)
|
|
90
|
+
})
|
|
91
|
+
})
|
|
@@ -6,16 +6,13 @@
|
|
|
6
6
|
* to inspect Y coordinates, rather than checking SVG output.
|
|
7
7
|
*/
|
|
8
8
|
import { describe, it, expect } from 'vitest'
|
|
9
|
-
import { parseSequenceDiagram } from '
|
|
10
|
-
import { layoutSequenceDiagram } from '
|
|
9
|
+
import { parseSequenceDiagram } from '@zombie-mermaid/mermaid-parser'
|
|
10
|
+
import { layoutSequenceDiagram } from '@zombie-mermaid/svg-renderer'
|
|
11
|
+
import { splitStatements } from '@zombie-mermaid/core'
|
|
11
12
|
|
|
12
13
|
/** Helper: parse and layout a sequence diagram from source lines */
|
|
13
14
|
function layout(source: string) {
|
|
14
|
-
|
|
15
|
-
.split('\n')
|
|
16
|
-
.map((l) => l.trim())
|
|
17
|
-
.filter((l) => l.length > 0 && !l.startsWith('%%'))
|
|
18
|
-
return layoutSequenceDiagram(parseSequenceDiagram(lines))
|
|
15
|
+
return layoutSequenceDiagram(parseSequenceDiagram(splitStatements(source)))
|
|
19
16
|
}
|
|
20
17
|
|
|
21
18
|
describe('sequence layout – block spacing', () => {
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Syntactic error-detection tests for the sequence diagram parser
|
|
3
|
+
* (issue #762).
|
|
4
|
+
*
|
|
5
|
+
* The 8 pre-existing `throw` sites in `parseSequenceDiagram` all guard
|
|
6
|
+
* *semantic* rules mirrored from mermaid.js (duplicate actor ids, box
|
|
7
|
+
* nesting, create/destroy message mismatch — see
|
|
8
|
+
* sequence-parser.test.ts/sequence-activation-check.test.ts for those). No
|
|
9
|
+
* *syntactic* error was ever raised: a malformed arrow like `->>>` silently
|
|
10
|
+
* absorbed the extra character into the actor name instead of being
|
|
11
|
+
* rejected (verified: `Alice->>>Bob: Hello` created an actor literally
|
|
12
|
+
* named `">Bob"`), an unmatched `end` was silently ignored, and an
|
|
13
|
+
* `alt`/`loop`/`box` left open at EOF was silently accepted as if closed.
|
|
14
|
+
* These tests assert the parser now throws an actionable error instead,
|
|
15
|
+
* while confirming valid syntax — including short arrows, activation
|
|
16
|
+
* shorthand, and actor names that legitimately start with "x" — is
|
|
17
|
+
* unaffected.
|
|
18
|
+
*/
|
|
19
|
+
import { describe, it, expect } from 'vitest'
|
|
20
|
+
import { parseSequenceDiagram } from '@zombie-mermaid/mermaid-parser'
|
|
21
|
+
import { splitStatements } from '@zombie-mermaid/core'
|
|
22
|
+
|
|
23
|
+
function parse(text: string) {
|
|
24
|
+
return parseSequenceDiagram(splitStatements(text))
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
describe('parseSequenceDiagram – malformed arrows', () => {
|
|
28
|
+
it('throws on an extra ">" past the longest known long-arrow token', () => {
|
|
29
|
+
// The issue's own verified repro: silently created an actor named
|
|
30
|
+
// `">Bob"` before this pass.
|
|
31
|
+
expect(() =>
|
|
32
|
+
parse(`sequenceDiagram
|
|
33
|
+
Alice->>>Bob: Hello`),
|
|
34
|
+
).toThrow(/Malformed sequence-diagram arrow.*"->>>"/)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('throws on an extra ")" past a short open-arrow token', () => {
|
|
38
|
+
expect(() =>
|
|
39
|
+
parse(`sequenceDiagram
|
|
40
|
+
Alice-))Bob: Hello`),
|
|
41
|
+
).toThrow(/Malformed sequence-diagram arrow.*"-\)\)"/)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('throws on an extra ")" past a short dashed-open-arrow token', () => {
|
|
45
|
+
expect(() =>
|
|
46
|
+
parse(`sequenceDiagram
|
|
47
|
+
Alice--))Bob: Hello`),
|
|
48
|
+
).toThrow(/Malformed sequence-diagram arrow/)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('mentions the known-good arrow set in the message', () => {
|
|
52
|
+
expect(() =>
|
|
53
|
+
parse(`sequenceDiagram
|
|
54
|
+
Alice->>>Bob: Hello`),
|
|
55
|
+
).toThrow(/->, -->, ->>, -->>, -x, --x, -\), --\)/)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('reports the 1-based line number', () => {
|
|
59
|
+
expect(() =>
|
|
60
|
+
parse(`sequenceDiagram
|
|
61
|
+
participant Alice
|
|
62
|
+
Alice->>>Bob: Hello`),
|
|
63
|
+
).toThrow(/^Line 3:/)
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
describe('parseSequenceDiagram – unmatched "end"', () => {
|
|
68
|
+
it('throws on an "end" with no open block or box', () => {
|
|
69
|
+
expect(() =>
|
|
70
|
+
parse(`sequenceDiagram
|
|
71
|
+
Alice->>Bob: Hi
|
|
72
|
+
end`),
|
|
73
|
+
).toThrow(/"end" does not match any open block/)
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
describe('parseSequenceDiagram – unclosed blocks/boxes at EOF', () => {
|
|
78
|
+
it('throws when a loop is never closed', () => {
|
|
79
|
+
expect(() =>
|
|
80
|
+
parse(`sequenceDiagram
|
|
81
|
+
loop Every 5s
|
|
82
|
+
Alice->>Bob: Hi`),
|
|
83
|
+
).toThrow(/unclosed "loop" block/)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('throws when an alt is never closed', () => {
|
|
87
|
+
expect(() =>
|
|
88
|
+
parse(`sequenceDiagram
|
|
89
|
+
alt Happy path
|
|
90
|
+
Alice->>Bob: Hi`),
|
|
91
|
+
).toThrow(/unclosed "alt" block/)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('throws when a box is never closed', () => {
|
|
95
|
+
expect(() =>
|
|
96
|
+
parse(`sequenceDiagram
|
|
97
|
+
box Group1
|
|
98
|
+
participant Alice
|
|
99
|
+
Alice->>Alice: Hi`),
|
|
100
|
+
).toThrow(/unclosed "box Group1"/)
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it('reports the line the block opened on', () => {
|
|
104
|
+
expect(() =>
|
|
105
|
+
parse(`sequenceDiagram
|
|
106
|
+
Alice->>Bob: Hi
|
|
107
|
+
loop Every 5s
|
|
108
|
+
Bob->>Alice: Pong`),
|
|
109
|
+
).toThrow(/^Line 3:/)
|
|
110
|
+
})
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
describe('parseSequenceDiagram – valid syntax is unaffected', () => {
|
|
114
|
+
it('still parses correct long arrows without throwing', () => {
|
|
115
|
+
expect(() =>
|
|
116
|
+
parse(`sequenceDiagram
|
|
117
|
+
Alice->>Bob: Hi
|
|
118
|
+
Bob-->>Alice: Hi back`),
|
|
119
|
+
).not.toThrow()
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('still parses activation shorthand (+/-) without throwing', () => {
|
|
123
|
+
const d = parse(`sequenceDiagram
|
|
124
|
+
Alice->>+Bob: Hi
|
|
125
|
+
Bob-->>-Alice: Hi back`)
|
|
126
|
+
expect(d.messages[0]!.activate).toBe(true)
|
|
127
|
+
expect(d.messages[1]!.deactivate).toBe(true)
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
it('still parses short open/cross arrows without throwing', () => {
|
|
131
|
+
const d = parse(`sequenceDiagram
|
|
132
|
+
Alice-)Bob: fire and forget
|
|
133
|
+
Bob--xAlice: lost message`)
|
|
134
|
+
expect(d.messages).toHaveLength(2)
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
it('still parses bidirectional arrows without throwing', () => {
|
|
138
|
+
expect(() =>
|
|
139
|
+
parse(`sequenceDiagram
|
|
140
|
+
Alice<<->>Bob: sync
|
|
141
|
+
Alice<<-->>Bob: async sync`),
|
|
142
|
+
).not.toThrow()
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
it('does not throw for an actor name that legitimately starts with "x" after a -x arrow', () => {
|
|
146
|
+
const d = parse(`sequenceDiagram
|
|
147
|
+
Alice-xXavier: lost`)
|
|
148
|
+
expect(d.actors.map((a) => a.id)).toContain('Xavier')
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
it('still parses a properly closed loop/alt/box without throwing', () => {
|
|
152
|
+
expect(() =>
|
|
153
|
+
parse(`sequenceDiagram
|
|
154
|
+
box Group1
|
|
155
|
+
participant Alice
|
|
156
|
+
participant Bob
|
|
157
|
+
end
|
|
158
|
+
loop Every 5s
|
|
159
|
+
alt Happy path
|
|
160
|
+
Alice->>Bob: Hi
|
|
161
|
+
else Sad path
|
|
162
|
+
Alice-xBob: nope
|
|
163
|
+
end
|
|
164
|
+
end`),
|
|
165
|
+
).not.toThrow()
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
it('still closes an "end" that matches a block nested inside a box', () => {
|
|
169
|
+
const d = parse(`sequenceDiagram
|
|
170
|
+
box Group1
|
|
171
|
+
participant Alice
|
|
172
|
+
loop Every 5s
|
|
173
|
+
Alice->>Alice: ping
|
|
174
|
+
end
|
|
175
|
+
end`)
|
|
176
|
+
expect(d.blocks).toHaveLength(1)
|
|
177
|
+
expect(d.boxes).toHaveLength(1)
|
|
178
|
+
})
|
|
179
|
+
})
|
|
@@ -9,15 +9,12 @@ import {
|
|
|
9
9
|
parseSequenceDiagram,
|
|
10
10
|
isBlockType,
|
|
11
11
|
toBlockType,
|
|
12
|
-
} from '
|
|
12
|
+
} from '@zombie-mermaid/mermaid-parser'
|
|
13
|
+
import { splitStatements } from '@zombie-mermaid/core'
|
|
13
14
|
|
|
14
15
|
/** Helper to parse — preprocesses text the same way index.ts does */
|
|
15
16
|
function parse(text: string) {
|
|
16
|
-
|
|
17
|
-
.split('\n')
|
|
18
|
-
.map((l) => l.trim())
|
|
19
|
-
.filter((l) => l.length > 0 && !l.startsWith('%%'))
|
|
20
|
-
return parseSequenceDiagram(lines)
|
|
17
|
+
return parseSequenceDiagram(splitStatements(text))
|
|
21
18
|
}
|
|
22
19
|
|
|
23
20
|
// ============================================================================
|