diff --git a/cli/npm/resolution/graph.rs b/cli/npm/resolution/graph.rs index 6e468d2e141a8..81ff58546f2e0 100644 --- a/cli/npm/resolution/graph.rs +++ b/cli/npm/resolution/graph.rs @@ -303,6 +303,7 @@ impl Graph { parent_id: &NpmPackageId, ) { let mut child = (*child).lock(); + assert_ne!(child.id, *parent_id); let mut parent = (**self.packages.get(parent_id).unwrap_or_else(|| { panic!( "could not find {} in list of packages when setting child {}", @@ -311,7 +312,6 @@ impl Graph { ) })) .lock(); - assert_ne!(parent.id, child.id); parent .children .insert(specifier.to_string(), child.id.clone()); @@ -476,7 +476,7 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi> package_req: &NpmPackageReq, package_info: &NpmPackageInfo, ) -> Result<(), AnyError> { - let node = self.resolve_node_from_info( + let (_, node) = self.resolve_node_from_info( &package_req.name, package_req, package_info, @@ -498,7 +498,7 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi> parent_id: &NpmPackageId, visited_versions: &Arc, ) -> Result>, AnyError> { - let node = self.resolve_node_from_info( + let (id, node) = self.resolve_node_from_info( &entry.name, match entry.kind { NpmDependencyEntryKind::Dep => &entry.version_req, @@ -514,12 +514,17 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi> package_info, Some(parent_id), )?; - self.graph.set_child_parent( - &entry.bare_specifier, - &node, - &NodeParent::Node(parent_id.clone()), - ); - self.try_add_pending_unresolved_node(Some(visited_versions), &node); + // Some packages may resolves to themselves as a dependency. If this occurs, + // just ignore adding these as dependencies because this is likely a mistake + // in the package. + if id != *parent_id { + self.graph.set_child_parent( + &entry.bare_specifier, + &node, + &NodeParent::Node(parent_id.clone()), + ); + self.try_add_pending_unresolved_node(Some(visited_versions), &node); + } Ok(node) } @@ -555,7 +560,7 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi> version_matcher: &impl NpmVersionMatcher, package_info: &NpmPackageInfo, parent_id: Option<&NpmPackageId>, - ) -> Result>, AnyError> { + ) -> Result<(NpmPackageId, Arc>), AnyError> { let version_and_info = self .resolve_best_package_version_and_info(version_matcher, package_info)?; let id = NpmPackageId { @@ -587,7 +592,7 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi> node.no_peers = node.deps.is_empty(); } - Ok(node) + Ok((id, node)) } pub async fn resolve_pending(&mut self) -> Result<(), AnyError> { @@ -2655,6 +2660,65 @@ mod test { ); } + #[tokio::test] + async fn package_has_self_as_dependency() { + let api = TestNpmRegistryApi::default(); + api.ensure_package_version("package-a", "1.0.0"); + api.add_dependency(("package-a", "1.0.0"), ("package-a", "1")); + + let (packages, package_reqs) = + run_resolver_and_get_output(api, vec!["npm:package-a@1.0"]).await; + assert_eq!( + packages, + vec![NpmResolutionPackage { + id: NpmPackageId::from_serialized("package-a@1.0.0").unwrap(), + copy_index: 0, + // in this case, we just ignore that the package did this + dependencies: Default::default(), + dist: Default::default(), + }] + ); + assert_eq!( + package_reqs, + vec![("package-a@1.0".to_string(), "package-a@1.0.0".to_string())] + ); + } + + #[tokio::test] + async fn package_has_self_but_different_version_as_dependency() { + let api = TestNpmRegistryApi::default(); + api.ensure_package_version("package-a", "1.0.0"); + api.ensure_package_version("package-a", "0.5.0"); + api.add_dependency(("package-a", "1.0.0"), ("package-a", "^0.5")); + + let (packages, package_reqs) = + run_resolver_and_get_output(api, vec!["npm:package-a@1.0"]).await; + assert_eq!( + packages, + vec![ + NpmResolutionPackage { + id: NpmPackageId::from_serialized("package-a@0.5.0").unwrap(), + copy_index: 0, + dependencies: Default::default(), + dist: Default::default(), + }, + NpmResolutionPackage { + id: NpmPackageId::from_serialized("package-a@1.0.0").unwrap(), + copy_index: 0, + dependencies: HashMap::from([( + "package-a".to_string(), + NpmPackageId::from_serialized("package-a@0.5.0").unwrap(), + )]), + dist: Default::default(), + }, + ] + ); + assert_eq!( + package_reqs, + vec![("package-a@1.0".to_string(), "package-a@1.0.0".to_string())] + ); + } + async fn run_resolver_and_get_output( api: TestNpmRegistryApi, reqs: Vec<&str>,