Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 18 additions & 32 deletions lib/dep_graph.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* Detects cycles and throws an Error if one is detected (unless the "circular"
* parameter is "true" in which case it ignores them).
*
* @param edges The edges to DFS through (this is a Map of node to Array of nodes)
* @param edges The edges to DFS through (this is a Map of node to Set of nodes)
* @param leavesOnly Whether to only return "leaf" nodes (ones who have no edges)
* @param result An array in which the results will be populated
* @param circular A boolean to allow circular dependencies
Expand Down Expand Up @@ -45,7 +45,7 @@ function createDFS(edges, leavesOnly, result, circular) {

inCurrentPath.add(node);
currentPath.push(node);
var nodeEdges = edges.get(node);
var nodeEdges = [...edges.get(node)];
// (push edges onto the todo stack in reverse order to be order-compatible with the old DFS implementation)
for (var i = nodeEdges.length - 1; i >= 0; i--) {
todo.push({ node: nodeEdges[i], processed: false });
Expand All @@ -57,7 +57,7 @@ function createDFS(edges, leavesOnly, result, circular) {
currentPath.pop();
inCurrentPath.delete(node);
visited.add(node);
if (!leavesOnly || edges.get(node).length === 0) {
if (!leavesOnly || edges.get(node).size === 0) {
result.push(node);
}
}
Expand All @@ -70,8 +70,8 @@ function createDFS(edges, leavesOnly, result, circular) {
*/
var DepGraph = (exports.DepGraph = function DepGraph(opts) {
this.nodes = new Map(); // Node -> Node/Data
this.outgoingEdges = new Map(); // Node -> [Dependency Node]
this.incomingEdges = new Map(); // Node -> [Dependant Node]
this.outgoingEdges = new Map(); // Node -> {Dependency Node}
this.incomingEdges = new Map(); // Node -> {Dependant Node}
this.circular = opts && !!opts.circular; // Allows circular deps
});
DepGraph.prototype = {
Expand All @@ -92,8 +92,8 @@ DepGraph.prototype = {
} else {
this.nodes.set(node, node);
}
this.outgoingEdges.set(node, []);
this.incomingEdges.set(node, []);
this.outgoingEdges.set(node, new Set());
this.incomingEdges.set(node, new Set());
}
},
/**
Expand All @@ -106,10 +106,7 @@ DepGraph.prototype = {
this.incomingEdges.delete(node);
[this.incomingEdges, this.outgoingEdges].forEach(function (edgeList) {
edgeList.forEach(function (v) {
var idx = v.indexOf(node);
if (idx >= 0) {
v.splice(idx, 1);
}
v.delete(node);
});
});
}
Expand Down Expand Up @@ -151,31 +148,20 @@ DepGraph.prototype = {
if (!this.hasNode(to)) {
throw new Error("Node does not exist: " + to);
}
if (this.outgoingEdges.get(from).indexOf(to) === -1) {
this.outgoingEdges.get(from).push(to);
}
if (this.incomingEdges.get(to).indexOf(from) === -1) {
this.incomingEdges.get(to).push(from);
}
this.outgoingEdges.get(from).add(to);
this.incomingEdges.get(to).add(from);
return true;
},
/**
* Remove a dependency between two nodes.
*/
removeDependency: function (from, to) {
var idx;
if (this.hasNode(from)) {
idx = this.outgoingEdges.get(from).indexOf(to);
if (idx >= 0) {
this.outgoingEdges.get(from).splice(idx, 1);
}
this.outgoingEdges.get(from).delete(to);
}

if (this.hasNode(to)) {
idx = this.incomingEdges.get(to).indexOf(from);
if (idx >= 0) {
this.incomingEdges.get(to).splice(idx, 1);
}
this.incomingEdges.get(to).delete(from);
}
},
/**
Expand All @@ -187,8 +173,8 @@ DepGraph.prototype = {
var result = new DepGraph();
source.nodes.forEach(function (v, n) {
result.nodes.set(n, v);
result.outgoingEdges.set(n, source.outgoingEdges.get(n).slice(0));
result.incomingEdges.set(n, source.incomingEdges.get(n).slice(0));
result.outgoingEdges.set(n, new Set(source.outgoingEdges.get(n)));
result.incomingEdges.set(n, new Set(source.incomingEdges.get(n)));
});
result.circular = source.circular;
return result;
Expand All @@ -200,7 +186,7 @@ DepGraph.prototype = {
*/
directDependenciesOf: function (node) {
if (this.hasNode(node)) {
return this.outgoingEdges.get(node).slice(0);
return [...this.outgoingEdges.get(node)];
} else {
throw new Error("Node does not exist: " + node);
}
Expand All @@ -212,7 +198,7 @@ DepGraph.prototype = {
*/
directDependantsOf: function (node) {
if (this.hasNode(node)) {
return this.incomingEdges.get(node).slice(0);
return [...this.incomingEdges.get(node)];
} else {
throw new Error("Node does not exist: " + node);
}
Expand Down Expand Up @@ -303,7 +289,7 @@ DepGraph.prototype = {
// run a DFS starting at these points to get the order
keys
.filter(function (node) {
return self.incomingEdges.get(node).length === 0;
return self.incomingEdges.get(node).size === 0;
})
.forEach(function (n) {
DFS(n);
Expand Down Expand Up @@ -331,7 +317,7 @@ DepGraph.prototype = {
entryNodes: function () {
var self = this;
return Array.from(this.nodes.keys()).filter(function (node) {
return self.incomingEdges.get(node).length === 0;
return self.incomingEdges.get(node).size === 0;
});
},
};
Expand Down
15 changes: 14 additions & 1 deletion specs/dep_graph_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,20 @@ describe("DepGraph Performance", function () {
var start = new Date().getTime();
g.overallOrder();
var end = new Date().getTime();
expect(start - end).toBeLessThan(1000);
expect(end - start).toBeLessThan(1000);
});

it("should construct very large graph with high-degree node in a reasonable amount of time", function () {
var start = new Date().getTime();
var g = new DepGraph();
// Create a graph with 100000 nodes, all depending on the same hub
g.addNode("hub");
for (var i = 0; i < 100000; i++) {
g.addNode(i.toString());
g.addDependency(i.toString(), "hub");
}
var end = new Date().getTime();
expect(end - start).toBeLessThan(1000);
});
});

Expand Down