@parcel/graph 3.2.1-dev.3187 → 3.2.1-dev.3189
Sign up to get free protection for your applications and to get access to all the features.
- package/lib/AdjacencyList.js +1 -1
- package/lib/Graph.js +4 -115
- package/package.json +3 -3
- package/src/AdjacencyList.js +2 -2
- package/src/Graph.js +6 -153
- package/test/Graph.test.js +2 -228
package/lib/AdjacencyList.js
CHANGED
@@ -240,7 +240,7 @@ class AdjacencyList {
|
|
240
240
|
*
|
241
241
|
* Note that this method does not increment the node count
|
242
242
|
* (that only happens in `addEdge`), it _may_ preemptively resize
|
243
|
-
* the nodes array if it is at capacity, under the
|
243
|
+
* the nodes array if it is at capacity, under the asumption that
|
244
244
|
* at least 1 edge to or from this new node will be added.
|
245
245
|
*
|
246
246
|
* Returns the id of the added node.
|
package/lib/Graph.js
CHANGED
@@ -17,15 +17,6 @@ function _nullthrows() {
|
|
17
17
|
}
|
18
18
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
19
19
|
const ALL_EDGE_TYPES = exports.ALL_EDGE_TYPES = -1;
|
20
|
-
|
21
|
-
/**
|
22
|
-
* Internal type used for queue iterative DFS implementation.
|
23
|
-
*/
|
24
|
-
|
25
|
-
/**
|
26
|
-
* Options for DFS traversal.
|
27
|
-
*/
|
28
|
-
|
29
20
|
class Graph {
|
30
21
|
constructor(opts) {
|
31
22
|
this.nodes = (opts === null || opts === void 0 ? void 0 : opts.nodes) || [];
|
@@ -188,7 +179,7 @@ class Graph {
|
|
188
179
|
if (type === ALL_EDGE_TYPES && enter && (typeof visit === 'function' || !visit.exit)) {
|
189
180
|
return this.dfsFast(enter, startNodeId);
|
190
181
|
} else {
|
191
|
-
return this.
|
182
|
+
return this.dfs({
|
192
183
|
visit,
|
193
184
|
startNodeId,
|
194
185
|
getChildren: nodeId => this.getNodeIdsConnectedFrom(nodeId, type)
|
@@ -206,7 +197,7 @@ class Graph {
|
|
206
197
|
});
|
207
198
|
}
|
208
199
|
dfsFast(visit, startNodeId) {
|
209
|
-
let traversalStartNode = (0, _nullthrows().default)(startNodeId
|
200
|
+
let traversalStartNode = (0, _nullthrows().default)(startNodeId ?? this.rootNodeId, 'A start node is required to traverse');
|
210
201
|
this._assertHasNodeId(traversalStartNode);
|
211
202
|
let visited;
|
212
203
|
if (!this._visited || this._visited.capacity < this.nodes.length) {
|
@@ -268,7 +259,7 @@ class Graph {
|
|
268
259
|
|
269
260
|
// A post-order implementation of dfsFast
|
270
261
|
postOrderDfsFast(visit, startNodeId) {
|
271
|
-
let traversalStartNode = (0, _nullthrows().default)(startNodeId
|
262
|
+
let traversalStartNode = (0, _nullthrows().default)(startNodeId ?? this.rootNodeId, 'A start node is required to traverse');
|
272
263
|
this._assertHasNodeId(traversalStartNode);
|
273
264
|
let visited;
|
274
265
|
if (!this._visited || this._visited.capacity < this.nodes.length) {
|
@@ -310,114 +301,12 @@ class Graph {
|
|
310
301
|
}
|
311
302
|
this._visited = visited;
|
312
303
|
}
|
313
|
-
|
314
|
-
/**
|
315
|
-
* Iterative implementation of DFS that supports all use-cases.
|
316
|
-
*/
|
317
|
-
dfsNew({
|
318
|
-
visit,
|
319
|
-
startNodeId,
|
320
|
-
getChildren
|
321
|
-
}) {
|
322
|
-
let traversalStartNode = (0, _nullthrows().default)(startNodeId !== null && startNodeId !== void 0 ? startNodeId : this.rootNodeId, 'A start node is required to traverse');
|
323
|
-
this._assertHasNodeId(traversalStartNode);
|
324
|
-
let visited;
|
325
|
-
if (!this._visited || this._visited.capacity < this.nodes.length) {
|
326
|
-
this._visited = new _BitSet.BitSet(this.nodes.length);
|
327
|
-
visited = this._visited;
|
328
|
-
} else {
|
329
|
-
visited = this._visited;
|
330
|
-
visited.clear();
|
331
|
-
}
|
332
|
-
// Take shared instance to avoid re-entrancy issues.
|
333
|
-
this._visited = null;
|
334
|
-
let stopped = false;
|
335
|
-
let skipped = false;
|
336
|
-
let actions = {
|
337
|
-
skipChildren() {
|
338
|
-
skipped = true;
|
339
|
-
},
|
340
|
-
stop() {
|
341
|
-
stopped = true;
|
342
|
-
}
|
343
|
-
};
|
344
|
-
const queue = [{
|
345
|
-
nodeId: traversalStartNode,
|
346
|
-
context: null
|
347
|
-
}];
|
348
|
-
while (queue.length !== 0) {
|
349
|
-
const command = queue.pop();
|
350
|
-
if (command.exit != null) {
|
351
|
-
let {
|
352
|
-
nodeId,
|
353
|
-
context,
|
354
|
-
exit
|
355
|
-
} = command;
|
356
|
-
let newContext = exit(nodeId, command.context, actions);
|
357
|
-
if (typeof newContext !== 'undefined') {
|
358
|
-
// $FlowFixMe[reassign-const]
|
359
|
-
context = newContext;
|
360
|
-
}
|
361
|
-
if (skipped) {
|
362
|
-
continue;
|
363
|
-
}
|
364
|
-
if (stopped) {
|
365
|
-
this._visited = visited;
|
366
|
-
return context;
|
367
|
-
}
|
368
|
-
} else {
|
369
|
-
let {
|
370
|
-
nodeId,
|
371
|
-
context
|
372
|
-
} = command;
|
373
|
-
if (!this.hasNode(nodeId) || visited.has(nodeId)) continue;
|
374
|
-
visited.add(nodeId);
|
375
|
-
skipped = false;
|
376
|
-
let enter = typeof visit === 'function' ? visit : visit.enter;
|
377
|
-
if (enter) {
|
378
|
-
let newContext = enter(nodeId, context, actions);
|
379
|
-
if (typeof newContext !== 'undefined') {
|
380
|
-
// $FlowFixMe[reassign-const]
|
381
|
-
context = newContext;
|
382
|
-
}
|
383
|
-
}
|
384
|
-
if (skipped) {
|
385
|
-
continue;
|
386
|
-
}
|
387
|
-
if (stopped) {
|
388
|
-
this._visited = visited;
|
389
|
-
return context;
|
390
|
-
}
|
391
|
-
if (typeof visit !== 'function' && visit.exit) {
|
392
|
-
queue.push({
|
393
|
-
nodeId,
|
394
|
-
exit: visit.exit,
|
395
|
-
context
|
396
|
-
});
|
397
|
-
}
|
398
|
-
|
399
|
-
// TODO turn into generator function
|
400
|
-
const children = getChildren(nodeId);
|
401
|
-
for (let i = children.length - 1; i > -1; i -= 1) {
|
402
|
-
const child = children[i];
|
403
|
-
if (visited.has(child)) {
|
404
|
-
continue;
|
405
|
-
}
|
406
|
-
queue.push({
|
407
|
-
nodeId: child,
|
408
|
-
context
|
409
|
-
});
|
410
|
-
}
|
411
|
-
}
|
412
|
-
}
|
413
|
-
this._visited = visited;
|
414
|
-
}
|
415
304
|
dfs({
|
416
305
|
visit,
|
417
306
|
startNodeId,
|
418
307
|
getChildren
|
419
308
|
}) {
|
420
|
-
let traversalStartNode = (0, _nullthrows().default)(startNodeId
|
309
|
+
let traversalStartNode = (0, _nullthrows().default)(startNodeId ?? this.rootNodeId, 'A start node is required to traverse');
|
421
310
|
this._assertHasNodeId(traversalStartNode);
|
422
311
|
let visited;
|
423
312
|
if (!this._visited || this._visited.capacity < this.nodes.length) {
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@parcel/graph",
|
3
|
-
"version": "3.2.1-dev.
|
3
|
+
"version": "3.2.1-dev.3189+406c0ceef",
|
4
4
|
"description": "Blazing fast, zero configuration web application bundler",
|
5
5
|
"license": "MIT",
|
6
6
|
"publishConfig": {
|
@@ -17,10 +17,10 @@
|
|
17
17
|
"main": "lib/index.js",
|
18
18
|
"source": "src/index.js",
|
19
19
|
"engines": {
|
20
|
-
"node": ">=
|
20
|
+
"node": ">= 16.0.0"
|
21
21
|
},
|
22
22
|
"dependencies": {
|
23
23
|
"nullthrows": "^1.1.1"
|
24
24
|
},
|
25
|
-
"gitHead": "
|
25
|
+
"gitHead": "406c0ceefb854ea98435f97e4e317164150c9883"
|
26
26
|
}
|
package/src/AdjacencyList.js
CHANGED
@@ -30,7 +30,7 @@ export type AdjacencyListOptions<TEdgeType> = {|
|
|
30
30
|
minGrowFactor?: number,
|
31
31
|
/** The size after which to grow the capacity by the minimum factor. */
|
32
32
|
peakCapacity?: number,
|
33
|
-
/** The percentage of deleted edges above which the
|
33
|
+
/** The percentage of deleted edges above which the capcity should shink. */
|
34
34
|
unloadFactor?: number,
|
35
35
|
/** The amount by which to shrink the capacity. */
|
36
36
|
shrinkFactor?: number,
|
@@ -328,7 +328,7 @@ export default class AdjacencyList<TEdgeType: number = 1> {
|
|
328
328
|
*
|
329
329
|
* Note that this method does not increment the node count
|
330
330
|
* (that only happens in `addEdge`), it _may_ preemptively resize
|
331
|
-
* the nodes array if it is at capacity, under the
|
331
|
+
* the nodes array if it is at capacity, under the asumption that
|
332
332
|
* at least 1 edge to or from this new node will be added.
|
333
333
|
*
|
334
334
|
* Returns the id of the added node.
|
package/src/Graph.js
CHANGED
@@ -28,51 +28,6 @@ export type SerializedGraph<TNode, TEdgeType: number = 1> = {|
|
|
28
28
|
export type AllEdgeTypes = -1;
|
29
29
|
export const ALL_EDGE_TYPES: AllEdgeTypes = -1;
|
30
30
|
|
31
|
-
type DFSCommandVisit<TContext> = {|
|
32
|
-
nodeId: NodeId,
|
33
|
-
context: TContext | null,
|
34
|
-
|};
|
35
|
-
|
36
|
-
type DFSCommandExit<TContext> = {|
|
37
|
-
nodeId: NodeId,
|
38
|
-
exit: GraphTraversalCallback<NodeId, TContext>,
|
39
|
-
context: TContext | null,
|
40
|
-
|};
|
41
|
-
|
42
|
-
/**
|
43
|
-
* Internal type used for queue iterative DFS implementation.
|
44
|
-
*/
|
45
|
-
type DFSCommand<TContext> =
|
46
|
-
| DFSCommandVisit<TContext>
|
47
|
-
| DFSCommandExit<TContext>;
|
48
|
-
|
49
|
-
/**
|
50
|
-
* Options for DFS traversal.
|
51
|
-
*/
|
52
|
-
type DFSParams<TContext> = {|
|
53
|
-
visit: GraphVisitor<NodeId, TContext>,
|
54
|
-
/**
|
55
|
-
* Custom function to get next entries to visit.
|
56
|
-
*
|
57
|
-
* This can be a performance bottle-neck as arrays are created on every node visit.
|
58
|
-
*
|
59
|
-
* @deprecated This will be replaced by a static `traversalType` set of orders in the future
|
60
|
-
*
|
61
|
-
* Currently this is only used in 3 ways:
|
62
|
-
*
|
63
|
-
* - Traversing down the tree (normal DFS)
|
64
|
-
* - Traversing up the tree (ancestors)
|
65
|
-
* - Filtered version of traversal; which does not need to exist at the DFS level as the visitor
|
66
|
-
* can handle filtering
|
67
|
-
* - Sorted traversal of BundleGraph entries, which does not have a clear use-case, but may
|
68
|
-
* not be safe to remove
|
69
|
-
*
|
70
|
-
* Only due to the latter we aren't replacing this.
|
71
|
-
*/
|
72
|
-
getChildren: (nodeId: NodeId) => Array<NodeId>,
|
73
|
-
startNodeId?: ?NodeId,
|
74
|
-
|};
|
75
|
-
|
76
31
|
export default class Graph<TNode, TEdgeType: number = 1> {
|
77
32
|
nodes: Array<TNode | null>;
|
78
33
|
adjacencyList: AdjacencyList<TEdgeType>;
|
@@ -334,7 +289,7 @@ export default class Graph<TNode, TEdgeType: number = 1> {
|
|
334
289
|
) {
|
335
290
|
return this.dfsFast(enter, startNodeId);
|
336
291
|
} else {
|
337
|
-
return this.
|
292
|
+
return this.dfs({
|
338
293
|
visit,
|
339
294
|
startNodeId,
|
340
295
|
getChildren: nodeId => this.getNodeIdsConnectedFrom(nodeId, type),
|
@@ -494,117 +449,15 @@ export default class Graph<TNode, TEdgeType: number = 1> {
|
|
494
449
|
return;
|
495
450
|
}
|
496
451
|
|
497
|
-
/**
|
498
|
-
* Iterative implementation of DFS that supports all use-cases.
|
499
|
-
*/
|
500
|
-
dfsNew<TContext>({
|
501
|
-
visit,
|
502
|
-
startNodeId,
|
503
|
-
getChildren,
|
504
|
-
}: DFSParams<TContext>): ?TContext {
|
505
|
-
let traversalStartNode = nullthrows(
|
506
|
-
startNodeId ?? this.rootNodeId,
|
507
|
-
'A start node is required to traverse',
|
508
|
-
);
|
509
|
-
this._assertHasNodeId(traversalStartNode);
|
510
|
-
|
511
|
-
let visited;
|
512
|
-
if (!this._visited || this._visited.capacity < this.nodes.length) {
|
513
|
-
this._visited = new BitSet(this.nodes.length);
|
514
|
-
visited = this._visited;
|
515
|
-
} else {
|
516
|
-
visited = this._visited;
|
517
|
-
visited.clear();
|
518
|
-
}
|
519
|
-
// Take shared instance to avoid re-entrancy issues.
|
520
|
-
this._visited = null;
|
521
|
-
|
522
|
-
let stopped = false;
|
523
|
-
let skipped = false;
|
524
|
-
let actions: TraversalActions = {
|
525
|
-
skipChildren() {
|
526
|
-
skipped = true;
|
527
|
-
},
|
528
|
-
stop() {
|
529
|
-
stopped = true;
|
530
|
-
},
|
531
|
-
};
|
532
|
-
|
533
|
-
const queue: DFSCommand<TContext>[] = [
|
534
|
-
{nodeId: traversalStartNode, context: null},
|
535
|
-
];
|
536
|
-
while (queue.length !== 0) {
|
537
|
-
const command = queue.pop();
|
538
|
-
|
539
|
-
if (command.exit != null) {
|
540
|
-
let {nodeId, context, exit} = command;
|
541
|
-
let newContext = exit(nodeId, command.context, actions);
|
542
|
-
if (typeof newContext !== 'undefined') {
|
543
|
-
// $FlowFixMe[reassign-const]
|
544
|
-
context = newContext;
|
545
|
-
}
|
546
|
-
|
547
|
-
if (skipped) {
|
548
|
-
continue;
|
549
|
-
}
|
550
|
-
|
551
|
-
if (stopped) {
|
552
|
-
this._visited = visited;
|
553
|
-
return context;
|
554
|
-
}
|
555
|
-
} else {
|
556
|
-
let {nodeId, context} = command;
|
557
|
-
if (!this.hasNode(nodeId) || visited.has(nodeId)) continue;
|
558
|
-
visited.add(nodeId);
|
559
|
-
|
560
|
-
skipped = false;
|
561
|
-
let enter = typeof visit === 'function' ? visit : visit.enter;
|
562
|
-
if (enter) {
|
563
|
-
let newContext = enter(nodeId, context, actions);
|
564
|
-
if (typeof newContext !== 'undefined') {
|
565
|
-
// $FlowFixMe[reassign-const]
|
566
|
-
context = newContext;
|
567
|
-
}
|
568
|
-
}
|
569
|
-
|
570
|
-
if (skipped) {
|
571
|
-
continue;
|
572
|
-
}
|
573
|
-
|
574
|
-
if (stopped) {
|
575
|
-
this._visited = visited;
|
576
|
-
return context;
|
577
|
-
}
|
578
|
-
|
579
|
-
if (typeof visit !== 'function' && visit.exit) {
|
580
|
-
queue.push({
|
581
|
-
nodeId,
|
582
|
-
exit: visit.exit,
|
583
|
-
context,
|
584
|
-
});
|
585
|
-
}
|
586
|
-
|
587
|
-
// TODO turn into generator function
|
588
|
-
const children = getChildren(nodeId);
|
589
|
-
for (let i = children.length - 1; i > -1; i -= 1) {
|
590
|
-
const child = children[i];
|
591
|
-
if (visited.has(child)) {
|
592
|
-
continue;
|
593
|
-
}
|
594
|
-
|
595
|
-
queue.push({nodeId: child, context});
|
596
|
-
}
|
597
|
-
}
|
598
|
-
}
|
599
|
-
|
600
|
-
this._visited = visited;
|
601
|
-
}
|
602
|
-
|
603
452
|
dfs<TContext>({
|
604
453
|
visit,
|
605
454
|
startNodeId,
|
606
455
|
getChildren,
|
607
|
-
}:
|
456
|
+
}: {|
|
457
|
+
visit: GraphVisitor<NodeId, TContext>,
|
458
|
+
getChildren(nodeId: NodeId): Array<NodeId>,
|
459
|
+
startNodeId?: ?NodeId,
|
460
|
+
|}): ?TContext {
|
608
461
|
let traversalStartNode = nullthrows(
|
609
462
|
startNodeId ?? this.rootNodeId,
|
610
463
|
'A start node is required to traverse',
|
package/test/Graph.test.js
CHANGED
@@ -2,10 +2,9 @@
|
|
2
2
|
|
3
3
|
import assert from 'assert';
|
4
4
|
import sinon from 'sinon';
|
5
|
-
import type {TraversalActions} from '@parcel/types-internal';
|
6
5
|
|
7
|
-
import Graph
|
8
|
-
import {toNodeId
|
6
|
+
import Graph from '../src/Graph';
|
7
|
+
import {toNodeId} from '../src/types';
|
9
8
|
|
10
9
|
describe('Graph', () => {
|
11
10
|
it('constructor should initialize an empty graph', () => {
|
@@ -341,229 +340,4 @@ describe('Graph', () => {
|
|
341
340
|
assert.deepEqual(graph.nodes.filter(Boolean), ['root']);
|
342
341
|
assert.deepStrictEqual(Array.from(graph.getAllEdges()), []);
|
343
342
|
});
|
344
|
-
|
345
|
-
describe('dfs(...)', () => {
|
346
|
-
function testSuite(
|
347
|
-
name: string,
|
348
|
-
dfsImpl: (graph: Graph<string>, DFSParams<mixed>) => mixed | null | void,
|
349
|
-
) {
|
350
|
-
it(`${name} throws if the graph is empty`, () => {
|
351
|
-
const graph = new Graph();
|
352
|
-
const visit = sinon.stub();
|
353
|
-
const getChildren = sinon.stub();
|
354
|
-
assert.throws(() => {
|
355
|
-
dfsImpl(graph, {
|
356
|
-
visit,
|
357
|
-
startNodeId: 0,
|
358
|
-
getChildren,
|
359
|
-
});
|
360
|
-
}, /Does not have node 0/);
|
361
|
-
});
|
362
|
-
|
363
|
-
it(`${name} visits a single node`, () => {
|
364
|
-
const graph = new Graph();
|
365
|
-
graph.addNode('root');
|
366
|
-
const visit = sinon.stub();
|
367
|
-
const getChildren = () => [];
|
368
|
-
dfsImpl(graph, {
|
369
|
-
visit,
|
370
|
-
startNodeId: 0,
|
371
|
-
getChildren,
|
372
|
-
});
|
373
|
-
|
374
|
-
assert(visit.calledOnce);
|
375
|
-
});
|
376
|
-
|
377
|
-
it(`${name} visits all connected nodes in DFS order`, () => {
|
378
|
-
const graph = new Graph();
|
379
|
-
graph.addNode('0');
|
380
|
-
graph.addNode('1');
|
381
|
-
graph.addNode('2');
|
382
|
-
graph.addNode('3');
|
383
|
-
graph.addNode('disconnected-1');
|
384
|
-
graph.addNode('disconnected-2');
|
385
|
-
graph.addEdge(0, 1);
|
386
|
-
graph.addEdge(0, 2);
|
387
|
-
graph.addEdge(1, 3);
|
388
|
-
graph.addEdge(2, 3);
|
389
|
-
|
390
|
-
const order = [];
|
391
|
-
const visit = (node: NodeId) => {
|
392
|
-
order.push(node);
|
393
|
-
};
|
394
|
-
const getChildren = (node: NodeId) =>
|
395
|
-
graph.getNodeIdsConnectedFrom(node);
|
396
|
-
dfsImpl(graph, {
|
397
|
-
visit,
|
398
|
-
startNodeId: 0,
|
399
|
-
getChildren,
|
400
|
-
});
|
401
|
-
|
402
|
-
assert.deepEqual(order, [0, 1, 3, 2]);
|
403
|
-
});
|
404
|
-
|
405
|
-
describe(`${name} actions tests`, () => {
|
406
|
-
it(`${name} skips children if skip is called on a node`, () => {
|
407
|
-
const graph = new Graph();
|
408
|
-
graph.addNode('0');
|
409
|
-
graph.addNode('1');
|
410
|
-
graph.addNode('2');
|
411
|
-
graph.addNode('3');
|
412
|
-
graph.addNode('disconnected-1');
|
413
|
-
graph.addNode('disconnected-2');
|
414
|
-
graph.addEdge(0, 1);
|
415
|
-
graph.addEdge(1, 2);
|
416
|
-
graph.addEdge(0, 3);
|
417
|
-
|
418
|
-
const order = [];
|
419
|
-
const visit = (
|
420
|
-
node: NodeId,
|
421
|
-
context: mixed | null,
|
422
|
-
actions: TraversalActions,
|
423
|
-
) => {
|
424
|
-
if (node === 1) actions.skipChildren();
|
425
|
-
order.push(node);
|
426
|
-
};
|
427
|
-
const getChildren = (node: NodeId) =>
|
428
|
-
graph.getNodeIdsConnectedFrom(node);
|
429
|
-
dfsImpl(graph, {
|
430
|
-
visit,
|
431
|
-
startNodeId: 0,
|
432
|
-
getChildren,
|
433
|
-
});
|
434
|
-
|
435
|
-
assert.deepEqual(order, [0, 1, 3]);
|
436
|
-
});
|
437
|
-
|
438
|
-
it(`${name} stops the traversal if stop is called`, () => {
|
439
|
-
const graph = new Graph();
|
440
|
-
graph.addNode('0');
|
441
|
-
graph.addNode('1');
|
442
|
-
graph.addNode('2');
|
443
|
-
graph.addNode('3');
|
444
|
-
graph.addNode('disconnected-1');
|
445
|
-
graph.addNode('disconnected-2');
|
446
|
-
graph.addEdge(0, 1);
|
447
|
-
graph.addEdge(1, 2);
|
448
|
-
graph.addEdge(1, 3);
|
449
|
-
graph.addEdge(0, 2);
|
450
|
-
graph.addEdge(2, 3);
|
451
|
-
|
452
|
-
const order = [];
|
453
|
-
const visit = (
|
454
|
-
node: NodeId,
|
455
|
-
context: mixed | null,
|
456
|
-
actions: TraversalActions,
|
457
|
-
) => {
|
458
|
-
order.push(node);
|
459
|
-
if (node === 1) {
|
460
|
-
actions.stop();
|
461
|
-
return 'result';
|
462
|
-
}
|
463
|
-
return 'other';
|
464
|
-
};
|
465
|
-
const getChildren = (node: NodeId) =>
|
466
|
-
graph.getNodeIdsConnectedFrom(node);
|
467
|
-
const result = dfsImpl(graph, {
|
468
|
-
visit,
|
469
|
-
startNodeId: 0,
|
470
|
-
getChildren,
|
471
|
-
});
|
472
|
-
|
473
|
-
assert.deepEqual(order, [0, 1]);
|
474
|
-
assert.equal(result, 'result');
|
475
|
-
});
|
476
|
-
});
|
477
|
-
|
478
|
-
describe(`${name} context tests`, () => {
|
479
|
-
it(`${name} passes the context between visitors`, () => {
|
480
|
-
const graph = new Graph();
|
481
|
-
graph.addNode('0');
|
482
|
-
graph.addNode('1');
|
483
|
-
graph.addNode('2');
|
484
|
-
graph.addNode('3');
|
485
|
-
graph.addNode('disconnected-1');
|
486
|
-
graph.addNode('disconnected-2');
|
487
|
-
graph.addEdge(0, 1);
|
488
|
-
graph.addEdge(1, 2);
|
489
|
-
graph.addEdge(1, 3);
|
490
|
-
graph.addEdge(0, 2);
|
491
|
-
graph.addEdge(2, 3);
|
492
|
-
|
493
|
-
const contexts = [];
|
494
|
-
const visit = (node: NodeId, context: mixed | null) => {
|
495
|
-
contexts.push([node, context]);
|
496
|
-
return `node-${node}-created-context`;
|
497
|
-
};
|
498
|
-
const getChildren = (node: NodeId) =>
|
499
|
-
graph.getNodeIdsConnectedFrom(node);
|
500
|
-
const result = dfsImpl(graph, {
|
501
|
-
visit,
|
502
|
-
startNodeId: 0,
|
503
|
-
getChildren,
|
504
|
-
});
|
505
|
-
|
506
|
-
assert.deepEqual(contexts, [
|
507
|
-
[0, undefined],
|
508
|
-
[1, 'node-0-created-context'],
|
509
|
-
[2, 'node-1-created-context'],
|
510
|
-
[3, 'node-2-created-context'],
|
511
|
-
]);
|
512
|
-
assert.equal(result, undefined);
|
513
|
-
});
|
514
|
-
});
|
515
|
-
|
516
|
-
describe(`${name} exit visitor tests`, () => {
|
517
|
-
it(`${name} calls the exit visitor`, () => {
|
518
|
-
const graph = new Graph();
|
519
|
-
graph.addNode('0');
|
520
|
-
graph.addNode('1');
|
521
|
-
graph.addNode('2');
|
522
|
-
graph.addNode('3');
|
523
|
-
graph.addNode('disconnected-1');
|
524
|
-
graph.addNode('disconnected-2');
|
525
|
-
graph.addEdge(0, 1);
|
526
|
-
graph.addEdge(1, 2);
|
527
|
-
graph.addEdge(1, 3);
|
528
|
-
graph.addEdge(0, 2);
|
529
|
-
|
530
|
-
const contexts = [];
|
531
|
-
const visit = (node: NodeId, context: mixed | null) => {
|
532
|
-
contexts.push([node, context]);
|
533
|
-
return `node-${node}-created-context`;
|
534
|
-
};
|
535
|
-
const visitExit = (node: NodeId, context: mixed | null) => {
|
536
|
-
contexts.push(['exit', node, context]);
|
537
|
-
return `node-exit-${node}-created-context`;
|
538
|
-
};
|
539
|
-
const getChildren = (node: NodeId) =>
|
540
|
-
graph.getNodeIdsConnectedFrom(node);
|
541
|
-
const result = dfsImpl(graph, {
|
542
|
-
visit: {
|
543
|
-
enter: visit,
|
544
|
-
exit: visitExit,
|
545
|
-
},
|
546
|
-
startNodeId: 0,
|
547
|
-
getChildren,
|
548
|
-
});
|
549
|
-
|
550
|
-
assert.deepEqual(contexts, [
|
551
|
-
[0, undefined],
|
552
|
-
[1, 'node-0-created-context'],
|
553
|
-
[2, 'node-1-created-context'],
|
554
|
-
['exit', 2, 'node-2-created-context'],
|
555
|
-
[3, 'node-1-created-context'],
|
556
|
-
['exit', 3, 'node-3-created-context'],
|
557
|
-
['exit', 1, 'node-1-created-context'],
|
558
|
-
['exit', 0, 'node-0-created-context'],
|
559
|
-
]);
|
560
|
-
assert.equal(result, undefined);
|
561
|
-
});
|
562
|
-
});
|
563
|
-
}
|
564
|
-
|
565
|
-
testSuite('dfs', (graph, params) => graph.dfs(params));
|
566
|
-
|
567
|
-
testSuite('dfsNew', (graph, params) => graph.dfsNew(params));
|
568
|
-
});
|
569
343
|
});
|