@atlaspack/graph 3.2.1-dev.3520 → 3.2.1-dev.3566

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.
@@ -450,16 +450,36 @@ 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
- let edge = this.#nodes.lastOut(node);
457
- while (edge !== null) {
458
- let to = this.#edges.to(edge);
459
- if (fn(to)) {
460
- return;
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, type = 1) {
471
+ const matches = node => type === _Graph.ALL_EDGE_TYPES || type === this.#nodes.typeOf(node);
472
+ let node = this.#nodes.head(to);
473
+ while (node !== null) {
474
+ if (matches(node)) {
475
+ let edge = this.#nodes.firstIn(node);
476
+ while (edge !== null) {
477
+ let from = this.#edges.from(edge);
478
+ if (fn(from)) {
479
+ return;
480
+ }
481
+ edge = this.#edges.nextIn(edge);
461
482
  }
462
- edge = this.#edges.prevOut(edge);
463
483
  }
464
484
  node = this.#nodes.next(node);
465
485
  }
@@ -813,6 +833,7 @@ class SharedTypeMap {
813
833
  // Trick Flow into believing in `Symbol.iterator`.
814
834
  // See https://github.com/facebook/flow/issues/1163#issuecomment-353523840
815
835
  /*:: @@iterator(): Iterator<TAddress> { return ({}: any); } */
836
+
816
837
  // $FlowFixMe[unsupported-syntax]
817
838
  *[Symbol.iterator]() {
818
839
  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
- this.adjacencyList = adjacencyList ? _AdjacencyList.default.deserialize(adjacencyList) : new _AdjacencyList.default();
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, type = ALL_EDGE_TYPES) {
89
+ this._assertHasNodeId(to);
90
+ this.adjacencyList.forEachNodeIdConnectedTo(to, fn, type);
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.3520+8a5346e28",
3
+ "version": "3.2.1-dev.3566+facdfb05f",
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.3520+8a5346e28",
19
+ "@atlaspack/feature-flags": "2.12.1-dev.3566+facdfb05f",
20
20
  "nullthrows": "^1.1.1"
21
21
  },
22
- "gitHead": "8a5346e28c1bb3b9cd40f1c4e77c66dd6666f1e4"
22
+ "gitHead": "facdfb05f693e50037a82a4afa101adf093fd8c9"
23
23
  }
@@ -598,16 +598,46 @@ 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
- let edge = this.#nodes.lastOut(node);
605
- while (edge !== null) {
606
- let to = this.#edges.to(edge);
607
- if (fn(to)) {
608
- return;
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(
623
+ to: NodeId,
624
+ fn: (nodeId: NodeId) => boolean | void,
625
+ type: AllEdgeTypes | TEdgeType | NullEdgeType = 1,
626
+ ) {
627
+ const matches = (node) =>
628
+ type === ALL_EDGE_TYPES || type === this.#nodes.typeOf(node);
629
+
630
+ let node = this.#nodes.head(to);
631
+ while (node !== null) {
632
+ if (matches(node)) {
633
+ let edge = this.#nodes.firstIn(node);
634
+ while (edge !== null) {
635
+ let from = this.#edges.from(edge);
636
+ if (fn(from)) {
637
+ return;
638
+ }
639
+ edge = this.#edges.nextIn(edge);
609
640
  }
610
- edge = this.#edges.prevOut(edge);
611
641
  }
612
642
  node = this.#nodes.next(node);
613
643
  }
@@ -973,6 +1003,7 @@ export class SharedTypeMap<TItemType, THash, TAddress: number>
973
1003
  // Trick Flow into believing in `Symbol.iterator`.
974
1004
  // See https://github.com/facebook/flow/issues/1163#issuecomment-353523840
975
1005
  /*:: @@iterator(): Iterator<TAddress> { return ({}: any); } */
1006
+
976
1007
  // $FlowFixMe[unsupported-syntax]
977
1008
  *[Symbol.iterator](): Iterator<TAddress> {
978
1009
  let max = this.count;
@@ -1086,6 +1117,7 @@ export class NodeTypeMap<TEdgeType> extends SharedTypeMap<
1086
1117
  get nextId(): NodeId {
1087
1118
  return toNodeId(this.data[NodeTypeMap.#NEXT_ID]);
1088
1119
  }
1120
+
1089
1121
  set nextId(nextId: NodeId) {
1090
1122
  this.data[NodeTypeMap.#NEXT_ID] = fromNodeId(nextId);
1091
1123
  }
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,33 @@ export default class Graph<TNode, TEdgeType: number = 1> {
159
163
  return this.adjacencyList.hasEdge(from, to, type);
160
164
  }
161
165
 
166
+ forEachNodeIdConnectedTo(
167
+ to: NodeId,
168
+ fn: (nodeId: NodeId) => boolean | void,
169
+ type: AllEdgeTypes | TEdgeType | NullEdgeType = ALL_EDGE_TYPES,
170
+ ) {
171
+ this._assertHasNodeId(to);
172
+
173
+ this.adjacencyList.forEachNodeIdConnectedTo(to, fn, type);
174
+ }
175
+
176
+ forEachNodeIdConnectedFrom(
177
+ from: NodeId,
178
+ fn: (nodeId: NodeId) => void,
179
+ type: AllEdgeTypes | TEdgeType | NullEdgeType = ALL_EDGE_TYPES,
180
+ ) {
181
+ this._assertHasNodeId(from);
182
+
183
+ this.adjacencyList.forEachNodeIdConnectedFromReverse(
184
+ from,
185
+ (id) => {
186
+ fn(id);
187
+ return false;
188
+ },
189
+ type,
190
+ );
191
+ }
192
+
162
193
  getNodeIdsConnectedTo(
163
194
  nodeId: NodeId,
164
195
  type:
@@ -1,11 +1,13 @@
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
 
7
8
  import AdjacencyList, {NodeTypeMap, EdgeTypeMap} from '../src/AdjacencyList';
8
9
  import {toNodeId} from '../src/types';
10
+ import {ALL_EDGE_TYPES} from '../src/Graph';
9
11
 
10
12
  describe('AdjacencyList', () => {
11
13
  it('constructor should initialize an empty graph', () => {
@@ -272,6 +274,218 @@ describe('AdjacencyList', () => {
272
274
  assert.ok(graph.hasEdge(b, c, [2, 3]));
273
275
  });
274
276
 
277
+ describe('forEachNodeIdConnectedTo', () => {
278
+ it('iterates no edges if there are none', () => {
279
+ const graph = new AdjacencyList();
280
+ const a = graph.addNode();
281
+ const b = graph.addNode();
282
+ const c = graph.addNode();
283
+
284
+ graph.addEdge(a, b);
285
+ graph.addEdge(c, b);
286
+
287
+ const callback = sinon.spy(() => {});
288
+ graph.forEachNodeIdConnectedTo(c, callback);
289
+
290
+ assert.equal(callback.callCount, 0);
291
+ });
292
+
293
+ it('iterates incoming edges to a node', () => {
294
+ const graph = new AdjacencyList();
295
+ const a = graph.addNode();
296
+ const b = graph.addNode();
297
+ const c = graph.addNode();
298
+
299
+ graph.addEdge(a, b);
300
+ graph.addEdge(c, b);
301
+
302
+ const nodeIds = [];
303
+ graph.forEachNodeIdConnectedTo(b, (id) => {
304
+ nodeIds.push(id);
305
+ });
306
+
307
+ assert.deepEqual(nodeIds, [a, c]);
308
+ });
309
+
310
+ it('stops traversal if the return value is true', () => {
311
+ const graph = new AdjacencyList();
312
+ const a = graph.addNode();
313
+ const b = graph.addNode();
314
+ const c = graph.addNode();
315
+ const d = graph.addNode();
316
+
317
+ graph.addEdge(a, d);
318
+ graph.addEdge(b, d);
319
+ graph.addEdge(c, d);
320
+
321
+ const nodeIds = [];
322
+ graph.forEachNodeIdConnectedTo(d, (nodeId) => {
323
+ nodeIds.push(nodeId);
324
+ return true;
325
+ });
326
+
327
+ assert.deepEqual(nodeIds, [a]);
328
+ });
329
+
330
+ it('terminates if the graph is cyclic', () => {
331
+ const graph = new AdjacencyList();
332
+ const a = graph.addNode();
333
+ const b = graph.addNode();
334
+ const c = graph.addNode();
335
+
336
+ graph.addEdge(a, b);
337
+ graph.addEdge(c, b);
338
+ graph.addEdge(b, b);
339
+
340
+ const nodeIds = [];
341
+ graph.forEachNodeIdConnectedTo(b, (id) => {
342
+ nodeIds.push(id);
343
+ });
344
+
345
+ assert.deepEqual(nodeIds, [a, c, b]);
346
+ });
347
+
348
+ it('respects the edge type', () => {
349
+ const graph = new AdjacencyList();
350
+ const a = graph.addNode();
351
+ const b = graph.addNode();
352
+ const c = graph.addNode();
353
+
354
+ graph.addEdge(a, b);
355
+ graph.addEdge(c, b, 2);
356
+
357
+ const nodeIds = [];
358
+ graph.forEachNodeIdConnectedTo(b, (id) => {
359
+ nodeIds.push(id);
360
+ });
361
+
362
+ assert.deepEqual(nodeIds, [a]);
363
+ });
364
+
365
+ it('iterates all edges if all edge type is provided', () => {
366
+ const graph = new AdjacencyList();
367
+ const a = graph.addNode();
368
+ const b = graph.addNode();
369
+ const c = graph.addNode();
370
+
371
+ graph.addEdge(a, b);
372
+ graph.addEdge(c, b, 2);
373
+
374
+ const nodeIds = [];
375
+ graph.forEachNodeIdConnectedTo(
376
+ b,
377
+ (id) => {
378
+ nodeIds.push(id);
379
+ },
380
+ ALL_EDGE_TYPES,
381
+ );
382
+
383
+ assert.deepEqual(nodeIds, [a, c]);
384
+ });
385
+ });
386
+
387
+ describe('forEachNodeIdConnectedFromReverse', () => {
388
+ it('iterates no edges if there are none', () => {
389
+ const graph = new AdjacencyList();
390
+ const a = graph.addNode();
391
+ const b = graph.addNode();
392
+ const c = graph.addNode();
393
+
394
+ graph.addEdge(a, b);
395
+ graph.addEdge(b, c);
396
+
397
+ const callback = sinon.spy(() => false);
398
+ graph.forEachNodeIdConnectedFromReverse(c, callback);
399
+
400
+ assert.equal(callback.callCount, 0);
401
+ });
402
+
403
+ it('iterates outgoing edges from a node', () => {
404
+ const graph = new AdjacencyList();
405
+ const a = graph.addNode();
406
+ const b = graph.addNode();
407
+ const c = graph.addNode();
408
+ const d = graph.addNode();
409
+ const e = graph.addNode();
410
+
411
+ graph.addEdge(a, b, 1);
412
+ graph.addEdge(a, c, 2);
413
+ graph.addEdge(b, d, 3);
414
+ graph.addEdge(c, e, 4);
415
+
416
+ const nodeIds = [];
417
+ graph.forEachNodeIdConnectedFromReverse(a, (nodeId) => {
418
+ nodeIds.push(nodeId);
419
+ return false;
420
+ });
421
+
422
+ assert.deepEqual(nodeIds, [b, c]);
423
+ });
424
+
425
+ it('terminates if the graph is cyclic', () => {
426
+ const graph = new AdjacencyList();
427
+ const a = graph.addNode();
428
+ const b = graph.addNode();
429
+ const c = graph.addNode();
430
+
431
+ graph.addEdge(a, b);
432
+ graph.addEdge(a, c);
433
+ graph.addEdge(a, a);
434
+
435
+ const nodeIds = [];
436
+ graph.forEachNodeIdConnectedFromReverse(a, (nodeId) => {
437
+ nodeIds.push(nodeId);
438
+ return false;
439
+ });
440
+
441
+ assert.deepEqual(nodeIds, [a, c, b]);
442
+ });
443
+
444
+ it('stops traversal if the return value is true', () => {
445
+ const graph = new AdjacencyList();
446
+ const a = graph.addNode();
447
+ const b = graph.addNode();
448
+ const c = graph.addNode();
449
+ const d = graph.addNode();
450
+
451
+ graph.addEdge(a, b);
452
+ graph.addEdge(a, c);
453
+ graph.addEdge(a, d);
454
+
455
+ const nodeIds = [];
456
+ graph.forEachNodeIdConnectedFromReverse(a, (nodeId) => {
457
+ nodeIds.push(nodeId);
458
+ return true;
459
+ });
460
+
461
+ assert.deepEqual(nodeIds, [d]);
462
+ });
463
+
464
+ it('filters edges by type', () => {
465
+ const graph = new AdjacencyList();
466
+ const a = graph.addNode();
467
+ const b = graph.addNode();
468
+ const c = graph.addNode();
469
+ const d = graph.addNode();
470
+
471
+ graph.addEdge(a, b, 2);
472
+ graph.addEdge(a, c, 2);
473
+ graph.addEdge(a, d);
474
+
475
+ const nodeIds = [];
476
+ graph.forEachNodeIdConnectedFromReverse(
477
+ a,
478
+ (nodeId) => {
479
+ nodeIds.push(nodeId);
480
+ return false;
481
+ },
482
+ 2,
483
+ );
484
+
485
+ assert.deepEqual(nodeIds, [c, b]);
486
+ });
487
+ });
488
+
275
489
  describe('deserialize', function () {
276
490
  this.timeout(10000);
277
491
 
@@ -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
  });