summaryrefslogtreecommitdiff
path: root/cli/npm/resolution/graph.rs
diff options
context:
space:
mode:
authorDavid Sherret <dsherret@users.noreply.github.com>2022-11-17 21:50:43 -0500
committerGitHub <noreply@github.com>2022-11-17 21:50:43 -0500
commit5867a12920e95265a6532f1b4ee358d9b4ed4599 (patch)
tree12b6e93ee7cd063fb065fbd7900394852606d862 /cli/npm/resolution/graph.rs
parent409e6c84028f0c6eee4a90d8cf952fdec97e67be (diff)
perf(npm): make dependency resolution faster (#16694)
Diffstat (limited to 'cli/npm/resolution/graph.rs')
-rw-r--r--cli/npm/resolution/graph.rs34
1 files changed, 28 insertions, 6 deletions
diff --git a/cli/npm/resolution/graph.rs b/cli/npm/resolution/graph.rs
index 8995b4076..15ee48fb1 100644
--- a/cli/npm/resolution/graph.rs
+++ b/cli/npm/resolution/graph.rs
@@ -139,6 +139,11 @@ struct Node {
pub parents: BTreeMap<String, BTreeSet<NodeParent>>,
pub children: BTreeMap<String, NpmPackageId>,
pub deps: Arc<Vec<NpmDependencyEntry>>,
+ /// Whether the node has demonstrated to have no peer dependencies in its
+ /// descendants. If this is true then we can skip analyzing this node
+ /// again when we encounter it another time in the dependency tree, which
+ /// is much faster.
+ pub no_peers: bool,
}
impl Node {
@@ -225,6 +230,7 @@ impl Graph {
parents: Default::default(),
children: Default::default(),
deps: Default::default(),
+ no_peers: false,
}));
self
.packages_by_name
@@ -492,7 +498,7 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi>
package_info: &NpmPackageInfo,
parent_id: &NpmPackageId,
visited_versions: &Arc<VisitedVersionsPath>,
- ) -> Result<(), AnyError> {
+ ) -> Result<Arc<Mutex<Node>>, AnyError> {
let node = self.resolve_node_from_info(
&entry.name,
match entry.kind {
@@ -515,7 +521,7 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi>
&NodeParent::Node(parent_id.clone()),
);
self.try_add_pending_unresolved_node(Some(visited_versions), &node);
- Ok(())
+ Ok(node)
}
fn try_add_pending_unresolved_node(
@@ -523,7 +529,13 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi>
maybe_previous_visited_versions: Option<&Arc<VisitedVersionsPath>>,
node: &Arc<Mutex<Node>>,
) {
- let node_id = node.lock().id.clone();
+ let node_id = {
+ let node = node.lock();
+ if node.no_peers {
+ return; // skip, no need to analyze this again
+ }
+ node.id.clone()
+ };
let visited_versions = match maybe_previous_visited_versions {
Some(previous_visited_versions) => {
match previous_visited_versions.with_id(&node_id) {
@@ -576,6 +588,7 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi>
// so these are resolved in that order
deps.sort();
node.deps = Arc::new(deps);
+ node.no_peers = node.deps.is_empty();
}
Ok(node)
@@ -589,8 +602,8 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi>
{
let (mut parent_id, deps, existing_children) = {
let parent_node = parent_node.lock();
- if parent_node.forgotten {
- // todo(dsherret): we should try to reproduce this scenario and write a test
+ if parent_node.forgotten || parent_node.no_peers {
+ // todo(dsherret): we should try to reproduce this forgotten scenario and write a test
continue;
}
@@ -622,20 +635,25 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi>
}
// resolve the dependencies
+ let mut found_peer = false;
for dep in deps.iter() {
let package_info = self.api.package_info(&dep.name).await?;
match dep.kind {
NpmDependencyEntryKind::Dep => {
- self.analyze_dependency(
+ let node = self.analyze_dependency(
dep,
&package_info,
&parent_id,
&visited_versions,
)?;
+ if !found_peer {
+ found_peer = !node.lock().no_peers;
+ }
}
NpmDependencyEntryKind::Peer
| NpmDependencyEntryKind::OptionalPeer => {
+ found_peer = true;
let maybe_new_parent_id = self.resolve_peer_dep(
&dep.bare_specifier,
&parent_id,
@@ -654,6 +672,10 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi>
}
}
}
+
+ if !found_peer {
+ self.graph.borrow_node(&parent_id).no_peers = true;
+ }
}
}
Ok(())