@atlaspack/graph 3.2.1-dev.3520 → 3.2.1-dev.3565
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/lib/AdjacencyList.js +23 -5
- package/lib/Graph.js +15 -1
- package/package.json +3 -3
- package/src/AdjacencyList.js +27 -4
- package/src/Graph.js +28 -1
- package/test/AdjacencyList.test.js +175 -0
- package/test/Graph.test.js +48 -0
package/lib/AdjacencyList.js
CHANGED
|
@@ -450,16 +450,33 @@ class AdjacencyList {
|
|
|
450
450
|
}
|
|
451
451
|
return nodes;
|
|
452
452
|
}
|
|
453
|
-
forEachNodeIdConnectedFromReverse(from, fn) {
|
|
453
|
+
forEachNodeIdConnectedFromReverse(from, fn, type = _Graph.ALL_EDGE_TYPES) {
|
|
454
|
+
const matches = node => type === _Graph.ALL_EDGE_TYPES || type === this.#nodes.typeOf(node);
|
|
454
455
|
let node = this.#nodes.head(from);
|
|
455
456
|
while (node !== null) {
|
|
456
|
-
|
|
457
|
+
if (matches(node)) {
|
|
458
|
+
let edge = this.#nodes.lastOut(node);
|
|
459
|
+
while (edge !== null) {
|
|
460
|
+
let to = this.#edges.to(edge);
|
|
461
|
+
if (fn(to)) {
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
edge = this.#edges.prevOut(edge);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
node = this.#nodes.next(node);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
forEachNodeIdConnectedTo(to, fn) {
|
|
471
|
+
let node = this.#nodes.head(to);
|
|
472
|
+
while (node !== null) {
|
|
473
|
+
let edge = this.#nodes.firstIn(node);
|
|
457
474
|
while (edge !== null) {
|
|
458
|
-
let
|
|
459
|
-
if (fn(
|
|
475
|
+
let from = this.#edges.from(edge);
|
|
476
|
+
if (fn(from)) {
|
|
460
477
|
return;
|
|
461
478
|
}
|
|
462
|
-
edge = this.#edges.
|
|
479
|
+
edge = this.#edges.nextIn(edge);
|
|
463
480
|
}
|
|
464
481
|
node = this.#nodes.next(node);
|
|
465
482
|
}
|
|
@@ -813,6 +830,7 @@ class SharedTypeMap {
|
|
|
813
830
|
// Trick Flow into believing in `Symbol.iterator`.
|
|
814
831
|
// See https://github.com/facebook/flow/issues/1163#issuecomment-353523840
|
|
815
832
|
/*:: @@iterator(): Iterator<TAddress> { return ({}: any); } */
|
|
833
|
+
|
|
816
834
|
// $FlowFixMe[unsupported-syntax]
|
|
817
835
|
*[Symbol.iterator]() {
|
|
818
836
|
let max = this.count;
|
package/lib/Graph.js
CHANGED
|
@@ -31,7 +31,10 @@ class Graph {
|
|
|
31
31
|
this.nodes = (opts === null || opts === void 0 ? void 0 : opts.nodes) || [];
|
|
32
32
|
this.setRootNodeId(opts === null || opts === void 0 ? void 0 : opts.rootNodeId);
|
|
33
33
|
let adjacencyList = opts === null || opts === void 0 ? void 0 : opts.adjacencyList;
|
|
34
|
-
|
|
34
|
+
let initialCapacity = opts === null || opts === void 0 ? void 0 : opts.initialCapacity;
|
|
35
|
+
this.adjacencyList = adjacencyList ? _AdjacencyList.default.deserialize(adjacencyList) : new _AdjacencyList.default(typeof initialCapacity === 'number' ? {
|
|
36
|
+
initialCapacity
|
|
37
|
+
} : undefined);
|
|
35
38
|
}
|
|
36
39
|
setRootNodeId(id) {
|
|
37
40
|
this.rootNodeId = id;
|
|
@@ -82,6 +85,17 @@ class Graph {
|
|
|
82
85
|
hasEdge(from, to, type = 1) {
|
|
83
86
|
return this.adjacencyList.hasEdge(from, to, type);
|
|
84
87
|
}
|
|
88
|
+
forEachNodeIdConnectedTo(to, fn) {
|
|
89
|
+
this._assertHasNodeId(to);
|
|
90
|
+
this.adjacencyList.forEachNodeIdConnectedTo(to, fn);
|
|
91
|
+
}
|
|
92
|
+
forEachNodeIdConnectedFrom(from, fn, type = ALL_EDGE_TYPES) {
|
|
93
|
+
this._assertHasNodeId(from);
|
|
94
|
+
this.adjacencyList.forEachNodeIdConnectedFromReverse(from, id => {
|
|
95
|
+
fn(id);
|
|
96
|
+
return false;
|
|
97
|
+
}, type);
|
|
98
|
+
}
|
|
85
99
|
getNodeIdsConnectedTo(nodeId, type = 1) {
|
|
86
100
|
this._assertHasNodeId(nodeId);
|
|
87
101
|
return this.adjacencyList.getNodeIdsConnectedTo(nodeId, type);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaspack/graph",
|
|
3
|
-
"version": "3.2.1-dev.
|
|
3
|
+
"version": "3.2.1-dev.3565+b31bc6b33",
|
|
4
4
|
"description": "Blazing fast, zero configuration web application bundler",
|
|
5
5
|
"license": "(MIT OR Apache-2.0)",
|
|
6
6
|
"publishConfig": {
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
"node": ">= 16.0.0"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@atlaspack/feature-flags": "2.12.1-dev.
|
|
19
|
+
"@atlaspack/feature-flags": "2.12.1-dev.3565+b31bc6b33",
|
|
20
20
|
"nullthrows": "^1.1.1"
|
|
21
21
|
},
|
|
22
|
-
"gitHead": "
|
|
22
|
+
"gitHead": "b31bc6b33de40c158b9f4d8ff177238be0de409d"
|
|
23
23
|
}
|
package/src/AdjacencyList.js
CHANGED
|
@@ -598,16 +598,37 @@ export default class AdjacencyList<TEdgeType: number = 1> {
|
|
|
598
598
|
forEachNodeIdConnectedFromReverse(
|
|
599
599
|
from: NodeId,
|
|
600
600
|
fn: (nodeId: NodeId) => boolean,
|
|
601
|
+
type: AllEdgeTypes | TEdgeType | NullEdgeType = ALL_EDGE_TYPES,
|
|
601
602
|
) {
|
|
603
|
+
const matches = (node) =>
|
|
604
|
+
type === ALL_EDGE_TYPES || type === this.#nodes.typeOf(node);
|
|
605
|
+
|
|
602
606
|
let node = this.#nodes.head(from);
|
|
603
607
|
while (node !== null) {
|
|
604
|
-
|
|
608
|
+
if (matches(node)) {
|
|
609
|
+
let edge = this.#nodes.lastOut(node);
|
|
610
|
+
while (edge !== null) {
|
|
611
|
+
let to = this.#edges.to(edge);
|
|
612
|
+
if (fn(to)) {
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
edge = this.#edges.prevOut(edge);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
node = this.#nodes.next(node);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
forEachNodeIdConnectedTo(to: NodeId, fn: (nodeId: NodeId) => boolean | void) {
|
|
623
|
+
let node = this.#nodes.head(to);
|
|
624
|
+
while (node !== null) {
|
|
625
|
+
let edge = this.#nodes.firstIn(node);
|
|
605
626
|
while (edge !== null) {
|
|
606
|
-
let
|
|
607
|
-
if (fn(
|
|
627
|
+
let from = this.#edges.from(edge);
|
|
628
|
+
if (fn(from)) {
|
|
608
629
|
return;
|
|
609
630
|
}
|
|
610
|
-
edge = this.#edges.
|
|
631
|
+
edge = this.#edges.nextIn(edge);
|
|
611
632
|
}
|
|
612
633
|
node = this.#nodes.next(node);
|
|
613
634
|
}
|
|
@@ -973,6 +994,7 @@ export class SharedTypeMap<TItemType, THash, TAddress: number>
|
|
|
973
994
|
// Trick Flow into believing in `Symbol.iterator`.
|
|
974
995
|
// See https://github.com/facebook/flow/issues/1163#issuecomment-353523840
|
|
975
996
|
/*:: @@iterator(): Iterator<TAddress> { return ({}: any); } */
|
|
997
|
+
|
|
976
998
|
// $FlowFixMe[unsupported-syntax]
|
|
977
999
|
*[Symbol.iterator](): Iterator<TAddress> {
|
|
978
1000
|
let max = this.count;
|
|
@@ -1086,6 +1108,7 @@ export class NodeTypeMap<TEdgeType> extends SharedTypeMap<
|
|
|
1086
1108
|
get nextId(): NodeId {
|
|
1087
1109
|
return toNodeId(this.data[NodeTypeMap.#NEXT_ID]);
|
|
1088
1110
|
}
|
|
1111
|
+
|
|
1089
1112
|
set nextId(nextId: NodeId) {
|
|
1090
1113
|
this.data[NodeTypeMap.#NEXT_ID] = fromNodeId(nextId);
|
|
1091
1114
|
}
|
package/src/Graph.js
CHANGED
|
@@ -17,6 +17,7 @@ export type GraphOpts<TNode, TEdgeType: number = 1> = {|
|
|
|
17
17
|
nodes?: Array<TNode | null>,
|
|
18
18
|
adjacencyList?: SerializedAdjacencyList<TEdgeType>,
|
|
19
19
|
rootNodeId?: ?NodeId,
|
|
20
|
+
initialCapacity?: number,
|
|
20
21
|
|};
|
|
21
22
|
|
|
22
23
|
export type SerializedGraph<TNode, TEdgeType: number = 1> = {|
|
|
@@ -84,9 +85,12 @@ export default class Graph<TNode, TEdgeType: number = 1> {
|
|
|
84
85
|
this.setRootNodeId(opts?.rootNodeId);
|
|
85
86
|
|
|
86
87
|
let adjacencyList = opts?.adjacencyList;
|
|
88
|
+
let initialCapacity = opts?.initialCapacity;
|
|
87
89
|
this.adjacencyList = adjacencyList
|
|
88
90
|
? AdjacencyList.deserialize(adjacencyList)
|
|
89
|
-
: new AdjacencyList<TEdgeType>(
|
|
91
|
+
: new AdjacencyList<TEdgeType>(
|
|
92
|
+
typeof initialCapacity === 'number' ? {initialCapacity} : undefined,
|
|
93
|
+
);
|
|
90
94
|
}
|
|
91
95
|
|
|
92
96
|
setRootNodeId(id: ?NodeId) {
|
|
@@ -159,6 +163,29 @@ export default class Graph<TNode, TEdgeType: number = 1> {
|
|
|
159
163
|
return this.adjacencyList.hasEdge(from, to, type);
|
|
160
164
|
}
|
|
161
165
|
|
|
166
|
+
forEachNodeIdConnectedTo(to: NodeId, fn: (nodeId: NodeId) => boolean | void) {
|
|
167
|
+
this._assertHasNodeId(to);
|
|
168
|
+
|
|
169
|
+
this.adjacencyList.forEachNodeIdConnectedTo(to, fn);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
forEachNodeIdConnectedFrom(
|
|
173
|
+
from: NodeId,
|
|
174
|
+
fn: (nodeId: NodeId) => void,
|
|
175
|
+
type: AllEdgeTypes | TEdgeType | NullEdgeType = ALL_EDGE_TYPES,
|
|
176
|
+
) {
|
|
177
|
+
this._assertHasNodeId(from);
|
|
178
|
+
|
|
179
|
+
this.adjacencyList.forEachNodeIdConnectedFromReverse(
|
|
180
|
+
from,
|
|
181
|
+
(id) => {
|
|
182
|
+
fn(id);
|
|
183
|
+
return false;
|
|
184
|
+
},
|
|
185
|
+
type,
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
162
189
|
getNodeIdsConnectedTo(
|
|
163
190
|
nodeId: NodeId,
|
|
164
191
|
type:
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// @flow strict-local
|
|
2
2
|
|
|
3
3
|
import assert from 'assert';
|
|
4
|
+
import sinon from 'sinon';
|
|
4
5
|
import path from 'path';
|
|
5
6
|
import {Worker} from 'worker_threads';
|
|
6
7
|
|
|
@@ -272,6 +273,180 @@ describe('AdjacencyList', () => {
|
|
|
272
273
|
assert.ok(graph.hasEdge(b, c, [2, 3]));
|
|
273
274
|
});
|
|
274
275
|
|
|
276
|
+
describe('forEachNodeIdConnectedTo', () => {
|
|
277
|
+
it('iterates no edges if there are none', () => {
|
|
278
|
+
const graph = new AdjacencyList();
|
|
279
|
+
const a = graph.addNode();
|
|
280
|
+
const b = graph.addNode();
|
|
281
|
+
const c = graph.addNode();
|
|
282
|
+
|
|
283
|
+
graph.addEdge(a, b);
|
|
284
|
+
graph.addEdge(c, b);
|
|
285
|
+
|
|
286
|
+
const callback = sinon.spy(() => {});
|
|
287
|
+
graph.forEachNodeIdConnectedTo(c, callback);
|
|
288
|
+
|
|
289
|
+
assert.equal(callback.callCount, 0);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('iterates incoming edges to a node', () => {
|
|
293
|
+
const graph = new AdjacencyList();
|
|
294
|
+
const a = graph.addNode();
|
|
295
|
+
const b = graph.addNode();
|
|
296
|
+
const c = graph.addNode();
|
|
297
|
+
|
|
298
|
+
graph.addEdge(a, b);
|
|
299
|
+
graph.addEdge(c, b);
|
|
300
|
+
|
|
301
|
+
const nodeIds = [];
|
|
302
|
+
graph.forEachNodeIdConnectedTo(b, (id) => {
|
|
303
|
+
nodeIds.push(id);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
assert.deepEqual(nodeIds, [a, c]);
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
it('stops traversal if the return value is true', () => {
|
|
310
|
+
const graph = new AdjacencyList();
|
|
311
|
+
const a = graph.addNode();
|
|
312
|
+
const b = graph.addNode();
|
|
313
|
+
const c = graph.addNode();
|
|
314
|
+
const d = graph.addNode();
|
|
315
|
+
|
|
316
|
+
graph.addEdge(a, d);
|
|
317
|
+
graph.addEdge(b, d);
|
|
318
|
+
graph.addEdge(c, d);
|
|
319
|
+
|
|
320
|
+
const nodeIds = [];
|
|
321
|
+
graph.forEachNodeIdConnectedTo(d, (nodeId) => {
|
|
322
|
+
nodeIds.push(nodeId);
|
|
323
|
+
return true;
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
assert.deepEqual(nodeIds, [a]);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
it('terminates if the graph is cyclic', () => {
|
|
330
|
+
const graph = new AdjacencyList();
|
|
331
|
+
const a = graph.addNode();
|
|
332
|
+
const b = graph.addNode();
|
|
333
|
+
const c = graph.addNode();
|
|
334
|
+
|
|
335
|
+
graph.addEdge(a, b);
|
|
336
|
+
graph.addEdge(c, b);
|
|
337
|
+
graph.addEdge(b, b);
|
|
338
|
+
|
|
339
|
+
const nodeIds = [];
|
|
340
|
+
graph.forEachNodeIdConnectedTo(b, (id) => {
|
|
341
|
+
nodeIds.push(id);
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
assert.deepEqual(nodeIds, [a, c, b]);
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
describe('forEachNodeIdConnectedFromReverse', () => {
|
|
349
|
+
it('iterates no edges if there are none', () => {
|
|
350
|
+
const graph = new AdjacencyList();
|
|
351
|
+
const a = graph.addNode();
|
|
352
|
+
const b = graph.addNode();
|
|
353
|
+
const c = graph.addNode();
|
|
354
|
+
|
|
355
|
+
graph.addEdge(a, b);
|
|
356
|
+
graph.addEdge(b, c);
|
|
357
|
+
|
|
358
|
+
const callback = sinon.spy(() => false);
|
|
359
|
+
graph.forEachNodeIdConnectedFromReverse(c, callback);
|
|
360
|
+
|
|
361
|
+
assert.equal(callback.callCount, 0);
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
it('iterates outgoing edges from a node', () => {
|
|
365
|
+
const graph = new AdjacencyList();
|
|
366
|
+
const a = graph.addNode();
|
|
367
|
+
const b = graph.addNode();
|
|
368
|
+
const c = graph.addNode();
|
|
369
|
+
const d = graph.addNode();
|
|
370
|
+
const e = graph.addNode();
|
|
371
|
+
|
|
372
|
+
graph.addEdge(a, b, 1);
|
|
373
|
+
graph.addEdge(a, c, 2);
|
|
374
|
+
graph.addEdge(b, d, 3);
|
|
375
|
+
graph.addEdge(c, e, 4);
|
|
376
|
+
|
|
377
|
+
const nodeIds = [];
|
|
378
|
+
graph.forEachNodeIdConnectedFromReverse(a, (nodeId) => {
|
|
379
|
+
nodeIds.push(nodeId);
|
|
380
|
+
return false;
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
assert.deepEqual(nodeIds, [b, c]);
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
it('terminates if the graph is cyclic', () => {
|
|
387
|
+
const graph = new AdjacencyList();
|
|
388
|
+
const a = graph.addNode();
|
|
389
|
+
const b = graph.addNode();
|
|
390
|
+
const c = graph.addNode();
|
|
391
|
+
|
|
392
|
+
graph.addEdge(a, b);
|
|
393
|
+
graph.addEdge(a, c);
|
|
394
|
+
graph.addEdge(a, a);
|
|
395
|
+
|
|
396
|
+
const nodeIds = [];
|
|
397
|
+
graph.forEachNodeIdConnectedFromReverse(a, (nodeId) => {
|
|
398
|
+
nodeIds.push(nodeId);
|
|
399
|
+
return false;
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
assert.deepEqual(nodeIds, [a, c, b]);
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
it('stops traversal if the return value is true', () => {
|
|
406
|
+
const graph = new AdjacencyList();
|
|
407
|
+
const a = graph.addNode();
|
|
408
|
+
const b = graph.addNode();
|
|
409
|
+
const c = graph.addNode();
|
|
410
|
+
const d = graph.addNode();
|
|
411
|
+
|
|
412
|
+
graph.addEdge(a, b);
|
|
413
|
+
graph.addEdge(a, c);
|
|
414
|
+
graph.addEdge(a, d);
|
|
415
|
+
|
|
416
|
+
const nodeIds = [];
|
|
417
|
+
graph.forEachNodeIdConnectedFromReverse(a, (nodeId) => {
|
|
418
|
+
nodeIds.push(nodeId);
|
|
419
|
+
return true;
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
assert.deepEqual(nodeIds, [d]);
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
it('filters edges by type', () => {
|
|
426
|
+
const graph = new AdjacencyList();
|
|
427
|
+
const a = graph.addNode();
|
|
428
|
+
const b = graph.addNode();
|
|
429
|
+
const c = graph.addNode();
|
|
430
|
+
const d = graph.addNode();
|
|
431
|
+
|
|
432
|
+
graph.addEdge(a, b, 2);
|
|
433
|
+
graph.addEdge(a, c, 2);
|
|
434
|
+
graph.addEdge(a, d);
|
|
435
|
+
|
|
436
|
+
const nodeIds = [];
|
|
437
|
+
graph.forEachNodeIdConnectedFromReverse(
|
|
438
|
+
a,
|
|
439
|
+
(nodeId) => {
|
|
440
|
+
nodeIds.push(nodeId);
|
|
441
|
+
return false;
|
|
442
|
+
},
|
|
443
|
+
2,
|
|
444
|
+
);
|
|
445
|
+
|
|
446
|
+
assert.deepEqual(nodeIds, [c, b]);
|
|
447
|
+
});
|
|
448
|
+
});
|
|
449
|
+
|
|
275
450
|
describe('deserialize', function () {
|
|
276
451
|
this.timeout(10000);
|
|
277
452
|
|
package/test/Graph.test.js
CHANGED
|
@@ -556,4 +556,52 @@ describe('Graph', () => {
|
|
|
556
556
|
});
|
|
557
557
|
});
|
|
558
558
|
});
|
|
559
|
+
|
|
560
|
+
describe('forEachNodeConnectedTo', () => {
|
|
561
|
+
it('calls the callback for each node connected to the given node', () => {
|
|
562
|
+
const graph = new Graph();
|
|
563
|
+
const a = graph.addNode('0');
|
|
564
|
+
const b = graph.addNode('1');
|
|
565
|
+
const c = graph.addNode('2');
|
|
566
|
+
const d = graph.addNode('3');
|
|
567
|
+
graph.addNode('disconnected-1');
|
|
568
|
+
graph.addNode('disconnected-2');
|
|
569
|
+
graph.addEdge(a, b);
|
|
570
|
+
graph.addEdge(b, c);
|
|
571
|
+
graph.addEdge(b, d);
|
|
572
|
+
graph.addEdge(a, c);
|
|
573
|
+
graph.addEdge(c, d);
|
|
574
|
+
|
|
575
|
+
const order = [];
|
|
576
|
+
graph.forEachNodeIdConnectedTo(d, (node) => {
|
|
577
|
+
order.push(node);
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
assert.deepEqual(order, [b, c]);
|
|
581
|
+
});
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
describe('forEachNodeConnectedFrom', () => {
|
|
585
|
+
it('calls the callback for each node connected from the given node', () => {
|
|
586
|
+
const graph = new Graph();
|
|
587
|
+
const a = graph.addNode('0');
|
|
588
|
+
const b = graph.addNode('1');
|
|
589
|
+
const c = graph.addNode('2');
|
|
590
|
+
const d = graph.addNode('3');
|
|
591
|
+
graph.addNode('disconnected-1');
|
|
592
|
+
graph.addNode('disconnected-2');
|
|
593
|
+
graph.addEdge(a, b);
|
|
594
|
+
graph.addEdge(b, c);
|
|
595
|
+
graph.addEdge(b, d);
|
|
596
|
+
graph.addEdge(a, c);
|
|
597
|
+
graph.addEdge(c, d);
|
|
598
|
+
|
|
599
|
+
const order = [];
|
|
600
|
+
graph.forEachNodeIdConnectedFrom(a, (node) => {
|
|
601
|
+
order.push(node);
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
assert.deepEqual(order, [c, b]);
|
|
605
|
+
});
|
|
606
|
+
});
|
|
559
607
|
});
|