@atlaspack/bundler-experimental 2.12.1-canary.3626 → 2.12.1-canary.3628
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.
@@ -0,0 +1,58 @@
|
|
1
|
+
"use strict";
|
2
|
+
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
4
|
+
value: true
|
5
|
+
});
|
6
|
+
exports.findStronglyConnectedComponents = findStronglyConnectedComponents;
|
7
|
+
/**
|
8
|
+
* Robert Tarjan's algorithm to find strongly connected components in a graph.
|
9
|
+
*
|
10
|
+
* Time complexity: O(V + E)
|
11
|
+
* Space complexity (worst case): O(V)
|
12
|
+
*
|
13
|
+
* * https://web.archive.org/web/20170829214726id_/http://www.cs.ucsb.edu/~gilbert/cs240a/old/cs240aSpr2011/slides/TarjanDFS.pdf
|
14
|
+
* * https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
|
15
|
+
*/
|
16
|
+
function findStronglyConnectedComponents(graph) {
|
17
|
+
const result = [];
|
18
|
+
let index = 0;
|
19
|
+
const stack = [];
|
20
|
+
const state = new Array(graph.nodes.length).fill(null).map(() => ({
|
21
|
+
index: null,
|
22
|
+
lowlink: null,
|
23
|
+
onStack: false
|
24
|
+
}));
|
25
|
+
graph.nodes.forEach((node, nodeId) => {
|
26
|
+
if (node != null && state[nodeId].index == null) {
|
27
|
+
strongConnect(nodeId);
|
28
|
+
}
|
29
|
+
});
|
30
|
+
function strongConnect(nodeId) {
|
31
|
+
const nodeState = state[nodeId];
|
32
|
+
nodeState.index = index;
|
33
|
+
nodeState.lowlink = index;
|
34
|
+
index += 1;
|
35
|
+
stack.push(nodeId);
|
36
|
+
nodeState.onStack = true;
|
37
|
+
for (let neighborId of graph.getNodeIdsConnectedFrom(nodeId)) {
|
38
|
+
const neighborState = state[neighborId];
|
39
|
+
if (neighborState.index == null) {
|
40
|
+
strongConnect(neighborId);
|
41
|
+
nodeState.lowlink = Math.min(Number(nodeState.lowlink), Number(neighborState.lowlink));
|
42
|
+
} else if (neighborState.onStack) {
|
43
|
+
nodeState.lowlink = Math.min(Number(nodeState.lowlink), Number(neighborState.index));
|
44
|
+
}
|
45
|
+
}
|
46
|
+
if (nodeState.lowlink === nodeState.index) {
|
47
|
+
const component = [];
|
48
|
+
let member;
|
49
|
+
do {
|
50
|
+
member = stack.pop();
|
51
|
+
state[member].onStack = false;
|
52
|
+
component.push(member);
|
53
|
+
} while (member !== nodeId);
|
54
|
+
result.push(component);
|
55
|
+
}
|
56
|
+
}
|
57
|
+
return result;
|
58
|
+
}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@atlaspack/bundler-experimental",
|
3
|
-
"version": "2.12.1-canary.
|
3
|
+
"version": "2.12.1-canary.3628+719a78290",
|
4
4
|
"license": "(MIT OR Apache-2.0)",
|
5
5
|
"publishConfig": {
|
6
6
|
"access": "public"
|
@@ -16,19 +16,19 @@
|
|
16
16
|
"parcel": "^2.12.0"
|
17
17
|
},
|
18
18
|
"dependencies": {
|
19
|
-
"@atlaspack/core": "2.12.1-canary.
|
20
|
-
"@atlaspack/diagnostic": "2.12.1-canary.
|
21
|
-
"@atlaspack/feature-flags": "2.12.1-canary.
|
22
|
-
"@atlaspack/graph": "3.2.1-canary.
|
23
|
-
"@atlaspack/logger": "2.12.1-canary.
|
24
|
-
"@atlaspack/plugin": "2.12.1-canary.
|
25
|
-
"@atlaspack/rust": "2.12.1-canary.
|
26
|
-
"@atlaspack/types": "2.12.1-canary.
|
27
|
-
"@atlaspack/utils": "2.12.1-canary.
|
19
|
+
"@atlaspack/core": "2.12.1-canary.3628+719a78290",
|
20
|
+
"@atlaspack/diagnostic": "2.12.1-canary.3628+719a78290",
|
21
|
+
"@atlaspack/feature-flags": "2.12.1-canary.3628+719a78290",
|
22
|
+
"@atlaspack/graph": "3.2.1-canary.3628+719a78290",
|
23
|
+
"@atlaspack/logger": "2.12.1-canary.3628+719a78290",
|
24
|
+
"@atlaspack/plugin": "2.12.1-canary.3628+719a78290",
|
25
|
+
"@atlaspack/rust": "2.12.1-canary.3628+719a78290",
|
26
|
+
"@atlaspack/types": "2.12.1-canary.3628+719a78290",
|
27
|
+
"@atlaspack/utils": "2.12.1-canary.3628+719a78290",
|
28
28
|
"nullthrows": "^1.1.1"
|
29
29
|
},
|
30
30
|
"devDependencies": {
|
31
|
-
"@atlaspack/fs": "2.12.1-canary.
|
31
|
+
"@atlaspack/fs": "2.12.1-canary.3628+719a78290"
|
32
32
|
},
|
33
|
-
"gitHead": "
|
33
|
+
"gitHead": "719a7829084ea6e2cdabba030f913b0801dde852"
|
34
34
|
}
|
@@ -0,0 +1,79 @@
|
|
1
|
+
// @flow strict-local
|
2
|
+
|
3
|
+
import type {Graph, NodeId} from '@atlaspack/graph';
|
4
|
+
|
5
|
+
export type StronglyConnectedComponent = NodeId[];
|
6
|
+
|
7
|
+
/**
|
8
|
+
* Robert Tarjan's algorithm to find strongly connected components in a graph.
|
9
|
+
*
|
10
|
+
* Time complexity: O(V + E)
|
11
|
+
* Space complexity (worst case): O(V)
|
12
|
+
*
|
13
|
+
* * https://web.archive.org/web/20170829214726id_/http://www.cs.ucsb.edu/~gilbert/cs240a/old/cs240aSpr2011/slides/TarjanDFS.pdf
|
14
|
+
* * https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
|
15
|
+
*/
|
16
|
+
export function findStronglyConnectedComponents<T, E: number>(
|
17
|
+
graph: Graph<T, E>,
|
18
|
+
): StronglyConnectedComponent[] {
|
19
|
+
type State = {|
|
20
|
+
index: null | NodeId,
|
21
|
+
lowlink: null | NodeId,
|
22
|
+
onStack: boolean,
|
23
|
+
|};
|
24
|
+
const result = [];
|
25
|
+
let index = 0;
|
26
|
+
const stack = [];
|
27
|
+
const state: State[] = new Array(graph.nodes.length).fill(null).map(() => ({
|
28
|
+
index: null,
|
29
|
+
lowlink: null,
|
30
|
+
onStack: false,
|
31
|
+
}));
|
32
|
+
graph.nodes.forEach((node, nodeId) => {
|
33
|
+
if (node != null && state[nodeId].index == null) {
|
34
|
+
strongConnect(nodeId);
|
35
|
+
}
|
36
|
+
});
|
37
|
+
function strongConnect(nodeId) {
|
38
|
+
const nodeState = state[nodeId];
|
39
|
+
nodeState.index = index;
|
40
|
+
nodeState.lowlink = index;
|
41
|
+
|
42
|
+
index += 1;
|
43
|
+
|
44
|
+
stack.push(nodeId);
|
45
|
+
nodeState.onStack = true;
|
46
|
+
|
47
|
+
for (let neighborId of graph.getNodeIdsConnectedFrom(nodeId)) {
|
48
|
+
const neighborState = state[neighborId];
|
49
|
+
if (neighborState.index == null) {
|
50
|
+
strongConnect(neighborId);
|
51
|
+
|
52
|
+
nodeState.lowlink = Math.min(
|
53
|
+
Number(nodeState.lowlink),
|
54
|
+
Number(neighborState.lowlink),
|
55
|
+
);
|
56
|
+
} else if (neighborState.onStack) {
|
57
|
+
nodeState.lowlink = Math.min(
|
58
|
+
Number(nodeState.lowlink),
|
59
|
+
Number(neighborState.index),
|
60
|
+
);
|
61
|
+
}
|
62
|
+
}
|
63
|
+
|
64
|
+
if (nodeState.lowlink === nodeState.index) {
|
65
|
+
const component = [];
|
66
|
+
let member;
|
67
|
+
|
68
|
+
do {
|
69
|
+
member = stack.pop();
|
70
|
+
state[member].onStack = false;
|
71
|
+
component.push(member);
|
72
|
+
} while (member !== nodeId);
|
73
|
+
|
74
|
+
result.push(component);
|
75
|
+
}
|
76
|
+
}
|
77
|
+
|
78
|
+
return result;
|
79
|
+
}
|
@@ -0,0 +1,81 @@
|
|
1
|
+
// @flow strict-local
|
2
|
+
|
3
|
+
import {Graph} from '@atlaspack/graph';
|
4
|
+
import assert from 'assert';
|
5
|
+
import {findStronglyConnectedComponents} from '../../../src/DominatorBundler/cycleBreaker/findStronglyConnectedComponents';
|
6
|
+
|
7
|
+
describe('findStronglyConnectedComponents', () => {
|
8
|
+
it('should find strongly connected components', () => {
|
9
|
+
/*
|
10
|
+
digraph graph {
|
11
|
+
root -> a;
|
12
|
+
a -> b;
|
13
|
+
b -> a;
|
14
|
+
c;
|
15
|
+
}
|
16
|
+
*/
|
17
|
+
const graph = new Graph();
|
18
|
+
const root = graph.addNode('root');
|
19
|
+
graph.setRootNodeId(root);
|
20
|
+
const a = graph.addNode('a');
|
21
|
+
const b = graph.addNode('b');
|
22
|
+
const c = graph.addNode('c');
|
23
|
+
|
24
|
+
graph.addEdge(root, a);
|
25
|
+
graph.addEdge(a, b);
|
26
|
+
graph.addEdge(b, a);
|
27
|
+
|
28
|
+
const result = findStronglyConnectedComponents(graph);
|
29
|
+
|
30
|
+
assert.deepStrictEqual(result, [[b, a], [root], [c]]);
|
31
|
+
});
|
32
|
+
|
33
|
+
it('works over an empty graph with a root', () => {
|
34
|
+
/*
|
35
|
+
digraph graph {
|
36
|
+
root;
|
37
|
+
}
|
38
|
+
*/
|
39
|
+
const graph = new Graph();
|
40
|
+
const root = graph.addNode('root');
|
41
|
+
graph.setRootNodeId(root);
|
42
|
+
|
43
|
+
const result = findStronglyConnectedComponents(graph);
|
44
|
+
assert.deepStrictEqual(result, [[root]]);
|
45
|
+
});
|
46
|
+
|
47
|
+
it('works over an empty graph', () => {
|
48
|
+
/*
|
49
|
+
digraph graph {
|
50
|
+
}
|
51
|
+
*/
|
52
|
+
const graph = new Graph();
|
53
|
+
|
54
|
+
const result = findStronglyConnectedComponents(graph);
|
55
|
+
assert.deepStrictEqual(result, []);
|
56
|
+
});
|
57
|
+
|
58
|
+
it('returns all nodes if there are no cycles', () => {
|
59
|
+
/*
|
60
|
+
digraph graph {
|
61
|
+
root -> a;
|
62
|
+
a -> b;
|
63
|
+
b -> c;
|
64
|
+
}
|
65
|
+
*/
|
66
|
+
const graph = new Graph();
|
67
|
+
const root = graph.addNode('root');
|
68
|
+
graph.setRootNodeId(root);
|
69
|
+
const a = graph.addNode('a');
|
70
|
+
const b = graph.addNode('b');
|
71
|
+
const c = graph.addNode('c');
|
72
|
+
|
73
|
+
graph.addEdge(root, a);
|
74
|
+
graph.addEdge(a, b);
|
75
|
+
graph.addEdge(b, c);
|
76
|
+
|
77
|
+
const result = findStronglyConnectedComponents(graph);
|
78
|
+
|
79
|
+
assert.deepStrictEqual(result, [[c], [b], [a], [root]]);
|
80
|
+
});
|
81
|
+
});
|