ts-graphviz 0.16.0 → 1.0.0-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.
@@ -0,0 +1,444 @@
1
+ import { isNodeRefGroupLike, toNodeRefGroup, toNodeRef, isNodeRefLike } from '#/lib/common';
2
+ import { fromModel, stringify } from '#/lib/ast';
3
+
4
+ const attribute = new Proxy(Object.freeze({}), {
5
+ get: (_, key) => key,
6
+ });
7
+
8
+ /**
9
+ * Classes implemented in the 'ts-graphviz' library are designed to inherit from this class.
10
+ */
11
+ class GraphvizObject {}
12
+ /**
13
+ * Classes implemented in the 'ts-graphviz' library that implement the `toDot` method are designed to inherit from this class.
14
+ */
15
+ class DotObject extends GraphvizObject {}
16
+ /**
17
+ */
18
+ class AttributesBase extends DotObject {
19
+ /** @hidden */
20
+ #attrs = new Map();
21
+ constructor(attributes) {
22
+ super();
23
+ if (attributes !== undefined) {
24
+ this.apply(attributes);
25
+ }
26
+ }
27
+ get values() {
28
+ return Array.from(this.#attrs.entries());
29
+ }
30
+ /** The size of the attribute. */
31
+ get size() {
32
+ return this.#attrs.size;
33
+ }
34
+ /** The size of the attribute. */
35
+ get(key) {
36
+ return this.#attrs.get(key);
37
+ }
38
+ /** Set a value to the attribute. */
39
+ set(key, value) {
40
+ if (value !== null && value !== undefined) {
41
+ this.#attrs.set(key, value);
42
+ }
43
+ }
44
+ delete(key) {
45
+ this.#attrs.delete(key);
46
+ }
47
+ apply(attributes) {
48
+ const entries = Array.isArray(attributes) ? attributes : Object.entries(attributes);
49
+ for (const [key, value] of entries) {
50
+ this.set(key, value);
51
+ }
52
+ }
53
+ clear() {
54
+ this.#attrs.clear();
55
+ }
56
+ }
57
+ /**
58
+ * A set of attribute values for any object.
59
+ */
60
+ class AttributesGroupModel extends AttributesBase {
61
+ /** Comments to include when outputting with toDot. */
62
+ comment;
63
+ }
64
+ /**
65
+ * A set of attribute values for any object.
66
+ */
67
+ class AttributeList extends AttributesBase {
68
+ $$kind;
69
+ get $$type() {
70
+ return 'AttributeList';
71
+ }
72
+ /** Comments to include when outputting with toDot. */
73
+ comment;
74
+ constructor($$kind, attributes) {
75
+ super(attributes);
76
+ this.$$kind = $$kind;
77
+ }
78
+ }
79
+ /**
80
+ * Base class for clusters.
81
+ * @hidden
82
+ */
83
+ class GraphBase extends AttributesBase {
84
+ /** Cluster ID */
85
+ id;
86
+ /** Comments to include when outputting with toDot. */
87
+ comment;
88
+ /**
89
+ * Nodes in the cluster.
90
+ * @hidden
91
+ */
92
+ get nodes() {
93
+ return Array.from(this.#objects.nodes.values());
94
+ }
95
+ /**
96
+ * Edges in the cluster.
97
+ * @hidden
98
+ */
99
+ get edges() {
100
+ return Array.from(this.#objects.edges.values());
101
+ }
102
+ /**
103
+ * Subgraphs in the cluster.
104
+ * @hidden
105
+ */
106
+ get subgraphs() {
107
+ return Array.from(this.#objects.subgraphs.values());
108
+ }
109
+ #objects = {
110
+ nodes: new Map(),
111
+ edges: new Set(),
112
+ subgraphs: new Set(),
113
+ };
114
+ /**
115
+ * Add a Node to the cluster.
116
+ */
117
+ addNode(node) {
118
+ this.#objects.nodes.set(node.id, node);
119
+ }
120
+ /**
121
+ * Add Edge to the cluster.
122
+ */
123
+ addEdge(edge) {
124
+ this.#objects.edges.add(edge);
125
+ }
126
+ /**
127
+ * Add a Subgraph to the cluster.
128
+ */
129
+ addSubgraph(subgraph) {
130
+ this.#objects.subgraphs.add(subgraph);
131
+ }
132
+ /**
133
+ * Check if the Node exists in the cluster.
134
+ */
135
+ existNode(nodeId) {
136
+ return this.#objects.nodes.has(nodeId);
137
+ }
138
+ /**
139
+ * Check if the Edge exists in the cluster.
140
+ */
141
+ existEdge(edge) {
142
+ return this.#objects.edges.has(edge);
143
+ }
144
+ /**
145
+ * Check if the Subgraph exists in the cluster.
146
+ */
147
+ existSubgraph(subgraph) {
148
+ return this.#objects.subgraphs.has(subgraph);
149
+ }
150
+ createSubgraph(...args) {
151
+ const id = args.find((arg) => typeof arg === 'string');
152
+ const attributes = args.find((arg) => typeof arg === 'object');
153
+ const graph = new Subgraph(id, attributes);
154
+ this.#objects.subgraphs.add(graph);
155
+ return graph;
156
+ }
157
+ /**
158
+ * Remove Node from the cluster.
159
+ */
160
+ removeNode(node) {
161
+ this.#objects.nodes.delete(typeof node === 'string' ? node : node.id);
162
+ }
163
+ /**
164
+ * Remove Edge from the cluster.
165
+ */
166
+ removeEdge(edge) {
167
+ this.#objects.edges.delete(edge);
168
+ }
169
+ /**
170
+ * Remove Subgraph from the cluster.
171
+ */
172
+ removeSubgraph(subgraph) {
173
+ this.#objects.subgraphs.delete(subgraph);
174
+ }
175
+ /**
176
+ * Create a Node in the cluster.
177
+ */
178
+ createNode(id, attributes) {
179
+ const node = new Node(id, attributes);
180
+ this.#objects.nodes.set(id, node);
181
+ return node;
182
+ }
183
+ /**
184
+ * Get Subgraph in cluster by specifying id.
185
+ *
186
+ * If there is no Subgraph with the specified id in the cluster, return undefined.
187
+ */
188
+ getSubgraph(id) {
189
+ return Array.from(this.#objects.subgraphs.values()).find((subgraph) => subgraph.id === id);
190
+ }
191
+ /**EdgeAttributesObject
192
+ * Get Node in cluster by specifying id.
193
+ *
194
+ * @description
195
+ * If there is no Node with the specified id in the cluster, return undefined.
196
+ */
197
+ getNode(id) {
198
+ return this.#objects.nodes.get(id);
199
+ }
200
+ /** Create Edge and add it to the cluster. */
201
+ createEdge(targets, attributes) {
202
+ const ts = targets.map((t) => (isNodeRefGroupLike(t) ? toNodeRefGroup(t) : toNodeRef(t)));
203
+ const edge = new Edge(ts, attributes);
204
+ this.#objects.edges.add(edge);
205
+ return edge;
206
+ }
207
+ subgraph(...args) {
208
+ const id = args.find((arg) => typeof arg === 'string');
209
+ const attributes = args.find((arg) => typeof arg === 'object' && arg !== null);
210
+ const callback = args.find((arg) => typeof arg === 'function');
211
+ const subgraph = id ? this.getSubgraph(id) ?? this.createSubgraph(id) : this.createSubgraph();
212
+ if (attributes !== undefined) {
213
+ subgraph.apply(attributes);
214
+ }
215
+ if (callback !== undefined) {
216
+ callback(subgraph);
217
+ }
218
+ return subgraph;
219
+ }
220
+ node(firstArg, ...args) {
221
+ if (typeof firstArg === 'string') {
222
+ const id = firstArg;
223
+ const attributes = args.find((arg) => typeof arg === 'object' && arg !== null);
224
+ const callback = args.find((arg) => typeof arg === 'function');
225
+ const node = this.getNode(id) ?? this.createNode(id);
226
+ if (attributes !== undefined) {
227
+ node.attributes.apply(attributes);
228
+ }
229
+ if (callback !== undefined) {
230
+ callback(node);
231
+ }
232
+ return node;
233
+ }
234
+ if (typeof firstArg === 'object' && firstArg !== null) {
235
+ this.attributes.node.apply(firstArg);
236
+ }
237
+ }
238
+ edge(firstArg, ...args) {
239
+ if (Array.isArray(firstArg)) {
240
+ const targets = firstArg;
241
+ const attributes = args.find((arg) => typeof arg === 'object');
242
+ const callback = args.find((arg) => typeof arg === 'function');
243
+ const edge = this.createEdge(targets, attributes);
244
+ if (callback !== undefined) {
245
+ callback(edge);
246
+ }
247
+ return edge;
248
+ }
249
+ if (typeof firstArg === 'object' && firstArg !== null) {
250
+ this.attributes.edge.apply(firstArg);
251
+ }
252
+ }
253
+ /**
254
+ * Set a common attribute for the clusters in the cluster.
255
+ *
256
+ * ```ts
257
+ * const G = digraph('G', (g) => {
258
+ * g.graph({
259
+ * [attribute.color]: 'red',
260
+ * [attribute.label]: 'my label',
261
+ * });
262
+ * });
263
+ *
264
+ * console.log(toDot(G));
265
+ * // digraph "G" {
266
+ * // graph [
267
+ * // color = "red",
268
+ * // label = "my label",
269
+ * // ];
270
+ * // }
271
+ * ```
272
+ * @param attributes Object of attributes to be adapted to the clusters.
273
+ */
274
+ graph(attributes) {
275
+ this.attributes.graph.apply(attributes);
276
+ }
277
+ }
278
+ /**
279
+ * Subgraph object.
280
+ */
281
+ class Subgraph extends GraphBase {
282
+ get $$type() {
283
+ return 'Subgraph';
284
+ }
285
+ id;
286
+ attributes = Object.freeze({
287
+ graph: new AttributeList('Graph'),
288
+ edge: new AttributeList('Edge'),
289
+ node: new AttributeList('Node'),
290
+ });
291
+ constructor(...args) {
292
+ super();
293
+ this.id = args.find((arg) => typeof arg === 'string');
294
+ const attributes = args.find((arg) => typeof arg === 'object' && arg !== null);
295
+ if (attributes !== undefined) {
296
+ this.apply(attributes);
297
+ }
298
+ }
299
+ /** Determines whether the Subgraph is a SubgraphCluster. */
300
+ isSubgraphCluster() {
301
+ if (typeof this.id === 'string') {
302
+ return this.id.startsWith('cluster');
303
+ }
304
+ return false;
305
+ }
306
+ }
307
+ /**
308
+ * Node object.
309
+ */
310
+ class Node extends DotObject {
311
+ id;
312
+ get $$type() {
313
+ return 'Node';
314
+ }
315
+ /** Comments to include when outputting with toDot. */
316
+ comment;
317
+ attributes;
318
+ constructor(id, attributes) {
319
+ super();
320
+ this.id = id;
321
+ this.attributes = new AttributesGroupModel(attributes);
322
+ }
323
+ /** Returns ForwardRefNode with port and compass specified. */
324
+ port(port) {
325
+ if (typeof port === 'string') {
326
+ return { id: this.id, port };
327
+ }
328
+ return { id: this.id, ...port };
329
+ }
330
+ }
331
+ /**
332
+ */
333
+ class Edge extends DotObject {
334
+ targets;
335
+ get $$type() {
336
+ return 'Edge';
337
+ }
338
+ /** Comments to include when outputting with toDot. */
339
+ comment;
340
+ attributes;
341
+ constructor(targets, attributes) {
342
+ super();
343
+ this.targets = targets;
344
+ if (targets.length < 2 && (isNodeRefLike(targets[0]) && isNodeRefLike(targets[1])) === false) {
345
+ throw Error('The element of Edge target is missing or not satisfied as Edge target.');
346
+ }
347
+ this.attributes = new AttributesGroupModel(attributes);
348
+ }
349
+ }
350
+ /**
351
+ * Base class for RootGraph.
352
+ *
353
+ */
354
+ class RootGraph extends GraphBase {
355
+ get $$type() {
356
+ return 'Graph';
357
+ }
358
+ id;
359
+ /**
360
+ * Strict mode.
361
+ *
362
+ * @description
363
+ * A graph may also be described as strict.
364
+ * This forbids the creation of multi-edges, i.e., there can be at most one edge with a given tail node and head node in the directed case.
365
+ * For undirected graphs, there can be at most one edge connected to the same two nodes.
366
+ * Subsequent edge statements using the same two nodes will identify the edge with the previously defined one and apply any attributes given in the edge statement.
367
+ */
368
+ strict;
369
+ attributes = Object.freeze({
370
+ graph: new AttributeList('Graph'),
371
+ edge: new AttributeList('Edge'),
372
+ node: new AttributeList('Node'),
373
+ });
374
+ constructor(...args) {
375
+ super();
376
+ this.id = args.find((arg) => typeof arg === 'string');
377
+ this.strict = args.find((arg) => typeof arg === 'boolean') ?? false;
378
+ const attributes = args.find((arg) => typeof arg === 'object' && arg !== null);
379
+ if (attributes !== undefined) {
380
+ this.apply(attributes);
381
+ }
382
+ }
383
+ }
384
+ class Graph extends RootGraph {
385
+ get directed() {
386
+ return false;
387
+ }
388
+ }
389
+ class Digraph extends RootGraph {
390
+ get directed() {
391
+ return true;
392
+ }
393
+ }
394
+
395
+ /** @hidden */
396
+ function builder(directed, strictMode) {
397
+ const C = directed ? Digraph : Graph;
398
+ return (...args) => {
399
+ const id = args.find((arg) => typeof arg === 'string');
400
+ const attributes = args.find((arg) => typeof arg === 'object');
401
+ const callback = args.find((arg) => typeof arg === 'function');
402
+ const g = new C(id, strictMode, attributes);
403
+ if (typeof callback === 'function') {
404
+ callback(g);
405
+ }
406
+ return g;
407
+ };
408
+ }
409
+ /** API for creating directional graph objects. */
410
+ const digraph = builder(true, false);
411
+ /** API for creating omnidirectional graph objects. */
412
+ const graph = builder(false, false);
413
+ /** Provides a strict mode API. */
414
+ const strict = Object.freeze({
415
+ /** API for creating directional graph objects in strict mode. */
416
+ digraph: builder(true, true),
417
+ /** API for creating omnidirectional graph objects in strict mode. */
418
+ graph: builder(false, true),
419
+ });
420
+
421
+ function toDot(model, options) {
422
+ const ast = fromModel(model, options?.convert);
423
+ return stringify(ast, options?.print);
424
+ }
425
+
426
+ export {
427
+ AttributeList,
428
+ AttributesBase,
429
+ AttributesGroupModel,
430
+ Digraph,
431
+ DotObject,
432
+ Edge,
433
+ Graph,
434
+ GraphBase,
435
+ GraphvizObject,
436
+ Node,
437
+ RootGraph,
438
+ Subgraph,
439
+ attribute,
440
+ digraph,
441
+ graph,
442
+ strict,
443
+ toDot,
444
+ };
package/lib/index.cjs ADDED
@@ -0,0 +1,25 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var common = require('#/lib/common');
6
+ var core = require('#/lib/core');
7
+
8
+ Object.keys(common).forEach(function (k) {
9
+ if (k !== 'default' && !exports.hasOwnProperty(k))
10
+ Object.defineProperty(exports, k, {
11
+ enumerable: true,
12
+ get: function () {
13
+ return common[k];
14
+ },
15
+ });
16
+ });
17
+ Object.keys(core).forEach(function (k) {
18
+ if (k !== 'default' && !exports.hasOwnProperty(k))
19
+ Object.defineProperty(exports, k, {
20
+ enumerable: true,
21
+ get: function () {
22
+ return core[k];
23
+ },
24
+ });
25
+ });