ts-graphviz 1.1.0 → 1.2.1

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.ja.md CHANGED
@@ -101,6 +101,8 @@ const dot = toDot(G);
101
101
  <details>
102
102
  <summary>高度な使い方</summary>
103
103
 
104
+ ##### カスタム・クラス 🤖
105
+
104
106
  クラスを継承することで独自の実装を加えることもできます。
105
107
 
106
108
  ```typescript
@@ -114,9 +116,9 @@ class MyCustomDigraph extends Digraph {
114
116
  }
115
117
  }
116
118
  class MyCustomNode extends Node {
117
- constructor(id: number) {
118
- super(`node${id}`, {
119
- [_.label]: `This is Custom Node ${id}`
119
+ constructor(id: string) {
120
+ super(`node_${id}`, {
121
+ [_.label]: `This is Custom Node ${id}`,
120
122
  });
121
123
  }
122
124
  }
@@ -124,33 +126,92 @@ class MyCustomNode extends Node {
124
126
  class MyCustomEdge extends Edge {
125
127
  constructor(targets: EdgeTargetTuple) {
126
128
  super(targets, {
127
- [_.label]: 'This is Custom Edge'
129
+ [_.label]: 'This is Custom Edge',
128
130
  });
129
131
  }
130
132
  }
131
133
 
132
134
  const digraph = new MyCustomDigraph();
133
- const node1 = new MyCustomNode(1);
134
- const node2 = new MyCustomNode(2);
135
+ const node1 = new MyCustomNode('A');
136
+ const node2 = new MyCustomNode('B');
135
137
  const edge = new MyCustomEdge([node1, node2]);
136
138
  digraph.addNode(node1);
137
139
  digraph.addNode(node2);
138
140
  digraph.addEdge(edge);
139
- const dot = toDot(g);
141
+ const dot = toDot(digraph);
140
142
  // digraph "G" {
141
143
  // label = "This is Custom Digraph";
142
- // "node1" [
143
- // label = "This is Custom Node 1",
144
+ // "node_A" [
145
+ // label = "This is Custom Node A";
144
146
  // ];
145
- // "node2" [
146
- // label = "This is Custom Node 2",
147
+ // "node_B" [
148
+ // label = "This is Custom Node B";
147
149
  // ];
148
- // "node1" -> "node2" [
149
- // label = "This is Custom Edge",
150
+ // "node_A" -> "node_B" [
151
+ // label = "This is Custom Edge";
150
152
  // ];
151
153
  // }
152
154
  ```
153
155
 
156
+ ##### Models Context API ( `with` メソッド) 🧅
157
+
158
+ あなたは _Models Context API_ をつかうことで、Graphの内部で生成されるオブジェクトもカスタムクラスにすることができます。
159
+
160
+
161
+ `GraphBaseModel` の実装である `Digraph`, `Graph`, `Subgraph` が持つ `with` メソッドは、 カスタムモデルを事前定義するために提供されています。
162
+
163
+ ```typescript
164
+ const g = new Digraph();
165
+ g.with({
166
+ Node: MyCustomNode,
167
+ Edge: MyCustomEdge,
168
+ });
169
+ const a = g.createNode('A'); // MyCustomNode
170
+ const b = g.createNode('B'); // MyCustomNode
171
+ g.createEdge([a, b]); // MyCustomEdge
172
+ const dot = toDot(g);
173
+ // digraph {
174
+ // "node_A" [
175
+ // label = "This is Custom Node A";
176
+ // ];
177
+ // "node_B" [
178
+ // label = "This is Custom Node B";
179
+ // ];
180
+ // "node_A" -> "node_B" [
181
+ // label = "This is Custom Edge";
182
+ // ];
183
+ // }
184
+ ```
185
+
186
+ ##### `fromDot` 関数 ⏪
187
+
188
+ > この関数のステータスは ![beta](https://img.shields.io/badge/-beta-orange) です。
189
+
190
+ このライブラリを使用するメインシナリオは `toDot` 関数を使用することにありますが、逆方向の変換もサポートしています。
191
+
192
+ **DOT** を **Model** に変換により、コードの一部をDOT言語で記述することができます。
193
+
194
+
195
+ ```typescript
196
+ const G = fromDot(
197
+ `digraph {
198
+ node_A [
199
+ label = "This is a Label of Node A";
200
+ ];
201
+ }`,
202
+ );
203
+
204
+ G.edge(['node_A', 'node_B']);
205
+
206
+ const dot = toDot(G)
207
+ // digraph {
208
+ // "node_A" [
209
+ // label = "This is a Label of Node A";
210
+ // ];
211
+ // "node_A" -> "node_B";
212
+ // }
213
+ ```
214
+
154
215
  </details>
155
216
 
156
217
 
@@ -219,6 +280,48 @@ const dot = toDot(G);
219
280
  > // }
220
281
  > ```
221
282
 
283
+
284
+ <details>
285
+ <summary>高度な使い方</summary>
286
+
287
+ ##### Models Context API ( `withContext` 関数 ) 💈
288
+
289
+
290
+ `withContext` 関数は、 _Model Factory_ 関数を返します。
291
+
292
+ この _Model Factory_ は、 `Digraph` や `Graph` など、 `RootGraphModel` をカスタムクラスに置き換える手段を提供します。
293
+
294
+ これのAPIにより、宣言的APIとカスタムクラスを統合する手段を提供します。
295
+
296
+ ```typescript
297
+ const { digraph } = withContext({
298
+ Digraph: MyCustomDigraph,
299
+ Node: MyCustomNode,
300
+ Edge: MyCustomEdge,
301
+ });
302
+
303
+ const G = digraph((g) => {
304
+ const a = g.node('A'); // MyCustomNode
305
+ const b = g.node('B'); // MyCustomNode
306
+ g.edge([a, b]); // MyCustomEdge
307
+ });
308
+ const dot = toDot(g);
309
+ // digraph "G" {
310
+ // label = "This is Custom Digraph";
311
+ // "node_A" [
312
+ // label = "This is Custom Node A";
313
+ // ];
314
+ // "node_B" [
315
+ // label = "This is Custom Node B";
316
+ // ];
317
+ // "node_A" -> "node_B" [
318
+ // label = "This is Custom Edge";
319
+ // ];
320
+ // }
321
+ ```
322
+
323
+ </details>
324
+
222
325
  ### `ts-graphviz/ast` モジュール 🔢
223
326
 
224
327
  > このパッケージのステータスは ![beta](https://img.shields.io/badge/-beta-orange) です。
@@ -230,10 +333,11 @@ const dot = toDot(G);
230
333
  状態遷移図で記載している通り、下記の関数を提供しています。
231
334
 
232
335
  - **Model** から **AST** に変換する `fromModel` 関数
336
+ - **AST** から **Model** に変換する `toModel` 関数
233
337
  - **AST** から **DOT** に変換する `stringify` 関数
234
338
  - **DOT** から **AST** に変換する `parse` 関数
235
339
 
236
- > **Note** 上記の図からわかるように、`ts-graphviz` パッケージで提供している `toDot` 関数は、 `fromModel` と `stringify` の合成関数です。
340
+ > **Note** 上記の図からわかるように、`ts-graphviz` パッケージで提供している `toDot` 関数は、 `fromModel` と `stringify` の合成関数です。また、`fromDot` 関数は、 `parse` と `toModel` の合成関数です。
237
341
 
238
342
  詳しい利用方法は整備中です。
239
343
  TypeScriptの型定義を参考にしてください。
package/README.md CHANGED
@@ -1,3 +1,4 @@
1
+ [![Codacy Badge](https://api.codacy.com/project/badge/Grade/d6485f9858ed4b3e8ef76611a2896bc4)](https://app.codacy.com/gh/ts-graphviz/ts-graphviz?utm_source=github.com&utm_medium=referral&utm_content=ts-graphviz/ts-graphviz&utm_campaign=Badge_Grade_Settings)
1
2
  [![GitHub Action](https://github.com/kamiazya/ts-graphviz/workflows/NodeCI/badge.svg)](https://github.com/kamiazya/ts-graphviz/actions?workflow=NodeCI)
2
3
  [![npm version](https://badge.fury.io/js/ts-graphviz.svg)](https://badge.fury.io/js/ts-graphviz)
3
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
@@ -101,6 +102,8 @@ const dot = toDot(G);
101
102
  <details>
102
103
  <summary>Advanced Usage</summary>
103
104
 
105
+ ##### Custom Class 🤖
106
+
104
107
  You can also add your own implementation by inheriting from the class.
105
108
 
106
109
  ```typescript
@@ -114,9 +117,9 @@ class MyCustomDigraph extends Digraph {
114
117
  }
115
118
  }
116
119
  class MyCustomNode extends Node {
117
- constructor(id: number) {
118
- super(`node${id}`, {
119
- [_.label]: `This is Custom Node ${id}`
120
+ constructor(id: string) {
121
+ super(`node_${id}`, {
122
+ [_.label]: `This is Custom Node ${id}`,
120
123
  });
121
124
  }
122
125
  }
@@ -124,30 +127,88 @@ class MyCustomNode extends Node {
124
127
  class MyCustomEdge extends Edge {
125
128
  constructor(targets: EdgeTargetTuple) {
126
129
  super(targets, {
127
- [_.label]: 'This is Custom Edge'
130
+ [_.label]: 'This is Custom Edge',
128
131
  });
129
132
  }
130
133
  }
131
134
 
132
135
  const digraph = new MyCustomDigraph();
133
- const node1 = new MyCustomNode(1);
134
- const node2 = new MyCustomNode(2);
136
+ const node1 = new MyCustomNode('A');
137
+ const node2 = new MyCustomNode('B');
135
138
  const edge = new MyCustomEdge([node1, node2]);
136
139
  digraph.addNode(node1);
137
140
  digraph.addNode(node2);
138
141
  digraph.addEdge(edge);
139
- const dot = toDot(g);
142
+ const dot = toDot(digraph);
140
143
  // digraph "G" {
141
144
  // label = "This is Custom Digraph";
142
- // "node1" [
143
- // label = "This is Custom Node 1",
145
+ // "node_A" [
146
+ // label = "This is Custom Node A";
147
+ // ];
148
+ // "node_B" [
149
+ // label = "This is Custom Node B";
150
+ // ];
151
+ // "node_A" -> "node_B" [
152
+ // label = "This is Custom Edge";
153
+ // ];
154
+ // }
155
+ ```
156
+
157
+ ##### Models Context API ( `with` method) 🧅
158
+
159
+ You can also use the _Models Context API_ to create custom classes for objects generated inside of Graph.
160
+
161
+
162
+ The `with` methods of `Digraph`, `Graph`, and `Subgraph`, which are implementations of `GraphBaseModel`, are provided to predefine custom models.
163
+
164
+ ```typescript
165
+ const g = new Digraph();
166
+ g.with({
167
+ Node: MyCustomNode,
168
+ Edge: MyCustomEdge,
169
+ });
170
+ const a = g.createNode('A'); // MyCustomNode
171
+ const b = g.createNode('B'); // MyCustomNode
172
+ g.createEdge([a, b]); // MyCustomEdge
173
+ const dot = toDot(g);
174
+ // digraph {
175
+ // "node_A" [
176
+ // label = "This is Custom Node A";
177
+ // ];
178
+ // "node_B" [
179
+ // label = "This is Custom Node B";
144
180
  // ];
145
- // "node2" [
146
- // label = "This is Custom Node 2",
181
+ // "node_A" -> "node_B" [
182
+ // label = "This is Custom Edge";
147
183
  // ];
148
- // "node1" -> "node2" [
149
- // label = "This is Custom Edge",
184
+ // }
185
+ ```
186
+
187
+ ##### `fromDot` function ⏪
188
+
189
+ > The status of this function is ! [beta](https://img.shields.io/badge/-beta-orange).
190
+
191
+ The main scenario for using this library is to use the `toDot` function, but it also supports conversions in the reverse direction.
192
+
193
+ By converting **DOT** to **Model**, a portion of the code can be written in the DOT language.
194
+
195
+ ```typescript
196
+ const G = fromDot(
197
+ `digraph {
198
+ node_A [
199
+ label = "This is a Label of Node A";
200
+ ];
201
+ }`,
202
+ );
203
+
204
+ G.edge(['node_A', 'node_B']);
205
+
206
+ const dot = toDot(G)
207
+ // digraph {
208
+ // "node_A" [
209
+ // label = "This is a Label of Node A";
150
210
  // ];
211
+ // "node_A" -> "node_B";
151
212
  // }
152
213
  ```
153
214
 
@@ -219,6 +280,48 @@ const dot = toDot(G);
219
280
  > // }
220
281
  > ```
221
282
 
283
+
284
+ <details>
285
+ <summary>Advanced Usage</summary>
286
+
287
+ ##### Models Context API ( `withContext` function ) 💈
288
+
289
+
290
+ The `withContext` function returns a _Model Factory_ function.
291
+
292
+ This _Model Factory_ provides a means to replace `RootGraphModel` with a custom class, such as `Digraph` or `Graph`.
293
+
294
+ This API provides a way to integrate declarative APIs and custom classes.
295
+
296
+ ```typescript
297
+ const { digraph } = withContext({
298
+ Digraph: MyCustomDigraph,
299
+ Node: MyCustomNode,
300
+ Edge: MyCustomEdge,
301
+ });
302
+
303
+ const G = digraph((g) => {
304
+ const a = g.node('A'); // MyCustomNode
305
+ const b = g.node('B'); // MyCustomNode
306
+ g.edge([a, b]); // MyCustomEdge
307
+ });
308
+ const dot = toDot(g);
309
+ // digraph "G" {
310
+ // label = "This is Custom Digraph";
311
+ // "node_A" [
312
+ // label = "This is Custom Node A";
313
+ // ];
314
+ // "node_B" [
315
+ // label = "This is Custom Node B";
316
+ // ];
317
+ // "node_A" -> "node_B" [
318
+ // label = "This is Custom Edge";
319
+ // ];
320
+ // }
321
+ ```
322
+
323
+ </details>
324
+
222
325
  ### `ts-graphviz/ast` Module 🔢
223
326
 
224
327
  > This module status is ![beta](https://img.shields.io/badge/-beta-orange).
@@ -230,10 +333,11 @@ An API is provided to handle ASTs for advanced use.
230
333
  The following functions are provided as described in the state transition diagram.
231
334
 
232
335
  - The `fromModel` function converts **Model** to **AST**.
336
+ - The `toModel` function converts **AST** to **Model**.
233
337
  - The `stringify` function converts **AST** to **DOT**.
234
338
  - The `parse` function to convert from **DOT** to **AST**.
235
339
 
236
- > **Note** As you can see from the above figure, the `toDot` function provided by the `ts-graphviz` package is a composite function of `fromModel` and `stringify`.
340
+ > **Note** As you can see from the above figure, the `toDot` function provided by the `ts-graphviz` package is a composite of `fromModel` and `stringify`. The `fromDot` function is a composite of `parse` and `toModel`.
237
341
 
238
342
  Detailed usage is TODO.
239
343
  Please refer to the TypeScript type definition.
package/lib/ast/index.cjs CHANGED
@@ -59,7 +59,7 @@ const endOfLine = (eol) => {
59
59
  }
60
60
  };
61
61
 
62
- const AttributeListPrintPlugin$1 = {
62
+ const AttributeListPrintPlugin = {
63
63
  match(ast) {
64
64
  return ast.type === 'AttributeList';
65
65
  },
@@ -253,8 +253,8 @@ const SubgraphPrintPlugin = {
253
253
  },
254
254
  };
255
255
 
256
- const defaultPlugins$1 = [
257
- AttributeListPrintPlugin$1,
256
+ const defaultPlugins$2 = [
257
+ AttributeListPrintPlugin,
258
258
  AttributePrintPlugin,
259
259
  CommentPrintPlugin,
260
260
  DotPrintPlugin,
@@ -273,7 +273,7 @@ const defaultPlugins$1 = [
273
273
  class Printer {
274
274
  options;
275
275
  /** @internal */
276
- #plugins = [...defaultPlugins$1];
276
+ #plugins = [...defaultPlugins$2];
277
277
  constructor(options = {}) {
278
278
  this.options = options;
279
279
  }
@@ -5342,7 +5342,7 @@ function convertClusterChildren(context, model) {
5342
5342
  );
5343
5343
  }
5344
5344
 
5345
- const AttributeListPrintPlugin = {
5345
+ const AttributeListPlugin = {
5346
5346
  match(model) {
5347
5347
  return model.$$type === 'AttributeList';
5348
5348
  },
@@ -5357,7 +5357,7 @@ const AttributeListPrintPlugin = {
5357
5357
  },
5358
5358
  };
5359
5359
 
5360
- const EdgeConvertPlugin = {
5360
+ const EdgePlugin$1 = {
5361
5361
  match(model) {
5362
5362
  return model.$$type === 'Edge';
5363
5363
  },
@@ -5427,7 +5427,7 @@ const EdgeConvertPlugin = {
5427
5427
  },
5428
5428
  };
5429
5429
 
5430
- const GraphConvertPlugin = {
5430
+ const GraphPlugin$1 = {
5431
5431
  match(model) {
5432
5432
  return model.$$type === 'Graph';
5433
5433
  },
@@ -5456,7 +5456,7 @@ const GraphConvertPlugin = {
5456
5456
  },
5457
5457
  };
5458
5458
 
5459
- const NodeConvertPlugin = {
5459
+ const NodePlugin$1 = {
5460
5460
  match(model) {
5461
5461
  return model.$$type === 'Node';
5462
5462
  },
@@ -5481,7 +5481,7 @@ const NodeConvertPlugin = {
5481
5481
  },
5482
5482
  };
5483
5483
 
5484
- const SubgraphConvertPlugin = {
5484
+ const SubgraphPlugin$1 = {
5485
5485
  match(model) {
5486
5486
  return model.$$type === 'Subgraph';
5487
5487
  },
@@ -5505,13 +5505,7 @@ const SubgraphConvertPlugin = {
5505
5505
  },
5506
5506
  };
5507
5507
 
5508
- const defaultPlugins = [
5509
- AttributeListPrintPlugin,
5510
- EdgeConvertPlugin,
5511
- NodeConvertPlugin,
5512
- GraphConvertPlugin,
5513
- SubgraphConvertPlugin,
5514
- ];
5508
+ const defaultPlugins$1 = [AttributeListPlugin, EdgePlugin$1, NodePlugin$1, GraphPlugin$1, SubgraphPlugin$1];
5515
5509
 
5516
5510
  /**
5517
5511
  * @group Convert Model to AST
@@ -5519,7 +5513,7 @@ const defaultPlugins = [
5519
5513
  class FromModelConverter {
5520
5514
  options;
5521
5515
  /** @hidden */
5522
- #plugins = [...defaultPlugins];
5516
+ #plugins = [...defaultPlugins$1];
5523
5517
  constructor(options = {}) {
5524
5518
  this.options = options;
5525
5519
  }
@@ -5530,7 +5524,6 @@ class FromModelConverter {
5530
5524
  commentKind,
5531
5525
  convert(m) {
5532
5526
  for (const plugin of plugins) {
5533
- /* */
5534
5527
  if (plugin.match(m)) {
5535
5528
  return plugin.convert(context, m);
5536
5529
  }
@@ -5549,11 +5542,226 @@ function fromModel(model, options) {
5549
5542
  return new FromModelConverter(options).convert(model);
5550
5543
  }
5551
5544
 
5545
+ class CommentHolder {
5546
+ comment = null;
5547
+ set(comment) {
5548
+ this.comment = comment;
5549
+ }
5550
+ reset() {
5551
+ this.comment = null;
5552
+ }
5553
+ apply(model, location) {
5554
+ if (location && this.comment?.location) {
5555
+ if (this.comment?.kind === 'Block') {
5556
+ if (this.comment.location.end.line === location.start.line - 1) {
5557
+ model.comment = this.comment.value;
5558
+ }
5559
+ } else {
5560
+ if (this.comment.location.end.line === location.start.line) {
5561
+ model.comment = this.comment.value;
5562
+ }
5563
+ }
5564
+ } else {
5565
+ model.comment = this.comment?.value;
5566
+ }
5567
+ this.reset();
5568
+ }
5569
+ }
5570
+
5571
+ const DotPlugin = {
5572
+ match(ast) {
5573
+ return ast.type === 'Dot';
5574
+ },
5575
+ convert(context, ast) {
5576
+ const commentHolder = new CommentHolder();
5577
+ for (const stmt of ast.children) {
5578
+ switch (stmt.type) {
5579
+ case 'Comment':
5580
+ commentHolder.set(stmt);
5581
+ break;
5582
+ case 'Graph':
5583
+ const graph = context.convert(stmt);
5584
+ commentHolder.apply(graph, stmt.location);
5585
+ return graph;
5586
+ }
5587
+ }
5588
+ throw Error();
5589
+ },
5590
+ };
5591
+
5592
+ function convertToEdgeTargetTuple(edge) {
5593
+ return edge.targets.map((t) => {
5594
+ switch (t.type) {
5595
+ case 'NodeRef':
5596
+ return { id: t.id.value, port: t.port?.value, compass: t.compass?.value };
5597
+ case 'NodeRefGroup':
5598
+ return t.children.map((t) => ({ id: t.id.value, port: t.port?.value, compass: t.compass?.value }));
5599
+ }
5600
+ });
5601
+ }
5602
+
5603
+ const EdgePlugin = {
5604
+ match(ast) {
5605
+ return ast.type === 'Edge';
5606
+ },
5607
+ convert(context, ast) {
5608
+ const edge = new context.models.Edge(
5609
+ convertToEdgeTargetTuple(ast),
5610
+ ast.children
5611
+ .filter((v) => v.type === 'Attribute')
5612
+ .reduce((prev, curr) => ({ ...prev, [curr.key.value]: curr.value.value }), {}),
5613
+ );
5614
+ return edge;
5615
+ },
5616
+ };
5617
+
5618
+ function applyStatements(graph, statements) {
5619
+ const commentHolder = new CommentHolder();
5620
+ for (const stmt of statements) {
5621
+ switch (stmt.type) {
5622
+ case 'Subgraph':
5623
+ const subgraph = stmt.id ? graph.subgraph(stmt.id.value) : graph.subgraph();
5624
+ applyStatements(subgraph, stmt.children);
5625
+ commentHolder.apply(subgraph, stmt.location);
5626
+ break;
5627
+ case 'Attribute':
5628
+ graph.set(stmt.key.value, stmt.value.value);
5629
+ commentHolder.reset();
5630
+ break;
5631
+ case 'Node':
5632
+ commentHolder.apply(
5633
+ graph.node(
5634
+ stmt.id.value,
5635
+ stmt.children
5636
+ .filter((v) => v.type === 'Attribute')
5637
+ .reduce((prev, curr) => ({ ...prev, [curr.key.value]: curr.value.value }), {}),
5638
+ ),
5639
+ stmt.location,
5640
+ );
5641
+ break;
5642
+ case 'Edge':
5643
+ commentHolder.apply(
5644
+ graph.edge(
5645
+ convertToEdgeTargetTuple(stmt),
5646
+ stmt.children
5647
+ .filter((v) => v.type === 'Attribute')
5648
+ .reduce((prev, curr) => ({ ...prev, [curr.key.value]: curr.value.value }), {}),
5649
+ ),
5650
+ stmt.location,
5651
+ );
5652
+ break;
5653
+ case 'AttributeList':
5654
+ const attrs = stmt.children
5655
+ .filter((v) => v.type === 'Attribute')
5656
+ .reduce((prev, curr) => ({ ...prev, [curr.key.value]: curr.value.value }), {});
5657
+ switch (stmt.kind) {
5658
+ case 'Edge':
5659
+ graph.edge(attrs);
5660
+ break;
5661
+ case 'Node':
5662
+ graph.node(attrs);
5663
+ break;
5664
+ case 'Graph':
5665
+ graph.graph(attrs);
5666
+ break;
5667
+ }
5668
+ commentHolder.reset();
5669
+ break;
5670
+ case 'Comment':
5671
+ commentHolder.set(stmt);
5672
+ }
5673
+ }
5674
+ }
5675
+
5676
+ const GraphPlugin = {
5677
+ match(ast) {
5678
+ return ast.type === 'Graph';
5679
+ },
5680
+ convert(context, ast) {
5681
+ const G = ast.directed ? context.models.Digraph : context.models.Graph;
5682
+ const graph = new G(ast.id?.value, ast.strict);
5683
+ applyStatements(graph, ast.children);
5684
+ return graph;
5685
+ },
5686
+ };
5687
+
5688
+ const SubgraphPlugin = {
5689
+ match(ast) {
5690
+ return ast.type === 'Subgraph';
5691
+ },
5692
+ convert(context, ast) {
5693
+ const subgraph = new context.models.Subgraph(ast.id?.value);
5694
+ applyStatements(subgraph, ast.children);
5695
+ return subgraph;
5696
+ },
5697
+ };
5698
+
5699
+ const NodePlugin = {
5700
+ match(ast) {
5701
+ return ast.type === 'Node';
5702
+ },
5703
+ convert(context, ast) {
5704
+ const node = new context.models.Node(
5705
+ ast.id.value,
5706
+ ast.children
5707
+ .filter((v) => v.type === 'Attribute')
5708
+ .reduce((prev, curr) => ({ ...prev, [curr.key.value]: curr.value.value }), {}),
5709
+ );
5710
+ return node;
5711
+ },
5712
+ };
5713
+
5714
+ const defaultPlugins = [NodePlugin, EdgePlugin, SubgraphPlugin, GraphPlugin, DotPlugin];
5715
+
5716
+ /**
5717
+ * @group Convert AST to Model
5718
+ * @alpha
5719
+ */
5720
+ class ToModelConverter {
5721
+ options;
5722
+ /** @hidden */
5723
+ plugins = [...defaultPlugins];
5724
+ constructor(options = {}) {
5725
+ this.options = options;
5726
+ }
5727
+ /**
5728
+ * Convert AST to Model.
5729
+ *
5730
+ * @param ast AST node.
5731
+ * @alpha
5732
+ */
5733
+ convert(ast) {
5734
+ const plugins = [...this.plugins];
5735
+ const context = {
5736
+ models: common.createModelsContext(this.options.models ?? {}),
5737
+ convert(m) {
5738
+ for (const plugin of plugins) {
5739
+ if (plugin.match(m)) {
5740
+ return plugin.convert(context, m);
5741
+ }
5742
+ }
5743
+ throw Error();
5744
+ },
5745
+ };
5746
+ return context.convert(ast);
5747
+ }
5748
+ }
5749
+
5750
+ /**
5751
+ * @group Convert AST to Model
5752
+ * @beta
5753
+ */
5754
+ function toModel(ast, options) {
5755
+ return new ToModelConverter(options).convert(ast);
5756
+ }
5757
+
5552
5758
  exports.Builder = Builder;
5553
5759
  exports.FromModelConverter = FromModelConverter;
5554
5760
  exports.Printer = Printer;
5555
5761
  exports.SyntaxError = SyntaxError;
5762
+ exports.ToModelConverter = ToModelConverter;
5556
5763
  exports.createElement = createElement;
5557
5764
  exports.fromModel = fromModel;
5558
5765
  exports.parse = parse;
5559
5766
  exports.stringify = stringify;
5767
+ exports.toModel = toModel;