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.
Files changed (251) hide show
  1. package/LICENSE +1 -0
  2. package/README.md +7 -2
  3. package/dist/ascii.cjs +9 -9
  4. package/dist/ascii.cjs.map +1 -1
  5. package/dist/ascii.js +4069 -3969
  6. package/dist/ascii.js.map +1 -1
  7. package/dist/cli.js +7605 -7381
  8. package/dist/index.cjs +52 -52
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.js +7613 -7459
  11. package/dist/index.js.map +1 -1
  12. package/dist/mcp.cjs +14142 -13717
  13. package/dist/mcp.cjs.map +1 -1
  14. package/dist/mcp.js +14142 -13717
  15. package/dist/mcp.js.map +1 -1
  16. package/package.json +27 -5
  17. package/packages/ascii-renderer/LICENSE +22 -0
  18. package/{src/ascii → packages/ascii-renderer/src}/class-diagram.ts +277 -57
  19. package/{src/ascii → packages/ascii-renderer/src}/er-diagram.ts +2 -2
  20. package/packages/ascii-renderer/src/flowchart.ts +94 -0
  21. package/{src/ascii → packages/ascii-renderer/src}/grid.ts +23 -0
  22. package/{src/ascii → packages/ascii-renderer/src}/index.ts +13 -84
  23. package/{src/ascii → packages/ascii-renderer/src}/registry.ts +23 -12
  24. package/{src/ascii → packages/ascii-renderer/src}/sequence.ts +55 -8
  25. package/{src/ascii → packages/ascii-renderer/src}/xychart.ts +6 -3
  26. package/packages/core/LICENSE +22 -0
  27. package/packages/core/src/click-directive.ts +106 -0
  28. package/packages/core/src/color-utils.ts +205 -0
  29. package/packages/core/src/diagram-type.ts +35 -0
  30. package/packages/core/src/direction-override.ts +46 -0
  31. package/packages/core/src/direction.ts +56 -0
  32. package/packages/core/src/index.ts +34 -0
  33. package/packages/core/src/init-directive.ts +248 -0
  34. package/packages/core/src/multiline-utils.ts +275 -0
  35. package/packages/core/src/statements.ts +197 -0
  36. package/packages/core/src/style-directives.ts +214 -0
  37. package/packages/core/src/text-metrics.ts +498 -0
  38. package/packages/core/src/theme.ts +696 -0
  39. package/packages/core/src/types.ts +670 -0
  40. package/packages/mcp/LICENSE +22 -0
  41. package/{src/mcp → packages/mcp/src}/server.ts +34 -2
  42. package/{src/mcp → packages/mcp/src}/tools/check-sequence-activations.ts +8 -5
  43. package/packages/mcp/src/tools/fix-sequence-activations.ts +77 -0
  44. package/{src/mcp → packages/mcp/src}/tools/render-ascii.ts +2 -2
  45. package/{src/mcp → packages/mcp/src}/tools/render-svg.ts +1 -1
  46. package/packages/mermaid-parser/LICENSE +22 -0
  47. package/{src → packages/mermaid-parser/src}/class/parser.ts +86 -8
  48. package/{src → packages/mermaid-parser/src}/er/parser.ts +59 -26
  49. package/packages/mermaid-parser/src/index.ts +48 -0
  50. package/{src → packages/mermaid-parser/src}/sequence/activation-check.ts +4 -3
  51. package/packages/mermaid-parser/src/sequence/activation-fix.ts +112 -0
  52. package/{src → packages/mermaid-parser/src}/sequence/parser.ts +74 -16
  53. package/{src → packages/mermaid-parser/src}/xychart/parser.ts +84 -6
  54. package/packages/svg-renderer/LICENSE +22 -0
  55. package/{src → packages/svg-renderer/src}/class/layout.ts +8 -4
  56. package/{src → packages/svg-renderer/src}/class/renderer.ts +7 -6
  57. package/packages/svg-renderer/src/edge-curves.ts +204 -0
  58. package/packages/svg-renderer/src/elk-instance.ts +292 -0
  59. package/{src → packages/svg-renderer/src}/er/layout.ts +6 -4
  60. package/{src → packages/svg-renderer/src}/er/renderer.ts +4 -4
  61. package/packages/svg-renderer/src/index.ts +58 -0
  62. package/packages/svg-renderer/src/layout-engine/constants.ts +19 -0
  63. package/packages/svg-renderer/src/layout-engine/edge-bundling.ts +379 -0
  64. package/packages/svg-renderer/src/layout-engine/elk-adapter-utils.ts +81 -0
  65. package/packages/svg-renderer/src/layout-engine/elk-graph-builder.ts +240 -0
  66. package/packages/svg-renderer/src/layout-engine/from-elk.ts +685 -0
  67. package/packages/svg-renderer/src/layout-engine/layer-alignment.ts +174 -0
  68. package/packages/svg-renderer/src/layout-engine/to-elk.ts +695 -0
  69. package/packages/svg-renderer/src/layout-engine.ts +74 -0
  70. package/packages/svg-renderer/src/layout.ts +8 -0
  71. package/packages/svg-renderer/src/renderer.ts +1485 -0
  72. package/packages/svg-renderer/src/resolve-colors.ts +339 -0
  73. package/{src → packages/svg-renderer/src}/sequence/layout.ts +2 -6
  74. package/{src → packages/svg-renderer/src}/sequence/renderer.ts +4 -4
  75. package/packages/svg-renderer/src/shape-clipping.ts +197 -0
  76. package/packages/svg-renderer/src/styles.ts +118 -0
  77. package/{src → packages/svg-renderer/src}/xychart/layout.ts +2 -2
  78. package/{src → packages/svg-renderer/src}/xychart/renderer.ts +6 -6
  79. package/src/__tests__/ascii-ansi.test.ts +6 -2
  80. package/src/__tests__/ascii-canvas-write.test.ts +2 -2
  81. package/src/__tests__/ascii-charset-border-junctions.test.ts +1 -1
  82. package/src/__tests__/ascii-cjk-width.test.ts +1 -1
  83. package/src/__tests__/ascii-class-box-occupancy.test.ts +147 -45
  84. package/src/__tests__/ascii-class-column-width-488-489.test.ts +1 -1
  85. package/src/__tests__/ascii-class-cross-level-jog-corruption.test.ts +187 -0
  86. package/src/__tests__/ascii-class-detour-label-routing-487.test.ts +1 -1
  87. package/src/__tests__/ascii-class-diagram-compartments.test.ts +1 -1
  88. package/src/__tests__/ascii-class-label-box-collision.test.ts +1 -1
  89. package/src/__tests__/ascii-class-label-row-collision-531.test.ts +1 -1
  90. package/src/__tests__/ascii-class-label-territory-row-awareness.test.ts +1 -1
  91. package/src/__tests__/ascii-class-padding.test.ts +1 -1
  92. package/src/__tests__/ascii-class-reciprocal-relationships-448.test.ts +86 -1
  93. package/src/__tests__/ascii-combining-mark-width.test.ts +2 -2
  94. package/src/__tests__/ascii-coords-overlay.test.ts +2 -2
  95. package/src/__tests__/ascii-decision-lr-box-start.test.ts +2 -2
  96. package/src/__tests__/ascii-display-width-unit.test.ts +1 -1
  97. package/src/__tests__/ascii-draw-arrows-coverage.test.ts +1 -1
  98. package/src/__tests__/ascii-draw-arrows-single-point-path.test.ts +12 -5
  99. package/src/__tests__/ascii-draw-bundles.test.ts +5 -5
  100. package/src/__tests__/ascii-edge-bundling-rank-violation-454.test.ts +1 -1
  101. package/src/__tests__/ascii-edge-bundling-routing.test.ts +9 -5
  102. package/src/__tests__/ascii-edge-cross-style-overlap.test.ts +10 -6
  103. package/src/__tests__/ascii-edge-ending-glyphs.test.ts +1 -1
  104. package/src/__tests__/ascii-edge-label-diagonal-fallback-418.test.ts +2 -2
  105. package/src/__tests__/ascii-edge-routing-fixes.test.ts +1 -1
  106. package/src/__tests__/ascii-edge-routing-single-point-path.test.ts +8 -4
  107. package/src/__tests__/ascii-edge-styles.test.ts +1 -1
  108. package/src/__tests__/ascii-emoji-cluster-width.test.ts +2 -2
  109. package/src/__tests__/ascii-er-box-occupancy.test.ts +1 -1
  110. package/src/__tests__/ascii-er-cardinality.test.ts +1 -1
  111. package/src/__tests__/ascii-er-corner-glyphs.test.ts +1 -1
  112. package/src/__tests__/ascii-er-jog-stray-line.test.ts +1 -1
  113. package/src/__tests__/ascii-er-label-padding.test.ts +1 -1
  114. package/src/__tests__/ascii-er-padding.test.ts +1 -1
  115. package/src/__tests__/ascii-er-relationship-label-corruption-350.test.ts +1 -1
  116. package/src/__tests__/ascii-er-relationship-overwrite.test.ts +1 -1
  117. package/src/__tests__/ascii-er-stray-connectors.test.ts +7 -4
  118. package/src/__tests__/ascii-er-unrelated-stem-separation-411.test.ts +1 -1
  119. package/src/__tests__/ascii-er-vertical-one-marker.test.ts +1 -1
  120. package/src/__tests__/ascii-grid-deep-chain.test.ts +4 -4
  121. package/src/__tests__/ascii-hyperlinks.test.ts +9 -6
  122. package/src/__tests__/ascii-label-line-terminal-fallback.test.ts +8 -4
  123. package/src/__tests__/ascii-lane-search.test.ts +1 -1
  124. package/src/__tests__/ascii-multibox-cjk-width.test.ts +1 -1
  125. package/src/__tests__/ascii-multiline.test.ts +6 -3
  126. package/src/__tests__/ascii-nested-subgraph-sibling-order-444.test.ts +8 -5
  127. package/src/__tests__/ascii-package-boundary.test.ts +47 -37
  128. package/src/__tests__/ascii-padding-edge-cases.test.ts +1 -1
  129. package/src/__tests__/ascii-parallel-edges-329.test.ts +8 -5
  130. package/src/__tests__/ascii-pathfinder-route-edge.test.ts +14 -4
  131. package/src/__tests__/ascii-reverse-direction-edges-629.test.ts +9 -5
  132. package/src/__tests__/ascii-sequence-alt-else-label.test.ts +1 -1
  133. package/src/__tests__/ascii-sequence-block-wall-clearance.test.ts +1 -1
  134. package/src/__tests__/ascii-sequence-box-group.test.ts +1 -1
  135. package/src/__tests__/ascii-sequence-cjk-width.test.ts +1 -1
  136. package/src/__tests__/ascii-sequence-create-destroy.test.ts +1 -1
  137. package/src/__tests__/ascii-sequence-form-invariants.test.ts +48 -50
  138. package/src/__tests__/ascii-sequence-mermaid-parity.test.ts +1 -1
  139. package/src/__tests__/ascii-sequence-notes.test.ts +1 -1
  140. package/src/__tests__/ascii-sequence-padding.test.ts +1 -1
  141. package/src/__tests__/ascii-sequence-self-arrow.test.ts +1 -1
  142. package/src/__tests__/ascii-shape-diamond.test.ts +3 -3
  143. package/src/__tests__/ascii-shape-rectangle.test.ts +5 -5
  144. package/src/__tests__/ascii-shape-rounded.test.ts +3 -3
  145. package/src/__tests__/ascii-shapes-circle.test.ts +3 -3
  146. package/src/__tests__/ascii-shapes-hexagon.test.ts +3 -3
  147. package/src/__tests__/ascii-shapes-special.test.ts +10 -5
  148. package/src/__tests__/ascii-shapes-stadium.test.ts +3 -3
  149. package/src/__tests__/ascii-shapes-state.test.ts +15 -5
  150. package/src/__tests__/ascii-state-bidirectional-label-swap-530.test.ts +1 -1
  151. package/src/__tests__/ascii-subgraph-bbox.test.ts +8 -4
  152. package/src/__tests__/ascii-subgraph-direction-honored-445.test.ts +1 -1
  153. package/src/__tests__/ascii-subgraph-label-border-clip.test.ts +88 -25
  154. package/src/__tests__/ascii-subgraph-later-edgeless-301.test.ts +8 -4
  155. package/src/__tests__/ascii-subgraph-title-padding.test.ts +1 -1
  156. package/src/__tests__/ascii-territory-unit.test.ts +2 -2
  157. package/src/__tests__/ascii-validate.test.ts +1 -1
  158. package/src/__tests__/ascii.test.ts +5 -2
  159. package/src/__tests__/box-color.test.ts +1 -1
  160. package/src/__tests__/class-arrow-directions.test.ts +1 -1
  161. package/src/__tests__/class-click-interactions.test.ts +3 -6
  162. package/src/__tests__/class-integration.test.ts +1 -1
  163. package/src/__tests__/class-notes.test.ts +3 -3
  164. package/src/__tests__/class-parser-error-messages.test.ts +162 -0
  165. package/src/__tests__/class-parser.test.ts +6 -6
  166. package/src/__tests__/class-styling.test.ts +2 -2
  167. package/src/__tests__/cli-render.test.ts +16 -1
  168. package/src/__tests__/direction-override.test.ts +1 -1
  169. package/src/__tests__/draw-lines.test.ts +2 -2
  170. package/src/__tests__/edge-cell-styles.test.ts +5 -2
  171. package/src/__tests__/elk-layout-cache.test.ts +6 -4
  172. package/src/__tests__/er-click-unsupported.test.ts +5 -8
  173. package/src/__tests__/er-parser-error-messages.test.ts +82 -0
  174. package/src/__tests__/er-parser.test.ts +3 -6
  175. package/src/__tests__/expanded-shapes.test.ts +1 -1
  176. package/src/__tests__/grid-occupancy.test.ts +1 -1
  177. package/src/__tests__/mcp-check-sequence-activations-tool.test.ts +1 -1
  178. package/src/__tests__/mcp-fix-sequence-activations-tool.test.ts +134 -0
  179. package/src/__tests__/mcp-render-ascii-tool.test.ts +5 -4
  180. package/src/__tests__/mcp-render-svg-theme-guard.test.ts +3 -3
  181. package/src/__tests__/mcp-render-svg-tool.test.ts +1 -1
  182. package/src/__tests__/mcp-server.test.ts +30 -1
  183. package/src/__tests__/multiline-labels.test.ts +29 -18
  184. package/src/__tests__/parser.test.ts +117 -5
  185. package/src/__tests__/pathfinder.test.ts +11 -4
  186. package/src/__tests__/render-options-overrides.test.ts +28 -21
  187. package/src/__tests__/render-options-per-type.test.ts +13 -9
  188. package/src/__tests__/resolve-colors.test.ts +1 -1
  189. package/src/__tests__/self-loop-edges.test.ts +112 -0
  190. package/src/__tests__/sequence-activation-check.test.ts +7 -8
  191. package/src/__tests__/sequence-activation-fix.test.ts +91 -0
  192. package/src/__tests__/sequence-layout.test.ts +4 -7
  193. package/src/__tests__/sequence-parser-syntax-errors.test.ts +179 -0
  194. package/src/__tests__/sequence-parser.test.ts +3 -6
  195. package/src/__tests__/statements.test.ts +53 -35
  196. package/src/__tests__/testdata/ascii/subgraph_direction_override.txt +21 -21
  197. package/src/__tests__/theme-palette-contrast.test.ts +84 -0
  198. package/src/__tests__/workspace-package-boundaries.test.ts +199 -25
  199. package/src/__tests__/xychart-ascii.test.ts +1 -1
  200. package/src/__tests__/xychart-colors.test.ts +3 -2
  201. package/src/__tests__/xychart-layout.test.ts +1 -1
  202. package/src/__tests__/xychart-parser-errors.test.ts +95 -0
  203. package/src/browser.ts +8 -2
  204. package/src/cli/mcp.ts +1 -1
  205. package/src/cli/render.ts +59 -25
  206. package/src/cli/web.ts +1 -1
  207. package/src/diagram-registry.ts +190 -25
  208. package/src/index.ts +34 -131
  209. package/src/parser.ts +102 -40
  210. /package/{src/ascii → packages/ascii-renderer/src}/ansi.ts +0 -0
  211. /package/{src/ascii → packages/ascii-renderer/src}/canvas.ts +0 -0
  212. /package/{src/ascii → packages/ascii-renderer/src}/converter.ts +0 -0
  213. /package/{src/ascii → packages/ascii-renderer/src}/coords.ts +0 -0
  214. /package/{src/ascii → packages/ascii-renderer/src}/display-width.ts +0 -0
  215. /package/{src/ascii → packages/ascii-renderer/src}/draw-arrows.ts +0 -0
  216. /package/{src/ascii → packages/ascii-renderer/src}/draw-boxes.ts +0 -0
  217. /package/{src/ascii → packages/ascii-renderer/src}/draw-bundles.ts +0 -0
  218. /package/{src/ascii → packages/ascii-renderer/src}/draw-lines.ts +0 -0
  219. /package/{src/ascii → packages/ascii-renderer/src}/draw-subgraphs.ts +0 -0
  220. /package/{src/ascii → packages/ascii-renderer/src}/draw.ts +0 -0
  221. /package/{src/ascii → packages/ascii-renderer/src}/edge-bundling.ts +0 -0
  222. /package/{src/ascii → packages/ascii-renderer/src}/edge-cell-styles.ts +0 -0
  223. /package/{src/ascii → packages/ascii-renderer/src}/edge-routing.ts +0 -0
  224. /package/{src/ascii → packages/ascii-renderer/src}/grid-occupancy.ts +0 -0
  225. /package/{src/ascii → packages/ascii-renderer/src}/hyperlinks.ts +0 -0
  226. /package/{src/ascii → packages/ascii-renderer/src}/lane-search.ts +0 -0
  227. /package/{src/ascii → packages/ascii-renderer/src}/multiline-utils.ts +0 -0
  228. /package/{src/ascii → packages/ascii-renderer/src}/pathfinder.ts +0 -0
  229. /package/{src/ascii → packages/ascii-renderer/src}/shapes/circle.ts +0 -0
  230. /package/{src/ascii → packages/ascii-renderer/src}/shapes/corners.ts +0 -0
  231. /package/{src/ascii → packages/ascii-renderer/src}/shapes/diamond.ts +0 -0
  232. /package/{src/ascii → packages/ascii-renderer/src}/shapes/hexagon.ts +0 -0
  233. /package/{src/ascii → packages/ascii-renderer/src}/shapes/index.ts +0 -0
  234. /package/{src/ascii → packages/ascii-renderer/src}/shapes/rectangle.ts +0 -0
  235. /package/{src/ascii → packages/ascii-renderer/src}/shapes/rounded.ts +0 -0
  236. /package/{src/ascii → packages/ascii-renderer/src}/shapes/special.ts +0 -0
  237. /package/{src/ascii → packages/ascii-renderer/src}/shapes/stadium.ts +0 -0
  238. /package/{src/ascii → packages/ascii-renderer/src}/shapes/state.ts +0 -0
  239. /package/{src/ascii → packages/ascii-renderer/src}/shapes/types.ts +0 -0
  240. /package/{src/ascii → packages/ascii-renderer/src}/territory.ts +0 -0
  241. /package/{src/ascii → packages/ascii-renderer/src}/types.ts +0 -0
  242. /package/{src/ascii → packages/ascii-renderer/src}/validate.ts +0 -0
  243. /package/{src/mcp → packages/mcp/src}/index.ts +0 -0
  244. /package/{src → packages/mermaid-parser/src}/class/format.ts +0 -0
  245. /package/{src → packages/mermaid-parser/src}/class/types.ts +0 -0
  246. /package/{src → packages/mermaid-parser/src}/er/types.ts +0 -0
  247. /package/{src → packages/mermaid-parser/src}/expanded-shapes.ts +0 -0
  248. /package/{src → packages/mermaid-parser/src}/sequence/box-color.ts +0 -0
  249. /package/{src → packages/mermaid-parser/src}/sequence/types.ts +0 -0
  250. /package/{src → packages/mermaid-parser/src}/xychart/colors.ts +0 -0
  251. /package/{src → packages/mermaid-parser/src}/xychart/types.ts +0 -0
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Error-message quality tests for the xychart-beta parser (issue #541).
3
+ *
4
+ * Before this pass, `parseXYChart` had zero `throw`/`Error` statements in
5
+ * the entire file (see docs/parser-error-audit-541.md): every malformed
6
+ * line — an unclosed bracket, a non-numeric value inside a numeric series —
7
+ * was silently dropped or silently coerced to `NaN`, producing a diagram
8
+ * that was missing content or subtly corrupted with no indication why.
9
+ * These tests assert the parser now throws an actionable error instead.
10
+ */
11
+ import { describe, it, expect } from 'vitest'
12
+ import { parseXYChart as parseXYChartStatements } from '@zombie-mermaid/mermaid-parser'
13
+ import { splitStatements } from '@zombie-mermaid/core'
14
+
15
+ /** Helper to parse — preprocesses text the same way index.ts does */
16
+ function parseXYChart(sourceLines: string[]) {
17
+ return parseXYChartStatements(splitStatements(sourceLines.join('\n')))
18
+ }
19
+
20
+ describe('parseXYChart – non-numeric series data', () => {
21
+ it('throws on a non-numeric value in a bar series', () => {
22
+ expect(() =>
23
+ parseXYChart(['xychart-beta', 'x-axis [a, b, c]', 'bar [1, two, 3]']),
24
+ ).toThrow(/Invalid numeric value "two"/)
25
+ })
26
+
27
+ it('throws on a quoted non-numeric value in a bar series', () => {
28
+ expect(() =>
29
+ parseXYChart(['xychart-beta', 'x-axis [a, b, c]', 'bar [1, "two", 3]']),
30
+ ).toThrow(/Invalid numeric value/)
31
+ })
32
+
33
+ it('throws on a non-numeric value in a line series', () => {
34
+ expect(() =>
35
+ parseXYChart(['xychart-beta', 'x-axis [a, b]', 'line [10, oops]']),
36
+ ).toThrow(/Invalid numeric value "oops"/)
37
+ })
38
+
39
+ it('reports the 1-based position of the bad value', () => {
40
+ expect(() =>
41
+ parseXYChart(['xychart-beta', 'bar [1, 2, three, 4]']),
42
+ ).toThrow(/position 3/)
43
+ })
44
+
45
+ it('throws on an empty element from a stray comma', () => {
46
+ expect(() => parseXYChart(['xychart-beta', 'bar [1, , 3]'])).toThrow(
47
+ /Invalid numeric value ""/,
48
+ )
49
+ })
50
+
51
+ it('still parses a fully numeric series without throwing', () => {
52
+ expect(() =>
53
+ parseXYChart(['xychart-beta', 'x-axis [a, b, c]', 'bar [1, 2, 3]']),
54
+ ).not.toThrow()
55
+ })
56
+ })
57
+
58
+ describe('parseXYChart – malformed directives', () => {
59
+ it('throws on an unclosed x-axis bracket', () => {
60
+ expect(() =>
61
+ parseXYChart(['xychart-beta', 'x-axis [a, b, c', 'bar [1, 2, 3]']),
62
+ ).toThrow(/Malformed xychart-beta "x-axis" directive/)
63
+ })
64
+
65
+ it('throws on an unclosed bar bracket', () => {
66
+ expect(() => parseXYChart(['xychart-beta', 'bar [1, 2, 3'])).toThrow(
67
+ /Malformed xychart-beta "bar" directive/,
68
+ )
69
+ })
70
+
71
+ it('throws on an unclosed line bracket', () => {
72
+ expect(() => parseXYChart(['xychart-beta', 'line [1, 2, 3'])).toThrow(
73
+ /Malformed xychart-beta "line" directive/,
74
+ )
75
+ })
76
+
77
+ it('throws on a title-only x-axis (unsupported form)', () => {
78
+ expect(() =>
79
+ parseXYChart(['xychart-beta', 'x-axis "Just a title"']),
80
+ ).toThrow(/Malformed xychart-beta "x-axis" directive/)
81
+ })
82
+
83
+ it('throws on an unquoted title', () => {
84
+ expect(() =>
85
+ parseXYChart(['xychart-beta', 'title Unquoted Title']),
86
+ ).toThrow(/Malformed xychart-beta "title" directive/)
87
+ })
88
+
89
+ it('does not throw on an unrecognized line with no known keyword', () => {
90
+ // Unchanged behavior: a line that doesn't even attempt one of the five
91
+ // known directives still falls through silently — only a recognized
92
+ // keyword with broken syntax is now an error.
93
+ expect(() => parseXYChart(['xychart-beta', '###nonsense###'])).not.toThrow()
94
+ })
95
+ })
package/src/browser.ts CHANGED
@@ -9,9 +9,15 @@
9
9
  // ============================================================================
10
10
 
11
11
  import { renderMermaidSVGAsync } from './index.ts'
12
- import { renderMermaidASCII, diagramColorsToAsciiTheme } from './ascii/index.ts'
12
+ import {
13
+ renderMermaidASCII,
14
+ diagramColorsToAsciiTheme,
15
+ } from '@zombie-mermaid/ascii-renderer'
13
16
  import { THEMES, isWideChar } from '@zombie-mermaid/core'
14
- import { getSeriesColor, CHART_ACCENT_FALLBACK } from './xychart/colors.ts'
17
+ import {
18
+ getSeriesColor,
19
+ CHART_ACCENT_FALLBACK,
20
+ } from '@zombie-mermaid/mermaid-parser'
15
21
 
16
22
  export interface MermaidBrowserGlobal {
17
23
  renderMermaidSVGAsync: typeof renderMermaidSVGAsync
package/src/cli/mcp.ts CHANGED
@@ -15,7 +15,7 @@
15
15
 
16
16
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
17
17
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
18
- import { createMcpServer } from '../mcp/index.ts'
18
+ import { createMcpServer } from '@zombie-mermaid/mcp'
19
19
 
20
20
  /**
21
21
  * Execute the `mcp` command: start the MCP server on stdio and resolve once
package/src/cli/render.ts CHANGED
@@ -2,15 +2,15 @@ import { access, readFile, writeFile } from 'node:fs/promises'
2
2
  import {
3
3
  renderMermaidASCII,
4
4
  diagramColorsToAsciiTheme,
5
- } from '../ascii/index.ts'
6
- import type { AsciiRenderOptions } from '../ascii/index.ts'
7
- import { displayWidth } from '../ascii/display-width.ts'
8
- import { stripOsc8 } from '../ascii/hyperlinks.ts'
5
+ } from '@zombie-mermaid/ascii-renderer'
6
+ import type { AsciiRenderOptions } from '@zombie-mermaid/ascii-renderer'
7
+ import { displayWidth } from '../../packages/ascii-renderer/src/display-width.ts'
8
+ import { stripOsc8 } from '../../packages/ascii-renderer/src/hyperlinks.ts'
9
9
  import {
10
10
  DEFAULT_PADDING_X,
11
11
  DEFAULT_PADDING_Y,
12
12
  DEFAULT_BOX_BORDER_PADDING,
13
- } from '../ascii/types.ts'
13
+ } from '../../packages/ascii-renderer/src/types.ts'
14
14
  import { renderMermaidSVG } from '../index.ts'
15
15
  import type { RenderOptions, DiagramColors } from '@zombie-mermaid/core'
16
16
  import { THEMES } from '@zombie-mermaid/core'
@@ -249,11 +249,15 @@ export async function runRender(
249
249
  }
250
250
  }
251
251
 
252
- if (asciiFile !== undefined) {
253
- await writeOutputFile(asciiFile, ascii + '\n', args.force)
254
- } else {
255
- out.write(ascii + '\n')
256
- }
252
+ // ASCII has no "skip" destination (the `if (args.ascii)` guard above
253
+ // already covers "not requested") — it goes to stdout whenever no file
254
+ // was resolved for it, so "going to stdout" is simply "no ascii file".
255
+ await emit(
256
+ resolveTarget(asciiFile === undefined, asciiFile),
257
+ ascii + '\n',
258
+ out,
259
+ args.force,
260
+ )
257
261
  }
258
262
 
259
263
  if (args.svg) {
@@ -263,22 +267,14 @@ export async function runRender(
263
267
  }
264
268
  if (args.direction !== undefined) svgOpts.direction = args.direction
265
269
  const svg = renderMermaidSVG(text, svgOpts)
266
- if (svgToStdout) {
267
- out.write(svg)
268
- } else if (svgFile !== undefined) {
269
- await writeOutputFile(svgFile, svg, args.force)
270
- }
270
+ await emit(resolveTarget(svgToStdout, svgFile), svg, out, args.force)
271
271
  }
272
272
 
273
273
  if (args.html) {
274
274
  const svg = renderMermaidSVG(text, themeColors ?? {})
275
275
  const title = args.input ? parsePath(args.input).name : undefined
276
276
  const html = buildHtmlViewer({ svg, title })
277
- if (htmlToStdout) {
278
- out.write(html)
279
- } else if (htmlFile !== undefined) {
280
- await writeOutputFile(htmlFile, html, args.force)
281
- }
277
+ await emit(resolveTarget(htmlToStdout, htmlFile), html, out, args.force)
282
278
  }
283
279
 
284
280
  if (args.png) {
@@ -290,11 +286,7 @@ export async function runRender(
290
286
  if (args.direction !== undefined) svgOpts.direction = args.direction
291
287
  const svg = renderMermaidSVG(text, svgOpts)
292
288
  const png = await renderPng(svg)
293
- if (pngToStdout) {
294
- out.write(png)
295
- } else if (pngFile !== undefined) {
296
- await writeOutputFile(pngFile, png, args.force)
297
- }
289
+ await emit(resolveTarget(pngToStdout, pngFile), png, out, args.force)
298
290
  }
299
291
  }
300
292
 
@@ -302,6 +294,48 @@ export async function runRender(
302
294
  // Helpers
303
295
  // ============================================================================
304
296
 
297
+ /**
298
+ * Where one rendered format's output goes. Every format in `runRender`
299
+ * (ascii/svg/html/png) reduces to the same three-way choice — stdout, a
300
+ * named file, or nowhere — so the decision is made once here instead of
301
+ * being re-derived per format.
302
+ */
303
+ type OutputTarget =
304
+ { kind: 'stdout' } | { kind: 'file'; path: string } | { kind: 'skip' }
305
+
306
+ /**
307
+ * Decide a format's output target: stdout when `toStdout`, else the given
308
+ * `file` path when one was resolved for it, else skip (the format wasn't
309
+ * requested to go anywhere — see each call site's precomputed booleans).
310
+ */
311
+ function resolveTarget(
312
+ toStdout: boolean,
313
+ file: string | undefined,
314
+ ): OutputTarget {
315
+ if (toStdout) return { kind: 'stdout' }
316
+ if (file !== undefined) return { kind: 'file', path: file }
317
+ return { kind: 'skip' }
318
+ }
319
+
320
+ /** Write `content` to the resolved `target`, or do nothing for `'skip'`. */
321
+ async function emit(
322
+ target: OutputTarget,
323
+ content: string | Uint8Array,
324
+ out: Writable,
325
+ force: boolean,
326
+ ): Promise<void> {
327
+ switch (target.kind) {
328
+ case 'stdout':
329
+ out.write(content)
330
+ break
331
+ case 'file':
332
+ await writeOutputFile(target.path, content, force)
333
+ break
334
+ case 'skip':
335
+ break
336
+ }
337
+ }
338
+
305
339
  function overwriteRefusal(path: string): Error {
306
340
  return new Error(
307
341
  `Refusing to overwrite existing file "${path}" — pass --force to replace it`,
package/src/cli/web.ts CHANGED
@@ -12,7 +12,7 @@
12
12
 
13
13
  import { createServer } from 'node:http'
14
14
  import type { IncomingMessage, Server, ServerResponse } from 'node:http'
15
- import { renderMermaidASCII } from '../ascii/index.ts'
15
+ import { renderMermaidASCII } from '@zombie-mermaid/ascii-renderer'
16
16
  import { renderMermaidSVG } from '../index.ts'
17
17
  import type { WebArgs } from './parse-args.ts'
18
18
 
@@ -11,13 +11,15 @@
11
11
  // imported back out of here, a cycle that blocks the monorepo split and
12
12
  // already dragged `elkjs` into `dist/ascii.js`).
13
13
  //
14
- // Only 'xychart' and 'er' are registered here. Those two are the only
15
- // diagram types whose existing renderer signatures adapt to a shared shape
16
- // with zero behavior change — see the issue-533 scoping doc (in the PR/
17
- // issue body) for why 'sequence', 'class', and 'flowchart' are NOT
18
- // registered yet and what would need to change first. The front door
19
- // checks this table first; anything absent falls through to its own
20
- // switch, completely unchanged.
14
+ // Every diagram type is registered here, including 'flowchart' — see
15
+ // docs/decisions/diagram-type-registry-partial.md for why it was the last
16
+ // holdout (its ASCII path had no per-type wrapper function to slot in until
17
+ // #745 extracted `renderFlowchartAscii`, and its SVG path carries
18
+ // `%%{init: ...}%%` directive handling no other type has — see
19
+ // `flowchartModule.layoutForSvg` below for how that's folded in without
20
+ // growing `SvgRenderContext`). The front door (`renderMermaidSVGRaw` in
21
+ // src/index.ts) has no fallback switch left; every `DiagramType` is a hit
22
+ // here.
21
23
  //
22
24
  // `packages/core/src/diagram-type.ts` (the `DiagramType` union + `detectDiagramType`)
23
25
  // stays exactly as-is and is what the front doors use to key into this
@@ -29,19 +31,43 @@ import type {
29
31
  RenderOptions,
30
32
  DiagramColors,
31
33
  SvgEmitOptions,
34
+ MermaidGraph,
35
+ PositionedGraph,
36
+ CurveStyle,
37
+ Statement,
32
38
  } from '@zombie-mermaid/core'
33
39
  import type { FontSizes } from '@zombie-mermaid/svg-renderer'
34
40
  import { withDirectionOverride } from '@zombie-mermaid/core'
35
41
 
36
- import { parseXYChart } from './xychart/parser.ts'
37
- import { layoutXYChart } from './xychart/layout.ts'
38
- import { renderXYChartSvg } from './xychart/renderer.ts'
39
- import type { XYChart, PositionedXYChart } from './xychart/types.ts'
40
-
41
- import { parseErDiagram } from './er/parser.ts'
42
- import { layoutErDiagramSync } from './er/layout.ts'
43
- import { renderErSvg } from './er/renderer.ts'
44
- import type { ErDiagram, PositionedErDiagram } from './er/types.ts'
42
+ import {
43
+ parseXYChart,
44
+ parseErDiagram,
45
+ parseSequenceDiagram,
46
+ parseClassDiagram,
47
+ } from '@zombie-mermaid/mermaid-parser'
48
+ import type {
49
+ XYChart,
50
+ PositionedXYChart,
51
+ ErDiagram,
52
+ PositionedErDiagram,
53
+ SequenceDiagram,
54
+ PositionedSequenceDiagram,
55
+ ClassDiagram,
56
+ PositionedClassDiagram,
57
+ } from '@zombie-mermaid/mermaid-parser'
58
+ import {
59
+ layoutXYChart,
60
+ renderXYChartSvg,
61
+ layoutErDiagramSync,
62
+ renderErSvg,
63
+ layoutSequenceDiagram,
64
+ renderSequenceSvg,
65
+ layoutClassDiagramSync,
66
+ renderClassSvg,
67
+ layoutGraphSync,
68
+ renderSvg as renderFlowchartSvg,
69
+ } from '@zombie-mermaid/svg-renderer'
70
+ import { parseMermaid } from './parser.ts'
45
71
 
46
72
  /**
47
73
  * Parameters shared by every per-type SVG renderer today, factored out of
@@ -85,10 +111,23 @@ export interface SvgRenderContext {
85
111
  * parse itself from raw text — which is why the ASCII entries live in
86
112
  * src/ascii/registry.ts as plain `(text, …) => string` functions instead of
87
113
  * a `renderAscii` method on this interface.
114
+ *
115
+ * `parse` takes both `lines` (the pre-split statement list from
116
+ * `splitStatements(decoded)`, computed once by the front door — what every
117
+ * already-registered type's parser wants) and `text` (the raw, un-split
118
+ * source `flowchartModule` below needs instead): `parseMermaid`'s
119
+ * `%%{init: ...}%%` directive extraction reads raw, un-commented lines that
120
+ * `splitStatements` has already discarded by the time `lines` exists, and
121
+ * its multi-line-statement continuation merging needs each statement's
122
+ * *originating physical line* grouping, which `splitStatements`'s flattened
123
+ * array has already lost. `xychartModule`/`erModule`/`sequenceModule`/
124
+ * `classModule` all ignore the second parameter — JS/TS functions may take
125
+ * fewer parameters than their declared type allows, so `parse: parseXYChart`
126
+ * (etc.) is unchanged from before this parameter was added.
88
127
  */
89
128
  export interface DiagramModule<TDiagram = unknown, TPositioned = unknown> {
90
129
  readonly type: DiagramType
91
- parse(lines: string[]): TDiagram
130
+ parse(lines: Statement[], text: string): TDiagram
92
131
  layoutForSvg(diagram: TDiagram, options: RenderOptions): TPositioned
93
132
  renderSvg(
94
133
  positioned: TPositioned,
@@ -123,6 +162,69 @@ const xychartModule: DiagramModule<XYChart, PositionedXYChart> = {
123
162
  },
124
163
  }
125
164
 
165
+ const sequenceModule: DiagramModule<
166
+ SequenceDiagram,
167
+ PositionedSequenceDiagram
168
+ > = {
169
+ type: 'sequence',
170
+ parse: parseSequenceDiagram,
171
+ layoutForSvg: layoutSequenceDiagram,
172
+ renderSvg(positioned, ctx) {
173
+ return renderSequenceSvg(
174
+ positioned,
175
+ ctx.colors,
176
+ ctx.font,
177
+ ctx.transparent,
178
+ ctx.fontSizes,
179
+ ctx.embedSource,
180
+ ctx.title,
181
+ ctx.decorative,
182
+ ctx.emit,
183
+ )
184
+ },
185
+ }
186
+
187
+ /**
188
+ * Mirrors `resolveLinksEnabled()` in src/index.ts exactly (`interactivity`
189
+ * defaults unset to `'static'`; only `'none'` turns links off) — duplicated
190
+ * here rather than imported since that helper is private to src/index.ts and
191
+ * this module is imported BY src/index.ts, so importing it back would cycle.
192
+ */
193
+ function resolveLinksEnabled(options: RenderOptions): boolean {
194
+ const interactivity = options.interactivity ?? 'static'
195
+ return interactivity !== 'none'
196
+ }
197
+
198
+ /**
199
+ * Mirrors `resolveAnimationEnabled()` in src/index.ts exactly (`interactivity`
200
+ * defaults unset to `'static'`; only `'full'` turns animation on) — duplicated
201
+ * here for the same import-direction reason `resolveLinksEnabled` above is.
202
+ */
203
+ function resolveAnimationEnabled(options: RenderOptions): boolean {
204
+ const interactivity = options.interactivity ?? 'static'
205
+ return interactivity === 'full'
206
+ }
207
+
208
+ const classModule: DiagramModule<ClassDiagram, PositionedClassDiagram> = {
209
+ type: 'class',
210
+ parse: parseClassDiagram,
211
+ layoutForSvg: layoutClassDiagramSync,
212
+ renderSvg(positioned, ctx, options) {
213
+ return renderClassSvg(
214
+ positioned,
215
+ ctx.colors,
216
+ ctx.font,
217
+ ctx.transparent,
218
+ ctx.fontSizes,
219
+ ctx.embedSource,
220
+ ctx.title,
221
+ ctx.decorative,
222
+ resolveLinksEnabled(options),
223
+ ctx.emit,
224
+ )
225
+ },
226
+ }
227
+
126
228
  const erModule: DiagramModule<ErDiagram, PositionedErDiagram> = {
127
229
  type: 'er',
128
230
  parse: parseErDiagram,
@@ -151,16 +253,76 @@ const erModule: DiagramModule<ErDiagram, PositionedErDiagram> = {
151
253
  }
152
254
 
153
255
  /**
154
- * The registry proper. Only diagram types listed here are looked up by the
155
- * SVG front door; anything absent (currently 'sequence', 'class',
156
- * 'flowchart') falls through to that front door's own switch, unchanged.
157
- * The ASCII front door's equivalent table is `asciiRegistry` in
158
- * src/ascii/registry.ts.
256
+ * `renderSvg`'s (the low-level flowchart/state SVG emitter, imported above
257
+ * as `renderFlowchartSvg`) `curve` parameter is the one piece of
258
+ * `SvgRenderContext`-adjacent state no other registered type needs: a
259
+ * `%%{init: {"flowchart": {"curve": ...}}}%%` directive on the diagram
260
+ * itself can supply a default, and `options.curve` always wins when set —
261
+ * see `applyInitConfig()` in packages/core/src/init-directive.ts, which
262
+ * `src/index.ts`'s pre-registry flowchart path used to call directly.
263
+ *
264
+ * That resolution needs the parsed diagram (for `initConfig`) AND the
265
+ * caller's `options` together, and the *result* is only needed later, by
266
+ * `renderSvg` — so rather than teach `SvgRenderContext` a diagram-specific
267
+ * `curve` field (every other field there is genuinely shared across types),
268
+ * `layoutForSvg` below resolves it once and carries it forward on
269
+ * `PositionedFlowchart`, right next to the `PositionedGraph` it was
270
+ * resolved alongside. `animationEnabled`/`linksEnabled` need no such
271
+ * carry-through: both derive from `options` alone via the
272
+ * `resolveAnimationEnabled`/`resolveLinksEnabled` helpers above, exactly
273
+ * like `class`'s `linksEnabled` already does, so `flowchartModule.renderSvg`
274
+ * just calls them directly.
275
+ */
276
+ export interface PositionedFlowchart {
277
+ graph: PositionedGraph
278
+ curve: CurveStyle
279
+ }
280
+
281
+ const flowchartModule: DiagramModule<MermaidGraph, PositionedFlowchart> = {
282
+ type: 'flowchart',
283
+ // Ignores `lines` — see the `parse` doc comment on `DiagramModule` above
284
+ // for why flowchart/state needs the raw `text` instead.
285
+ parse: (_lines, text) => parseMermaid(text),
286
+ layoutForSvg(diagram, options) {
287
+ // Same order as src/index.ts's original fallback: direction override,
288
+ // then layout. `options.curve` wins over the diagram's own
289
+ // `%%{init: ...}%%` directive, which wins over the 'linear' default —
290
+ // see the doc comment on `PositionedFlowchart` above.
291
+ const graph = withDirectionOverride(diagram, options.direction)
292
+ return {
293
+ graph: layoutGraphSync(graph, options),
294
+ curve: options.curve ?? diagram.initConfig?.curve ?? 'linear',
295
+ }
296
+ },
297
+ renderSvg(positioned, ctx, options) {
298
+ return renderFlowchartSvg(
299
+ positioned.graph,
300
+ ctx.colors,
301
+ ctx.font,
302
+ ctx.transparent,
303
+ ctx.fontSizes,
304
+ positioned.curve,
305
+ ctx.embedSource,
306
+ resolveAnimationEnabled(options),
307
+ resolveLinksEnabled(options),
308
+ ctx.title,
309
+ ctx.decorative,
310
+ ctx.emit,
311
+ )
312
+ },
313
+ }
314
+
315
+ /**
316
+ * The registry proper — every `DiagramType` is looked up here by the SVG
317
+ * front door (`renderMermaidSVGRaw` in src/index.ts), which has no
318
+ * fallback switch left. The ASCII front door's equivalent table is
319
+ * `asciiRegistry` in src/ascii/registry.ts.
159
320
  *
160
321
  * Typed with `any` type parameters at the map level: each entry's own
161
322
  * `TDiagram`/`TPositioned` are only known inside that entry's own closure
162
- * (see `xychartModule`/`erModule` above, which are fully typed); the map
163
- * just needs one consistent shape to hold heterogeneous entries in.
323
+ * (see `xychartModule`/`erModule`/`flowchartModule` above, which are fully
324
+ * typed); the map just needs one consistent shape to hold heterogeneous
325
+ * entries in.
164
326
  */
165
327
  type AnyDiagramModule = DiagramModule<
166
328
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- see comment above; each entry is fully typed at its own definition site.
@@ -169,7 +331,10 @@ type AnyDiagramModule = DiagramModule<
169
331
  any
170
332
  >
171
333
 
172
- export const diagramRegistry: Partial<Record<DiagramType, AnyDiagramModule>> = {
334
+ export const diagramRegistry: Record<DiagramType, AnyDiagramModule> = {
173
335
  xychart: xychartModule,
174
336
  er: erModule,
337
+ sequence: sequenceModule,
338
+ class: classModule,
339
+ flowchart: flowchartModule,
175
340
  }