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.
- package/README.md +125 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/parser/class-parser.d.ts +15 -0
- package/dist/parser/class-parser.d.ts.map +1 -0
- package/dist/parser/class-parser.js +295 -0
- package/dist/parser/flowchart-parser.d.ts.map +1 -1
- package/dist/parser/flowchart-parser.js +32 -5
- package/dist/parser/index.d.ts +1 -0
- package/dist/parser/index.d.ts.map +1 -1
- package/dist/parser/index.js +7 -1
- package/dist/parser/sequence-parser.d.ts.map +1 -1
- package/dist/parser/sequence-parser.js +55 -0
- package/dist/parser/state-parser.d.ts +17 -0
- package/dist/parser/state-parser.d.ts.map +1 -0
- package/dist/parser/state-parser.js +221 -0
- package/dist/renderer/class-renderer.d.ts +12 -0
- package/dist/renderer/class-renderer.d.ts.map +1 -0
- package/dist/renderer/class-renderer.js +204 -0
- package/dist/renderer/doc.d.ts +318 -0
- package/dist/renderer/doc.d.ts.map +1 -0
- package/dist/renderer/doc.js +300 -0
- package/dist/renderer/flowchart-renderer.d.ts +2 -1
- package/dist/renderer/flowchart-renderer.d.ts.map +1 -1
- package/dist/renderer/flowchart-renderer.js +246 -86
- package/dist/renderer/index.d.ts +3 -2
- package/dist/renderer/index.d.ts.map +1 -1
- package/dist/renderer/index.js +9 -4
- package/dist/renderer/sequence-renderer.d.ts +2 -1
- package/dist/renderer/sequence-renderer.d.ts.map +1 -1
- package/dist/renderer/sequence-renderer.js +117 -119
- package/dist/renderer/state-renderer.d.ts +12 -0
- package/dist/renderer/state-renderer.d.ts.map +1 -0
- package/dist/renderer/state-renderer.js +133 -0
- package/dist/types/index.d.ts +9 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +8 -0
- package/dist/types/render-options.d.ts +57 -0
- package/dist/types/render-options.d.ts.map +1 -0
- package/dist/types/render-options.js +37 -0
- package/dist/types/state.d.ts +150 -0
- package/dist/types/state.d.ts.map +1 -0
- package/dist/types/state.js +20 -0
- package/dist/vendored/LICENSE +21 -0
- package/dist/vendored/grammars/sequence.jison +28 -4
- package/dist/vendored/grammars/state.jison +336 -0
- package/dist/vendored/parsers/sequence.js +121 -94
- package/dist/vendored/parsers/state.js +876 -0
- package/dist/vendored/sync-info.json +17 -2
- 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
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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,
|
|
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
|
|
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;
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
package/dist/parser/index.d.ts
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/parser/index.js
CHANGED
|
@@ -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;
|
|
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"}
|