packages/rollup-css-plugin/css-plugin-dependencies.js (62 lines of code) (raw):
const MAX_PASSES = 999999;
class DependencyGraph {
constructor() {
this.graph = new Map();
}
addDependency(module, dependency) {
if (!this.graph.has(module)) {
this.graph.set(module, new Set());
}
this.graph.get(module).add(dependency);
// Ensure the dependency is in the graph
if (!this.graph.has(dependency)) {
this.graph.set(dependency, new Set());
}
}
/*
It then iterates through the order, checking each module's dependencies.
If a dependency is found after the current module, it's moved to just before the current module.
This process repeats until no more reordering is needed.
*/
topologicalSort() {
const order = this.initialTopologicalSort();
let reordered;
let counter = 0;
do {
reordered = false;
if (counter++ > MAX_PASSES) {
throw new Error('Circular dependency detected');
}
for (let i = 0; i < order.length; i++) {
const module = order[i];
const dependencies = this.graph.get(module) || new Set();
for (const dependency of dependencies) {
const dependencyIndex = order.indexOf(dependency);
if (dependencyIndex > i) {
// Dependency is after the current module, need to move it
order.splice(dependencyIndex, 1);
order.splice(i, 0, dependency);
reordered = true;
break; // Start over with the new order
}
}
if (reordered) break;
}
} while (reordered);
return order;
}
// Initial topological sort using DFS
initialTopologicalSort() {
const visited = new Set();
const stack = [];
const dfs = node => {
visited.add(node);
const dependencies = this.graph.get(node) || new Set();
for (const dependency of dependencies) {
if (!visited.has(dependency)) {
dfs(dependency);
}
}
stack.push(node);
};
for (const node of this.graph.keys()) {
if (!visited.has(node)) {
dfs(node);
}
}
return stack.reverse();
}
}
module.exports = {DependencyGraph};