mermaid-ast 0.1.1 → 0.3.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.
Files changed (51) hide show
  1. package/README.md +125 -3
  2. package/dist/index.d.ts +2 -2
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +3 -3
  5. package/dist/parser/class-parser.d.ts +15 -0
  6. package/dist/parser/class-parser.d.ts.map +1 -0
  7. package/dist/parser/class-parser.js +295 -0
  8. package/dist/parser/flowchart-parser.d.ts.map +1 -1
  9. package/dist/parser/flowchart-parser.js +32 -5
  10. package/dist/parser/index.d.ts +1 -0
  11. package/dist/parser/index.d.ts.map +1 -1
  12. package/dist/parser/index.js +7 -1
  13. package/dist/parser/sequence-parser.d.ts.map +1 -1
  14. package/dist/parser/sequence-parser.js +55 -0
  15. package/dist/parser/state-parser.d.ts +17 -0
  16. package/dist/parser/state-parser.d.ts.map +1 -0
  17. package/dist/parser/state-parser.js +221 -0
  18. package/dist/renderer/class-renderer.d.ts +12 -0
  19. package/dist/renderer/class-renderer.d.ts.map +1 -0
  20. package/dist/renderer/class-renderer.js +204 -0
  21. package/dist/renderer/doc.d.ts +318 -0
  22. package/dist/renderer/doc.d.ts.map +1 -0
  23. package/dist/renderer/doc.js +300 -0
  24. package/dist/renderer/flowchart-renderer.d.ts +2 -1
  25. package/dist/renderer/flowchart-renderer.d.ts.map +1 -1
  26. package/dist/renderer/flowchart-renderer.js +246 -86
  27. package/dist/renderer/index.d.ts +3 -2
  28. package/dist/renderer/index.d.ts.map +1 -1
  29. package/dist/renderer/index.js +9 -4
  30. package/dist/renderer/sequence-renderer.d.ts +2 -1
  31. package/dist/renderer/sequence-renderer.d.ts.map +1 -1
  32. package/dist/renderer/sequence-renderer.js +117 -119
  33. package/dist/renderer/state-renderer.d.ts +12 -0
  34. package/dist/renderer/state-renderer.d.ts.map +1 -0
  35. package/dist/renderer/state-renderer.js +133 -0
  36. package/dist/types/index.d.ts +9 -2
  37. package/dist/types/index.d.ts.map +1 -1
  38. package/dist/types/index.js +8 -0
  39. package/dist/types/render-options.d.ts +57 -0
  40. package/dist/types/render-options.d.ts.map +1 -0
  41. package/dist/types/render-options.js +37 -0
  42. package/dist/types/state.d.ts +150 -0
  43. package/dist/types/state.d.ts.map +1 -0
  44. package/dist/types/state.js +20 -0
  45. package/dist/vendored/LICENSE +21 -0
  46. package/dist/vendored/grammars/sequence.jison +28 -4
  47. package/dist/vendored/grammars/state.jison +336 -0
  48. package/dist/vendored/parsers/sequence.js +121 -94
  49. package/dist/vendored/parsers/state.js +876 -0
  50. package/dist/vendored/sync-info.json +17 -2
  51. package/package.json +2 -1
package/README.md CHANGED
@@ -19,6 +19,7 @@ This library provides a way to programmatically work with Mermaid diagrams by pa
19
19
 
20
20
  - **Flowchart** (`flowchart`, `graph`)
21
21
  - **Sequence Diagram** (`sequenceDiagram`)
22
+ - **Class Diagram** (`classDiagram`)
22
23
 
23
24
  ## Installation
24
25
 
@@ -92,6 +93,33 @@ console.log(ast.statements.length); // 2
92
93
  const output = renderSequence(ast);
93
94
  ```
94
95
 
96
+ ### Class Diagrams
97
+
98
+ ```typescript
99
+ import { parseClassDiagram, renderClassDiagram } from "mermaid-ast";
100
+
101
+ const ast = parseClassDiagram(`classDiagram
102
+ class Animal {
103
+ +String name
104
+ +int age
105
+ +eat()
106
+ +sleep()
107
+ }
108
+ class Duck {
109
+ +String beakColor
110
+ +swim()
111
+ +quack()
112
+ }
113
+ Animal <|-- Duck`);
114
+
115
+ // Access AST properties
116
+ console.log(ast.classes.size); // 2
117
+ console.log(ast.relations.length); // 1
118
+
119
+ // Render back
120
+ const output = renderClassDiagram(ast);
121
+ ```
122
+
95
123
  ### Diagram Type Detection
96
124
 
97
125
  ```typescript
@@ -99,9 +127,59 @@ import { detectDiagramType } from "mermaid-ast";
99
127
 
100
128
  detectDiagramType("flowchart LR\n A --> B"); // "flowchart"
101
129
  detectDiagramType("sequenceDiagram\n A->>B: Hi"); // "sequence"
130
+ detectDiagramType("classDiagram\n class Animal"); // "class"
102
131
  detectDiagramType("unknown diagram"); // null
103
132
  ```
104
133
 
134
+ ### Render Options (Pretty-Print)
135
+
136
+ All render functions accept an optional `RenderOptions` object to customize output formatting:
137
+
138
+ ```typescript
139
+ import { renderFlowchart, parseFlowchart } from "mermaid-ast";
140
+ import type { RenderOptions } from "mermaid-ast";
141
+
142
+ const ast = parseFlowchart(`flowchart LR
143
+ A[Start] --> B[Middle] --> C[End]
144
+ classDef highlight fill:#f9f
145
+ class A highlight`);
146
+
147
+ // Default output (4-space indent)
148
+ renderFlowchart(ast);
149
+
150
+ // Custom indent (2 spaces)
151
+ renderFlowchart(ast, { indent: 2 });
152
+
153
+ // Tab indent
154
+ renderFlowchart(ast, { indent: "tab" });
155
+
156
+ // Sort nodes alphabetically
157
+ renderFlowchart(ast, { sortNodes: true });
158
+
159
+ // Flowchart-specific: inline classes (A:::highlight instead of separate class statement)
160
+ renderFlowchart(ast, { inlineClasses: true });
161
+
162
+ // Flowchart-specific: chain links (A --> B --> C on one line)
163
+ renderFlowchart(ast, { compactLinks: true });
164
+
165
+ // Combine options
166
+ renderFlowchart(ast, {
167
+ indent: 2,
168
+ sortNodes: true,
169
+ inlineClasses: true,
170
+ compactLinks: true,
171
+ });
172
+ ```
173
+
174
+ #### Available Options
175
+
176
+ | Option | Type | Default | Description |
177
+ |--------|------|---------|-------------|
178
+ | `indent` | `number \| "tab"` | `4` | Number of spaces for indentation, or `"tab"` for tabs |
179
+ | `sortNodes` | `boolean` | `false` | Sort node/actor/class declarations alphabetically |
180
+ | `inlineClasses` | `boolean` | `false` | (Flowchart only) Use `A:::className` instead of separate `class` statements |
181
+ | `compactLinks` | `boolean` | `false` | (Flowchart only) Chain consecutive links: `A --> B --> C` |
182
+
105
183
  ## API Reference
106
184
 
107
185
  ### Core Functions
@@ -109,7 +187,7 @@ detectDiagramType("unknown diagram"); // null
109
187
  | Function | Description |
110
188
  |----------|-------------|
111
189
  | `parse(input: string): MermaidAST` | Parse any supported diagram |
112
- | `render(ast: MermaidAST): string` | Render any supported AST |
190
+ | `render(ast: MermaidAST, options?: RenderOptions): string` | Render any supported AST |
113
191
  | `detectDiagramType(input: string): DiagramType \| null` | Detect diagram type |
114
192
 
115
193
  ### Flowchart Functions
@@ -117,7 +195,7 @@ detectDiagramType("unknown diagram"); // null
117
195
  | Function | Description |
118
196
  |----------|-------------|
119
197
  | `parseFlowchart(input: string): FlowchartAST` | Parse flowchart diagram |
120
- | `renderFlowchart(ast: FlowchartAST): string` | Render flowchart AST |
198
+ | `renderFlowchart(ast: FlowchartAST, options?: RenderOptions): string` | Render flowchart AST |
121
199
  | `isFlowchartDiagram(input: string): boolean` | Check if input is flowchart |
122
200
 
123
201
  ### Sequence Diagram Functions
@@ -125,9 +203,17 @@ detectDiagramType("unknown diagram"); // null
125
203
  | Function | Description |
126
204
  |----------|-------------|
127
205
  | `parseSequence(input: string): SequenceAST` | Parse sequence diagram |
128
- | `renderSequence(ast: SequenceAST): string` | Render sequence AST |
206
+ | `renderSequence(ast: SequenceAST, options?: RenderOptions): string` | Render sequence AST |
129
207
  | `isSequenceDiagram(input: string): boolean` | Check if input is sequence |
130
208
 
209
+ ### Class Diagram Functions
210
+
211
+ | Function | Description |
212
+ |----------|-------------|
213
+ | `parseClassDiagram(input: string): ClassDiagramAST` | Parse class diagram |
214
+ | `renderClassDiagram(ast: ClassDiagramAST, options?: RenderOptions): string` | Render class diagram AST |
215
+ | `isClassDiagram(input: string): boolean` | Check if input is class diagram |
216
+
131
217
  ## Supported Flowchart Features
132
218
 
133
219
  - **Directions**: LR, RL, TB, TD, BT
@@ -149,6 +235,40 @@ detectDiagramType("unknown diagram"); // null
149
235
  - **Notes**: `note left of`, `note right of`, `note over`
150
236
  - **Actor lifecycle**: `create`, `destroy`
151
237
 
238
+ ## Limitations
239
+
240
+ ### Comments Not Preserved
241
+
242
+ Mermaid supports `%%` line comments, but **comments are not preserved** during parsing. The JISON parsers discard comments during lexing, so they are not included in the AST and will not appear in rendered output.
243
+
244
+ ```mermaid
245
+ flowchart LR
246
+ %% This comment will be lost
247
+ A --> B
248
+ ```
249
+
250
+ After round-trip, the comment is gone:
251
+
252
+ ```mermaid
253
+ flowchart LR
254
+ A --> B
255
+ ```
256
+
257
+ ## Supported Class Diagram Features
258
+
259
+ - **Classes**: With labels, members (attributes and methods)
260
+ - **Visibility modifiers**: `+` (public), `-` (private), `#` (protected), `~` (package)
261
+ - **Relationships**: Inheritance (`<|--`), composition (`*--`), aggregation (`o--`), dependency (`<--`), lollipop (`()--`)
262
+ - **Line types**: Solid (`--`), dotted (`..`)
263
+ - **Cardinality**: `"1" --> "*"` relationship labels
264
+ - **Annotations**: `<<interface>>`, `<<abstract>>`, `<<service>>`, etc.
265
+ - **Namespaces**: Group related classes
266
+ - **Notes**: `note for Class "text"`
267
+ - **Direction**: `direction LR`, `direction TB`, etc.
268
+ - **Styling**: `cssClass`, `classDef`
269
+ - **Interactions**: `callback`, `link`
270
+ - **Generic types**: `class List~T~`
271
+
152
272
  ## Development
153
273
 
154
274
  ### Prerequisites
@@ -244,6 +364,8 @@ mermaid-ast/
244
364
 
245
365
  MIT
246
366
 
367
+ This project includes JISON parsers from [mermaid.js](https://github.com/mermaid-js/mermaid) (MIT License, Copyright (c) 2014 - 2022 Knut Sveidqvist). See [THIRD-PARTY-NOTICES.md](./THIRD-PARTY-NOTICES.md) for details.
368
+
247
369
  ---
248
370
 
249
371
  ## How This Library Was Built
package/dist/index.d.ts CHANGED
@@ -17,6 +17,6 @@
17
17
  * ```
18
18
  */
19
19
  export * from "./types/index.js";
20
- export { parse, parseFlowchart, parseSequence, detectDiagramType, isFlowchartDiagram, isSequenceDiagram, } from "./parser/index.js";
21
- export { render, renderFlowchart, renderSequence, } from "./renderer/index.js";
20
+ export { parse, parseFlowchart, parseSequence, parseClassDiagram, detectDiagramType, isFlowchartDiagram, isSequenceDiagram, isClassDiagram, } from "./parser/index.js";
21
+ export { render, renderFlowchart, renderSequence, renderClassDiagram, } from "./renderer/index.js";
22
22
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,cAAc,kBAAkB,CAAC;AAGjC,OAAO,EACL,KAAK,EACL,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACL,MAAM,EACN,eAAe,EACf,cAAc,GACf,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,cAAc,kBAAkB,CAAC;AAGjC,OAAO,EACL,KAAK,EACL,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,cAAc,GACf,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACL,MAAM,EACN,eAAe,EACf,cAAc,EACd,kBAAkB,GACnB,MAAM,qBAAqB,CAAC"}
package/dist/index.js CHANGED
@@ -19,6 +19,6 @@
19
19
  // Export types
20
20
  export * from "./types/index.js";
21
21
  // Export parser functions
22
- export { parse, parseFlowchart, parseSequence, detectDiagramType, isFlowchartDiagram, isSequenceDiagram, } from "./parser/index.js";
23
- // Export renderer functions (to be implemented)
24
- export { render, renderFlowchart, renderSequence, } from "./renderer/index.js";
22
+ export { parse, parseFlowchart, parseSequence, parseClassDiagram, detectDiagramType, isFlowchartDiagram, isSequenceDiagram, isClassDiagram, } from "./parser/index.js";
23
+ // Export renderer functions
24
+ export { render, renderFlowchart, renderSequence, renderClassDiagram, } from "./renderer/index.js";
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Class Diagram Parser
3
+ *
4
+ * Parses Mermaid class diagram syntax into an AST using the vendored JISON parser.
5
+ */
6
+ import { type ClassDiagramAST } from "../types/class.js";
7
+ /**
8
+ * Parse a class diagram string into an AST
9
+ */
10
+ export declare function parseClassDiagram(input: string): ClassDiagramAST;
11
+ /**
12
+ * Detect if input is a class diagram
13
+ */
14
+ export declare function isClassDiagram(input: string): boolean;
15
+ //# sourceMappingURL=class-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"class-parser.d.ts","sourceRoot":"","sources":["../../src/parser/class-parser.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,KAAK,eAAe,EASrB,MAAM,mBAAmB,CAAC;AAwS3B;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAsBhE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAOrD"}
@@ -0,0 +1,295 @@
1
+ /**
2
+ * Class Diagram Parser
3
+ *
4
+ * Parses Mermaid class diagram syntax into an AST using the vendored JISON parser.
5
+ */
6
+ import { createClassDiagramAST, } from "../types/class.js";
7
+ // Import the vendored parser
8
+ // @ts-ignore - Generated JS file without types
9
+ import classParser from "../vendored/parsers/class.js";
10
+ /**
11
+ * Parse a member string to extract visibility and type
12
+ */
13
+ function parseMember(memberStr) {
14
+ const trimmed = memberStr.trim();
15
+ let visibility;
16
+ let text = trimmed;
17
+ // Check for visibility prefix
18
+ if (trimmed.startsWith("+")) {
19
+ visibility = "+";
20
+ text = trimmed.slice(1).trim();
21
+ }
22
+ else if (trimmed.startsWith("-")) {
23
+ visibility = "-";
24
+ text = trimmed.slice(1).trim();
25
+ }
26
+ else if (trimmed.startsWith("#")) {
27
+ visibility = "#";
28
+ text = trimmed.slice(1).trim();
29
+ }
30
+ else if (trimmed.startsWith("~")) {
31
+ visibility = "~";
32
+ text = trimmed.slice(1).trim();
33
+ }
34
+ // Determine if method or attribute
35
+ const isMethod = text.includes("(") && text.includes(")");
36
+ return {
37
+ text,
38
+ visibility,
39
+ type: isMethod ? "method" : "attribute",
40
+ };
41
+ }
42
+ /**
43
+ * Map relation type number to string
44
+ */
45
+ function mapRelationType(type) {
46
+ if (typeof type === "string")
47
+ return type;
48
+ switch (type) {
49
+ case 0:
50
+ return "aggregation";
51
+ case 1:
52
+ return "extension";
53
+ case 2:
54
+ return "composition";
55
+ case 3:
56
+ return "dependency";
57
+ case 4:
58
+ return "lollipop";
59
+ default:
60
+ return "none";
61
+ }
62
+ }
63
+ /**
64
+ * Map line type number to string
65
+ */
66
+ function mapLineType(type) {
67
+ if (typeof type === "string")
68
+ return type;
69
+ return type === 0 ? "solid" : "dotted";
70
+ }
71
+ /**
72
+ * Create the yy object that the JISON parser uses
73
+ */
74
+ function createClassYY(ast) {
75
+ // Helper to get or create a class
76
+ function getOrCreateClass(id) {
77
+ let cls = ast.classes.get(id);
78
+ if (!cls) {
79
+ cls = {
80
+ id,
81
+ members: [],
82
+ annotations: [],
83
+ cssClasses: [],
84
+ styles: [],
85
+ };
86
+ ast.classes.set(id, cls);
87
+ }
88
+ return cls;
89
+ }
90
+ return {
91
+ // Relation type constants (used by the parser)
92
+ relationType: {
93
+ AGGREGATION: 0,
94
+ EXTENSION: 1,
95
+ COMPOSITION: 2,
96
+ DEPENDENCY: 3,
97
+ LOLLIPOP: 4,
98
+ },
99
+ // Line type constants
100
+ lineType: {
101
+ LINE: 0,
102
+ DOTTED_LINE: 1,
103
+ },
104
+ // Set diagram direction
105
+ setDirection(dir) {
106
+ ast.direction = dir;
107
+ },
108
+ // Add a class
109
+ addClass(id) {
110
+ getOrCreateClass(id);
111
+ },
112
+ // Set class label
113
+ setClassLabel(id, label) {
114
+ const cls = getOrCreateClass(id);
115
+ cls.label = label;
116
+ },
117
+ // Add members to a class
118
+ addMembers(id, members) {
119
+ const cls = getOrCreateClass(id);
120
+ for (const member of members) {
121
+ const trimmed = member.trim();
122
+ if (trimmed) { // Skip empty members
123
+ cls.members.push(parseMember(member));
124
+ }
125
+ }
126
+ },
127
+ // Add a single member (called from memberStatement)
128
+ addMember(id, member) {
129
+ const cls = getOrCreateClass(id);
130
+ const trimmed = member.trim();
131
+ if (trimmed) { // Skip empty members
132
+ cls.members.push(parseMember(member));
133
+ }
134
+ },
135
+ // Add a relation
136
+ addRelation(relation) {
137
+ // Ensure both classes exist
138
+ getOrCreateClass(relation.id1);
139
+ getOrCreateClass(relation.id2);
140
+ const rel = {
141
+ id1: relation.id1,
142
+ id2: relation.id2,
143
+ relation: {
144
+ type1: mapRelationType(relation.relation.type1),
145
+ type2: mapRelationType(relation.relation.type2),
146
+ lineType: mapLineType(relation.relation.lineType),
147
+ },
148
+ };
149
+ if (relation.relationTitle1 && relation.relationTitle1 !== "none") {
150
+ rel.relationTitle1 = relation.relationTitle1;
151
+ }
152
+ if (relation.relationTitle2 && relation.relationTitle2 !== "none") {
153
+ rel.relationTitle2 = relation.relationTitle2;
154
+ }
155
+ if (relation.title) {
156
+ rel.title = relation.title;
157
+ }
158
+ ast.relations.push(rel);
159
+ },
160
+ // Add annotation (like <<interface>>)
161
+ addAnnotation(className, annotation) {
162
+ const cls = getOrCreateClass(className);
163
+ cls.annotations.push(annotation);
164
+ },
165
+ // Add namespace
166
+ addNamespace(name) {
167
+ if (!ast.namespaces.has(name)) {
168
+ ast.namespaces.set(name, { name, classes: [] });
169
+ }
170
+ },
171
+ // Add classes to namespace
172
+ addClassesToNamespace(namespaceName, classIds) {
173
+ let ns = ast.namespaces.get(namespaceName);
174
+ if (!ns) {
175
+ ns = { name: namespaceName, classes: [] };
176
+ ast.namespaces.set(namespaceName, ns);
177
+ }
178
+ for (const classId of classIds) {
179
+ if (!ns.classes.includes(classId)) {
180
+ ns.classes.push(classId);
181
+ }
182
+ }
183
+ },
184
+ // Add note
185
+ addNote(text, forClass) {
186
+ const note = { text };
187
+ if (forClass) {
188
+ note.forClass = forClass;
189
+ }
190
+ ast.notes.push(note);
191
+ },
192
+ // Set CSS class on a class (id can be comma-separated list like "C1,C2")
193
+ setCssClass(id, className) {
194
+ // Split by comma to handle "cssClass C1,C2 styleClass" syntax
195
+ const classIds = id.split(",").map(s => s.trim()).filter(s => s.length > 0);
196
+ for (const classId of classIds) {
197
+ const cls = getOrCreateClass(classId);
198
+ if (!cls.cssClasses.includes(className)) {
199
+ cls.cssClasses.push(className);
200
+ }
201
+ }
202
+ },
203
+ // Set inline CSS style
204
+ setCssStyle(id, styles) {
205
+ const cls = getOrCreateClass(id);
206
+ cls.styles.push(...styles);
207
+ },
208
+ // Define a CSS class (classDef)
209
+ defineClass(classList, styles) {
210
+ for (const className of classList) {
211
+ ast.classDefs.set(className, { name: className, styles });
212
+ }
213
+ },
214
+ // Set click event
215
+ setClickEvent(id, callback, callbackArgs) {
216
+ const cls = getOrCreateClass(id);
217
+ cls.callback = callback;
218
+ if (callbackArgs) {
219
+ cls.callbackArgs = callbackArgs;
220
+ }
221
+ },
222
+ // Set link
223
+ setLink(id, href, target) {
224
+ const cls = getOrCreateClass(id);
225
+ cls.link = href;
226
+ if (target) {
227
+ cls.linkTarget = target;
228
+ }
229
+ },
230
+ // Set tooltip
231
+ setTooltip(id, tooltip) {
232
+ const cls = getOrCreateClass(id);
233
+ cls.tooltip = tooltip;
234
+ },
235
+ // Clean up label (remove surrounding quotes, colons)
236
+ cleanupLabel(label) {
237
+ let result = label;
238
+ if (result.startsWith(":")) {
239
+ result = result.slice(1);
240
+ }
241
+ result = result.trim();
242
+ if ((result.startsWith('"') && result.endsWith('"')) ||
243
+ (result.startsWith("'") && result.endsWith("'"))) {
244
+ result = result.slice(1, -1);
245
+ }
246
+ return result;
247
+ },
248
+ // Accessibility
249
+ setAccTitle(title) {
250
+ ast.accTitle = title;
251
+ },
252
+ setAccDescription(description) {
253
+ ast.accDescription = description;
254
+ },
255
+ // These are called but we don't need to do anything special
256
+ getTooltip: () => undefined,
257
+ lookUpDomId: (id) => id,
258
+ setDiagramTitle: () => { },
259
+ getDiagramTitle: () => "",
260
+ getConfig: () => ({}),
261
+ clear: () => { },
262
+ };
263
+ }
264
+ /**
265
+ * Parse a class diagram string into an AST
266
+ */
267
+ export function parseClassDiagram(input) {
268
+ const ast = createClassDiagramAST();
269
+ const yy = createClassYY(ast);
270
+ // Set up the parser with our yy object
271
+ classParser.yy = yy;
272
+ try {
273
+ classParser.parse(input);
274
+ }
275
+ catch (error) {
276
+ if (error instanceof Error) {
277
+ throw new Error(`Failed to parse class diagram: ${error.message}`);
278
+ }
279
+ throw error;
280
+ }
281
+ // Reverse members to maintain source order (JISON grammar processes bottom-up)
282
+ for (const [, cls] of ast.classes) {
283
+ cls.members.reverse();
284
+ }
285
+ return ast;
286
+ }
287
+ /**
288
+ * Detect if input is a class diagram
289
+ */
290
+ export function isClassDiagram(input) {
291
+ const trimmed = input.trim();
292
+ const firstLine = trimmed.split("\n")[0].trim().toLowerCase();
293
+ return (firstLine.startsWith("classdiagram") ||
294
+ firstLine.startsWith("classdiagram-v2"));
295
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"flowchart-parser.d.ts","sourceRoot":"","sources":["../../src/parser/flowchart-parser.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,KAAK,YAAY,EAalB,MAAM,uBAAuB,CAAC;AA+Z/B;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAkB1D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAQzD"}
1
+ {"version":3,"file":"flowchart-parser.d.ts","sourceRoot":"","sources":["../../src/parser/flowchart-parser.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,KAAK,YAAY,EAalB,MAAM,uBAAuB,CAAC;AAyb/B;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAkB1D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAQzD"}
@@ -205,10 +205,10 @@ function createFlowchartYY(ast) {
205
205
  subgraphId = `subGraph${subgraphIdCounter++}`;
206
206
  }
207
207
  else if (typeof id === "object" && "text" in id) {
208
- subgraphId = id.text;
208
+ subgraphId = id.text.trim();
209
209
  }
210
210
  else {
211
- subgraphId = id;
211
+ subgraphId = id.trim();
212
212
  }
213
213
  // Collect node IDs from the nodeList and look for direction statements
214
214
  const nodes = [];
@@ -216,10 +216,21 @@ function createFlowchartYY(ast) {
216
216
  if (Array.isArray(nodeList)) {
217
217
  for (const item of nodeList) {
218
218
  if (typeof item === "string") {
219
- nodes.push(item);
219
+ // Trim and filter empty strings
220
+ const trimmed = item.trim();
221
+ if (trimmed) {
222
+ nodes.push(trimmed);
223
+ }
220
224
  }
221
225
  else if (Array.isArray(item)) {
222
- nodes.push(...item.filter((x) => typeof x === "string"));
226
+ for (const x of item) {
227
+ if (typeof x === "string") {
228
+ const trimmed = x.trim();
229
+ if (trimmed) {
230
+ nodes.push(trimmed);
231
+ }
232
+ }
233
+ }
223
234
  }
224
235
  else if (item && typeof item === "object") {
225
236
  // Check for direction statement: {stmt: 'dir', value: 'TB'}
@@ -229,12 +240,28 @@ function createFlowchartYY(ast) {
229
240
  else if ("nodes" in item) {
230
241
  const itemNodes = item.nodes;
231
242
  if (Array.isArray(itemNodes)) {
232
- nodes.push(...itemNodes);
243
+ for (const x of itemNodes) {
244
+ if (typeof x === "string") {
245
+ const trimmed = x.trim();
246
+ if (trimmed) {
247
+ nodes.push(trimmed);
248
+ }
249
+ }
250
+ }
233
251
  }
234
252
  }
235
253
  }
236
254
  }
237
255
  }
256
+ else if (typeof nodeList === "string") {
257
+ // Handle case where nodeList is a string (might be space-separated node IDs)
258
+ const trimmed = nodeList.trim();
259
+ if (trimmed) {
260
+ // Split by whitespace and filter empty strings
261
+ const parts = trimmed.split(/\s+/).filter(p => p.length > 0);
262
+ nodes.push(...parts);
263
+ }
264
+ }
238
265
  const subgraph = {
239
266
  id: subgraphId,
240
267
  nodes,
@@ -5,6 +5,7 @@
5
5
  */
6
6
  export { parseFlowchart, isFlowchartDiagram } from "./flowchart-parser.js";
7
7
  export { parseSequence, isSequenceDiagram } from "./sequence-parser.js";
8
+ export { parseClassDiagram, isClassDiagram } from "./class-parser.js";
8
9
  import type { MermaidAST, DiagramType } from "../types/index.js";
9
10
  /**
10
11
  * Detect the diagram type from input text
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/parser/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3E,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAExE,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIjE;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAInE;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAiB/C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/parser/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3E,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACxE,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEtE,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAKjE;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAKnE;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAmB/C"}
@@ -5,8 +5,10 @@
5
5
  */
6
6
  export { parseFlowchart, isFlowchartDiagram } from "./flowchart-parser.js";
7
7
  export { parseSequence, isSequenceDiagram } from "./sequence-parser.js";
8
+ export { parseClassDiagram, isClassDiagram } from "./class-parser.js";
8
9
  import { parseFlowchart, isFlowchartDiagram } from "./flowchart-parser.js";
9
10
  import { parseSequence, isSequenceDiagram } from "./sequence-parser.js";
11
+ import { parseClassDiagram, isClassDiagram } from "./class-parser.js";
10
12
  /**
11
13
  * Detect the diagram type from input text
12
14
  */
@@ -15,6 +17,8 @@ export function detectDiagramType(input) {
15
17
  return "flowchart";
16
18
  if (isSequenceDiagram(input))
17
19
  return "sequence";
20
+ if (isClassDiagram(input))
21
+ return "class";
18
22
  return null;
19
23
  }
20
24
  /**
@@ -23,13 +27,15 @@ export function detectDiagramType(input) {
23
27
  export function parse(input) {
24
28
  const type = detectDiagramType(input);
25
29
  if (!type) {
26
- throw new Error("Unable to detect diagram type. Supported types: flowchart, sequence");
30
+ throw new Error("Unable to detect diagram type. Supported types: flowchart, sequence, class");
27
31
  }
28
32
  switch (type) {
29
33
  case "flowchart":
30
34
  return parseFlowchart(input);
31
35
  case "sequence":
32
36
  return parseSequence(input);
37
+ case "class":
38
+ return parseClassDiagram(input);
33
39
  default:
34
40
  throw new Error(`Unsupported diagram type: ${type}`);
35
41
  }
@@ -1 +1 @@
1
- {"version":3,"file":"sequence-parser.d.ts","sourceRoot":"","sources":["../../src/parser/sequence-parser.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,KAAK,WAAW,EAiBjB,MAAM,sBAAsB,CAAC;AAygB9B;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CAmBxD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAIxD"}
1
+ {"version":3,"file":"sequence-parser.d.ts","sourceRoot":"","sources":["../../src/parser/sequence-parser.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,KAAK,WAAW,EAiBjB,MAAM,sBAAsB,CAAC;AAkkB9B;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CAsBxD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAIxD"}