@zombie-mermaid/mermaid-parser 4.0.0 → 4.2.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zombie-mermaid/mermaid-parser",
3
- "version": "4.0.0",
3
+ "version": "4.2.0",
4
4
  "license": "MIT",
5
5
  "description": "Parsers for class, ER, sequence, and XY chart diagrams — the per-type parse functions both svg-renderer and ascii-renderer import directly.",
6
6
  "repository": {
@@ -35,6 +35,6 @@
35
35
  "LICENSE"
36
36
  ],
37
37
  "dependencies": {
38
- "@zombie-mermaid/core": "4.0.0"
38
+ "@zombie-mermaid/core": "4.2.0"
39
39
  }
40
40
  }
@@ -0,0 +1,54 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { parseMermaid } from '../flowchart-parser.ts'
3
+
4
+ // Upstream lukilabs/beautiful-mermaid#156: `A --> |label| B` (space before the
5
+ // label pipe) used to drop the target and label.
6
+ describe('edge label with a space before the pipe', () => {
7
+ const cases = [
8
+ ['-->', 'solid', true],
9
+ ['---', 'solid', false],
10
+ ['-.->', 'dotted', true],
11
+ ['==>', 'thick', true],
12
+ ] as const
13
+
14
+ for (const [op, style, hasArrowEnd] of cases) {
15
+ it(`parses A ${op} |deploy| B`, () => {
16
+ const g = parseMermaid(`graph LR\n A ${op} |deploy| B`)
17
+ expect(g.edges).toHaveLength(1)
18
+ expect(g.edges[0]).toMatchObject({
19
+ source: 'A',
20
+ target: 'B',
21
+ label: 'deploy',
22
+ style,
23
+ hasArrowEnd,
24
+ })
25
+ })
26
+
27
+ it(`parses A ${op} |deploy| B identically to the no-space form`, () => {
28
+ const spaced = parseMermaid(`graph LR\n A ${op} |deploy| B`)
29
+ const tight = parseMermaid(`graph LR\n A ${op}|deploy| B`)
30
+ expect(spaced.edges).toEqual(tight.edges)
31
+ })
32
+ }
33
+
34
+ it('still parses the no-space form', () => {
35
+ const g = parseMermaid('graph LR\n A -->|deploy| B')
36
+ expect(g.edges[0]).toMatchObject({ target: 'B', label: 'deploy' })
37
+ })
38
+
39
+ it('does not treat an unmarked `--`/`==` before a pipe as an arrow', () => {
40
+ // `--`/`==` unmarked is the text-label opener, so this must not become a
41
+ // labelled link with target B and label `x`.
42
+ for (const src of ['graph LR\n A -- |x| B', 'graph LR\n A == |x| B']) {
43
+ const g = parseMermaid(src)
44
+ expect(g.edges.some((e) => e.label === 'x' && e.target === 'B')).toBe(
45
+ false,
46
+ )
47
+ }
48
+ })
49
+
50
+ it('keeps text-embedded labels working', () => {
51
+ const g = parseMermaid('graph LR\n A -- Yes --> B')
52
+ expect(g.edges[0]).toMatchObject({ target: 'B', label: 'Yes' })
53
+ })
54
+ })
@@ -0,0 +1,308 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { parsePieChart } from '../index.ts'
3
+
4
+ // Fixtures ported from Mermaid's own pie tests (mermaid-js/mermaid, MIT):
5
+ // - packages/mermaid/src/diagrams/pie/pie.spec.ts (parser + pieDb)
6
+ // - packages/parser/tests/pie.test.ts (the Langium grammar)
7
+ // Inputs are copied verbatim; assertions are translated from Mermaid's
8
+ // `db.getSections()` Map / AST shape to this package's `PieChart`. The two
9
+ // `config` cases in pie.spec.ts (`it.todo` setConfig/resetConfig, and
10
+ // getConfig defaults) test Mermaid's diagram config, not parsing, and are
11
+ // left for the follow-up that adds `%%{init: {"pie": …}}%%` support.
12
+
13
+ /** `db.getSections()` equivalent: label → value, in source order. */
14
+ const sections = (text: string): Map<string, number> =>
15
+ new Map(parsePieChart(text).slices.map((s) => [s.label, s.value]))
16
+
17
+ describe('mermaid pie.spec.ts: parse', () => {
18
+ it('should handle very simple pie', () => {
19
+ expect(
20
+ sections(`pie
21
+ "ash": 100
22
+ `).get('ash'),
23
+ ).toBe(100)
24
+ })
25
+
26
+ it('should handle simple pie', () => {
27
+ const s = sections(`pie
28
+ "ash" : 60
29
+ "bat" : 40
30
+ `)
31
+ expect(s.get('ash')).toBe(60)
32
+ expect(s.get('bat')).toBe(40)
33
+ })
34
+
35
+ it('should handle simple pie with showData', () => {
36
+ const chart = parsePieChart(`pie showData
37
+ "ash" : 60
38
+ "bat" : 40
39
+ `)
40
+ expect(chart.showData).toBe(true)
41
+ expect(chart.slices).toEqual([
42
+ { label: 'ash', value: 60 },
43
+ { label: 'bat', value: 40 },
44
+ ])
45
+ })
46
+
47
+ it('should handle simple pie with comments', () => {
48
+ const s = sections(`pie
49
+ %% comments
50
+ "ash" : 60
51
+ "bat" : 40
52
+ `)
53
+ expect(s.get('ash')).toBe(60)
54
+ expect(s.get('bat')).toBe(40)
55
+ })
56
+
57
+ it('should handle simple pie with a title', () => {
58
+ const chart = parsePieChart(`pie title a 60/40 pie
59
+ "ash" : 60
60
+ "bat" : 40
61
+ `)
62
+ expect(chart.title).toBe('a 60/40 pie')
63
+ expect(chart.slices).toHaveLength(2)
64
+ })
65
+
66
+ it('should handle simple pie with an acc title (accTitle)', () => {
67
+ const chart = parsePieChart(`pie title a neat chart
68
+ accTitle: a neat acc title
69
+ "ash" : 60
70
+ "bat" : 40
71
+ `)
72
+ expect(chart.title).toBe('a neat chart')
73
+ expect(chart.accTitle).toBe('a neat acc title')
74
+ expect(chart.slices).toHaveLength(2)
75
+ })
76
+
77
+ it('should handle simple pie with an acc description (accDescr)', () => {
78
+ const chart = parsePieChart(`pie title a neat chart
79
+ accDescr: a neat description
80
+ "ash" : 60
81
+ "bat" : 40
82
+ `)
83
+ expect(chart.title).toBe('a neat chart')
84
+ expect(chart.accDescr).toBe('a neat description')
85
+ expect(chart.slices).toHaveLength(2)
86
+ })
87
+
88
+ it('should handle simple pie with a multiline acc description (accDescr)', () => {
89
+ const chart = parsePieChart(`pie title a neat chart
90
+ accDescr {
91
+ a neat description
92
+ on multiple lines
93
+ }
94
+ "ash" : 60
95
+ "bat" : 40
96
+ `)
97
+ expect(chart.title).toBe('a neat chart')
98
+ expect(chart.accDescr).toBe('a neat description\non multiple lines')
99
+ expect(chart.slices).toHaveLength(2)
100
+ })
101
+
102
+ it('should handle simple pie with positive decimal', () => {
103
+ const s = sections(`pie
104
+ "ash" : 60.67
105
+ "bat" : 40
106
+ `)
107
+ expect(s.get('ash')).toBe(60.67)
108
+ expect(s.get('bat')).toBe(40)
109
+ })
110
+
111
+ it('should handle simple pie with negative decimal', () => {
112
+ expect(() =>
113
+ parsePieChart(`pie
114
+ "ash" : -60.67
115
+ "bat" : 40.12
116
+ `),
117
+ ).toThrow()
118
+ })
119
+
120
+ it('should handle simple pie with zero slice value', () => {
121
+ const s = sections(`pie title Default text position: Animal adoption
122
+ accTitle: simple pie char demo
123
+ accDescr: pie chart with 3 sections: dogs, cats, rats. Most are dogs.
124
+ "dogs" : 0
125
+ "rats" : 40.12
126
+ `)
127
+ expect(s.get('dogs')).toBe(0)
128
+ expect(s.get('rats')).toBe(40.12)
129
+ })
130
+
131
+ it('should handle simple pie with negative slice value', () => {
132
+ expect(() =>
133
+ parsePieChart(`pie title Default text position: Animal adoption
134
+ accTitle: simple pie char demo
135
+ accDescr: pie chart with 3 sections: dogs, cats, rats. Most are dogs.
136
+ "dogs" : -60.67
137
+ "rats" : 40.12
138
+ `),
139
+ ).toThrow(
140
+ '"dogs" has invalid value: -60.67. Negative values are not allowed in pie charts. All slice values must be >= 0.',
141
+ )
142
+ })
143
+
144
+ it('should handle unsafe properties', () => {
145
+ const chart = parsePieChart(`pie title Unsafe props test
146
+ "__proto__" : 386
147
+ "constructor" : 85
148
+ "prototype" : 15`)
149
+ expect(chart.slices.map((s) => s.label)).toEqual([
150
+ '__proto__',
151
+ 'constructor',
152
+ 'prototype',
153
+ ])
154
+ })
155
+ })
156
+
157
+ describe('mermaid parser/tests/pie.test.ts: header with or without showData', () => {
158
+ it.each([
159
+ `pie`,
160
+ ` pie `,
161
+ `\tpie\t`,
162
+ `
163
+ \tpie
164
+ `,
165
+ ])('should handle regular pie: %j', (context) => {
166
+ const chart = parsePieChart(context)
167
+ expect(chart.showData).toBe(false)
168
+ expect(chart.slices).toEqual([])
169
+ })
170
+
171
+ it.each([
172
+ `pie showData`,
173
+ ` pie showData `,
174
+ `\tpie\tshowData\t`,
175
+ `
176
+ pie\tshowData
177
+ `,
178
+ ])('should handle regular showData: %j', (context) => {
179
+ expect(parsePieChart(context).showData).toBe(true)
180
+ })
181
+ })
182
+
183
+ describe('mermaid parser/tests/pie.test.ts: TitleAndAccessibilities', () => {
184
+ it.each([
185
+ `pie title sample title`,
186
+ ` pie title sample title `,
187
+ `\tpie\ttitle sample title\t`,
188
+ `pie
189
+ \ttitle sample title
190
+ `,
191
+ ])('should handle regular pie + title in same line: %j', (context) => {
192
+ expect(parsePieChart(context).title).toBe('sample title')
193
+ })
194
+
195
+ it.each([
196
+ `pie
197
+ title sample title`,
198
+ `pie
199
+ title sample title
200
+ `,
201
+ ])('should handle regular pie + title in different line: %j', (context) => {
202
+ expect(parsePieChart(context).title).toBe('sample title')
203
+ })
204
+
205
+ it.each([
206
+ `pie showData title sample title`,
207
+ `pie showData title sample title
208
+ `,
209
+ ])('should handle regular pie + showData + title: %j', (context) => {
210
+ const chart = parsePieChart(context)
211
+ expect(chart.showData).toBe(true)
212
+ expect(chart.title).toBe('sample title')
213
+ })
214
+
215
+ it.each([
216
+ `pie showData
217
+ title sample title`,
218
+ `pie showData
219
+ title sample title
220
+ `,
221
+ ])(
222
+ 'should handle regular showData + title in different line: %j',
223
+ (context) => {
224
+ const chart = parsePieChart(context)
225
+ expect(chart.showData).toBe(true)
226
+ expect(chart.title).toBe('sample title')
227
+ },
228
+ )
229
+ })
230
+
231
+ describe('mermaid parser/tests/pie.test.ts: sections', () => {
232
+ it.each([
233
+ `pie
234
+ "GitHub":100
235
+ "GitLab":50`,
236
+ `pie
237
+ "GitHub" : 100
238
+ "GitLab" : 50`,
239
+ `pie
240
+ "GitHub"\t:\t100
241
+ "GitLab"\t:\t50`,
242
+ `pie
243
+ \t"GitHub" \t : \t 100
244
+ \t"GitLab" \t : \t 50
245
+ `,
246
+ ])('should handle regular sections: %j', (context) => {
247
+ expect(parsePieChart(context).slices).toEqual([
248
+ { label: 'GitHub', value: 100 },
249
+ { label: 'GitLab', value: 50 },
250
+ ])
251
+ })
252
+
253
+ it('should handle sections with showData', () => {
254
+ const chart = parsePieChart(`pie showData
255
+ "GitHub": 100
256
+ "GitLab": 50`)
257
+ expect(chart.showData).toBe(true)
258
+ expect(chart.slices).toEqual([
259
+ { label: 'GitHub', value: 100 },
260
+ { label: 'GitLab', value: 50 },
261
+ ])
262
+ })
263
+
264
+ it('should handle sections with title', () => {
265
+ const chart = parsePieChart(`pie title sample wow
266
+ "GitHub": 100
267
+ "GitLab": 50`)
268
+ expect(chart.title).toBe('sample wow')
269
+ expect(chart.slices).toHaveLength(2)
270
+ })
271
+
272
+ it('should handle value with positive decimal', () => {
273
+ expect(
274
+ parsePieChart(`pie
275
+ "ash": 60.67
276
+ "bat": 40`).slices,
277
+ ).toEqual([
278
+ { label: 'ash', value: 60.67 },
279
+ { label: 'bat', value: 40 },
280
+ ])
281
+ })
282
+
283
+ it('should handle sections with accTitle', () => {
284
+ const chart = parsePieChart(`pie accTitle: sample wow
285
+ "GitHub": 100
286
+ "GitLab": 50`)
287
+ expect(chart.accTitle).toBe('sample wow')
288
+ expect(chart.slices).toHaveLength(2)
289
+ })
290
+
291
+ it('should handle sections with single line accDescr', () => {
292
+ const chart = parsePieChart(`pie accDescr: sample wow
293
+ "GitHub": 100
294
+ "GitLab": 50`)
295
+ expect(chart.accDescr).toBe('sample wow')
296
+ expect(chart.slices).toHaveLength(2)
297
+ })
298
+
299
+ it('should handle sections with multi line accDescr', () => {
300
+ const chart = parsePieChart(`pie accDescr {
301
+ sample wow
302
+ }
303
+ "GitHub": 100
304
+ "GitLab": 50`)
305
+ expect(chart.accDescr).toBe('sample wow')
306
+ expect(chart.slices).toHaveLength(2)
307
+ })
308
+ })