Framework updates
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
23
node_modules/webpack/lib/util/AppendOnlyStackedSet.js
generated
vendored
23
node_modules/webpack/lib/util/AppendOnlyStackedSet.js
generated
vendored
@@ -43,7 +43,9 @@ class AppendOnlyStackedSet {
|
||||
|
||||
clear() {
|
||||
this._sets = [];
|
||||
if (this._current) this._current.clear();
|
||||
if (this._current) {
|
||||
this._current = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,6 +54,25 @@ class AppendOnlyStackedSet {
|
||||
createChild() {
|
||||
return new AppendOnlyStackedSet(this._sets.length ? [...this._sets] : []);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Iterator<T>} iterable iterator
|
||||
*/
|
||||
[Symbol.iterator]() {
|
||||
const iterators = this._sets.map((map) => map[Symbol.iterator]());
|
||||
let current = iterators.pop();
|
||||
return {
|
||||
next() {
|
||||
if (!current) return { done: true, value: undefined };
|
||||
let result = current.next();
|
||||
while (result.done && iterators.length > 0) {
|
||||
current = /** @type {SetIterator<T>} */ (iterators.pop());
|
||||
result = current.next();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AppendOnlyStackedSet;
|
||||
|
||||
188
node_modules/webpack/lib/util/findGraphRoots.js
generated
vendored
188
node_modules/webpack/lib/util/findGraphRoots.js
generated
vendored
@@ -8,19 +8,13 @@
|
||||
const NO_MARKER = 0;
|
||||
const IN_PROGRESS_MARKER = 1;
|
||||
const DONE_MARKER = 2;
|
||||
const DONE_MAYBE_ROOT_CYCLE_MARKER = 3;
|
||||
const DONE_AND_ROOT_MARKER = 4;
|
||||
const CANDIDATE_MARKER = 3;
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {Set<Node<T>>} Nodes
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {Set<Cycle<T>>} Cycles
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
*/
|
||||
@@ -32,20 +26,24 @@ class Node {
|
||||
this.item = item;
|
||||
/** @type {Nodes<T>} */
|
||||
this.dependencies = new Set();
|
||||
this.marker = NO_MARKER;
|
||||
/** @type {Cycle<T> | undefined} */
|
||||
this.cycle = undefined;
|
||||
/** @type {SCC<T>} */
|
||||
this.scc = new SCC();
|
||||
// Each node starts as a single-node SCC
|
||||
this.scc.nodes.add(this);
|
||||
/** @type {number} */
|
||||
this.incoming = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SCC (strongly connected component)
|
||||
* @template T
|
||||
*/
|
||||
class Cycle {
|
||||
class SCC {
|
||||
constructor() {
|
||||
/** @type {Nodes<T>} */
|
||||
this.nodes = new Set();
|
||||
this.marker = NO_MARKER;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,10 +68,10 @@ module.exports = (items, getDependencies) => {
|
||||
itemToNode.set(item, node);
|
||||
}
|
||||
|
||||
// early exit when there is only a single item
|
||||
// Early exit when there is only one node
|
||||
if (itemToNode.size <= 1) return items;
|
||||
|
||||
// grab all the dependencies
|
||||
// Build graph edges
|
||||
for (const node of itemToNode.values()) {
|
||||
for (const dep of getDependencies(node.item)) {
|
||||
const depNode = itemToNode.get(dep);
|
||||
@@ -83,27 +81,16 @@ module.exports = (items, getDependencies) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Set of current root modules
|
||||
// items will be removed if a new reference to it has been found
|
||||
/** @type {Nodes<T>} */
|
||||
const roots = new Set();
|
||||
// All candidate root SCCs, they will be removed once an incoming edge is found
|
||||
/** @type {Set<SCC<T>>} */
|
||||
const rootSCCs = new Set();
|
||||
|
||||
// Set of current cycles without references to it
|
||||
// cycles will be removed if a new reference to it has been found
|
||||
// that is not part of the cycle
|
||||
/** @type {Cycles<T>} */
|
||||
const rootCycles = new Set();
|
||||
|
||||
// For all non-marked nodes
|
||||
for (const selectedNode of itemToNode.values()) {
|
||||
if (selectedNode.marker === NO_MARKER) {
|
||||
// deep-walk all referenced modules
|
||||
// in a non-recursive way
|
||||
// DFS walk only once per unseen SCC
|
||||
if (selectedNode.scc.marker === NO_MARKER) {
|
||||
selectedNode.scc.marker = IN_PROGRESS_MARKER;
|
||||
|
||||
// start by entering the selected node
|
||||
selectedNode.marker = IN_PROGRESS_MARKER;
|
||||
|
||||
// keep a stack to avoid recursive walk
|
||||
// Keep a stack to avoid recursive walk
|
||||
/** @type {StackEntry<T>[]} */
|
||||
const stack = [
|
||||
{
|
||||
@@ -112,130 +99,113 @@ module.exports = (items, getDependencies) => {
|
||||
}
|
||||
];
|
||||
|
||||
// process the top item until stack is empty
|
||||
while (stack.length > 0) {
|
||||
const topOfStack = stack[stack.length - 1];
|
||||
|
||||
// Are there still edges unprocessed in the current node?
|
||||
// Process one unvisited outgoing edge if available
|
||||
if (topOfStack.openEdges.length > 0) {
|
||||
// Process one dependency
|
||||
const dependency =
|
||||
/** @type {Node<T>} */
|
||||
(topOfStack.openEdges.pop());
|
||||
switch (dependency.marker) {
|
||||
const depSCC = dependency.scc;
|
||||
switch (depSCC.marker) {
|
||||
case NO_MARKER:
|
||||
// dependency has not be visited yet
|
||||
// mark it as in-progress and recurse
|
||||
// First time we see this SCC: enter it
|
||||
stack.push({
|
||||
node: dependency,
|
||||
openEdges: [...dependency.dependencies]
|
||||
});
|
||||
dependency.marker = IN_PROGRESS_MARKER;
|
||||
depSCC.marker = IN_PROGRESS_MARKER;
|
||||
break;
|
||||
case IN_PROGRESS_MARKER: {
|
||||
// It's a in-progress cycle
|
||||
let cycle = dependency.cycle;
|
||||
if (!cycle) {
|
||||
cycle = new Cycle();
|
||||
cycle.nodes.add(dependency);
|
||||
dependency.cycle = cycle;
|
||||
}
|
||||
// set cycle property for each node in the cycle
|
||||
// if nodes are already part of a cycle
|
||||
// we merge the cycles to a shared cycle
|
||||
// Back-edge to an SCC that is still on the stack
|
||||
// Example:
|
||||
// A -> B -> C -> D
|
||||
// ^ |
|
||||
// |_________|
|
||||
// If we are at `D` and traverse `D` -> `B`, then `B/C/D` must be in one SCC
|
||||
/** @type {Set<SCC<T>>} */
|
||||
const sccsToMerge = new Set();
|
||||
for (
|
||||
let i = stack.length - 1;
|
||||
stack[i].node !== dependency;
|
||||
stack[i].node.scc !== depSCC;
|
||||
i--
|
||||
) {
|
||||
const node = stack[i].node;
|
||||
if (node.cycle) {
|
||||
if (node.cycle !== cycle) {
|
||||
// merge cycles
|
||||
for (const cycleNode of node.cycle.nodes) {
|
||||
cycleNode.cycle = cycle;
|
||||
cycle.nodes.add(cycleNode);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
node.cycle = cycle;
|
||||
cycle.nodes.add(node);
|
||||
sccsToMerge.add(stack[i].node.scc);
|
||||
}
|
||||
for (const sccToMerge of sccsToMerge) {
|
||||
for (const nodeInMergedSCC of sccToMerge.nodes) {
|
||||
nodeInMergedSCC.scc = depSCC;
|
||||
depSCC.nodes.add(nodeInMergedSCC);
|
||||
}
|
||||
}
|
||||
// don't recurse into dependencies
|
||||
// these are already on the stack
|
||||
break;
|
||||
}
|
||||
case DONE_AND_ROOT_MARKER:
|
||||
// This node has be visited yet and is currently a root node
|
||||
// But as this is a new reference to the node
|
||||
// it's not really a root
|
||||
// so we have to convert it to a normal node
|
||||
dependency.marker = DONE_MARKER;
|
||||
roots.delete(dependency);
|
||||
case CANDIDATE_MARKER:
|
||||
// This finished SCC was previously considered as root SCC
|
||||
// We just found a new incoming edge, so it is no longer a candidate
|
||||
rootSCCs.delete(/** @type {SCC<T>} */ (depSCC));
|
||||
depSCC.marker = DONE_MARKER;
|
||||
break;
|
||||
case DONE_MAYBE_ROOT_CYCLE_MARKER:
|
||||
// This node has be visited yet and
|
||||
// is maybe currently part of a completed root cycle
|
||||
// we found a new reference to the cycle
|
||||
// so it's not really a root cycle
|
||||
// remove the cycle from the root cycles
|
||||
// and convert it to a normal node
|
||||
rootCycles.delete(/** @type {Cycle<T>} */ (dependency.cycle));
|
||||
dependency.marker = DONE_MARKER;
|
||||
case DONE_MARKER:
|
||||
// Already finalized and not a candidate
|
||||
break;
|
||||
// DONE_MARKER: nothing to do, don't recurse into dependencies
|
||||
}
|
||||
} else {
|
||||
// All dependencies of the current node has been visited
|
||||
// we leave the node
|
||||
// All dependencies of the current node have been processed
|
||||
// So we leave the node
|
||||
stack.pop();
|
||||
topOfStack.node.marker = DONE_MARKER;
|
||||
// Mark an SCC as DONE only when the popped node is the last
|
||||
// node from that SCC remaining on the current stack.
|
||||
// A -> B -> C -> D
|
||||
// ^ |
|
||||
// |_________|
|
||||
// If `B` is popped and the new stack top is `A`, they are in
|
||||
// different SCCs, so B's SCC can be finalized.
|
||||
if (
|
||||
stack.length &&
|
||||
topOfStack.node.scc !== stack[stack.length - 1].node.scc
|
||||
) {
|
||||
topOfStack.node.scc.marker = DONE_MARKER;
|
||||
}
|
||||
}
|
||||
}
|
||||
const cycle = selectedNode.cycle;
|
||||
if (cycle) {
|
||||
for (const node of cycle.nodes) {
|
||||
node.marker = DONE_MAYBE_ROOT_CYCLE_MARKER;
|
||||
}
|
||||
rootCycles.add(cycle);
|
||||
} else {
|
||||
selectedNode.marker = DONE_AND_ROOT_MARKER;
|
||||
roots.add(selectedNode);
|
||||
}
|
||||
const scc = selectedNode.scc;
|
||||
// This SCC is complete and currently has no known incoming edge
|
||||
scc.marker = CANDIDATE_MARKER;
|
||||
rootSCCs.add(scc);
|
||||
}
|
||||
}
|
||||
|
||||
// Extract roots from root cycles
|
||||
// We take the nodes with most incoming edges
|
||||
// inside of the cycle
|
||||
for (const cycle of rootCycles) {
|
||||
/** @type {Set<T>} */
|
||||
const rootNodes = new Set();
|
||||
|
||||
// For each root SCC, we select node with the most incoming edges
|
||||
// from within the same SCC
|
||||
for (const scc of rootSCCs) {
|
||||
let max = 0;
|
||||
/** @type {Nodes<T>} */
|
||||
const cycleRoots = new Set();
|
||||
const nodes = cycle.nodes;
|
||||
for (const node of nodes) {
|
||||
const nodes = new Set(scc.nodes);
|
||||
for (const node of scc.nodes) {
|
||||
for (const dep of node.dependencies) {
|
||||
if (nodes.has(dep)) {
|
||||
if (scc.nodes.has(dep)) {
|
||||
dep.incoming++;
|
||||
if (dep.incoming < max) continue;
|
||||
if (dep.incoming > max) {
|
||||
cycleRoots.clear();
|
||||
nodes.clear();
|
||||
max = dep.incoming;
|
||||
}
|
||||
cycleRoots.add(dep);
|
||||
nodes.add(dep);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const cycleRoot of cycleRoots) {
|
||||
roots.add(cycleRoot);
|
||||
for (const node of nodes) {
|
||||
rootNodes.add(node.item);
|
||||
}
|
||||
}
|
||||
|
||||
// When roots were found, return them
|
||||
if (roots.size > 0) {
|
||||
return Array.from(roots, (r) => r.item);
|
||||
}
|
||||
// When root nodes were found, return them
|
||||
if (rootNodes.size > 0) return rootNodes;
|
||||
|
||||
throw new Error("Implementation of findGraphRoots is broken");
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user