@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.
@@ -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
- let edge = this.#nodes.lastOut(node);
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 to = this.#edges.to(edge);
459
- if (fn(to)) {
475
+ let from = this.#edges.from(edge);
476
+ if (fn(from)) {
460
477
  return;
461
478
  }
462
- edge = this.#edges.prevOut(edge);
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
- 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) {
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.3520+8a5346e28",
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.3520+8a5346e28",
19
+ "@atlaspack/feature-flags": "2.12.1-dev.3565+b31bc6b33",
20
20
  "nullthrows": "^1.1.1"
21
21
  },
22
- "gitHead": "8a5346e28c1bb3b9cd40f1c4e77c66dd6666f1e4"
22
+ "gitHead": "b31bc6b33de40c158b9f4d8ff177238be0de409d"
23
23
  }
@@ -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
- let edge = this.#nodes.lastOut(node);
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 to = this.#edges.to(edge);
607
- if (fn(to)) {
627
+ let from = this.#edges.from(edge);
628
+ if (fn(from)) {
608
629
  return;
609
630
  }
610
- edge = this.#edges.prevOut(edge);
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
 
@@ -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
  });