diff --git a/cli/npm/resolution/graph.rs b/cli/npm/resolution/graph.rs index 8995b4076d..15ee48fb1e 100644 --- a/cli/npm/resolution/graph.rs +++ b/cli/npm/resolution/graph.rs @@ -139,6 +139,11 @@ struct Node { pub parents: BTreeMap>, pub children: BTreeMap, pub deps: Arc>, + /// 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, - ) -> Result<(), AnyError> { + ) -> Result>, 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>, node: &Arc>, ) { - 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(())