mermaid-ast 0.3.0 → 0.4.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 +120 -3
- package/dist/builder/class-builder.d.ts +148 -0
- package/dist/builder/class-builder.d.ts.map +1 -0
- package/dist/builder/class-builder.js +318 -0
- package/dist/builder/flowchart-builder.d.ts +123 -0
- package/dist/builder/flowchart-builder.d.ts.map +1 -0
- package/dist/builder/flowchart-builder.js +257 -0
- package/dist/builder/index.d.ts +10 -0
- package/dist/builder/index.d.ts.map +1 -0
- package/dist/builder/index.js +9 -0
- package/dist/builder/sequence-builder.d.ts +174 -0
- package/dist/builder/sequence-builder.d.ts.map +1 -0
- package/dist/builder/sequence-builder.js +443 -0
- package/dist/builder/state-builder.d.ts +156 -0
- package/dist/builder/state-builder.d.ts.map +1 -0
- package/dist/builder/state-builder.js +314 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -4
- package/dist/parser/class-parser.d.ts +1 -1
- package/dist/parser/class-parser.d.ts.map +1 -1
- package/dist/parser/class-parser.js +36 -32
- package/dist/parser/flowchart-parser.d.ts +1 -1
- package/dist/parser/flowchart-parser.d.ts.map +1 -1
- package/dist/parser/flowchart-parser.js +51 -53
- package/dist/parser/index.d.ts +4 -4
- package/dist/parser/index.d.ts.map +1 -1
- package/dist/parser/index.js +13 -13
- package/dist/parser/sequence-parser.d.ts +1 -1
- package/dist/parser/sequence-parser.d.ts.map +1 -1
- package/dist/parser/sequence-parser.js +88 -82
- package/dist/parser/state-parser.d.ts +1 -1
- package/dist/parser/state-parser.d.ts.map +1 -1
- package/dist/parser/state-parser.js +35 -36
- package/dist/renderer/class-renderer.d.ts +2 -2
- package/dist/renderer/class-renderer.d.ts.map +1 -1
- package/dist/renderer/class-renderer.js +27 -32
- package/dist/renderer/doc.d.ts.map +1 -1
- package/dist/renderer/doc.js +9 -9
- package/dist/renderer/flowchart-renderer.d.ts +2 -2
- package/dist/renderer/flowchart-renderer.d.ts.map +1 -1
- package/dist/renderer/flowchart-renderer.js +60 -76
- package/dist/renderer/index.d.ts +4 -4
- package/dist/renderer/index.d.ts.map +1 -1
- package/dist/renderer/index.js +7 -7
- package/dist/renderer/sequence-renderer.d.ts +2 -2
- package/dist/renderer/sequence-renderer.d.ts.map +1 -1
- package/dist/renderer/sequence-renderer.js +60 -59
- package/dist/renderer/state-renderer.d.ts +2 -2
- package/dist/renderer/state-renderer.d.ts.map +1 -1
- package/dist/renderer/state-renderer.js +19 -21
- package/dist/types/class.d.ts +6 -6
- package/dist/types/class.js +2 -2
- package/dist/types/flowchart.d.ts +8 -8
- package/dist/types/flowchart.js +2 -2
- package/dist/types/index.d.ts +8 -8
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +7 -7
- package/dist/types/render-options.d.ts +1 -1
- package/dist/types/render-options.js +4 -4
- package/dist/types/sequence.d.ts +20 -20
- package/dist/types/sequence.js +1 -1
- package/dist/types/state.d.ts +12 -12
- package/dist/types/state.js +2 -2
- package/dist/utils.d.ts +29 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +30 -0
- package/package.json +11 -3
package/README.md
CHANGED
|
@@ -17,9 +17,25 @@ This library provides a way to programmatically work with Mermaid diagrams by pa
|
|
|
17
17
|
|
|
18
18
|
## Supported Diagram Types
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
| Diagram Type | Parse | Render | Builder |
|
|
21
|
+
|--------------|-------|--------|---------|
|
|
22
|
+
| Flowchart (`flowchart`, `graph`) | ✅ | ✅ | ✅ |
|
|
23
|
+
| Sequence (`sequenceDiagram`) | ✅ | ✅ | ✅ |
|
|
24
|
+
| Class (`classDiagram`) | ✅ | ✅ | ✅ |
|
|
25
|
+
| State (`stateDiagram`) | ✅ | ✅ | ✅ |
|
|
26
|
+
| ER Diagram (`erDiagram`) | ❌ | ❌ | ❌ |
|
|
27
|
+
| Gantt (`gantt`) | ❌ | ❌ | ❌ |
|
|
28
|
+
| Journey (`journey`) | ❌ | ❌ | ❌ |
|
|
29
|
+
| Mindmap (`mindmap`) | ❌ | ❌ | ❌ |
|
|
30
|
+
| Timeline (`timeline`) | ❌ | ❌ | ❌ |
|
|
31
|
+
| Pie (`pie`) | ❌ | ❌ | ❌ |
|
|
32
|
+
| Quadrant (`quadrantChart`) | ❌ | ❌ | ❌ |
|
|
33
|
+
| Requirement (`requirementDiagram`) | ❌ | ❌ | ❌ |
|
|
34
|
+
| Git Graph (`gitGraph`) | ❌ | ❌ | ❌ |
|
|
35
|
+
| C4 (`C4Context`, etc.) | ❌ | ❌ | ❌ |
|
|
36
|
+
| Sankey (`sankey`) | ❌ | ❌ | ❌ |
|
|
37
|
+
| XY Chart (`xychart`) | ❌ | ❌ | ❌ |
|
|
38
|
+
| Block (`block`) | ❌ | ❌ | ❌ |
|
|
23
39
|
|
|
24
40
|
## Installation
|
|
25
41
|
|
|
@@ -131,6 +147,107 @@ detectDiagramType("classDiagram\n class Animal"); // "class"
|
|
|
131
147
|
detectDiagramType("unknown diagram"); // null
|
|
132
148
|
```
|
|
133
149
|
|
|
150
|
+
## Fluent Builder API
|
|
151
|
+
|
|
152
|
+
Build diagrams programmatically with a chainable, type-safe API:
|
|
153
|
+
|
|
154
|
+
### Flowchart Builder
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
import { flowchart, render } from "mermaid-ast";
|
|
158
|
+
|
|
159
|
+
const ast = flowchart("LR")
|
|
160
|
+
.node("A", "Start", { shape: "stadium" })
|
|
161
|
+
.node("B", "Process")
|
|
162
|
+
.node("C", "End", { shape: "circle" })
|
|
163
|
+
.link("A", "B", { text: "begin" })
|
|
164
|
+
.link("B", "C", { stroke: "dotted" })
|
|
165
|
+
.subgraph("sub1", "My Group", (s) => {
|
|
166
|
+
s.node("D", "Inner").link("D", "B");
|
|
167
|
+
})
|
|
168
|
+
.classDef("highlight", { fill: "#f9f" })
|
|
169
|
+
.class("A", "highlight")
|
|
170
|
+
.build();
|
|
171
|
+
|
|
172
|
+
const text = render(ast);
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Sequence Builder
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
import { sequence, render } from "mermaid-ast";
|
|
179
|
+
|
|
180
|
+
const ast = sequence()
|
|
181
|
+
.participant("A", "Alice")
|
|
182
|
+
.actor("B", "Bob")
|
|
183
|
+
.message("A", "B", "Hello!", { arrow: "solid" })
|
|
184
|
+
.loop("Every minute", (l) => {
|
|
185
|
+
l.message("B", "A", "Ping");
|
|
186
|
+
})
|
|
187
|
+
.alt([
|
|
188
|
+
{ condition: "Success", build: (b) => b.message("A", "B", "OK") },
|
|
189
|
+
{ condition: "Failure", build: (b) => b.message("A", "B", "Error") },
|
|
190
|
+
])
|
|
191
|
+
.note("A", "Important!", { placement: "right_of" })
|
|
192
|
+
.build();
|
|
193
|
+
|
|
194
|
+
const text = render(ast);
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Class Diagram Builder
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
import { classDiagram, render } from "mermaid-ast";
|
|
201
|
+
|
|
202
|
+
const ast = classDiagram()
|
|
203
|
+
.class("Animal", (c) => {
|
|
204
|
+
c.property("name: string", "+")
|
|
205
|
+
.property("age: int", "-")
|
|
206
|
+
.method("speak()", "+");
|
|
207
|
+
})
|
|
208
|
+
.class("Dog")
|
|
209
|
+
.extends("Dog", "Animal")
|
|
210
|
+
.composition("Dog", "Tail")
|
|
211
|
+
.class("Tail")
|
|
212
|
+
.build();
|
|
213
|
+
|
|
214
|
+
const text = render(ast);
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### State Diagram Builder
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
import { stateDiagram, render } from "mermaid-ast";
|
|
221
|
+
|
|
222
|
+
const ast = stateDiagram()
|
|
223
|
+
.state("Idle", { description: "Waiting for input" })
|
|
224
|
+
.state("Running")
|
|
225
|
+
.state("Done")
|
|
226
|
+
.initial("Idle")
|
|
227
|
+
.transition("Idle", "Running", { label: "start" })
|
|
228
|
+
.transition("Running", "Done", { label: "complete" })
|
|
229
|
+
.final("Done")
|
|
230
|
+
.composite("Running", (c) => {
|
|
231
|
+
c.state("Step1").state("Step2").transition("Step1", "Step2");
|
|
232
|
+
})
|
|
233
|
+
.build();
|
|
234
|
+
|
|
235
|
+
const text = render(ast);
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Builder Validation
|
|
239
|
+
|
|
240
|
+
By default, `.build()` validates the diagram (e.g., ensures links reference existing nodes):
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
// This throws FlowchartValidationError
|
|
244
|
+
flowchart().node("A").link("A", "B").build();
|
|
245
|
+
// Error: Link target node 'B' does not exist
|
|
246
|
+
|
|
247
|
+
// Skip validation if needed
|
|
248
|
+
flowchart().node("A").link("A", "B").build({ validate: false });
|
|
249
|
+
```
|
|
250
|
+
|
|
134
251
|
### Render Options (Pretty-Print)
|
|
135
252
|
|
|
136
253
|
All render functions accept an optional `RenderOptions` object to customize output formatting:
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Class Diagram Builder
|
|
3
|
+
*
|
|
4
|
+
* Fluent API for constructing Class Diagram ASTs programmatically.
|
|
5
|
+
*/
|
|
6
|
+
import { type ClassDiagramAST, type ClassDirection, type ClassMember, type LineType, type RelationType } from '../types/class.js';
|
|
7
|
+
/**
|
|
8
|
+
* Options for adding a class
|
|
9
|
+
*/
|
|
10
|
+
export interface ClassOptions {
|
|
11
|
+
label?: string;
|
|
12
|
+
annotations?: string[];
|
|
13
|
+
cssClasses?: string[];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Options for adding a relation
|
|
17
|
+
*/
|
|
18
|
+
export interface RelationOptions {
|
|
19
|
+
type1?: RelationType;
|
|
20
|
+
type2?: RelationType;
|
|
21
|
+
lineType?: LineType;
|
|
22
|
+
label1?: string;
|
|
23
|
+
label2?: string;
|
|
24
|
+
label?: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Options for build()
|
|
28
|
+
*/
|
|
29
|
+
export interface ClassBuildOptions {
|
|
30
|
+
validate?: boolean;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Validation error for invalid AST
|
|
34
|
+
*/
|
|
35
|
+
export declare class ClassDiagramValidationError extends Error {
|
|
36
|
+
constructor(message: string);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Builder for class members
|
|
40
|
+
*/
|
|
41
|
+
export declare class ClassMemberBuilder {
|
|
42
|
+
private members;
|
|
43
|
+
/**
|
|
44
|
+
* Add a property/attribute
|
|
45
|
+
*/
|
|
46
|
+
property(text: string, visibility?: '+' | '-' | '#' | '~'): this;
|
|
47
|
+
/**
|
|
48
|
+
* Add a method
|
|
49
|
+
*/
|
|
50
|
+
method(text: string, visibility?: '+' | '-' | '#' | '~'): this;
|
|
51
|
+
/**
|
|
52
|
+
* Get the members
|
|
53
|
+
*/
|
|
54
|
+
getMembers(): ClassMember[];
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Fluent builder for Class Diagram ASTs
|
|
58
|
+
*/
|
|
59
|
+
export declare class ClassDiagramBuilder {
|
|
60
|
+
private ast;
|
|
61
|
+
private currentNamespace;
|
|
62
|
+
constructor(direction?: ClassDirection);
|
|
63
|
+
/**
|
|
64
|
+
* Set the direction
|
|
65
|
+
*/
|
|
66
|
+
direction(direction: ClassDirection): this;
|
|
67
|
+
/**
|
|
68
|
+
* Add a class
|
|
69
|
+
*/
|
|
70
|
+
class(id: string, builderFn?: (builder: ClassMemberBuilder) => void, options?: ClassOptions): this;
|
|
71
|
+
/**
|
|
72
|
+
* Add a relation between two classes
|
|
73
|
+
*/
|
|
74
|
+
relation(id1: string, id2: string, options?: RelationOptions): this;
|
|
75
|
+
/**
|
|
76
|
+
* Add an inheritance/extension relation (A extends B)
|
|
77
|
+
*/
|
|
78
|
+
extends(child: string, parent: string, label?: string): this;
|
|
79
|
+
/**
|
|
80
|
+
* Add an implementation relation (A implements B)
|
|
81
|
+
*/
|
|
82
|
+
implements(implementer: string, interface_: string, label?: string): this;
|
|
83
|
+
/**
|
|
84
|
+
* Add a composition relation (A has B, strong ownership)
|
|
85
|
+
*/
|
|
86
|
+
composition(owner: string, owned: string, label?: string): this;
|
|
87
|
+
/**
|
|
88
|
+
* Add an aggregation relation (A has B, weak ownership)
|
|
89
|
+
*/
|
|
90
|
+
aggregation(owner: string, owned: string, label?: string): this;
|
|
91
|
+
/**
|
|
92
|
+
* Add a dependency relation (A depends on B)
|
|
93
|
+
*/
|
|
94
|
+
dependency(dependent: string, dependency: string, label?: string): this;
|
|
95
|
+
/**
|
|
96
|
+
* Add a namespace
|
|
97
|
+
*/
|
|
98
|
+
namespace(name: string, builderFn: (builder: ClassDiagramBuilder) => void): this;
|
|
99
|
+
/**
|
|
100
|
+
* Add a note
|
|
101
|
+
*/
|
|
102
|
+
note(text: string, forClass?: string): this;
|
|
103
|
+
/**
|
|
104
|
+
* Define a CSS class style
|
|
105
|
+
*/
|
|
106
|
+
classDef(name: string, styles: string[]): this;
|
|
107
|
+
/**
|
|
108
|
+
* Apply a CSS class to a class
|
|
109
|
+
*/
|
|
110
|
+
cssClass(classId: string, cssClassName: string): this;
|
|
111
|
+
/**
|
|
112
|
+
* Add a link to a class
|
|
113
|
+
*/
|
|
114
|
+
link(classId: string, url: string, target?: string): this;
|
|
115
|
+
/**
|
|
116
|
+
* Add a callback to a class
|
|
117
|
+
*/
|
|
118
|
+
callback(classId: string, callback: string, args?: string): this;
|
|
119
|
+
/**
|
|
120
|
+
* Add a tooltip to a class
|
|
121
|
+
*/
|
|
122
|
+
tooltip(classId: string, tooltip: string): this;
|
|
123
|
+
/**
|
|
124
|
+
* Add an annotation to a class (e.g., <<interface>>, <<abstract>>)
|
|
125
|
+
*/
|
|
126
|
+
annotate(classId: string, annotation: string): this;
|
|
127
|
+
/**
|
|
128
|
+
* Set the accessibility title
|
|
129
|
+
*/
|
|
130
|
+
accTitle(title: string): this;
|
|
131
|
+
/**
|
|
132
|
+
* Set the accessibility description
|
|
133
|
+
*/
|
|
134
|
+
accDescription(description: string): this;
|
|
135
|
+
/**
|
|
136
|
+
* Validate the AST
|
|
137
|
+
*/
|
|
138
|
+
private validate;
|
|
139
|
+
/**
|
|
140
|
+
* Build and return the ClassDiagramAST
|
|
141
|
+
*/
|
|
142
|
+
build(options?: ClassBuildOptions): ClassDiagramAST;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Create a new ClassDiagramBuilder
|
|
146
|
+
*/
|
|
147
|
+
export declare function classDiagram(direction?: ClassDirection): ClassDiagramBuilder;
|
|
148
|
+
//# sourceMappingURL=class-builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"class-builder.d.ts","sourceRoot":"","sources":["../../src/builder/class-builder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Class Diagram Builder
|
|
3
|
+
*
|
|
4
|
+
* Fluent API for constructing Class Diagram ASTs programmatically.
|
|
5
|
+
*/
|
|
6
|
+
import { createClassDiagramAST, } from '../types/class.js';
|
|
7
|
+
/**
|
|
8
|
+
* Validation error for invalid AST
|
|
9
|
+
*/
|
|
10
|
+
export class ClassDiagramValidationError extends Error {
|
|
11
|
+
constructor(message) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = 'ClassDiagramValidationError';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Builder for class members
|
|
18
|
+
*/
|
|
19
|
+
export class ClassMemberBuilder {
|
|
20
|
+
members = [];
|
|
21
|
+
/**
|
|
22
|
+
* Add a property/attribute
|
|
23
|
+
*/
|
|
24
|
+
property(text, visibility) {
|
|
25
|
+
this.members.push({
|
|
26
|
+
text,
|
|
27
|
+
visibility,
|
|
28
|
+
type: 'attribute',
|
|
29
|
+
});
|
|
30
|
+
return this;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Add a method
|
|
34
|
+
*/
|
|
35
|
+
method(text, visibility) {
|
|
36
|
+
this.members.push({
|
|
37
|
+
text,
|
|
38
|
+
visibility,
|
|
39
|
+
type: 'method',
|
|
40
|
+
});
|
|
41
|
+
return this;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Get the members
|
|
45
|
+
*/
|
|
46
|
+
getMembers() {
|
|
47
|
+
return this.members;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Fluent builder for Class Diagram ASTs
|
|
52
|
+
*/
|
|
53
|
+
export class ClassDiagramBuilder {
|
|
54
|
+
ast;
|
|
55
|
+
currentNamespace = null;
|
|
56
|
+
constructor(direction = 'TB') {
|
|
57
|
+
this.ast = createClassDiagramAST();
|
|
58
|
+
this.ast.direction = direction;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Set the direction
|
|
62
|
+
*/
|
|
63
|
+
direction(direction) {
|
|
64
|
+
this.ast.direction = direction;
|
|
65
|
+
return this;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Add a class
|
|
69
|
+
*/
|
|
70
|
+
class(id, builderFn, options) {
|
|
71
|
+
const memberBuilder = new ClassMemberBuilder();
|
|
72
|
+
if (builderFn) {
|
|
73
|
+
builderFn(memberBuilder);
|
|
74
|
+
}
|
|
75
|
+
const classDef = {
|
|
76
|
+
id,
|
|
77
|
+
label: options?.label,
|
|
78
|
+
members: memberBuilder.getMembers(),
|
|
79
|
+
annotations: options?.annotations ?? [],
|
|
80
|
+
cssClasses: options?.cssClasses ?? [],
|
|
81
|
+
styles: [],
|
|
82
|
+
};
|
|
83
|
+
this.ast.classes.set(id, classDef);
|
|
84
|
+
// Add to current namespace if inside one
|
|
85
|
+
if (this.currentNamespace) {
|
|
86
|
+
const ns = this.ast.namespaces.get(this.currentNamespace);
|
|
87
|
+
if (ns) {
|
|
88
|
+
ns.classes.push(id);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return this;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Add a relation between two classes
|
|
95
|
+
*/
|
|
96
|
+
relation(id1, id2, options) {
|
|
97
|
+
const relation = {
|
|
98
|
+
id1,
|
|
99
|
+
id2,
|
|
100
|
+
relation: {
|
|
101
|
+
type1: options?.type1 ?? 'none',
|
|
102
|
+
type2: options?.type2 ?? 'none',
|
|
103
|
+
lineType: options?.lineType ?? 'solid',
|
|
104
|
+
},
|
|
105
|
+
relationTitle1: options?.label1,
|
|
106
|
+
relationTitle2: options?.label2,
|
|
107
|
+
title: options?.label,
|
|
108
|
+
};
|
|
109
|
+
this.ast.relations.push(relation);
|
|
110
|
+
return this;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Add an inheritance/extension relation (A extends B)
|
|
114
|
+
*/
|
|
115
|
+
extends(child, parent, label) {
|
|
116
|
+
return this.relation(parent, child, {
|
|
117
|
+
type1: 'extension',
|
|
118
|
+
type2: 'none',
|
|
119
|
+
lineType: 'solid',
|
|
120
|
+
label,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Add an implementation relation (A implements B)
|
|
125
|
+
*/
|
|
126
|
+
implements(implementer, interface_, label) {
|
|
127
|
+
return this.relation(interface_, implementer, {
|
|
128
|
+
type1: 'extension',
|
|
129
|
+
type2: 'none',
|
|
130
|
+
lineType: 'dotted',
|
|
131
|
+
label,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Add a composition relation (A has B, strong ownership)
|
|
136
|
+
*/
|
|
137
|
+
composition(owner, owned, label) {
|
|
138
|
+
return this.relation(owner, owned, {
|
|
139
|
+
type1: 'composition',
|
|
140
|
+
type2: 'none',
|
|
141
|
+
lineType: 'solid',
|
|
142
|
+
label,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Add an aggregation relation (A has B, weak ownership)
|
|
147
|
+
*/
|
|
148
|
+
aggregation(owner, owned, label) {
|
|
149
|
+
return this.relation(owner, owned, {
|
|
150
|
+
type1: 'aggregation',
|
|
151
|
+
type2: 'none',
|
|
152
|
+
lineType: 'solid',
|
|
153
|
+
label,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Add a dependency relation (A depends on B)
|
|
158
|
+
*/
|
|
159
|
+
dependency(dependent, dependency, label) {
|
|
160
|
+
return this.relation(dependent, dependency, {
|
|
161
|
+
type1: 'none',
|
|
162
|
+
type2: 'dependency',
|
|
163
|
+
lineType: 'dotted',
|
|
164
|
+
label,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Add a namespace
|
|
169
|
+
*/
|
|
170
|
+
namespace(name, builderFn) {
|
|
171
|
+
const ns = {
|
|
172
|
+
name,
|
|
173
|
+
classes: [],
|
|
174
|
+
};
|
|
175
|
+
this.ast.namespaces.set(name, ns);
|
|
176
|
+
// Set current namespace context
|
|
177
|
+
this.currentNamespace = name;
|
|
178
|
+
builderFn(this);
|
|
179
|
+
this.currentNamespace = null;
|
|
180
|
+
return this;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Add a note
|
|
184
|
+
*/
|
|
185
|
+
note(text, forClass) {
|
|
186
|
+
const note = {
|
|
187
|
+
text,
|
|
188
|
+
forClass,
|
|
189
|
+
};
|
|
190
|
+
this.ast.notes.push(note);
|
|
191
|
+
return this;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Define a CSS class style
|
|
195
|
+
*/
|
|
196
|
+
classDef(name, styles) {
|
|
197
|
+
const styleDef = {
|
|
198
|
+
name,
|
|
199
|
+
styles,
|
|
200
|
+
};
|
|
201
|
+
this.ast.classDefs.set(name, styleDef);
|
|
202
|
+
return this;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Apply a CSS class to a class
|
|
206
|
+
*/
|
|
207
|
+
cssClass(classId, cssClassName) {
|
|
208
|
+
const classDef = this.ast.classes.get(classId);
|
|
209
|
+
if (classDef) {
|
|
210
|
+
classDef.cssClasses.push(cssClassName);
|
|
211
|
+
}
|
|
212
|
+
return this;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Add a link to a class
|
|
216
|
+
*/
|
|
217
|
+
link(classId, url, target) {
|
|
218
|
+
const classDef = this.ast.classes.get(classId);
|
|
219
|
+
if (classDef) {
|
|
220
|
+
classDef.link = url;
|
|
221
|
+
classDef.linkTarget = target;
|
|
222
|
+
}
|
|
223
|
+
return this;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Add a callback to a class
|
|
227
|
+
*/
|
|
228
|
+
callback(classId, callback, args) {
|
|
229
|
+
const classDef = this.ast.classes.get(classId);
|
|
230
|
+
if (classDef) {
|
|
231
|
+
classDef.callback = callback;
|
|
232
|
+
classDef.callbackArgs = args;
|
|
233
|
+
}
|
|
234
|
+
return this;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Add a tooltip to a class
|
|
238
|
+
*/
|
|
239
|
+
tooltip(classId, tooltip) {
|
|
240
|
+
const classDef = this.ast.classes.get(classId);
|
|
241
|
+
if (classDef) {
|
|
242
|
+
classDef.tooltip = tooltip;
|
|
243
|
+
}
|
|
244
|
+
return this;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Add an annotation to a class (e.g., <<interface>>, <<abstract>>)
|
|
248
|
+
*/
|
|
249
|
+
annotate(classId, annotation) {
|
|
250
|
+
const classDef = this.ast.classes.get(classId);
|
|
251
|
+
if (classDef) {
|
|
252
|
+
classDef.annotations.push(annotation);
|
|
253
|
+
}
|
|
254
|
+
return this;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Set the accessibility title
|
|
258
|
+
*/
|
|
259
|
+
accTitle(title) {
|
|
260
|
+
this.ast.accTitle = title;
|
|
261
|
+
return this;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Set the accessibility description
|
|
265
|
+
*/
|
|
266
|
+
accDescription(description) {
|
|
267
|
+
this.ast.accDescription = description;
|
|
268
|
+
return this;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Validate the AST
|
|
272
|
+
*/
|
|
273
|
+
validate() {
|
|
274
|
+
const errors = [];
|
|
275
|
+
// Check that all relation classes exist
|
|
276
|
+
for (const relation of this.ast.relations) {
|
|
277
|
+
if (!this.ast.classes.has(relation.id1)) {
|
|
278
|
+
errors.push(`Relation references non-existent class '${relation.id1}'`);
|
|
279
|
+
}
|
|
280
|
+
if (!this.ast.classes.has(relation.id2)) {
|
|
281
|
+
errors.push(`Relation references non-existent class '${relation.id2}'`);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
// Check that all namespace classes exist
|
|
285
|
+
for (const [nsName, ns] of this.ast.namespaces) {
|
|
286
|
+
for (const classId of ns.classes) {
|
|
287
|
+
if (!this.ast.classes.has(classId)) {
|
|
288
|
+
errors.push(`Namespace '${nsName}' references non-existent class '${classId}'`);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
// Check that all note references exist
|
|
293
|
+
for (const note of this.ast.notes) {
|
|
294
|
+
if (note.forClass && !this.ast.classes.has(note.forClass)) {
|
|
295
|
+
errors.push(`Note references non-existent class '${note.forClass}'`);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
if (errors.length > 0) {
|
|
299
|
+
throw new ClassDiagramValidationError(errors.join('\n'));
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Build and return the ClassDiagramAST
|
|
304
|
+
*/
|
|
305
|
+
build(options) {
|
|
306
|
+
const shouldValidate = options?.validate !== false;
|
|
307
|
+
if (shouldValidate) {
|
|
308
|
+
this.validate();
|
|
309
|
+
}
|
|
310
|
+
return this.ast;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Create a new ClassDiagramBuilder
|
|
315
|
+
*/
|
|
316
|
+
export function classDiagram(direction = 'TB') {
|
|
317
|
+
return new ClassDiagramBuilder(direction);
|
|
318
|
+
}
|