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.
Files changed (68) hide show
  1. package/README.md +120 -3
  2. package/dist/builder/class-builder.d.ts +148 -0
  3. package/dist/builder/class-builder.d.ts.map +1 -0
  4. package/dist/builder/class-builder.js +318 -0
  5. package/dist/builder/flowchart-builder.d.ts +123 -0
  6. package/dist/builder/flowchart-builder.d.ts.map +1 -0
  7. package/dist/builder/flowchart-builder.js +257 -0
  8. package/dist/builder/index.d.ts +10 -0
  9. package/dist/builder/index.d.ts.map +1 -0
  10. package/dist/builder/index.js +9 -0
  11. package/dist/builder/sequence-builder.d.ts +174 -0
  12. package/dist/builder/sequence-builder.d.ts.map +1 -0
  13. package/dist/builder/sequence-builder.js +443 -0
  14. package/dist/builder/state-builder.d.ts +156 -0
  15. package/dist/builder/state-builder.d.ts.map +1 -0
  16. package/dist/builder/state-builder.js +314 -0
  17. package/dist/index.d.ts +4 -3
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +6 -4
  20. package/dist/parser/class-parser.d.ts +1 -1
  21. package/dist/parser/class-parser.d.ts.map +1 -1
  22. package/dist/parser/class-parser.js +36 -32
  23. package/dist/parser/flowchart-parser.d.ts +1 -1
  24. package/dist/parser/flowchart-parser.d.ts.map +1 -1
  25. package/dist/parser/flowchart-parser.js +51 -53
  26. package/dist/parser/index.d.ts +4 -4
  27. package/dist/parser/index.d.ts.map +1 -1
  28. package/dist/parser/index.js +13 -13
  29. package/dist/parser/sequence-parser.d.ts +1 -1
  30. package/dist/parser/sequence-parser.d.ts.map +1 -1
  31. package/dist/parser/sequence-parser.js +88 -82
  32. package/dist/parser/state-parser.d.ts +1 -1
  33. package/dist/parser/state-parser.d.ts.map +1 -1
  34. package/dist/parser/state-parser.js +35 -36
  35. package/dist/renderer/class-renderer.d.ts +2 -2
  36. package/dist/renderer/class-renderer.d.ts.map +1 -1
  37. package/dist/renderer/class-renderer.js +27 -32
  38. package/dist/renderer/doc.d.ts.map +1 -1
  39. package/dist/renderer/doc.js +9 -9
  40. package/dist/renderer/flowchart-renderer.d.ts +2 -2
  41. package/dist/renderer/flowchart-renderer.d.ts.map +1 -1
  42. package/dist/renderer/flowchart-renderer.js +60 -76
  43. package/dist/renderer/index.d.ts +4 -4
  44. package/dist/renderer/index.d.ts.map +1 -1
  45. package/dist/renderer/index.js +7 -7
  46. package/dist/renderer/sequence-renderer.d.ts +2 -2
  47. package/dist/renderer/sequence-renderer.d.ts.map +1 -1
  48. package/dist/renderer/sequence-renderer.js +60 -59
  49. package/dist/renderer/state-renderer.d.ts +2 -2
  50. package/dist/renderer/state-renderer.d.ts.map +1 -1
  51. package/dist/renderer/state-renderer.js +19 -21
  52. package/dist/types/class.d.ts +6 -6
  53. package/dist/types/class.js +2 -2
  54. package/dist/types/flowchart.d.ts +8 -8
  55. package/dist/types/flowchart.js +2 -2
  56. package/dist/types/index.d.ts +8 -8
  57. package/dist/types/index.d.ts.map +1 -1
  58. package/dist/types/index.js +7 -7
  59. package/dist/types/render-options.d.ts +1 -1
  60. package/dist/types/render-options.js +4 -4
  61. package/dist/types/sequence.d.ts +20 -20
  62. package/dist/types/sequence.js +1 -1
  63. package/dist/types/state.d.ts +12 -12
  64. package/dist/types/state.js +2 -2
  65. package/dist/utils.d.ts +29 -0
  66. package/dist/utils.d.ts.map +1 -0
  67. package/dist/utils.js +30 -0
  68. 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
- - **Flowchart** (`flowchart`, `graph`)
21
- - **Sequence Diagram** (`sequenceDiagram`)
22
- - **Class Diagram** (`classDiagram`)
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
+ }