Skip to content

Commit

Permalink
fix(npm): handle an npm package that has itself as a dependency (deno…
Browse files Browse the repository at this point in the history
…land#17425)

I'm not sure this properly handles scenarios where an npm package uses
an alias that resolves to itself, we can fix that if we find a package
that actually depends on that behavior.

Closes denoland#17420
  • Loading branch information
dsherret committed Jan 14, 2023
1 parent 01e02d3 commit 7cd249d
Showing 1 changed file with 75 additions and 11 deletions.
86 changes: 75 additions & 11 deletions cli/npm/resolution/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}",
Expand All @@ -311,7 +312,6 @@ impl Graph {
)
}))
.lock();
assert_ne!(parent.id, child.id);
parent
.children
.insert(specifier.to_string(), child.id.clone());
Expand Down Expand Up @@ -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,
Expand All @@ -498,7 +498,7 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi>
parent_id: &NpmPackageId,
visited_versions: &Arc<VisitedVersionsPath>,
) -> Result<Arc<Mutex<Node>>, 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,
Expand All @@ -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)
}

Expand Down Expand Up @@ -555,7 +560,7 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi>
version_matcher: &impl NpmVersionMatcher,
package_info: &NpmPackageInfo,
parent_id: Option<&NpmPackageId>,
) -> Result<Arc<Mutex<Node>>, AnyError> {
) -> Result<(NpmPackageId, Arc<Mutex<Node>>), AnyError> {
let version_and_info = self
.resolve_best_package_version_and_info(version_matcher, package_info)?;
let id = NpmPackageId {
Expand Down Expand Up @@ -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> {
Expand Down Expand Up @@ -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:[email protected]"]).await;
assert_eq!(
packages,
vec![NpmResolutionPackage {
id: NpmPackageId::from_serialized("[email protected]").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![("[email protected]".to_string(), "[email protected]".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:[email protected]"]).await;
assert_eq!(
packages,
vec![
NpmResolutionPackage {
id: NpmPackageId::from_serialized("[email protected]").unwrap(),
copy_index: 0,
dependencies: Default::default(),
dist: Default::default(),
},
NpmResolutionPackage {
id: NpmPackageId::from_serialized("[email protected]").unwrap(),
copy_index: 0,
dependencies: HashMap::from([(
"package-a".to_string(),
NpmPackageId::from_serialized("[email protected]").unwrap(),
)]),
dist: Default::default(),
},
]
);
assert_eq!(
package_reqs,
vec![("[email protected]".to_string(), "[email protected]".to_string())]
);
}

async fn run_resolver_and_get_output(
api: TestNpmRegistryApi,
reqs: Vec<&str>,
Expand Down

0 comments on commit 7cd249d

Please sign in to comment.