1 | "use strict";
|
2 |
|
3 | Object.defineProperty(exports, "__esModule", {
|
4 | value: true
|
5 | });
|
6 | exports.mapVisitor = mapVisitor;
|
7 | exports.default = exports.ALL_EDGE_TYPES = void 0;
|
8 |
|
9 | var _assert = _interopRequireDefault(require("assert"));
|
10 |
|
11 | var _utils = require("@parcel/utils");
|
12 |
|
13 | var _nullthrows = _interopRequireDefault(require("nullthrows"));
|
14 |
|
15 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
16 |
|
17 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
18 |
|
19 | const ALL_EDGE_TYPES = '@@all_edge_types';
|
20 | exports.ALL_EDGE_TYPES = ALL_EDGE_TYPES;
|
21 |
|
22 | class Graph {
|
23 | constructor(opts = {})
|
24 | {
|
25 | _defineProperty(this, "nodes", void 0);
|
26 |
|
27 | _defineProperty(this, "inboundEdges", new _utils.DefaultMap(() => new _utils.DefaultMap(() => new Set())));
|
28 |
|
29 | _defineProperty(this, "outboundEdges", new _utils.DefaultMap(() => new _utils.DefaultMap(() => new Set())));
|
30 |
|
31 | _defineProperty(this, "rootNodeId", void 0);
|
32 |
|
33 | this.nodes = opts.nodes || new Map();
|
34 | this.rootNodeId = opts.rootNodeId;
|
35 |
|
36 | if (opts.edges) {
|
37 | for (let edge of opts.edges) {
|
38 | this.addEdge(edge.from, edge.to, edge.type);
|
39 | }
|
40 | }
|
41 | }
|
42 |
|
43 | static deserialize(opts) {
|
44 | return new this(opts);
|
45 | }
|
46 |
|
47 | serialize() {
|
48 | return {
|
49 | nodes: this.nodes,
|
50 | edges: this.getAllEdges(),
|
51 | rootNodeId: this.rootNodeId
|
52 | };
|
53 | }
|
54 |
|
55 |
|
56 |
|
57 |
|
58 | getAllEdges() {
|
59 | let edges = [];
|
60 |
|
61 | for (let [from, edgeList] of this.outboundEdges) {
|
62 | for (let [type, toNodes] of edgeList) {
|
63 | for (let to of toNodes) {
|
64 | edges.push({
|
65 | from,
|
66 | to,
|
67 | type
|
68 | });
|
69 | }
|
70 | }
|
71 | }
|
72 |
|
73 | return edges;
|
74 | }
|
75 |
|
76 | addNode(node) {
|
77 | this.nodes.set(node.id, node);
|
78 | return node;
|
79 | }
|
80 |
|
81 | hasNode(id) {
|
82 | return this.nodes.has(id);
|
83 | }
|
84 |
|
85 | getNode(id) {
|
86 | return this.nodes.get(id);
|
87 | }
|
88 |
|
89 | setRootNode(node) {
|
90 | this.addNode(node);
|
91 | this.rootNodeId = node.id;
|
92 | }
|
93 |
|
94 | getRootNode() {
|
95 | return this.rootNodeId ? this.getNode(this.rootNodeId) : null;
|
96 | }
|
97 |
|
98 | addEdge(from, to, type = null) {
|
99 | if (!this.getNode(from)) {
|
100 | throw new Error(`"from" node '${from}' not found`);
|
101 | }
|
102 |
|
103 | if (!this.getNode(to)) {
|
104 | throw new Error(`"to" node '${to}' not found`);
|
105 | }
|
106 |
|
107 | this.outboundEdges.get(from).get(type).add(to);
|
108 | this.inboundEdges.get(to).get(type).add(from);
|
109 | }
|
110 |
|
111 | hasEdge(from, to, type = null) {
|
112 | return this.outboundEdges.get(from).get(type).has(to);
|
113 | }
|
114 |
|
115 | getNodesConnectedTo(node, type = null) {
|
116 | assertHasNode(this, node);
|
117 | let nodes;
|
118 |
|
119 | if (type === ALL_EDGE_TYPES) {
|
120 | nodes = new Set();
|
121 |
|
122 | for (let [, typeNodes] of this.inboundEdges.get(node.id)) {
|
123 | for (let node of typeNodes) {
|
124 | nodes.add(node);
|
125 | }
|
126 | }
|
127 | } else {
|
128 | nodes = this.inboundEdges.get(node.id).get(type).values();
|
129 | }
|
130 |
|
131 | return [...nodes].map(to => (0, _nullthrows.default)(this.nodes.get(to)));
|
132 | }
|
133 |
|
134 | getNodesConnectedFrom(node, type = null) {
|
135 | assertHasNode(this, node);
|
136 | let nodes;
|
137 |
|
138 | if (type === ALL_EDGE_TYPES) {
|
139 | nodes = new Set();
|
140 |
|
141 | for (let [, typeNodes] of this.outboundEdges.get(node.id)) {
|
142 | for (let node of typeNodes) {
|
143 | nodes.add(node);
|
144 | }
|
145 | }
|
146 | } else {
|
147 | nodes = this.outboundEdges.get(node.id).get(type).values();
|
148 | }
|
149 |
|
150 | return [...nodes].map(to => (0, _nullthrows.default)(this.nodes.get(to)));
|
151 | }
|
152 |
|
153 | merge(graph) {
|
154 | for (let [, node] of graph.nodes) {
|
155 | this.addNode(node);
|
156 | }
|
157 |
|
158 | for (let edge of graph.getAllEdges()) {
|
159 | this.addEdge(edge.from, edge.to, edge.type);
|
160 | }
|
161 | }
|
162 |
|
163 |
|
164 | removeNode(node) {
|
165 | assertHasNode(this, node);
|
166 |
|
167 | for (let [type, nodesForType] of this.inboundEdges.get(node.id)) {
|
168 | for (let from of nodesForType) {
|
169 | this.removeEdge(from, node.id, type,
|
170 |
|
171 | false
|
172 |
|
173 | );
|
174 | }
|
175 | }
|
176 |
|
177 | for (let [type, toNodes] of this.outboundEdges.get(node.id)) {
|
178 | for (let to of toNodes) {
|
179 | this.removeEdge(node.id, to, type);
|
180 | }
|
181 | }
|
182 |
|
183 | let wasRemoved = this.nodes.delete(node.id);
|
184 | (0, _assert.default)(wasRemoved);
|
185 | }
|
186 |
|
187 | removeById(id) {
|
188 | let node = (0, _nullthrows.default)(this.getNode(id));
|
189 | this.removeNode(node);
|
190 | }
|
191 |
|
192 | removeEdges(node, type = null) {
|
193 | assertHasNode(this, node);
|
194 |
|
195 | for (let to of this.outboundEdges.get(node.id).get(type)) {
|
196 | this.removeEdge(node.id, to, type);
|
197 | }
|
198 | }
|
199 |
|
200 |
|
201 | removeEdge(from, to, type = null, removeOrphans = true) {
|
202 | if (!this.outboundEdges.get(from).get(type).has(to)) {
|
203 | throw new Error(`Outbound edge from ${from} to ${to} not found!`);
|
204 | }
|
205 |
|
206 | if (!this.inboundEdges.get(to).get(type).has(from)) {
|
207 | throw new Error(`Inbound edge from ${to} to ${from} not found!`);
|
208 | }
|
209 |
|
210 | this.outboundEdges.get(from).get(type).delete(to);
|
211 | this.inboundEdges.get(to).get(type).delete(from);
|
212 | let connectedNode = (0, _nullthrows.default)(this.nodes.get(to));
|
213 |
|
214 | if (removeOrphans && this.isOrphanedNode(connectedNode)) {
|
215 | this.removeNode(connectedNode);
|
216 | }
|
217 | }
|
218 |
|
219 | isOrphanedNode(node) {
|
220 | assertHasNode(this, node);
|
221 | let rootNode = this.getRootNode();
|
222 |
|
223 | if (rootNode == null) {
|
224 |
|
225 |
|
226 |
|
227 | for (let [, inboundNodeIds] of this.inboundEdges.get(node.id)) {
|
228 | if (inboundNodeIds.size > 0) {
|
229 | return false;
|
230 | }
|
231 | }
|
232 |
|
233 | return true;
|
234 | }
|
235 |
|
236 |
|
237 |
|
238 | let hasPathToRoot = false;
|
239 | this.traverseAncestors(node, (ancestor, _, actions) => {
|
240 | if (ancestor.id === rootNode.id) {
|
241 | hasPathToRoot = true;
|
242 | actions.stop();
|
243 | }
|
244 | },
|
245 | ALL_EDGE_TYPES);
|
246 |
|
247 | if (hasPathToRoot) {
|
248 | return false;
|
249 | }
|
250 |
|
251 | return true;
|
252 | }
|
253 |
|
254 | replaceNode(fromNode, toNode, type = null) {
|
255 | assertHasNode(this, fromNode);
|
256 | this.addNode(toNode);
|
257 |
|
258 | for (let parent of this.inboundEdges.get(fromNode.id).get(type)) {
|
259 | this.addEdge(parent, toNode.id, type);
|
260 | this.removeEdge(parent, fromNode.id, type);
|
261 | }
|
262 |
|
263 | this.removeNode(fromNode);
|
264 | }
|
265 |
|
266 |
|
267 | replaceNodesConnectedTo(fromNode, toNodes, replaceFilter, type = null) {
|
268 | assertHasNode(this, fromNode);
|
269 | let outboundEdges = this.outboundEdges.get(fromNode.id).get(type);
|
270 | let childrenToRemove = new Set(replaceFilter ? [...outboundEdges].filter(toNodeId => replaceFilter((0, _nullthrows.default)(this.nodes.get(toNodeId)))) : outboundEdges);
|
271 |
|
272 | for (let toNode of toNodes) {
|
273 | this.addNode(toNode);
|
274 | childrenToRemove.delete(toNode.id);
|
275 |
|
276 | if (!this.hasEdge(fromNode.id, toNode.id, type)) {
|
277 | this.addEdge(fromNode.id, toNode.id, type);
|
278 | }
|
279 | }
|
280 |
|
281 | for (let child of childrenToRemove) {
|
282 | this.removeEdge(fromNode.id, child, type);
|
283 | }
|
284 | }
|
285 |
|
286 | traverse(visit, startNode, type = null) {
|
287 | return this.dfs({
|
288 | visit,
|
289 | startNode,
|
290 | getChildren: node => this.getNodesConnectedFrom(node, type)
|
291 | });
|
292 | }
|
293 |
|
294 | filteredTraverse(filter, visit, startNode, type) {
|
295 | return this.traverse(mapVisitor(filter, visit), startNode, type);
|
296 | }
|
297 |
|
298 | traverseAncestors(startNode, visit, type = null) {
|
299 | return this.dfs({
|
300 | visit,
|
301 | startNode,
|
302 | getChildren: node => this.getNodesConnectedTo(node, type)
|
303 | });
|
304 | }
|
305 |
|
306 | dfs({
|
307 | visit,
|
308 | startNode,
|
309 | getChildren
|
310 | }) {
|
311 | let root = startNode !== null && startNode !== void 0 ? startNode : this.getRootNode();
|
312 |
|
313 | if (root == null) {
|
314 | throw new Error('A start node is required to traverse');
|
315 | }
|
316 |
|
317 | assertHasNode(this, root);
|
318 | let visited = new Set();
|
319 | let stopped = false;
|
320 | let skipped = false;
|
321 | let actions = {
|
322 | skipChildren() {
|
323 | skipped = true;
|
324 | },
|
325 |
|
326 | stop() {
|
327 | stopped = true;
|
328 | }
|
329 |
|
330 | };
|
331 |
|
332 | let walk = (node, context) => {
|
333 | visited.add(node);
|
334 | skipped = false;
|
335 | let enter = typeof visit === 'function' ? visit : visit.enter;
|
336 |
|
337 | if (enter) {
|
338 | let newContext = enter(node, context, actions);
|
339 |
|
340 | if (typeof newContext !== 'undefined') {
|
341 | context = newContext;
|
342 | }
|
343 | }
|
344 |
|
345 | if (skipped) {
|
346 | return;
|
347 | }
|
348 |
|
349 | if (stopped) {
|
350 | return context;
|
351 | }
|
352 |
|
353 | for (let child of getChildren(node)) {
|
354 | if (visited.has(child)) {
|
355 | continue;
|
356 | }
|
357 |
|
358 | visited.add(child);
|
359 | let result = walk(child, context);
|
360 |
|
361 | if (stopped) {
|
362 | return result;
|
363 | }
|
364 | }
|
365 |
|
366 | if (typeof visit !== 'function' && visit.exit) {
|
367 | let newContext = visit.exit(node, context, actions);
|
368 |
|
369 | if (typeof newContext !== 'undefined') {
|
370 | context = newContext;
|
371 | }
|
372 | }
|
373 |
|
374 | if (skipped) {
|
375 | return;
|
376 | }
|
377 |
|
378 | if (stopped) {
|
379 | return context;
|
380 | }
|
381 | };
|
382 |
|
383 | return walk(root);
|
384 | }
|
385 |
|
386 | bfs(visit) {
|
387 | let root = this.getRootNode();
|
388 |
|
389 | if (!root) {
|
390 | throw new Error('A root node is required to traverse');
|
391 | }
|
392 |
|
393 | let queue = [root];
|
394 | let visited = new Set([root]);
|
395 |
|
396 | while (queue.length > 0) {
|
397 | let node = queue.shift();
|
398 | let stop = visit(node);
|
399 |
|
400 | if (stop === true) {
|
401 | return node;
|
402 | }
|
403 |
|
404 | for (let child of this.getNodesConnectedFrom(node)) {
|
405 | if (!visited.has(child)) {
|
406 | visited.add(child);
|
407 | queue.push(child);
|
408 | }
|
409 | }
|
410 | }
|
411 |
|
412 | return null;
|
413 | }
|
414 |
|
415 | getSubGraph(node) {
|
416 | let graph = new this.constructor();
|
417 | graph.setRootNode(node);
|
418 | let nodes = [];
|
419 | this.traverse(node => {
|
420 | nodes.push(node);
|
421 | graph.addNode(node);
|
422 | }, node);
|
423 |
|
424 | for (let node of nodes) {
|
425 | for (let [type, toNodes] of this.outboundEdges.get(node.id)) {
|
426 | for (let to of toNodes) {
|
427 | graph.addEdge(node.id, to, type);
|
428 | }
|
429 | }
|
430 | }
|
431 |
|
432 | return graph;
|
433 | }
|
434 |
|
435 | findAncestor(node, fn) {
|
436 | let res = null;
|
437 | this.traverseAncestors(node, (node, ctx, traversal) => {
|
438 | if (fn(node)) {
|
439 | res = node;
|
440 | traversal.stop();
|
441 | }
|
442 | });
|
443 | return res;
|
444 | }
|
445 |
|
446 | findAncestors(node, fn) {
|
447 | let res = [];
|
448 | this.traverseAncestors(node, (node, ctx, traversal) => {
|
449 | if (fn(node)) {
|
450 | res.push(node);
|
451 | traversal.skipChildren();
|
452 | }
|
453 | });
|
454 | return res;
|
455 | }
|
456 |
|
457 | findDescendant(node, fn) {
|
458 | let res = null;
|
459 | this.traverse((node, ctx, traversal) => {
|
460 | if (fn(node)) {
|
461 | res = node;
|
462 | traversal.stop();
|
463 | }
|
464 | }, node);
|
465 | return res;
|
466 | }
|
467 |
|
468 | findDescendants(node, fn) {
|
469 | let res = [];
|
470 | this.traverse((node, ctx, traversal) => {
|
471 | if (fn(node)) {
|
472 | res.push(node);
|
473 | traversal.skipChildren();
|
474 | }
|
475 | }, node);
|
476 | return res;
|
477 | }
|
478 |
|
479 | findNode(predicate) {
|
480 | return Array.from(this.nodes.values()).find(predicate);
|
481 | }
|
482 |
|
483 | findNodes(predicate) {
|
484 | return Array.from(this.nodes.values()).filter(predicate);
|
485 | }
|
486 |
|
487 | }
|
488 |
|
489 | exports.default = Graph;
|
490 |
|
491 | function mapVisitor(filter, visit) {
|
492 | return {
|
493 | enter: (node, context, actions) => {
|
494 | let enter = typeof visit === 'function' ? visit : visit.enter;
|
495 |
|
496 | if (!enter) {
|
497 | return;
|
498 | }
|
499 |
|
500 | let value = filter(node, actions);
|
501 |
|
502 | if (value != null) {
|
503 | return enter(value, context, actions);
|
504 | }
|
505 | },
|
506 | exit: (node, context, actions) => {
|
507 | if (typeof visit === 'function') {
|
508 | return;
|
509 | }
|
510 |
|
511 | let exit = visit.exit;
|
512 |
|
513 | if (!exit) {
|
514 | return;
|
515 | }
|
516 |
|
517 | let value = filter(node, actions);
|
518 |
|
519 | if (value != null) {
|
520 | return exit(value, context, actions);
|
521 | }
|
522 | }
|
523 | };
|
524 | }
|
525 |
|
526 | function assertHasNode(graph, node) {
|
527 | if (!graph.hasNode(node.id)) {
|
528 | throw new Error('Does not have node ' + node.id);
|
529 | }
|
530 | } |
\ | No newline at end of file |