summaryrefslogtreecommitdiff
path: root/cli/npm/resolution/graph.rs
diff options
context:
space:
mode:
authorDavid Sherret <dsherret@users.noreply.github.com>2023-04-06 18:46:44 -0400
committerGitHub <noreply@github.com>2023-04-06 18:46:44 -0400
commitd07aa4a0723b04583b7cb1e09152457d866d13d3 (patch)
treef329a30becca95583fb71b4158c939c68228ce06 /cli/npm/resolution/graph.rs
parent1586c52b5b5ad511ec0bf896e94de8585f743cf8 (diff)
refactor(npm): use deno_npm and deno_semver (#18602)
Diffstat (limited to 'cli/npm/resolution/graph.rs')
-rw-r--r--cli/npm/resolution/graph.rs3726
1 files changed, 0 insertions, 3726 deletions
diff --git a/cli/npm/resolution/graph.rs b/cli/npm/resolution/graph.rs
deleted file mode 100644
index 65754f3bf..000000000
--- a/cli/npm/resolution/graph.rs
+++ /dev/null
@@ -1,3726 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-use std::collections::hash_map::DefaultHasher;
-use std::collections::BTreeMap;
-use std::collections::HashMap;
-use std::collections::HashSet;
-use std::collections::VecDeque;
-use std::hash::Hash;
-use std::hash::Hasher;
-use std::sync::Arc;
-
-use deno_core::anyhow::bail;
-use deno_core::anyhow::Context;
-use deno_core::error::AnyError;
-use deno_core::parking_lot::Mutex;
-use deno_graph::npm::NpmPackageNv;
-use deno_graph::npm::NpmPackageReq;
-use deno_graph::semver::Version;
-use deno_graph::semver::VersionReq;
-use log::debug;
-
-use crate::npm::registry::NpmDependencyEntry;
-use crate::npm::registry::NpmDependencyEntryKind;
-use crate::npm::registry::NpmPackageInfo;
-use crate::npm::registry::NpmPackageVersionInfo;
-use crate::npm::resolution::common::resolve_best_package_version_and_info;
-use crate::npm::resolution::snapshot::SnapshotPackageCopyIndexResolver;
-use crate::npm::NpmRegistryApi;
-
-use super::common::version_req_satisfies;
-use super::common::LATEST_VERSION_REQ;
-use super::snapshot::NpmResolutionSnapshot;
-use super::NpmPackageId;
-use super::NpmResolutionPackage;
-
-// todo(dsherret): for perf we should use an arena/bump allocator for
-// creating the nodes and paths since this is done in a phase
-
-/// A unique identifier to a node in the graph.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
-struct NodeId(u32);
-
-/// A resolved package in the resolution graph.
-#[derive(Debug)]
-struct Node {
- /// The specifier to child relationship in the graph. The specifier is
- /// the key in an npm package's dependencies map (ex. "express"). We
- /// use a BTreeMap for some determinism when creating the snapshot.
- ///
- /// Note: We don't want to store the children as a `NodeRef` because
- /// multiple paths might visit through the children and we don't want
- /// to share those references with those paths.
- pub children: BTreeMap<String, NodeId>,
- /// 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,
-}
-
-#[derive(Clone)]
-enum ResolvedIdPeerDep {
- /// This is a reference to the parent instead of the child because we only have a
- /// node reference to the parent, since we've traversed it, but the child node may
- /// change from under it.
- ParentReference {
- parent: GraphPathNodeOrRoot,
- child_pkg_nv: Arc<NpmPackageNv>,
- },
- /// A node that was created during snapshotting and is not being used in any path.
- SnapshotNodeId(NodeId),
-}
-
-impl ResolvedIdPeerDep {
- pub fn current_state_hash(&self) -> u64 {
- let mut hasher = DefaultHasher::new();
- self.current_state_hash_with_hasher(&mut hasher);
- hasher.finish()
- }
-
- pub fn current_state_hash_with_hasher(&self, hasher: &mut DefaultHasher) {
- match self {
- ResolvedIdPeerDep::ParentReference {
- parent,
- child_pkg_nv,
- } => {
- match parent {
- GraphPathNodeOrRoot::Root(root) => root.hash(hasher),
- GraphPathNodeOrRoot::Node(node) => node.node_id().hash(hasher),
- }
- child_pkg_nv.hash(hasher);
- }
- ResolvedIdPeerDep::SnapshotNodeId(node_id) => {
- node_id.hash(hasher);
- }
- }
- }
-}
-
-/// A pending resolved identifier used in the graph. At the end of resolution, these
-/// will become fully resolved to an `NpmPackageId`.
-#[derive(Clone)]
-struct ResolvedId {
- nv: Arc<NpmPackageNv>,
- peer_dependencies: Vec<ResolvedIdPeerDep>,
-}
-
-impl ResolvedId {
- /// Gets a hash of the resolved identifier at this current moment in time.
- ///
- /// WARNING: A resolved identifier references a value that could change in
- /// the future, so this should be used with that in mind.
- pub fn current_state_hash(&self) -> u64 {
- let mut hasher = DefaultHasher::new();
- self.nv.hash(&mut hasher);
- for dep in &self.peer_dependencies {
- dep.current_state_hash_with_hasher(&mut hasher);
- }
- hasher.finish()
- }
-
- pub fn push_peer_dep(&mut self, peer_dep: ResolvedIdPeerDep) -> bool {
- let new_hash = peer_dep.current_state_hash();
- for dep in &self.peer_dependencies {
- if new_hash == dep.current_state_hash() {
- return false; // peer dep already set
- }
- }
- self.peer_dependencies.push(peer_dep);
- true
- }
-}
-
-/// Mappings of node identifiers to resolved identifiers. Each node has exactly
-/// one resolved identifier.
-///
-/// The mapping from resolved to node_ids is imprecise and will do a best attempt
-/// at sharing nodes.
-#[derive(Default)]
-struct ResolvedNodeIds {
- node_to_resolved_id: HashMap<NodeId, (ResolvedId, u64)>,
- resolved_to_node_id: HashMap<u64, NodeId>,
-}
-
-impl ResolvedNodeIds {
- pub fn set(&mut self, node_id: NodeId, resolved_id: ResolvedId) {
- let resolved_id_hash = resolved_id.current_state_hash();
- if let Some((_, old_resolved_id_key)) = self
- .node_to_resolved_id
- .insert(node_id, (resolved_id, resolved_id_hash))
- {
- // ensure the old resolved id key is removed as it might be stale
- self.resolved_to_node_id.remove(&old_resolved_id_key);
- }
- self.resolved_to_node_id.insert(resolved_id_hash, node_id);
- }
-
- pub fn get(&self, node_id: NodeId) -> Option<&ResolvedId> {
- self.node_to_resolved_id.get(&node_id).map(|(id, _)| id)
- }
-
- pub fn get_node_id(&self, resolved_id: &ResolvedId) -> Option<NodeId> {
- self
- .resolved_to_node_id
- .get(&resolved_id.current_state_hash())
- .copied()
- }
-}
-
-// todo(dsherret): for some reason the lsp errors when using an Rc<RefCell<NodeId>> here
-// instead of an Arc<Mutex<NodeId>>. We should investigate and fix.
-
-/// A pointer to a specific node in a graph path. The underlying node id
-/// may change as peer dependencies are created.
-#[derive(Clone, Debug)]
-struct NodeIdRef(Arc<Mutex<NodeId>>);
-
-impl NodeIdRef {
- pub fn new(node_id: NodeId) -> Self {
- NodeIdRef(Arc::new(Mutex::new(node_id)))
- }
-
- pub fn change(&self, node_id: NodeId) {
- *self.0.lock() = node_id;
- }
-
- pub fn get(&self) -> NodeId {
- *self.0.lock()
- }
-}
-
-#[derive(Clone)]
-enum GraphPathNodeOrRoot {
- Node(Arc<GraphPath>),
- Root(Arc<NpmPackageNv>),
-}
-
-/// Path through the graph that represents a traversal through the graph doing
-/// the dependency resolution. The graph tries to share duplicate package
-/// information and we try to avoid traversing parts of the graph that we know
-/// are resolved.
-struct GraphPath {
- previous_node: Option<GraphPathNodeOrRoot>,
- node_id_ref: NodeIdRef,
- specifier: String,
- // we could consider not storing this here and instead reference the resolved
- // nodes, but we should performance profile this code first
- nv: Arc<NpmPackageNv>,
- /// Descendants in the path that circularly link to an ancestor in a child.These
- /// descendants should be kept up to date and always point to this node.
- linked_circular_descendants: Mutex<Vec<Arc<GraphPath>>>,
-}
-
-impl GraphPath {
- pub fn for_root(node_id: NodeId, nv: Arc<NpmPackageNv>) -> Arc<Self> {
- Arc::new(Self {
- previous_node: Some(GraphPathNodeOrRoot::Root(nv.clone())),
- node_id_ref: NodeIdRef::new(node_id),
- // use an empty specifier
- specifier: "".to_string(),
- nv,
- linked_circular_descendants: Default::default(),
- })
- }
-
- pub fn node_id(&self) -> NodeId {
- self.node_id_ref.get()
- }
-
- pub fn specifier(&self) -> &str {
- &self.specifier
- }
-
- pub fn change_id(&self, node_id: NodeId) {
- self.node_id_ref.change(node_id)
- }
-
- pub fn with_id(
- self: &Arc<GraphPath>,
- node_id: NodeId,
- specifier: String,
- nv: Arc<NpmPackageNv>,
- ) -> Arc<Self> {
- Arc::new(Self {
- previous_node: Some(GraphPathNodeOrRoot::Node(self.clone())),
- node_id_ref: NodeIdRef::new(node_id),
- specifier,
- nv,
- linked_circular_descendants: Default::default(),
- })
- }
-
- /// Gets if there is an ancestor with the same name & version along this path.
- pub fn find_ancestor(&self, nv: &NpmPackageNv) -> Option<Arc<GraphPath>> {
- let mut maybe_next_node = self.previous_node.as_ref();
- while let Some(GraphPathNodeOrRoot::Node(next_node)) = maybe_next_node {
- // we've visited this before, so stop
- if *next_node.nv == *nv {
- return Some(next_node.clone());
- }
- maybe_next_node = next_node.previous_node.as_ref();
- }
- None
- }
-
- /// Gets the bottom-up path to the ancestor not including the current or ancestor node.
- pub fn get_path_to_ancestor_exclusive(
- &self,
- ancestor_node_id: NodeId,
- ) -> Vec<&Arc<GraphPath>> {
- let mut path = Vec::new();
- let mut maybe_next_node = self.previous_node.as_ref();
- while let Some(GraphPathNodeOrRoot::Node(next_node)) = maybe_next_node {
- if next_node.node_id() == ancestor_node_id {
- break;
- }
- path.push(next_node);
- maybe_next_node = next_node.previous_node.as_ref();
- }
- debug_assert!(maybe_next_node.is_some());
- path
- }
-
- pub fn ancestors(&self) -> GraphPathAncestorIterator {
- GraphPathAncestorIterator {
- next: self.previous_node.as_ref(),
- }
- }
-}
-
-struct GraphPathAncestorIterator<'a> {
- next: Option<&'a GraphPathNodeOrRoot>,
-}
-
-impl<'a> Iterator for GraphPathAncestorIterator<'a> {
- type Item = &'a GraphPathNodeOrRoot;
- fn next(&mut self) -> Option<Self::Item> {
- if let Some(next) = self.next.take() {
- if let GraphPathNodeOrRoot::Node(node) = next {
- self.next = node.previous_node.as_ref();
- }
- Some(next)
- } else {
- None
- }
- }
-}
-
-#[derive(Default)]
-pub struct Graph {
- /// Each requirement is mapped to a specific name and version.
- package_reqs: HashMap<NpmPackageReq, Arc<NpmPackageNv>>,
- /// Then each name and version is mapped to an exact node id.
- /// Note: Uses a BTreeMap in order to create some determinism
- /// when creating the snapshot.
- root_packages: BTreeMap<Arc<NpmPackageNv>, NodeId>,
- package_name_versions: HashMap<String, HashSet<Version>>,
- nodes: HashMap<NodeId, Node>,
- resolved_node_ids: ResolvedNodeIds,
- // This will be set when creating from a snapshot, then
- // inform the final snapshot creation.
- packages_to_copy_index: HashMap<NpmPackageId, usize>,
- /// Packages that the resolver should resolve first.
- pending_unresolved_packages: Vec<Arc<NpmPackageNv>>,
-}
-
-impl Graph {
- pub fn from_snapshot(
- snapshot: NpmResolutionSnapshot,
- ) -> Result<Self, AnyError> {
- fn get_or_create_graph_node(
- graph: &mut Graph,
- pkg_id: &NpmPackageId,
- packages: &HashMap<NpmPackageId, NpmResolutionPackage>,
- created_package_ids: &mut HashMap<NpmPackageId, NodeId>,
- ) -> Result<NodeId, AnyError> {
- if let Some(id) = created_package_ids.get(pkg_id) {
- return Ok(*id);
- }
-
- let node_id = graph.create_node(&pkg_id.nv);
- created_package_ids.insert(pkg_id.clone(), node_id);
-
- let peer_dep_ids = pkg_id
- .peer_dependencies
- .iter()
- .map(|peer_dep| {
- Ok(ResolvedIdPeerDep::SnapshotNodeId(get_or_create_graph_node(
- graph,
- peer_dep,
- packages,
- created_package_ids,
- )?))
- })
- .collect::<Result<Vec<_>, AnyError>>()?;
- let graph_resolved_id = ResolvedId {
- nv: Arc::new(pkg_id.nv.clone()),
- peer_dependencies: peer_dep_ids,
- };
- graph.resolved_node_ids.set(node_id, graph_resolved_id);
- let resolution = match packages.get(pkg_id) {
- Some(resolved_id) => resolved_id,
- // maybe the user messed around with the lockfile
- None => bail!("not found package: {}", pkg_id.as_serialized()),
- };
- for (name, child_id) in &resolution.dependencies {
- let child_node_id = get_or_create_graph_node(
- graph,
- child_id,
- packages,
- created_package_ids,
- )?;
- graph.set_child_of_parent_node(node_id, name, child_node_id);
- }
- Ok(node_id)
- }
-
- let mut graph = Self {
- // Note: It might be more correct to store the copy index
- // from past resolutions with the node somehow, but maybe not.
- packages_to_copy_index: snapshot
- .packages
- .iter()
- .map(|(id, p)| (id.clone(), p.copy_index))
- .collect(),
- package_reqs: snapshot
- .package_reqs
- .into_iter()
- .map(|(k, v)| (k, Arc::new(v)))
- .collect(),
- pending_unresolved_packages: snapshot
- .pending_unresolved_packages
- .into_iter()
- .map(Arc::new)
- .collect(),
- ..Default::default()
- };
- let mut created_package_ids =
- HashMap::with_capacity(snapshot.packages.len());
- for (id, resolved_id) in snapshot.root_packages {
- let node_id = get_or_create_graph_node(
- &mut graph,
- &resolved_id,
- &snapshot.packages,
- &mut created_package_ids,
- )?;
- graph.root_packages.insert(Arc::new(id), node_id);
- }
- Ok(graph)
- }
-
- pub fn take_pending_unresolved(&mut self) -> Vec<Arc<NpmPackageNv>> {
- std::mem::take(&mut self.pending_unresolved_packages)
- }
-
- pub fn has_package_req(&self, req: &NpmPackageReq) -> bool {
- self.package_reqs.contains_key(req)
- }
-
- pub fn has_root_package(&self, id: &NpmPackageNv) -> bool {
- self.root_packages.contains_key(id)
- }
-
- fn get_npm_pkg_id(&self, node_id: NodeId) -> NpmPackageId {
- let resolved_id = self.resolved_node_ids.get(node_id).unwrap();
- self.get_npm_pkg_id_from_resolved_id(resolved_id, HashSet::new())
- }
-
- fn get_npm_pkg_id_from_resolved_id(
- &self,
- resolved_id: &ResolvedId,
- seen: HashSet<NodeId>,
- ) -> NpmPackageId {
- if resolved_id.peer_dependencies.is_empty() {
- NpmPackageId {
- nv: (*resolved_id.nv).clone(),
- peer_dependencies: Vec::new(),
- }
- } else {
- let mut npm_pkg_id = NpmPackageId {
- nv: (*resolved_id.nv).clone(),
- peer_dependencies: Vec::with_capacity(
- resolved_id.peer_dependencies.len(),
- ),
- };
- let mut seen_children_resolved_ids =
- HashSet::with_capacity(resolved_id.peer_dependencies.len());
- for peer_dep in &resolved_id.peer_dependencies {
- let maybe_node_and_resolved_id = match peer_dep {
- ResolvedIdPeerDep::SnapshotNodeId(node_id) => self
- .resolved_node_ids
- .get(*node_id)
- .map(|resolved_id| (*node_id, resolved_id)),
- ResolvedIdPeerDep::ParentReference {
- parent,
- child_pkg_nv: child_nv,
- } => match &parent {
- GraphPathNodeOrRoot::Root(_) => {
- self.root_packages.get(child_nv).and_then(|node_id| {
- self
- .resolved_node_ids
- .get(*node_id)
- .map(|resolved_id| (*node_id, resolved_id))
- })
- }
- GraphPathNodeOrRoot::Node(parent_path) => {
- self.nodes.get(&parent_path.node_id()).and_then(|parent| {
- parent
- .children
- .values()
- .filter_map(|child_id| {
- let child_id = *child_id;
- self
- .resolved_node_ids
- .get(child_id)
- .map(|resolved_id| (child_id, resolved_id))
- })
- .find(|(_, resolved_id)| resolved_id.nv == *child_nv)
- })
- }
- },
- };
- // this should always be set
- debug_assert!(maybe_node_and_resolved_id.is_some());
- if let Some((child_id, child_resolved_id)) = maybe_node_and_resolved_id
- {
- let mut new_seen = seen.clone();
- if new_seen.insert(child_id) {
- let child_peer = self.get_npm_pkg_id_from_resolved_id(
- child_resolved_id,
- new_seen.clone(),
- );
-
- if seen_children_resolved_ids.insert(child_peer.clone()) {
- npm_pkg_id.peer_dependencies.push(child_peer);
- }
- }
- }
- }
- npm_pkg_id
- }
- }
-
- fn get_or_create_for_id(
- &mut self,
- resolved_id: &ResolvedId,
- ) -> (bool, NodeId) {
- if let Some(node_id) = self.resolved_node_ids.get_node_id(resolved_id) {
- return (false, node_id);
- }
-
- let node_id = self.create_node(&resolved_id.nv);
- self.resolved_node_ids.set(node_id, resolved_id.clone());
- (true, node_id)
- }
-
- fn create_node(&mut self, pkg_nv: &NpmPackageNv) -> NodeId {
- let node_id = NodeId(self.nodes.len() as u32);
- let node = Node {
- children: Default::default(),
- no_peers: false,
- };
-
- self
- .package_name_versions
- .entry(pkg_nv.name.clone())
- .or_default()
- .insert(pkg_nv.version.clone());
- self.nodes.insert(node_id, node);
-
- node_id
- }
-
- fn borrow_node_mut(&mut self, node_id: NodeId) -> &mut Node {
- self.nodes.get_mut(&node_id).unwrap()
- }
-
- fn set_child_of_parent_node(
- &mut self,
- parent_id: NodeId,
- specifier: &str,
- child_id: NodeId,
- ) {
- assert_ne!(child_id, parent_id);
- let parent = self.borrow_node_mut(parent_id);
- parent.children.insert(specifier.to_string(), child_id);
- }
-
- pub async fn into_snapshot(
- self,
- api: &NpmRegistryApi,
- ) -> Result<NpmResolutionSnapshot, AnyError> {
- let packages_to_pkg_ids = self
- .nodes
- .keys()
- .map(|node_id| (*node_id, self.get_npm_pkg_id(*node_id)))
- .collect::<HashMap<_, _>>();
- let mut copy_index_resolver =
- SnapshotPackageCopyIndexResolver::from_map_with_capacity(
- self.packages_to_copy_index,
- self.nodes.len(),
- );
- let mut packages = HashMap::with_capacity(self.nodes.len());
- let mut packages_by_name: HashMap<String, Vec<_>> =
- HashMap::with_capacity(self.nodes.len());
-
- // todo(dsherret): there is a lurking bug within the peer dependencies code.
- // You can see it by using `NodeIds` instead of `NpmPackageIds` on this travered_ids
- // hashset, which will cause the bottom of the "tree" nodes to be populated in
- // the result instead of the top of the "tree". I think there's maybe one small
- // thing that's not being updated properly.
- let mut traversed_ids = HashSet::with_capacity(self.nodes.len());
- let mut pending = VecDeque::new();
-
- for root_id in self.root_packages.values().copied() {
- let pkg_id = packages_to_pkg_ids.get(&root_id).unwrap();
- if traversed_ids.insert(pkg_id.clone()) {
- pending.push_back((root_id, pkg_id));
- }
- }
-
- while let Some((node_id, pkg_id)) = pending.pop_front() {
- let node = self.nodes.get(&node_id).unwrap();
-
- packages_by_name
- .entry(pkg_id.nv.name.clone())
- .or_default()
- .push(pkg_id.clone());
-
- // todo(dsherret): grab this from the dep entry cache, which should have it
- let dist = api
- .package_version_info(&pkg_id.nv)
- .await?
- .unwrap_or_else(|| panic!("missing: {:?}", pkg_id.nv))
- .dist;
-
- let mut dependencies = HashMap::with_capacity(node.children.len());
- for (specifier, child_id) in &node.children {
- let child_id = *child_id;
- let child_pkg_id = packages_to_pkg_ids.get(&child_id).unwrap();
- if traversed_ids.insert(child_pkg_id.clone()) {
- pending.push_back((child_id, child_pkg_id));
- }
- dependencies.insert(specifier.clone(), (*child_pkg_id).clone());
- }
-
- packages.insert(
- (*pkg_id).clone(),
- NpmResolutionPackage {
- copy_index: copy_index_resolver.resolve(pkg_id),
- pkg_id: (*pkg_id).clone(),
- dist,
- dependencies,
- },
- );
- }
-
- Ok(NpmResolutionSnapshot {
- root_packages: self
- .root_packages
- .into_iter()
- .map(|(nv, node_id)| {
- (
- (*nv).clone(),
- packages_to_pkg_ids.get(&node_id).unwrap().clone(),
- )
- })
- .collect(),
- packages_by_name: packages_by_name
- .into_iter()
- .map(|(name, mut ids)| {
- ids.sort();
- ids.dedup();
- (name, ids)
- })
- .collect(),
- packages,
- package_reqs: self
- .package_reqs
- .into_iter()
- .map(|(req, nv)| (req, (*nv).clone()))
- .collect(),
- pending_unresolved_packages: self
- .pending_unresolved_packages
- .into_iter()
- .map(|nv| (*nv).clone())
- .collect(),
- })
- }
-
- // Debugging methods
-
- #[cfg(debug_assertions)]
- #[allow(unused)]
- fn output_path(&self, path: &Arc<GraphPath>) {
- eprintln!("-----------");
- self.output_node(path.node_id(), false);
- for path in path.ancestors() {
- match path {
- GraphPathNodeOrRoot::Node(node) => {
- self.output_node(node.node_id(), false)
- }
- GraphPathNodeOrRoot::Root(pkg_id) => {
- let node_id = self.root_packages.get(pkg_id).unwrap();
- eprintln!(
- "Root: {} ({}: {})",
- pkg_id,
- node_id.0,
- self.get_npm_pkg_id(*node_id).as_serialized()
- )
- }
- }
- }
- eprintln!("-----------");
- }
-
- #[cfg(debug_assertions)]
- #[allow(unused)]
- fn output_node(&self, node_id: NodeId, show_children: bool) {
- eprintln!(
- "{:>4}: {}",
- node_id.0,
- self.get_npm_pkg_id(node_id).as_serialized()
- );
-
- if show_children {
- let node = self.nodes.get(&node_id).unwrap();
- eprintln!(" Children:");
- for (specifier, child_id) in &node.children {
- eprintln!(" {}: {}", specifier, child_id.0);
- }
- }
- }
-
- #[cfg(debug_assertions)]
- #[allow(unused)]
- pub fn output_nodes(&self) {
- eprintln!("~~~");
- let mut node_ids = self
- .resolved_node_ids
- .node_to_resolved_id
- .keys()
- .copied()
- .collect::<Vec<_>>();
- node_ids.sort_by(|a, b| a.0.cmp(&b.0));
- for node_id in node_ids {
- self.output_node(node_id, true);
- }
- eprintln!("~~~");
- }
-}
-
-#[derive(Default)]
-struct DepEntryCache(HashMap<Arc<NpmPackageNv>, Arc<Vec<NpmDependencyEntry>>>);
-
-impl DepEntryCache {
- pub fn store(
- &mut self,
- nv: Arc<NpmPackageNv>,
- version_info: &NpmPackageVersionInfo,
- ) -> Result<Arc<Vec<NpmDependencyEntry>>, AnyError> {
- debug_assert!(!self.0.contains_key(&nv)); // we should not be re-inserting
- let mut deps = version_info
- .dependencies_as_entries()
- .with_context(|| format!("npm package: {nv}"))?;
- // Ensure name alphabetical and then version descending
- // so these are resolved in that order
- deps.sort();
- let deps = Arc::new(deps);
- self.0.insert(nv, deps.clone());
- Ok(deps)
- }
-
- pub fn get(
- &self,
- id: &NpmPackageNv,
- ) -> Option<&Arc<Vec<NpmDependencyEntry>>> {
- self.0.get(id)
- }
-}
-
-struct UnresolvedOptionalPeer {
- specifier: String,
- graph_path: Arc<GraphPath>,
-}
-
-pub struct GraphDependencyResolver<'a> {
- graph: &'a mut Graph,
- api: &'a NpmRegistryApi,
- pending_unresolved_nodes: VecDeque<Arc<GraphPath>>,
- unresolved_optional_peers:
- HashMap<Arc<NpmPackageNv>, Vec<UnresolvedOptionalPeer>>,
- dep_entry_cache: DepEntryCache,
-}
-
-impl<'a> GraphDependencyResolver<'a> {
- pub fn new(graph: &'a mut Graph, api: &'a NpmRegistryApi) -> Self {
- Self {
- graph,
- api,
- pending_unresolved_nodes: Default::default(),
- unresolved_optional_peers: Default::default(),
- dep_entry_cache: Default::default(),
- }
- }
-
- pub fn add_root_package(
- &mut self,
- package_nv: &NpmPackageNv,
- package_info: &NpmPackageInfo,
- ) -> Result<(), AnyError> {
- if self.graph.root_packages.contains_key(package_nv) {
- return Ok(()); // already added
- }
-
- // todo(dsherret): using a version requirement here is a temporary hack
- // to reuse code in a large refactor. We should resolve the node directly
- // from the package name and version
- let version_req =
- VersionReq::parse_from_specifier(&format!("{}", package_nv.version))
- .unwrap();
- let (pkg_nv, node_id) = self.resolve_node_from_info(
- &package_nv.name,
- &version_req,
- package_info,
- None,
- )?;
- self.graph.root_packages.insert(pkg_nv.clone(), node_id);
- self
- .pending_unresolved_nodes
- .push_back(GraphPath::for_root(node_id, pkg_nv));
- Ok(())
- }
-
- pub fn add_package_req(
- &mut self,
- package_req: &NpmPackageReq,
- package_info: &NpmPackageInfo,
- ) -> Result<(), AnyError> {
- if self.graph.package_reqs.contains_key(package_req) {
- return Ok(()); // already added
- }
-
- let (pkg_id, node_id) = self.resolve_node_from_info(
- &package_req.name,
- package_req
- .version_req
- .as_ref()
- .unwrap_or(&*LATEST_VERSION_REQ),
- package_info,
- None,
- )?;
- self
- .graph
- .package_reqs
- .insert(package_req.clone(), pkg_id.clone());
- self.graph.root_packages.insert(pkg_id.clone(), node_id);
- self
- .pending_unresolved_nodes
- .push_back(GraphPath::for_root(node_id, pkg_id));
- Ok(())
- }
-
- fn analyze_dependency(
- &mut self,
- entry: &NpmDependencyEntry,
- package_info: &NpmPackageInfo,
- parent_path: &Arc<GraphPath>,
- ) -> Result<NodeId, AnyError> {
- debug_assert_eq!(entry.kind, NpmDependencyEntryKind::Dep);
- let parent_id = parent_path.node_id();
- let (child_nv, mut child_id) = self.resolve_node_from_info(
- &entry.name,
- &entry.version_req,
- package_info,
- Some(parent_id),
- )?;
- // 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 child_id != parent_id {
- let maybe_ancestor = parent_path.find_ancestor(&child_nv);
- if let Some(ancestor) = &maybe_ancestor {
- child_id = ancestor.node_id();
- }
-
- let new_path = parent_path.with_id(
- child_id,
- entry.bare_specifier.to_string(),
- child_nv,
- );
- if let Some(ancestor) = maybe_ancestor {
- // this node is circular, so we link it to the ancestor
- self.add_linked_circular_descendant(&ancestor, new_path);
- } else {
- self.graph.set_child_of_parent_node(
- parent_id,
- &entry.bare_specifier,
- child_id,
- );
- self.pending_unresolved_nodes.push_back(new_path);
- }
- }
- Ok(child_id)
- }
-
- fn resolve_node_from_info(
- &mut self,
- pkg_req_name: &str,
- version_req: &VersionReq,
- package_info: &NpmPackageInfo,
- parent_id: Option<NodeId>,
- ) -> Result<(Arc<NpmPackageNv>, NodeId), AnyError> {
- let version_and_info = resolve_best_package_version_and_info(
- version_req,
- package_info,
- self
- .graph
- .package_name_versions
- .entry(package_info.name.clone())
- .or_default()
- .iter(),
- )?;
- let resolved_id = ResolvedId {
- nv: Arc::new(NpmPackageNv {
- name: package_info.name.to_string(),
- version: version_and_info.version.clone(),
- }),
- peer_dependencies: Vec::new(),
- };
- let (_, node_id) = self.graph.get_or_create_for_id(&resolved_id);
- let pkg_nv = resolved_id.nv;
-
- let has_deps = if let Some(deps) = self.dep_entry_cache.get(&pkg_nv) {
- !deps.is_empty()
- } else {
- let deps = self
- .dep_entry_cache
- .store(pkg_nv.clone(), version_and_info.info)?;
- !deps.is_empty()
- };
-
- if !has_deps {
- // ensure this is set if not, as it's an optimization
- let mut node = self.graph.borrow_node_mut(node_id);
- node.no_peers = true;
- }
-
- debug!(
- "{} - Resolved {}@{} to {}",
- match parent_id {
- Some(parent_id) => self.graph.get_npm_pkg_id(parent_id).as_serialized(),
- None => "<package-req>".to_string(),
- },
- pkg_req_name,
- version_req.version_text(),
- pkg_nv.to_string(),
- );
-
- Ok((pkg_nv, node_id))
- }
-
- pub async fn resolve_pending(&mut self) -> Result<(), AnyError> {
- // go down through the dependencies by tree depth
- while let Some(parent_path) = self.pending_unresolved_nodes.pop_front() {
- let (parent_nv, child_deps) = {
- let node_id = parent_path.node_id();
- if self.graph.nodes.get(&node_id).unwrap().no_peers {
- // We can skip as there's no reason to analyze this graph segment further.
- continue;
- }
-
- let pkg_nv = self
- .graph
- .resolved_node_ids
- .get(node_id)
- .unwrap()
- .nv
- .clone();
- let deps = if let Some(deps) = self.dep_entry_cache.get(&pkg_nv) {
- deps.clone()
- } else {
- // the api should have this in the cache at this point, so no need to parallelize
- match self.api.package_version_info(&pkg_nv).await? {
- Some(version_info) => {
- self.dep_entry_cache.store(pkg_nv.clone(), &version_info)?
- }
- None => {
- bail!("Could not find version information for {}", pkg_nv)
- }
- }
- };
-
- (pkg_nv, deps)
- };
-
- // cache all the dependencies' registry infos in parallel if should
- self
- .api
- .cache_in_parallel({
- child_deps.iter().map(|dep| dep.name.clone()).collect()
- })
- .await?;
-
- // resolve the dependencies
- let mut found_peer = false;
-
- for dep in child_deps.iter() {
- let package_info = self.api.package_info(&dep.name).await?;
-
- match dep.kind {
- NpmDependencyEntryKind::Dep => {
- let parent_id = parent_path.node_id();
- let node = self.graph.nodes.get(&parent_id).unwrap();
- let child_id = match node.children.get(&dep.bare_specifier) {
- Some(child_id) => {
- // this dependency was previously analyzed by another path
- // so we don't attempt to resolve the version again
- let child_id = *child_id;
- let child_nv = self
- .graph
- .resolved_node_ids
- .get(child_id)
- .unwrap()
- .nv
- .clone();
- let maybe_ancestor = parent_path.find_ancestor(&child_nv);
- let child_path = parent_path.with_id(
- child_id,
- dep.bare_specifier.clone(),
- child_nv,
- );
- if let Some(ancestor) = maybe_ancestor {
- // when the nv appears as an ancestor, use that node
- // and mark this as circular
- self.add_linked_circular_descendant(&ancestor, child_path);
- } else {
- // mark the child as pending
- self.pending_unresolved_nodes.push_back(child_path);
- }
- child_id
- }
- None => {
- self.analyze_dependency(dep, &package_info, &parent_path)?
- }
- };
-
- if !found_peer {
- found_peer = !self.graph.borrow_node_mut(child_id).no_peers;
- }
- }
- NpmDependencyEntryKind::Peer
- | NpmDependencyEntryKind::OptionalPeer => {
- found_peer = true;
- // we need to re-evaluate peer dependencies every time and can't
- // skip over them because they might be evaluated differently based
- // on the current path
- let maybe_new_id = self.resolve_peer_dep(
- &dep.bare_specifier,
- dep,
- &package_info,
- &parent_path,
- )?;
-
- // For optional dependencies, we want to resolve them if any future
- // same parent version resolves them. So when not resolved, store them to be
- // potentially resolved later.
- //
- // Note: This is not a good solution, but will probably work ok in most
- // scenarios. We can work on improving this in the future. We probably
- // want to resolve future optional peers to the same dependency for example.
- if dep.kind == NpmDependencyEntryKind::OptionalPeer {
- match maybe_new_id {
- Some(new_id) => {
- if let Some(unresolved_optional_peers) =
- self.unresolved_optional_peers.remove(&parent_nv)
- {
- for optional_peer in unresolved_optional_peers {
- let peer_parent = GraphPathNodeOrRoot::Node(
- optional_peer.graph_path.clone(),
- );
- self.set_new_peer_dep(
- &[&optional_peer.graph_path],
- peer_parent,
- &optional_peer.specifier,
- new_id,
- );
- }
- }
- }
- None => {
- // store this for later if it's resolved for this version
- self
- .unresolved_optional_peers
- .entry(parent_nv.clone())
- .or_default()
- .push(UnresolvedOptionalPeer {
- specifier: dep.bare_specifier.clone(),
- graph_path: parent_path.clone(),
- });
- }
- }
- }
- }
- }
- }
-
- if !found_peer {
- self.graph.borrow_node_mut(parent_path.node_id()).no_peers = true;
- }
- }
- Ok(())
- }
-
- fn resolve_peer_dep(
- &mut self,
- specifier: &str,
- peer_dep: &NpmDependencyEntry,
- peer_package_info: &NpmPackageInfo,
- ancestor_path: &Arc<GraphPath>,
- ) -> Result<Option<NodeId>, AnyError> {
- debug_assert!(matches!(
- peer_dep.kind,
- NpmDependencyEntryKind::Peer | NpmDependencyEntryKind::OptionalPeer
- ));
-
- let mut path = vec![ancestor_path];
-
- // the current dependency might have had the peer dependency
- // in another bare specifier slot... if so resolve it to that
- {
- let maybe_peer_dep = self.find_peer_dep_in_node(
- ancestor_path,
- peer_dep,
- peer_package_info,
- )?;
-
- if let Some((peer_parent, peer_dep_id)) = maybe_peer_dep {
- // this will always have an ancestor because we're not at the root
- self.set_new_peer_dep(&path, peer_parent, specifier, peer_dep_id);
- return Ok(Some(peer_dep_id));
- }
- }
-
- // Peer dependencies are resolved based on its ancestors' siblings.
- // If not found, then it resolves based on the version requirement if non-optional.
- for ancestor_node in ancestor_path.ancestors() {
- match ancestor_node {
- GraphPathNodeOrRoot::Node(ancestor_graph_path_node) => {
- path.push(ancestor_graph_path_node);
- let maybe_peer_dep = self.find_peer_dep_in_node(
- ancestor_graph_path_node,
- peer_dep,
- peer_package_info,
- )?;
- if let Some((parent, peer_dep_id)) = maybe_peer_dep {
- // this will always have an ancestor because we're not at the root
- self.set_new_peer_dep(&path, parent, specifier, peer_dep_id);
- return Ok(Some(peer_dep_id));
- }
- }
- GraphPathNodeOrRoot::Root(root_pkg_id) => {
- // in this case, the parent is the root so the children are all the package requirements
- if let Some(child_id) = find_matching_child(
- peer_dep,
- peer_package_info,
- self.graph.root_packages.iter().map(|(nv, id)| (*id, nv)),
- )? {
- let peer_parent = GraphPathNodeOrRoot::Root(root_pkg_id.clone());
- self.set_new_peer_dep(&path, peer_parent, specifier, child_id);
- return Ok(Some(child_id));
- }
- }
- }
- }
-
- // We didn't find anything by searching the ancestor siblings, so we need
- // to resolve based on the package info
- if !peer_dep.kind.is_optional() {
- let parent_id = ancestor_path.node_id();
- let (_, node_id) = self.resolve_node_from_info(
- &peer_dep.name,
- peer_dep
- .peer_dep_version_req
- .as_ref()
- .unwrap_or(&peer_dep.version_req),
- peer_package_info,
- Some(parent_id),
- )?;
- let peer_parent = GraphPathNodeOrRoot::Node(ancestor_path.clone());
- self.set_new_peer_dep(&[ancestor_path], peer_parent, specifier, node_id);
- Ok(Some(node_id))
- } else {
- Ok(None)
- }
- }
-
- fn find_peer_dep_in_node(
- &self,
- path: &Arc<GraphPath>,
- peer_dep: &NpmDependencyEntry,
- peer_package_info: &NpmPackageInfo,
- ) -> Result<Option<(GraphPathNodeOrRoot, NodeId)>, AnyError> {
- let node_id = path.node_id();
- let resolved_node_id = self.graph.resolved_node_ids.get(node_id).unwrap();
- // check if this node itself is a match for
- // the peer dependency and if so use that
- if resolved_node_id.nv.name == peer_dep.name
- && version_req_satisfies(
- &peer_dep.version_req,
- &resolved_node_id.nv.version,
- peer_package_info,
- None,
- )?
- {
- let parent = path.previous_node.as_ref().unwrap().clone();
- Ok(Some((parent, node_id)))
- } else {
- let node = self.graph.nodes.get(&node_id).unwrap();
- let children = node.children.values().map(|child_node_id| {
- let child_node_id = *child_node_id;
- (
- child_node_id,
- &self.graph.resolved_node_ids.get(child_node_id).unwrap().nv,
- )
- });
- find_matching_child(peer_dep, peer_package_info, children).map(
- |maybe_child_id| {
- maybe_child_id.map(|child_id| {
- let parent = GraphPathNodeOrRoot::Node(path.clone());
- (parent, child_id)
- })
- },
- )
- }
- }
-
- fn add_peer_deps_to_path(
- &mut self,
- // path from the node above the resolved dep to just above the peer dep
- path: &[&Arc<GraphPath>],
- peer_deps: &[(&ResolvedIdPeerDep, Arc<NpmPackageNv>)],
- ) {
- debug_assert!(!path.is_empty());
-
- for graph_path_node in path.iter().rev() {
- let old_node_id = graph_path_node.node_id();
- let old_resolved_id = self
- .graph
- .resolved_node_ids
- .get(old_node_id)
- .unwrap()
- .clone();
-
- let mut new_resolved_id = old_resolved_id;
- let mut has_changed = false;
- for (peer_dep, nv) in peer_deps {
- if *nv == new_resolved_id.nv {
- continue;
- }
- if new_resolved_id.push_peer_dep((*peer_dep).clone()) {
- has_changed = true;
- }
- }
-
- if !has_changed {
- continue; // nothing to change
- }
-
- let (created, new_node_id) =
- self.graph.get_or_create_for_id(&new_resolved_id);
-
- if created {
- let old_children =
- self.graph.borrow_node_mut(old_node_id).children.clone();
- // copy over the old children to this new one
- for (specifier, child_id) in &old_children {
- self.graph.set_child_of_parent_node(
- new_node_id,
- specifier,
- *child_id,
- );
- }
- }
-
- graph_path_node.change_id(new_node_id);
-
- let circular_descendants =
- graph_path_node.linked_circular_descendants.lock().clone();
- for descendant in circular_descendants {
- let path = descendant.get_path_to_ancestor_exclusive(new_node_id);
- self.add_peer_deps_to_path(&path, peer_deps);
- descendant.change_id(new_node_id);
-
- // update the bottom node to point to this new node id
- let bottom_node_id = path[0].node_id();
- self.graph.set_child_of_parent_node(
- bottom_node_id,
- descendant.specifier(),
- descendant.node_id(),
- );
- }
-
- // update the previous parent to have this as its child
- match graph_path_node.previous_node.as_ref().unwrap() {
- GraphPathNodeOrRoot::Root(pkg_id) => {
- self.graph.root_packages.insert(pkg_id.clone(), new_node_id);
- }
- GraphPathNodeOrRoot::Node(parent_node_path) => {
- let parent_node_id = parent_node_path.node_id();
- let parent_node = self.graph.borrow_node_mut(parent_node_id);
- parent_node
- .children
- .insert(graph_path_node.specifier().to_string(), new_node_id);
- }
- }
- }
- }
-
- fn set_new_peer_dep(
- &mut self,
- // path from the node above the resolved dep to just above the peer dep
- path: &[&Arc<GraphPath>],
- peer_dep_parent: GraphPathNodeOrRoot,
- peer_dep_specifier: &str,
- peer_dep_id: NodeId,
- ) {
- debug_assert!(!path.is_empty());
- let peer_dep_nv = self
- .graph
- .resolved_node_ids
- .get(peer_dep_id)
- .unwrap()
- .nv
- .clone();
-
- let peer_dep = ResolvedIdPeerDep::ParentReference {
- parent: peer_dep_parent,
- child_pkg_nv: peer_dep_nv.clone(),
- };
-
- let top_node = path.last().unwrap();
- let (maybe_circular_ancestor, path) = if top_node.nv == peer_dep_nv {
- // it's circular, so exclude the top node
- (Some(top_node), &path[0..path.len() - 1])
- } else {
- (None, path)
- };
- self.add_peer_deps_to_path(path, &[(&peer_dep, peer_dep_nv.clone())]);
-
- // now set the peer dependency
- let bottom_node = path.first().unwrap();
- self.graph.set_child_of_parent_node(
- bottom_node.node_id(),
- peer_dep_specifier,
- peer_dep_id,
- );
-
- // queue next step
- let new_path = bottom_node.with_id(
- peer_dep_id,
- peer_dep_specifier.to_string(),
- peer_dep_nv,
- );
- if let Some(ancestor_node) = maybe_circular_ancestor {
- // it's circular, so link this in step with the ancestor node
- ancestor_node
- .linked_circular_descendants
- .lock()
- .push(new_path);
- } else {
- // mark the peer dep as needing to be analyzed
- self.pending_unresolved_nodes.push_back(new_path);
- }
-
- debug!(
- "Resolved peer dependency for {} in {} to {}",
- peer_dep_specifier,
- &self
- .graph
- .get_npm_pkg_id(bottom_node.node_id())
- .as_serialized(),
- &self.graph.get_npm_pkg_id(peer_dep_id).as_serialized(),
- );
- }
-
- fn add_linked_circular_descendant(
- &mut self,
- ancestor: &Arc<GraphPath>,
- descendant: Arc<GraphPath>,
- ) {
- let ancestor_node_id = ancestor.node_id();
- let path = descendant.get_path_to_ancestor_exclusive(ancestor_node_id);
-
- let ancestor_resolved_id = self
- .graph
- .resolved_node_ids
- .get(ancestor_node_id)
- .unwrap()
- .clone();
-
- let peer_deps = ancestor_resolved_id
- .peer_dependencies
- .iter()
- .map(|peer_dep| {
- (
- peer_dep,
- match &peer_dep {
- ResolvedIdPeerDep::ParentReference { child_pkg_nv, .. } => {
- child_pkg_nv.clone()
- }
- ResolvedIdPeerDep::SnapshotNodeId(node_id) => self
- .graph
- .resolved_node_ids
- .get(*node_id)
- .unwrap()
- .nv
- .clone(),
- },
- )
- })
- .collect::<Vec<_>>();
- if !peer_deps.is_empty() {
- self.add_peer_deps_to_path(&path, &peer_deps);
- }
-
- let bottom_node_id = path[0].node_id();
- self.graph.set_child_of_parent_node(
- bottom_node_id,
- descendant.specifier(),
- descendant.node_id(),
- );
-
- ancestor.linked_circular_descendants.lock().push(descendant);
- }
-}
-
-fn find_matching_child<'a>(
- peer_dep: &NpmDependencyEntry,
- peer_package_info: &NpmPackageInfo,
- children: impl Iterator<Item = (NodeId, &'a Arc<NpmPackageNv>)>,
-) -> Result<Option<NodeId>, AnyError> {
- for (child_id, pkg_id) in children {
- if pkg_id.name == peer_dep.name
- && version_req_satisfies(
- &peer_dep.version_req,
- &pkg_id.version,
- peer_package_info,
- None,
- )?
- {
- return Ok(Some(child_id));
- }
- }
- Ok(None)
-}
-
-#[cfg(test)]
-mod test {
- use deno_graph::npm::NpmPackageReqReference;
- use pretty_assertions::assert_eq;
-
- use crate::npm::registry::TestNpmRegistryApiInner;
-
- use super::*;
-
- #[test]
- fn resolved_id_tests() {
- let mut ids = ResolvedNodeIds::default();
- let node_id = NodeId(0);
- let resolved_id = ResolvedId {
- nv: Arc::new(NpmPackageNv::from_str("package@1.1.1").unwrap()),
- peer_dependencies: Vec::new(),
- };
- ids.set(node_id, resolved_id.clone());
- assert!(ids.get(node_id).is_some());
- assert!(ids.get(NodeId(1)).is_none());
- assert_eq!(ids.get_node_id(&resolved_id), Some(node_id));
-
- let resolved_id_new = ResolvedId {
- nv: Arc::new(NpmPackageNv::from_str("package@1.1.2").unwrap()),
- peer_dependencies: Vec::new(),
- };
- ids.set(node_id, resolved_id_new.clone());
- assert_eq!(ids.get_node_id(&resolved_id), None); // stale entry should have been removed
- assert!(ids.get(node_id).is_some());
- assert_eq!(ids.get_node_id(&resolved_id_new), Some(node_id));
- }
-
- #[tokio::test]
- async fn resolve_deps_no_peer() {
- let api = TestNpmRegistryApiInner::default();
- api.ensure_package_version("package-a", "1.0.0");
- api.ensure_package_version("package-b", "2.0.0");
- api.ensure_package_version("package-c", "0.1.0");
- api.ensure_package_version("package-c", "0.0.10");
- api.ensure_package_version("package-d", "3.2.1");
- api.ensure_package_version("package-d", "3.2.0");
- api.add_dependency(("package-a", "1.0.0"), ("package-b", "^2"));
- api.add_dependency(("package-a", "1.0.0"), ("package-c", "^0.1"));
- api.add_dependency(("package-c", "0.1.0"), ("package-d", "*"));
-
- let (packages, package_reqs) =
- run_resolver_and_get_output(api, vec!["npm:package-a@1"]).await;
- assert_eq!(
- packages,
- vec![
- TestNpmResolutionPackage {
- pkg_id: "package-a@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([
- ("package-b".to_string(), "package-b@2.0.0".to_string(),),
- ("package-c".to_string(), "package-c@0.1.0".to_string(),),
- ]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-b@2.0.0".to_string(),
- copy_index: 0,
- dependencies: Default::default(),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-c@0.1.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-d".to_string(),
- "package-d@3.2.1".to_string(),
- )])
- },
- TestNpmResolutionPackage {
- pkg_id: "package-d@3.2.1".to_string(),
- copy_index: 0,
- dependencies: Default::default(),
- },
- ]
- );
- assert_eq!(
- package_reqs,
- vec![("package-a@1".to_string(), "package-a@1.0.0".to_string())]
- );
- }
-
- #[tokio::test]
- async fn resolve_deps_circular() {
- let api = TestNpmRegistryApiInner::default();
- api.ensure_package_version("package-a", "1.0.0");
- api.ensure_package_version("package-b", "2.0.0");
- api.add_dependency(("package-a", "1.0.0"), ("package-b", "*"));
- api.add_dependency(("package-b", "2.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![
- TestNpmResolutionPackage {
- pkg_id: "package-a@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-b".to_string(),
- "package-b@2.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-b@2.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-a".to_string(),
- "package-a@1.0.0".to_string(),
- )]),
- },
- ]
- );
- assert_eq!(
- package_reqs,
- vec![("package-a@1.0".to_string(), "package-a@1.0.0".to_string())]
- );
- }
-
- #[tokio::test]
- async fn peer_deps_simple_top_tree() {
- let api = TestNpmRegistryApiInner::default();
- api.ensure_package_version("package-a", "1.0.0");
- api.ensure_package_version("package-b", "1.0.0");
- api.ensure_package_version("package-peer", "1.0.0");
- api.add_dependency(("package-a", "1.0.0"), ("package-b", "1"));
- api.add_peer_dependency(("package-b", "1.0.0"), ("package-peer", "*"));
-
- let (packages, package_reqs) = run_resolver_and_get_output(
- api,
- vec!["npm:package-a@1.0", "npm:package-peer@1.0"],
- )
- .await;
- assert_eq!(
- packages,
- vec![
- TestNpmResolutionPackage {
- pkg_id: "package-a@1.0.0_package-peer@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-b".to_string(),
- "package-b@1.0.0_package-peer@1.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-b@1.0.0_package-peer@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-peer".to_string(),
- "package-peer@1.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-peer@1.0.0".to_string(),
- copy_index: 0,
- dependencies: Default::default(),
- }
- ]
- );
- assert_eq!(
- package_reqs,
- vec![
- (
- "package-a@1.0".to_string(),
- "package-a@1.0.0_package-peer@1.0.0".to_string()
- ),
- (
- "package-peer@1.0".to_string(),
- "package-peer@1.0.0".to_string()
- )
- ]
- );
- }
-
- #[tokio::test]
- async fn peer_deps_simple_root_pkg_children() {
- let api = TestNpmRegistryApiInner::default();
- api.ensure_package_version("package-0", "1.0.0");
- api.ensure_package_version("package-a", "1.0.0");
- api.ensure_package_version("package-b", "1.0.0");
- api.ensure_package_version("package-peer", "1.0.0");
- api.add_dependency(("package-0", "1.0.0"), ("package-a", "1"));
- api.add_dependency(("package-0", "1.0.0"), ("package-peer", "1"));
- api.add_dependency(("package-a", "1.0.0"), ("package-b", "1"));
- api.add_peer_dependency(("package-b", "1.0.0"), ("package-peer", "*"));
-
- let (packages, package_reqs) =
- run_resolver_and_get_output(api, vec!["npm:package-0@1.0"]).await;
- assert_eq!(
- packages,
- vec![
- TestNpmResolutionPackage {
- pkg_id: "package-0@1.0.0_package-peer@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([
- (
- "package-a".to_string(),
- "package-a@1.0.0_package-peer@1.0.0".to_string(),
- ),
- ("package-peer".to_string(), "package-peer@1.0.0".to_string(),)
- ]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-a@1.0.0_package-peer@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-b".to_string(),
- "package-b@1.0.0_package-peer@1.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-b@1.0.0_package-peer@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-peer".to_string(),
- "package-peer@1.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-peer@1.0.0".to_string(),
- copy_index: 0,
- dependencies: Default::default(),
- }
- ]
- );
- assert_eq!(
- package_reqs,
- vec![(
- "package-0@1.0".to_string(),
- "package-0@1.0.0_package-peer@1.0.0".to_string()
- ),]
- );
- }
-
- #[tokio::test]
- async fn peer_deps_simple_deeper() {
- let api = TestNpmRegistryApiInner::default();
- api.ensure_package_version("package-0", "1.0.0");
- api.ensure_package_version("package-1", "1.0.0");
- api.ensure_package_version("package-a", "1.0.0");
- api.ensure_package_version("package-b", "1.0.0");
- api.ensure_package_version("package-peer", "1.0.0");
- api.add_dependency(("package-0", "1.0.0"), ("package-1", "1"));
- api.add_dependency(("package-1", "1.0.0"), ("package-a", "1"));
- api.add_dependency(("package-1", "1.0.0"), ("package-peer", "1"));
- api.add_dependency(("package-a", "1.0.0"), ("package-b", "1"));
- api.add_peer_dependency(("package-b", "1.0.0"), ("package-peer", "*"));
-
- let (packages, package_reqs) =
- run_resolver_and_get_output(api, vec!["npm:package-0@1.0"]).await;
- assert_eq!(
- packages,
- vec![
- TestNpmResolutionPackage {
- pkg_id: "package-0@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-1".to_string(),
- "package-1@1.0.0_package-peer@1.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-1@1.0.0_package-peer@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([
- (
- "package-a".to_string(),
- "package-a@1.0.0_package-peer@1.0.0".to_string(),
- ),
- ("package-peer".to_string(), "package-peer@1.0.0".to_string(),)
- ]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-a@1.0.0_package-peer@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-b".to_string(),
- "package-b@1.0.0_package-peer@1.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-b@1.0.0_package-peer@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-peer".to_string(),
- "package-peer@1.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-peer@1.0.0".to_string(),
- copy_index: 0,
- dependencies: Default::default(),
- }
- ]
- );
- assert_eq!(
- package_reqs,
- vec![("package-0@1.0".to_string(), "package-0@1.0.0".to_string()),]
- );
- }
-
- #[tokio::test]
- async fn resolve_with_peer_deps_top_tree() {
- let api = TestNpmRegistryApiInner::default();
- api.ensure_package_version("package-a", "1.0.0");
- api.ensure_package_version("package-b", "2.0.0");
- api.ensure_package_version("package-c", "3.0.0");
- api.ensure_package_version("package-peer", "4.0.0");
- api.ensure_package_version("package-peer", "4.1.0");
- api.add_dependency(("package-a", "1.0.0"), ("package-b", "^2"));
- api.add_dependency(("package-a", "1.0.0"), ("package-c", "^3"));
- api.add_peer_dependency(("package-b", "2.0.0"), ("package-peer", "4"));
- api.add_peer_dependency(("package-c", "3.0.0"), ("package-peer", "*"));
-
- let (packages, package_reqs) = run_resolver_and_get_output(
- api,
- // the peer dependency is specified here at the top of the tree
- // so it should resolve to 4.0.0 instead of 4.1.0
- vec!["npm:package-a@1", "npm:package-peer@4.0.0"],
- )
- .await;
- assert_eq!(
- packages,
- vec![
- TestNpmResolutionPackage {
- pkg_id: "package-a@1.0.0_package-peer@4.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([
- (
- "package-b".to_string(),
- "package-b@2.0.0_package-peer@4.0.0".to_string(),
- ),
- (
- "package-c".to_string(),
- "package-c@3.0.0_package-peer@4.0.0".to_string(),
- ),
- ]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-b@2.0.0_package-peer@4.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-peer".to_string(),
- "package-peer@4.0.0".to_string(),
- )])
- },
- TestNpmResolutionPackage {
- pkg_id: "package-c@3.0.0_package-peer@4.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-peer".to_string(),
- "package-peer@4.0.0".to_string(),
- )])
- },
- TestNpmResolutionPackage {
- pkg_id: "package-peer@4.0.0".to_string(),
- copy_index: 0,
- dependencies: Default::default(),
- },
- ]
- );
- assert_eq!(
- package_reqs,
- vec![
- (
- "package-a@1".to_string(),
- "package-a@1.0.0_package-peer@4.0.0".to_string()
- ),
- (
- "package-peer@4.0.0".to_string(),
- "package-peer@4.0.0".to_string()
- )
- ]
- );
- }
-
- #[tokio::test]
- async fn resolve_with_peer_deps_ancestor_sibling_not_top_tree() {
- let api = TestNpmRegistryApiInner::default();
- api.ensure_package_version("package-0", "1.1.1");
- api.ensure_package_version("package-a", "1.0.0");
- api.ensure_package_version("package-b", "2.0.0");
- api.ensure_package_version("package-c", "3.0.0");
- api.ensure_package_version("package-peer", "4.0.0");
- api.ensure_package_version("package-peer", "4.1.0");
- api.add_dependency(("package-0", "1.1.1"), ("package-a", "1"));
- api.add_dependency(("package-a", "1.0.0"), ("package-b", "^2"));
- api.add_dependency(("package-a", "1.0.0"), ("package-c", "^3"));
- // the peer dependency is specified here as a sibling of "a" and "b"
- // so it should resolve to 4.0.0 instead of 4.1.0
- api.add_dependency(("package-a", "1.0.0"), ("package-peer", "4.0.0"));
- api.add_peer_dependency(("package-b", "2.0.0"), ("package-peer", "4"));
- api.add_peer_dependency(("package-c", "3.0.0"), ("package-peer", "*"));
-
- let (packages, package_reqs) =
- run_resolver_and_get_output(api, vec!["npm:package-0@1.1.1"]).await;
- assert_eq!(
- packages,
- vec![
- TestNpmResolutionPackage {
- pkg_id: "package-0@1.1.1".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-a".to_string(),
- "package-a@1.0.0_package-peer@4.0.0".to_string(),
- ),]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-a@1.0.0_package-peer@4.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([
- (
- "package-b".to_string(),
- "package-b@2.0.0_package-peer@4.0.0".to_string(),
- ),
- (
- "package-c".to_string(),
- "package-c@3.0.0_package-peer@4.0.0".to_string(),
- ),
- ("package-peer".to_string(), "package-peer@4.0.0".to_string(),),
- ]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-b@2.0.0_package-peer@4.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-peer".to_string(),
- "package-peer@4.0.0".to_string(),
- )])
- },
- TestNpmResolutionPackage {
- pkg_id: "package-c@3.0.0_package-peer@4.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-peer".to_string(),
- "package-peer@4.0.0".to_string(),
- )])
- },
- TestNpmResolutionPackage {
- pkg_id: "package-peer@4.0.0".to_string(),
- copy_index: 0,
- dependencies: Default::default(),
- },
- ]
- );
- assert_eq!(
- package_reqs,
- vec![("package-0@1.1.1".to_string(), "package-0@1.1.1".to_string())]
- );
- }
-
- #[tokio::test]
- async fn resolve_with_peer_deps_auto_resolved() {
- // in this case, the peer dependency is not found in the tree
- // so it's auto-resolved based on the registry
- let api = TestNpmRegistryApiInner::default();
- api.ensure_package_version("package-a", "1.0.0");
- api.ensure_package_version("package-b", "2.0.0");
- api.ensure_package_version("package-c", "3.0.0");
- api.ensure_package_version("package-peer", "4.0.0");
- api.ensure_package_version("package-peer", "4.1.0");
- api.add_dependency(("package-a", "1.0.0"), ("package-b", "^2"));
- api.add_dependency(("package-a", "1.0.0"), ("package-c", "^3"));
- api.add_peer_dependency(("package-b", "2.0.0"), ("package-peer", "4"));
- api.add_peer_dependency(("package-c", "3.0.0"), ("package-peer", "*"));
-
- let (packages, package_reqs) =
- run_resolver_and_get_output(api, vec!["npm:package-a@1"]).await;
- assert_eq!(
- packages,
- vec![
- TestNpmResolutionPackage {
- pkg_id: "package-a@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([
- (
- "package-b".to_string(),
- "package-b@2.0.0_package-peer@4.1.0".to_string(),
- ),
- (
- "package-c".to_string(),
- "package-c@3.0.0_package-peer@4.1.0".to_string(),
- ),
- ]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-b@2.0.0_package-peer@4.1.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-peer".to_string(),
- "package-peer@4.1.0".to_string(),
- )])
- },
- TestNpmResolutionPackage {
- pkg_id: "package-c@3.0.0_package-peer@4.1.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-peer".to_string(),
- "package-peer@4.1.0".to_string(),
- )])
- },
- TestNpmResolutionPackage {
- pkg_id: "package-peer@4.1.0".to_string(),
- copy_index: 0,
- dependencies: Default::default(),
- },
- ]
- );
- assert_eq!(
- package_reqs,
- vec![("package-a@1".to_string(), "package-a@1.0.0".to_string())]
- );
- }
-
- #[tokio::test]
- async fn resolve_with_optional_peer_dep_not_resolved() {
- // in this case, the peer dependency is not found in the tree
- // so it's auto-resolved based on the registry
- let api = TestNpmRegistryApiInner::default();
- api.ensure_package_version("package-a", "1.0.0");
- api.ensure_package_version("package-b", "2.0.0");
- api.ensure_package_version("package-c", "3.0.0");
- api.ensure_package_version("package-peer", "4.0.0");
- api.ensure_package_version("package-peer", "4.1.0");
- api.add_dependency(("package-a", "1.0.0"), ("package-b", "^2"));
- api.add_dependency(("package-a", "1.0.0"), ("package-c", "^3"));
- api.add_optional_peer_dependency(
- ("package-b", "2.0.0"),
- ("package-peer", "4"),
- );
- api.add_optional_peer_dependency(
- ("package-c", "3.0.0"),
- ("package-peer", "*"),
- );
-
- let (packages, package_reqs) =
- run_resolver_and_get_output(api, vec!["npm:package-a@1"]).await;
- assert_eq!(
- packages,
- vec![
- TestNpmResolutionPackage {
- pkg_id: "package-a@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([
- ("package-b".to_string(), "package-b@2.0.0".to_string(),),
- ("package-c".to_string(), "package-c@3.0.0".to_string(),),
- ]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-b@2.0.0".to_string(),
- copy_index: 0,
- dependencies: Default::default(),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-c@3.0.0".to_string(),
- copy_index: 0,
- dependencies: Default::default(),
- },
- ]
- );
- assert_eq!(
- package_reqs,
- vec![("package-a@1".to_string(), "package-a@1.0.0".to_string())]
- );
- }
-
- #[tokio::test]
- async fn resolve_with_optional_peer_found() {
- let api = TestNpmRegistryApiInner::default();
- api.ensure_package_version("package-a", "1.0.0");
- api.ensure_package_version("package-b", "2.0.0");
- api.ensure_package_version("package-c", "3.0.0");
- api.ensure_package_version("package-peer", "4.0.0");
- api.ensure_package_version("package-peer", "4.1.0");
- api.add_dependency(("package-a", "1.0.0"), ("package-b", "^2"));
- api.add_dependency(("package-a", "1.0.0"), ("package-c", "^3"));
- api.add_optional_peer_dependency(
- ("package-b", "2.0.0"),
- ("package-peer", "4"),
- );
- api.add_optional_peer_dependency(
- ("package-c", "3.0.0"),
- ("package-peer", "*"),
- );
-
- let (packages, package_reqs) = run_resolver_and_get_output(
- api,
- vec!["npm:package-a@1", "npm:package-peer@4.0.0"],
- )
- .await;
- assert_eq!(
- packages,
- vec![
- TestNpmResolutionPackage {
- pkg_id: "package-a@1.0.0_package-peer@4.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([
- (
- "package-b".to_string(),
- "package-b@2.0.0_package-peer@4.0.0".to_string(),
- ),
- (
- "package-c".to_string(),
- "package-c@3.0.0_package-peer@4.0.0".to_string(),
- ),
- ]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-b@2.0.0_package-peer@4.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-peer".to_string(),
- "package-peer@4.0.0".to_string(),
- )])
- },
- TestNpmResolutionPackage {
- pkg_id: "package-c@3.0.0_package-peer@4.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-peer".to_string(),
- "package-peer@4.0.0".to_string(),
- )])
- },
- TestNpmResolutionPackage {
- pkg_id: "package-peer@4.0.0".to_string(),
- copy_index: 0,
- dependencies: Default::default(),
- },
- ]
- );
- assert_eq!(
- package_reqs,
- vec![
- (
- "package-a@1".to_string(),
- "package-a@1.0.0_package-peer@4.0.0".to_string()
- ),
- (
- "package-peer@4.0.0".to_string(),
- "package-peer@4.0.0".to_string()
- )
- ]
- );
- }
-
- #[tokio::test]
- async fn resolve_optional_peer_first_not_resolved_second_resolved_scenario1()
- {
- // When resolving a dependency a second time and it has an optional
- // peer dependency that wasn't previously resolved, it should resolve all the
- // previous versions to the new one
- let api = TestNpmRegistryApiInner::default();
- api.ensure_package_version("package-a", "1.0.0");
- api.ensure_package_version("package-b", "1.0.0");
- api.ensure_package_version("package-peer", "1.0.0");
- api.add_dependency(("package-a", "1.0.0"), ("package-b", "^1"));
- api.add_dependency(("package-a", "1.0.0"), ("package-peer", "^1"));
- api.add_optional_peer_dependency(
- ("package-b", "1.0.0"),
- ("package-peer", "*"),
- );
-
- let (packages, package_reqs) = run_resolver_and_get_output(
- api,
- vec!["npm:package-a@1", "npm:package-b@1"],
- )
- .await;
- assert_eq!(
- packages,
- vec![
- TestNpmResolutionPackage {
- pkg_id: "package-a@1.0.0_package-peer@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([
- (
- "package-b".to_string(),
- "package-b@1.0.0_package-peer@1.0.0".to_string(),
- ),
- ("package-peer".to_string(), "package-peer@1.0.0".to_string(),),
- ]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-b@1.0.0_package-peer@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-peer".to_string(),
- "package-peer@1.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-peer@1.0.0".to_string(),
- copy_index: 0,
- dependencies: Default::default(),
- },
- ]
- );
- assert_eq!(
- package_reqs,
- vec![
- (
- "package-a@1".to_string(),
- "package-a@1.0.0_package-peer@1.0.0".to_string()
- ),
- (
- "package-b@1".to_string(),
- "package-b@1.0.0_package-peer@1.0.0".to_string()
- )
- ]
- );
- }
-
- #[tokio::test]
- async fn resolve_optional_peer_first_not_resolved_second_resolved_scenario2()
- {
- let api = TestNpmRegistryApiInner::default();
- api.ensure_package_version("package-a", "1.0.0");
- api.ensure_package_version("package-b", "1.0.0");
- api.ensure_package_version("package-peer", "2.0.0");
- api.add_optional_peer_dependency(
- ("package-a", "1.0.0"),
- ("package-peer", "*"),
- );
- api.add_dependency(("package-b", "1.0.0"), ("package-a", "1.0.0"));
- api.add_dependency(("package-b", "1.0.0"), ("package-peer", "2.0.0"));
-
- let (packages, package_reqs) = run_resolver_and_get_output(
- api,
- vec!["npm:package-a@1", "npm:package-b@1"],
- )
- .await;
- assert_eq!(
- packages,
- vec![
- TestNpmResolutionPackage {
- pkg_id: "package-a@1.0.0_package-peer@2.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-peer".to_string(),
- "package-peer@2.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-b@1.0.0_package-peer@2.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([
- (
- "package-a".to_string(),
- "package-a@1.0.0_package-peer@2.0.0".to_string(),
- ),
- ("package-peer".to_string(), "package-peer@2.0.0".to_string(),)
- ]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-peer@2.0.0".to_string(),
- copy_index: 0,
- dependencies: Default::default(),
- },
- ]
- );
- assert_eq!(
- package_reqs,
- vec![
- (
- "package-a@1".to_string(),
- "package-a@1.0.0_package-peer@2.0.0".to_string()
- ),
- (
- "package-b@1".to_string(),
- "package-b@1.0.0_package-peer@2.0.0".to_string()
- )
- ]
- );
- }
-
- #[tokio::test]
- async fn resolve_optional_dep_npm_req_top() {
- let api = TestNpmRegistryApiInner::default();
- api.ensure_package_version("package-a", "1.0.0");
- api.ensure_package_version("package-peer", "1.0.0");
- api.add_optional_peer_dependency(
- ("package-a", "1.0.0"),
- ("package-peer", "*"),
- );
-
- let (packages, package_reqs) = run_resolver_and_get_output(
- api,
- vec!["npm:package-a@1", "npm:package-peer@1"],
- )
- .await;
- assert_eq!(
- packages,
- vec![
- TestNpmResolutionPackage {
- pkg_id: "package-a@1.0.0_package-peer@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-peer".to_string(),
- "package-peer@1.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-peer@1.0.0".to_string(),
- copy_index: 0,
- dependencies: Default::default(),
- },
- ]
- );
- assert_eq!(
- package_reqs,
- vec![
- (
- "package-a@1".to_string(),
- "package-a@1.0.0_package-peer@1.0.0".to_string()
- ),
- (
- "package-peer@1".to_string(),
- "package-peer@1.0.0".to_string()
- )
- ]
- );
- }
-
- #[tokio::test]
- async fn resolve_optional_dep_different_resolution_second_time() {
- let api = TestNpmRegistryApiInner::default();
- api.ensure_package_version("package-a", "1.0.0");
- api.ensure_package_version("package-b", "1.0.0");
- api.ensure_package_version("package-peer", "1.0.0");
- api.ensure_package_version("package-peer", "2.0.0");
- api.add_optional_peer_dependency(
- ("package-a", "1.0.0"),
- ("package-peer", "*"),
- );
- api.add_dependency(("package-b", "1.0.0"), ("package-a", "1.0.0"));
- api.add_dependency(("package-b", "1.0.0"), ("package-peer", "2.0.0"));
-
- let (packages, package_reqs) = run_resolver_and_get_output(
- api,
- vec![
- "npm:package-a@1",
- "npm:package-b@1",
- "npm:package-peer@1.0.0",
- ],
- )
- .await;
- assert_eq!(
- packages,
- vec![
- TestNpmResolutionPackage {
- pkg_id: "package-a@1.0.0_package-peer@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-peer".to_string(),
- "package-peer@1.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-a@1.0.0_package-peer@2.0.0".to_string(),
- copy_index: 1,
- dependencies: BTreeMap::from([(
- "package-peer".to_string(),
- "package-peer@2.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-b@1.0.0_package-peer@2.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([
- ("package-peer".to_string(), "package-peer@2.0.0".to_string(),),
- (
- "package-a".to_string(),
- "package-a@1.0.0_package-peer@2.0.0".to_string(),
- ),
- ]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-peer@1.0.0".to_string(),
- copy_index: 0,
- dependencies: Default::default(),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-peer@2.0.0".to_string(),
- copy_index: 0,
- dependencies: Default::default(),
- },
- ]
- );
- assert_eq!(
- package_reqs,
- vec![
- (
- "package-a@1".to_string(),
- "package-a@1.0.0_package-peer@1.0.0".to_string()
- ),
- (
- "package-b@1".to_string(),
- "package-b@1.0.0_package-peer@2.0.0".to_string()
- ),
- (
- "package-peer@1.0.0".to_string(),
- "package-peer@1.0.0".to_string()
- )
- ]
- );
- }
- #[tokio::test]
- async fn resolve_peer_dep_other_specifier_slot() {
- let api = TestNpmRegistryApiInner::default();
- api.ensure_package_version("package-a", "1.0.0");
- api.ensure_package_version("package-peer", "2.0.0");
- // bit of an edge case... probably nobody has ever done this
- api.add_dependency(
- ("package-a", "1.0.0"),
- ("package-peer2", "npm:package-peer@2"),
- );
- api.add_peer_dependency(("package-a", "1.0.0"), ("package-peer", "2"));
-
- let (packages, package_reqs) =
- run_resolver_and_get_output(api, vec!["npm:package-a@1"]).await;
- assert_eq!(
- packages,
- vec![
- TestNpmResolutionPackage {
- pkg_id: "package-a@1.0.0_package-peer@2.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([
- ("package-peer".to_string(), "package-peer@2.0.0".to_string(),),
- (
- "package-peer2".to_string(),
- "package-peer@2.0.0".to_string(),
- ),
- ]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-peer@2.0.0".to_string(),
- copy_index: 0,
- dependencies: Default::default(),
- },
- ]
- );
- assert_eq!(
- package_reqs,
- vec![(
- "package-a@1".to_string(),
- "package-a@1.0.0_package-peer@2.0.0".to_string()
- ),]
- );
- }
-
- #[tokio::test]
- async fn resolve_nested_peer_deps_auto_resolved() {
- let api = TestNpmRegistryApiInner::default();
- api.ensure_package_version("package-0", "1.0.0");
- api.ensure_package_version("package-peer-a", "2.0.0");
- api.ensure_package_version("package-peer-b", "3.0.0");
- api.add_peer_dependency(("package-0", "1.0.0"), ("package-peer-a", "2"));
- api.add_peer_dependency(
- ("package-peer-a", "2.0.0"),
- ("package-peer-b", "3"),
- );
-
- let (packages, package_reqs) =
- run_resolver_and_get_output(api, vec!["npm:package-0@1.0"]).await;
- assert_eq!(
- packages,
- vec![
- TestNpmResolutionPackage {
- pkg_id: "package-0@1.0.0_package-peer-a@2.0.0__package-peer-b@3.0.0"
- .to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-peer-a".to_string(),
- "package-peer-a@2.0.0_package-peer-b@3.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-peer-a@2.0.0_package-peer-b@3.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-peer-b".to_string(),
- "package-peer-b@3.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-peer-b@3.0.0".to_string(),
- copy_index: 0,
- dependencies: Default::default(),
- },
- ]
- );
- assert_eq!(
- package_reqs,
- vec![(
- "package-0@1.0".to_string(),
- "package-0@1.0.0_package-peer-a@2.0.0__package-peer-b@3.0.0"
- .to_string()
- )]
- );
- }
-
- #[tokio::test]
- async fn resolve_nested_peer_deps_ancestor_sibling_deps() {
- let api = TestNpmRegistryApiInner::default();
- api.ensure_package_version("package-0", "1.0.0");
- api.ensure_package_version("package-peer-a", "2.0.0");
- api.ensure_package_version("package-peer-b", "3.0.0");
- api.add_dependency(("package-0", "1.0.0"), ("package-peer-b", "*"));
- api.add_peer_dependency(("package-0", "1.0.0"), ("package-peer-a", "2"));
- api.add_peer_dependency(
- ("package-peer-a", "2.0.0"),
- ("package-peer-b", "3"),
- );
-
- let (packages, package_reqs) = run_resolver_and_get_output(
- api,
- vec![
- "npm:package-0@1.0",
- "npm:package-peer-a@2",
- "npm:package-peer-b@3",
- ],
- )
- .await;
- assert_eq!(
- packages,
- vec![
- TestNpmResolutionPackage {
- pkg_id: "package-0@1.0.0_package-peer-a@2.0.0__package-peer-b@3.0.0_package-peer-b@3.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([
- (
- "package-peer-a".to_string(),
- "package-peer-a@2.0.0_package-peer-b@3.0.0".to_string(),
- ),
- (
- "package-peer-b".to_string(),
- "package-peer-b@3.0.0".to_string(),
- )
- ]),
-
- },
- TestNpmResolutionPackage {
- pkg_id: "package-peer-a@2.0.0_package-peer-b@3.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-peer-b".to_string(),
- "package-peer-b@3.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-peer-b@3.0.0".to_string(),
- copy_index: 0,
- dependencies: Default::default(),
-
- },
- ]
- );
- assert_eq!(
- package_reqs,
- vec![
- (
- "package-0@1.0".to_string(),
- "package-0@1.0.0_package-peer-a@2.0.0__package-peer-b@3.0.0_package-peer-b@3.0.0"
- .to_string()
- ),
- (
- "package-peer-a@2".to_string(),
- "package-peer-a@2.0.0_package-peer-b@3.0.0".to_string()
- ),
- (
- "package-peer-b@3".to_string(),
- "package-peer-b@3.0.0".to_string()
- )
- ]
- );
- }
-
- #[tokio::test]
- async fn resolve_with_peer_deps_multiple() {
- let api = TestNpmRegistryApiInner::default();
- api.ensure_package_version("package-0", "1.1.1");
- api.ensure_package_version("package-a", "1.0.0");
- api.ensure_package_version("package-b", "2.0.0");
- api.ensure_package_version("package-c", "3.0.0");
- api.ensure_package_version("package-d", "3.5.0");
- api.ensure_package_version("package-e", "3.6.0");
- api.ensure_package_version("package-peer-a", "4.0.0");
- api.ensure_package_version("package-peer-a", "4.1.0");
- api.ensure_package_version("package-peer-b", "5.3.0");
- api.ensure_package_version("package-peer-b", "5.4.1");
- api.ensure_package_version("package-peer-c", "6.2.0");
- api.add_dependency(("package-0", "1.1.1"), ("package-a", "1"));
- api.add_dependency(("package-a", "1.0.0"), ("package-b", "^2"));
- api.add_dependency(("package-a", "1.0.0"), ("package-c", "^3"));
- api.add_dependency(("package-a", "1.0.0"), ("package-d", "^3"));
- api.add_dependency(("package-a", "1.0.0"), ("package-peer-a", "4.0.0"));
- api.add_peer_dependency(("package-b", "2.0.0"), ("package-peer-a", "4"));
- api.add_peer_dependency(
- ("package-b", "2.0.0"),
- ("package-peer-c", "=6.2.0"), // will be auto-resolved
- );
- api.add_peer_dependency(("package-c", "3.0.0"), ("package-peer-a", "*"));
- api.add_peer_dependency(
- ("package-peer-a", "4.0.0"),
- ("package-peer-b", "^5.4"), // will be auto-resolved
- );
-
- let (packages, package_reqs) = run_resolver_and_get_output(
- api,
- vec!["npm:package-0@1.1.1", "npm:package-e@3"],
- )
- .await;
- assert_eq!(
- packages,
- vec![
- TestNpmResolutionPackage {
- pkg_id: "package-0@1.1.1".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-a".to_string(),
- "package-a@1.0.0_package-peer-a@4.0.0__package-peer-b@5.4.1".to_string(),
- ),]),
-
- },
- TestNpmResolutionPackage {
- pkg_id: "package-a@1.0.0_package-peer-a@4.0.0__package-peer-b@5.4.1".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([
- (
- "package-b".to_string(),
- "package-b@2.0.0_package-peer-a@4.0.0__package-peer-b@5.4.1_package-peer-c@6.2.0".to_string(),
- ),
- (
- "package-c".to_string(),
- "package-c@3.0.0_package-peer-a@4.0.0__package-peer-b@5.4.1".to_string(),
- ),
- (
- "package-d".to_string(),
- "package-d@3.5.0".to_string(),
- ),
- (
- "package-peer-a".to_string(),
- "package-peer-a@4.0.0_package-peer-b@5.4.1".to_string(),
- ),
- ]),
-
- },
- TestNpmResolutionPackage {
- pkg_id: "package-b@2.0.0_package-peer-a@4.0.0__package-peer-b@5.4.1_package-peer-c@6.2.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([
- (
- "package-peer-a".to_string(),
- "package-peer-a@4.0.0_package-peer-b@5.4.1".to_string(),
- ),
- (
- "package-peer-c".to_string(),
- "package-peer-c@6.2.0".to_string(),
- )
- ])
- },
- TestNpmResolutionPackage {
- pkg_id: "package-c@3.0.0_package-peer-a@4.0.0__package-peer-b@5.4.1".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-peer-a".to_string(),
- "package-peer-a@4.0.0_package-peer-b@5.4.1".to_string(),
- )])
- },
- TestNpmResolutionPackage {
- pkg_id: "package-d@3.5.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([]),
-
- },
- TestNpmResolutionPackage {
- pkg_id: "package-e@3.6.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([]),
-
- },
- TestNpmResolutionPackage {
- pkg_id: "package-peer-a@4.0.0_package-peer-b@5.4.1".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-peer-b".to_string(),
- "package-peer-b@5.4.1".to_string(),
- )])
- },
- TestNpmResolutionPackage {
- pkg_id: "package-peer-b@5.4.1".to_string(),
- copy_index: 0,
- dependencies: Default::default(),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-peer-c@6.2.0".to_string(),
- copy_index: 0,
- dependencies: Default::default(),
- },
- ]
- );
- assert_eq!(
- package_reqs,
- vec![
- ("package-0@1.1.1".to_string(), "package-0@1.1.1".to_string()),
- ("package-e@3".to_string(), "package-e@3.6.0".to_string()),
- ]
- );
- }
-
- #[tokio::test]
- async fn resolve_peer_deps_circular() {
- let api = TestNpmRegistryApiInner::default();
- api.ensure_package_version("package-a", "1.0.0");
- api.ensure_package_version("package-b", "2.0.0");
- api.add_dependency(("package-a", "1.0.0"), ("package-b", "*"));
- api.add_peer_dependency(("package-b", "2.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![
- TestNpmResolutionPackage {
- pkg_id: "package-a@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-b".to_string(),
- "package-b@2.0.0_package-a@1.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-b@2.0.0_package-a@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-a".to_string(),
- "package-a@1.0.0".to_string(),
- )]),
- },
- ]
- );
- assert_eq!(
- package_reqs,
- vec![("package-a@1.0".to_string(), "package-a@1.0.0".to_string())]
- );
- }
-
- #[tokio::test]
- async fn resolve_peer_deps_multiple_copies() {
- // repeat this a few times to have a higher probability of surfacing indeterminism
- for _ in 0..3 {
- let api = TestNpmRegistryApiInner::default();
- api.ensure_package_version("package-a", "1.0.0");
- api.ensure_package_version("package-b", "2.0.0");
- api.ensure_package_version("package-dep", "3.0.0");
- api.ensure_package_version("package-peer", "4.0.0");
- api.ensure_package_version("package-peer", "5.0.0");
- api.add_dependency(("package-a", "1.0.0"), ("package-dep", "*"));
- api.add_dependency(("package-a", "1.0.0"), ("package-peer", "4"));
- api.add_dependency(("package-b", "2.0.0"), ("package-dep", "*"));
- api.add_dependency(("package-b", "2.0.0"), ("package-peer", "5"));
- api.add_peer_dependency(("package-dep", "3.0.0"), ("package-peer", "*"));
-
- let (packages, package_reqs) = run_resolver_and_get_output(
- api,
- vec!["npm:package-a@1", "npm:package-b@2"],
- )
- .await;
- assert_eq!(
- packages,
- vec![
- TestNpmResolutionPackage {
- pkg_id: "package-a@1.0.0_package-peer@4.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([
- (
- "package-dep".to_string(),
- "package-dep@3.0.0_package-peer@4.0.0".to_string(),
- ),
- ("package-peer".to_string(), "package-peer@4.0.0".to_string(),),
- ]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-b@2.0.0_package-peer@5.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([
- (
- "package-dep".to_string(),
- "package-dep@3.0.0_package-peer@5.0.0".to_string(),
- ),
- ("package-peer".to_string(), "package-peer@5.0.0".to_string(),),
- ]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-dep@3.0.0_package-peer@4.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-peer".to_string(),
- "package-peer@4.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-dep@3.0.0_package-peer@5.0.0".to_string(),
- copy_index: 1,
- dependencies: BTreeMap::from([(
- "package-peer".to_string(),
- "package-peer@5.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-peer@4.0.0".to_string(),
- copy_index: 0,
- dependencies: Default::default(),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-peer@5.0.0".to_string(),
- copy_index: 0,
- dependencies: Default::default(),
- },
- ]
- );
- assert_eq!(
- package_reqs,
- vec![
- (
- "package-a@1".to_string(),
- "package-a@1.0.0_package-peer@4.0.0".to_string()
- ),
- (
- "package-b@2".to_string(),
- "package-b@2.0.0_package-peer@5.0.0".to_string()
- )
- ]
- );
- }
- }
-
- #[tokio::test]
- async fn resolve_dep_with_peer_deps_dep_then_peer() {
- let api = TestNpmRegistryApiInner::default();
- api.ensure_package_version("package-a", "1.0.0");
- api.ensure_package_version("package-b", "1.0.0");
- api.ensure_package_version("package-c", "1.0.0");
- api.ensure_package_version("package-peer", "1.0.0");
- api.add_peer_dependency(("package-b", "1.0.0"), ("package-peer", "1"));
- api.add_dependency(("package-a", "1.0.0"), ("package-c", "1"));
- api.add_dependency(("package-a", "1.0.0"), ("package-peer", "1"));
- api.add_peer_dependency(("package-c", "1.0.0"), ("package-b", "1"));
-
- let (packages, package_reqs) = run_resolver_and_get_output(
- api,
- vec!["npm:package-a@1.0", "npm:package-b@1.0"],
- )
- .await;
- assert_eq!(
- packages,
- vec![
- TestNpmResolutionPackage {
- pkg_id: "package-a@1.0.0_package-b@1.0.0__package-peer@1.0.0"
- .to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([
- (
- "package-c".to_string(),
- "package-c@1.0.0_package-b@1.0.0__package-peer@1.0.0".to_string(),
- ),
- ("package-peer".to_string(), "package-peer@1.0.0".to_string(),)
- ]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-b@1.0.0_package-peer@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-peer".to_string(),
- "package-peer@1.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-c@1.0.0_package-b@1.0.0__package-peer@1.0.0"
- .to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-b".to_string(),
- "package-b@1.0.0_package-peer@1.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-peer@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([]),
- },
- ]
- );
- assert_eq!(
- package_reqs,
- vec![
- (
- "package-a@1.0".to_string(),
- "package-a@1.0.0_package-b@1.0.0__package-peer@1.0.0".to_string()
- ),
- (
- "package-b@1.0".to_string(),
- "package-b@1.0.0_package-peer@1.0.0".to_string()
- )
- ]
- );
- }
-
- #[tokio::test]
- async fn resolve_dep_with_peer_deps_then_other_dep_with_different_peer() {
- let api = TestNpmRegistryApiInner::default();
- api.ensure_package_version("package-a", "1.0.0");
- api.ensure_package_version("package-b", "1.0.0");
- api.ensure_package_version("package-c", "1.0.0");
- api.ensure_package_version("package-peer", "1.1.0");
- api.ensure_package_version("package-peer", "1.2.0");
- api.add_peer_dependency(("package-a", "1.0.0"), ("package-peer", "*")); // should select 1.2.0, then 1.1.0
- api.add_dependency(("package-b", "1.0.0"), ("package-c", "1"));
- api.add_dependency(("package-b", "1.0.0"), ("package-peer", "=1.1.0"));
- api.add_dependency(("package-c", "1.0.0"), ("package-a", "1"));
-
- let (packages, package_reqs) = run_resolver_and_get_output(
- api,
- vec!["npm:package-a@1.0", "npm:package-b@1.0"],
- )
- .await;
- assert_eq!(
- packages,
- vec![
- TestNpmResolutionPackage {
- pkg_id: "package-a@1.0.0_package-peer@1.1.0".to_string(),
- copy_index: 1,
- dependencies: BTreeMap::from([(
- "package-peer".to_string(),
- "package-peer@1.1.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-a@1.0.0_package-peer@1.2.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-peer".to_string(),
- "package-peer@1.2.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-b@1.0.0_package-peer@1.1.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([
- (
- "package-c".to_string(),
- "package-c@1.0.0_package-peer@1.1.0".to_string(),
- ),
- ("package-peer".to_string(), "package-peer@1.1.0".to_string(),)
- ]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-c@1.0.0_package-peer@1.1.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-a".to_string(),
- "package-a@1.0.0_package-peer@1.1.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-peer@1.1.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-peer@1.2.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([]),
- },
- ]
- );
- assert_eq!(
- package_reqs,
- vec![
- (
- "package-a@1.0".to_string(),
- "package-a@1.0.0_package-peer@1.2.0".to_string()
- ),
- (
- "package-b@1.0".to_string(),
- "package-b@1.0.0_package-peer@1.1.0".to_string()
- )
- ]
- );
- }
-
- #[tokio::test]
- async fn resolve_dep_and_peer_dist_tag() {
- let api = TestNpmRegistryApiInner::default();
- api.ensure_package_version("package-a", "1.0.0");
- api.ensure_package_version("package-b", "2.0.0");
- api.ensure_package_version("package-b", "3.0.0");
- api.ensure_package_version("package-c", "1.0.0");
- api.ensure_package_version("package-d", "1.0.0");
- api.ensure_package_version("package-e", "1.0.0");
- api.add_dependency(("package-a", "1.0.0"), ("package-b", "some-tag"));
- api.add_dependency(("package-a", "1.0.0"), ("package-d", "1.0.0"));
- api.add_dependency(("package-a", "1.0.0"), ("package-c", "1.0.0"));
- api.add_dependency(("package-a", "1.0.0"), ("package-e", "1.0.0"));
- api.add_dependency(("package-e", "1.0.0"), ("package-b", "some-tag"));
- api.add_peer_dependency(("package-c", "1.0.0"), ("package-d", "other-tag"));
- api.add_dist_tag("package-b", "some-tag", "2.0.0");
- api.add_dist_tag("package-d", "other-tag", "1.0.0");
-
- let (packages, package_reqs) =
- run_resolver_and_get_output(api, vec!["npm:package-a@1.0"]).await;
- assert_eq!(
- packages,
- vec![
- TestNpmResolutionPackage {
- pkg_id: "package-a@1.0.0_package-d@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([
- ("package-b".to_string(), "package-b@2.0.0".to_string(),),
- (
- "package-c".to_string(),
- "package-c@1.0.0_package-d@1.0.0".to_string(),
- ),
- ("package-d".to_string(), "package-d@1.0.0".to_string(),),
- ("package-e".to_string(), "package-e@1.0.0".to_string(),),
- ]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-b@2.0.0".to_string(),
- copy_index: 0,
- dependencies: Default::default(),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-c@1.0.0_package-d@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-d".to_string(),
- "package-d@1.0.0".to_string(),
- ),]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-d@1.0.0".to_string(),
- copy_index: 0,
- dependencies: Default::default(),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-e@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-b".to_string(),
- "package-b@2.0.0".to_string(),
- )]),
- },
- ]
- );
- assert_eq!(
- package_reqs,
- vec![(
- "package-a@1.0".to_string(),
- "package-a@1.0.0_package-d@1.0.0".to_string()
- ),]
- );
- }
-
- #[tokio::test]
- async fn package_has_self_as_dependency() {
- let api = TestNpmRegistryApiInner::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![TestNpmResolutionPackage {
- pkg_id: "package-a@1.0.0".to_string(),
- copy_index: 0,
- // in this case, we just ignore that the package did this
- dependencies: 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 = TestNpmRegistryApiInner::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![
- TestNpmResolutionPackage {
- pkg_id: "package-a@0.5.0".to_string(),
- copy_index: 0,
- dependencies: Default::default(),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-a@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-a".to_string(),
- "package-a@0.5.0".to_string(),
- )]),
- },
- ]
- );
- assert_eq!(
- package_reqs,
- vec![("package-a@1.0".to_string(), "package-a@1.0.0".to_string())]
- );
- }
-
- #[tokio::test]
- async fn grand_child_package_has_self_as_peer_dependency_root() {
- let api = TestNpmRegistryApiInner::default();
- api.ensure_package_version("package-a", "1.0.0");
- api.ensure_package_version("package-b", "2.0.0");
- api.add_dependency(("package-a", "1.0.0"), ("package-b", "2"));
- api.add_peer_dependency(("package-b", "2.0.0"), ("package-a", "*"));
-
- let (packages, package_reqs) =
- run_resolver_and_get_output(api, vec!["npm:package-a@1.0"]).await;
- assert_eq!(
- packages,
- vec![
- TestNpmResolutionPackage {
- pkg_id: "package-a@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-b".to_string(),
- "package-b@2.0.0_package-a@1.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-b@2.0.0_package-a@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-a".to_string(),
- "package-a@1.0.0".to_string(),
- )]),
- }
- ]
- );
- assert_eq!(
- package_reqs,
- vec![("package-a@1.0".to_string(), "package-a@1.0.0".to_string())]
- );
- }
-
- #[tokio::test]
- async fn grand_child_package_has_self_as_peer_dependency_under_root() {
- let api = TestNpmRegistryApiInner::default();
- api.ensure_package_version("package-0", "1.0.0");
- api.ensure_package_version("package-a", "1.0.0");
- api.ensure_package_version("package-b", "2.0.0");
- api.add_dependency(("package-0", "1.0.0"), ("package-a", "*"));
- api.add_dependency(("package-a", "1.0.0"), ("package-b", "2"));
- api.add_peer_dependency(("package-b", "2.0.0"), ("package-a", "*"));
-
- let (packages, package_reqs) =
- run_resolver_and_get_output(api, vec!["npm:package-0@1.0"]).await;
- assert_eq!(
- packages,
- vec![
- TestNpmResolutionPackage {
- pkg_id: "package-0@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-a".to_string(),
- "package-a@1.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-a@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-b".to_string(),
- "package-b@2.0.0_package-a@1.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-b@2.0.0_package-a@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-a".to_string(),
- "package-a@1.0.0".to_string(),
- )]),
- }
- ]
- );
- assert_eq!(
- package_reqs,
- vec![("package-0@1.0".to_string(), "package-0@1.0.0".to_string())]
- );
- }
-
- #[tokio::test]
- async fn resolve_peer_deps_in_ancestor_root() {
- let api = TestNpmRegistryApiInner::default();
- api.ensure_package_version("package-a", "1.0.0");
- api.ensure_package_version("package-b", "1.0.0");
- api.ensure_package_version("package-c", "1.0.0");
- api.add_dependency(("package-a", "1.0.0"), ("package-b", "1"));
- api.add_dependency(("package-b", "1.0.0"), ("package-c", "1"));
- api.add_peer_dependency(("package-c", "1.0.0"), ("package-a", "1"));
-
- let (packages, package_reqs) =
- run_resolver_and_get_output(api, vec!["npm:package-a@1.0.0"]).await;
- assert_eq!(
- packages,
- vec![
- TestNpmResolutionPackage {
- pkg_id: "package-a@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-b".to_string(),
- "package-b@1.0.0_package-a@1.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-b@1.0.0_package-a@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-c".to_string(),
- "package-c@1.0.0_package-a@1.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-c@1.0.0_package-a@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-a".to_string(),
- "package-a@1.0.0".to_string(),
- )]),
- },
- ]
- );
- assert_eq!(
- package_reqs,
- vec![("package-a@1.0.0".to_string(), "package-a@1.0.0".to_string())]
- );
- }
-
- #[tokio::test]
- async fn resolve_peer_deps_in_ancestor_non_root() {
- let api = TestNpmRegistryApiInner::default();
- api.ensure_package_version("package-a", "1.0.0");
- api.ensure_package_version("package-b", "1.0.0");
- api.ensure_package_version("package-c", "1.0.0");
- api.add_dependency(("package-a", "1.0.0"), ("package-b", "1"));
- api.add_dependency(("package-b", "1.0.0"), ("package-c", "1"));
- api.add_peer_dependency(("package-c", "1.0.0"), ("package-b", "1"));
-
- let (packages, package_reqs) =
- run_resolver_and_get_output(api, vec!["npm:package-a@1.0.0"]).await;
- assert_eq!(
- packages,
- vec![
- TestNpmResolutionPackage {
- pkg_id: "package-a@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-b".to_string(),
- "package-b@1.0.0".to_string(),
- ),]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-b@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-c".to_string(),
- "package-c@1.0.0_package-b@1.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-c@1.0.0_package-b@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-b".to_string(),
- "package-b@1.0.0".to_string(),
- )]),
- },
- ]
- );
- assert_eq!(
- package_reqs,
- vec![("package-a@1.0.0".to_string(), "package-a@1.0.0".to_string())]
- );
- }
-
- #[tokio::test]
- async fn nested_deps_same_peer_dep_ancestor() {
- let api = TestNpmRegistryApiInner::default();
- api.ensure_package_version("package-0", "1.0.0");
- api.ensure_package_version("package-1", "1.0.0");
- api.ensure_package_version("package-a", "1.0.0");
- api.ensure_package_version("package-b", "1.0.0");
- api.ensure_package_version("package-c", "1.0.0");
- api.ensure_package_version("package-d", "1.0.0");
- api.add_dependency(("package-0", "1.0.0"), ("package-a", "1"));
- api.add_dependency(("package-0", "1.0.0"), ("package-1", "1"));
- api.add_dependency(("package-1", "1.0.0"), ("package-a", "1"));
- api.add_dependency(("package-a", "1.0.0"), ("package-b", "1"));
- api.add_dependency(("package-b", "1.0.0"), ("package-c", "1"));
- api.add_dependency(("package-c", "1.0.0"), ("package-d", "1"));
- api.add_peer_dependency(("package-b", "1.0.0"), ("package-a", "*"));
- api.add_peer_dependency(("package-c", "1.0.0"), ("package-a", "*"));
- api.add_peer_dependency(("package-d", "1.0.0"), ("package-a", "*"));
- api.add_peer_dependency(("package-b", "1.0.0"), ("package-0", "*"));
- api.add_peer_dependency(("package-c", "1.0.0"), ("package-0", "*"));
- api.add_peer_dependency(("package-d", "1.0.0"), ("package-0", "*"));
-
- let (packages, package_reqs) =
- run_resolver_and_get_output(api, vec!["npm:package-0@1.0"]).await;
- assert_eq!(
- packages,
- vec![
- TestNpmResolutionPackage {
- pkg_id: "package-0@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-a".to_string(),
- "package-a@1.0.0_package-0@1.0.0".to_string(),
- ), (
- "package-1".to_string(),
- "package-1@1.0.0_package-0@1.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-1@1.0.0_package-0@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-a".to_string(),
- "package-a@1.0.0_package-0@1.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-a@1.0.0_package-0@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-b".to_string(),
- "package-b@1.0.0_package-0@1.0.0_package-a@1.0.0__package-0@1.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-b@1.0.0_package-0@1.0.0_package-a@1.0.0__package-0@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([
- (
- "package-0".to_string(),
- "package-0@1.0.0".to_string(),
- ),
- (
- "package-a".to_string(),
- "package-a@1.0.0_package-0@1.0.0".to_string(),
- ),
- (
- "package-c".to_string(),
- "package-c@1.0.0_package-0@1.0.0_package-a@1.0.0__package-0@1.0.0".to_string(),
- )
- ]),
-
- },
- TestNpmResolutionPackage {
- pkg_id: "package-c@1.0.0_package-0@1.0.0_package-a@1.0.0__package-0@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([
- (
- "package-0".to_string(),
- "package-0@1.0.0".to_string(),
- ),
- (
- "package-a".to_string(),
- "package-a@1.0.0_package-0@1.0.0".to_string(),
- ),
- (
- "package-d".to_string(),
- "package-d@1.0.0_package-0@1.0.0_package-a@1.0.0__package-0@1.0.0".to_string(),
- )
- ]),
-
- },
- TestNpmResolutionPackage {
- pkg_id: "package-d@1.0.0_package-0@1.0.0_package-a@1.0.0__package-0@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([
- (
- "package-0".to_string(),
- "package-0@1.0.0".to_string(),
- ),
- (
- "package-a".to_string(),
- "package-a@1.0.0_package-0@1.0.0".to_string(),
- )
- ]),
-
- }
- ]
- );
- assert_eq!(
- package_reqs,
- vec![("package-0@1.0".to_string(), "package-0@1.0.0".to_string())]
- );
- }
-
- #[tokio::test]
- async fn peer_dep_resolved_then_resolved_deeper() {
- let api = TestNpmRegistryApiInner::default();
- api.ensure_package_version("package-0", "1.0.0");
- api.ensure_package_version("package-1", "1.0.0");
- api.ensure_package_version("package-a", "1.0.0");
- api.ensure_package_version("package-b", "1.0.0");
- api.ensure_package_version("package-peer", "1.0.0");
- api.add_dependency(("package-0", "1.0.0"), ("package-a", "1"));
- api.add_dependency(("package-0", "1.0.0"), ("package-1", "1"));
- api.add_dependency(("package-1", "1.0.0"), ("package-a", "1"));
- api.add_dependency(("package-a", "1.0.0"), ("package-b", "1"));
- api.add_peer_dependency(("package-b", "1.0.0"), ("package-peer", "*"));
-
- let (packages, package_reqs) = run_resolver_and_get_output(
- api,
- vec!["npm:package-0@1.0", "npm:package-peer@1.0"],
- )
- .await;
- assert_eq!(
- packages,
- vec![
- TestNpmResolutionPackage {
- pkg_id: "package-0@1.0.0_package-peer@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([
- (
- "package-1".to_string(),
- "package-1@1.0.0_package-peer@1.0.0".to_string(),
- ),
- (
- "package-a".to_string(),
- "package-a@1.0.0_package-peer@1.0.0".to_string(),
- )
- ]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-1@1.0.0_package-peer@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-a".to_string(),
- "package-a@1.0.0_package-peer@1.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-a@1.0.0_package-peer@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-b".to_string(),
- "package-b@1.0.0_package-peer@1.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-b@1.0.0_package-peer@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-peer".to_string(),
- "package-peer@1.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-peer@1.0.0".to_string(),
- copy_index: 0,
- dependencies: Default::default(),
- }
- ]
- );
- assert_eq!(
- package_reqs,
- vec![
- (
- "package-0@1.0".to_string(),
- "package-0@1.0.0_package-peer@1.0.0".to_string()
- ),
- (
- "package-peer@1.0".to_string(),
- "package-peer@1.0.0".to_string()
- )
- ]
- );
- }
-
- #[tokio::test]
- async fn resolve_dep_with_peer_deps_circular_1() {
- // a -> b -> c -> d -> c where c has a peer dependency on b
- let api = TestNpmRegistryApiInner::default();
- api.ensure_package_version("package-a", "1.0.0");
- api.ensure_package_version("package-b", "1.0.0");
- api.ensure_package_version("package-c", "1.0.0");
- api.ensure_package_version("package-d", "1.0.0");
- api.add_dependency(("package-a", "1.0.0"), ("package-b", "1"));
- api.add_dependency(("package-b", "1.0.0"), ("package-c", "1"));
- api.add_dependency(("package-c", "1.0.0"), ("package-d", "1"));
- api.add_dependency(("package-d", "1.0.0"), ("package-c", "1"));
- api.add_peer_dependency(("package-c", "1.0.0"), ("package-b", "1"));
-
- let (packages, package_reqs) =
- run_resolver_and_get_output(api, vec!["npm:package-a@1.0.0"]).await;
- assert_eq!(
- packages,
- vec![
- TestNpmResolutionPackage {
- pkg_id: "package-a@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-b".to_string(),
- "package-b@1.0.0".to_string(),
- ),]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-b@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-c".to_string(),
- "package-c@1.0.0_package-b@1.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-c@1.0.0_package-b@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([
- ("package-b".to_string(), "package-b@1.0.0".to_string(),),
- (
- "package-d".to_string(),
- "package-d@1.0.0_package-b@1.0.0".to_string(),
- )
- ]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-d@1.0.0_package-b@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-c".to_string(),
- "package-c@1.0.0_package-b@1.0.0".to_string(),
- )]),
- },
- ]
- );
- assert_eq!(
- package_reqs,
- vec![("package-a@1.0.0".to_string(), "package-a@1.0.0".to_string())]
- );
- }
-
- #[tokio::test]
- async fn resolve_dep_with_peer_deps_circular_2() {
- // a -> b -> c -> d -> c where c has a peer dependency on b
- // -> e -> f -> d -> c where f has a peer dep on a
- let api = TestNpmRegistryApiInner::default();
- api.ensure_package_version("package-a", "1.0.0");
- api.ensure_package_version("package-b", "1.0.0");
- api.ensure_package_version("package-c", "1.0.0");
- api.ensure_package_version("package-d", "1.0.0");
- api.ensure_package_version("package-e", "1.0.0");
- api.ensure_package_version("package-f", "1.0.0");
- api.add_dependency(("package-a", "1.0.0"), ("package-b", "1"));
- api.add_dependency(("package-b", "1.0.0"), ("package-c", "1"));
- api.add_dependency(("package-c", "1.0.0"), ("package-d", "1"));
- api.add_dependency(("package-c", "1.0.0"), ("package-e", "1"));
- api.add_dependency(("package-d", "1.0.0"), ("package-c", "1"));
- api.add_dependency(("package-e", "1.0.0"), ("package-f", "1"));
- api.add_dependency(("package-f", "1.0.0"), ("package-d", "1"));
- api.add_peer_dependency(("package-f", "1.0.0"), ("package-a", "1"));
- api.add_peer_dependency(("package-c", "1.0.0"), ("package-b", "1"));
-
- let (packages, package_reqs) =
- run_resolver_and_get_output(api, vec!["npm:package-a@1.0.0"]).await;
- assert_eq!(
- packages,
- vec![
- TestNpmResolutionPackage {
- pkg_id: "package-a@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-b".to_string(),
- "package-b@1.0.0_package-a@1.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-b@1.0.0_package-a@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-c".to_string(),
- "package-c@1.0.0_package-b@1.0.0__package-a@1.0.0_package-a@1.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-c@1.0.0_package-b@1.0.0__package-a@1.0.0_package-a@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([
- (
- "package-b".to_string(),
- "package-b@1.0.0_package-a@1.0.0".to_string(),
- ),
- (
- "package-d".to_string(),
- "package-d@1.0.0_package-b@1.0.0__package-a@1.0.0_package-a@1.0.0".to_string(),
- ),
- (
- "package-e".to_string(),
- "package-e@1.0.0_package-a@1.0.0_package-b@1.0.0__package-a@1.0.0".to_string()
- )
- ]),
-
- },
- TestNpmResolutionPackage {
- pkg_id: "package-d@1.0.0_package-b@1.0.0__package-a@1.0.0_package-a@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-c".to_string(),
- "package-c@1.0.0_package-b@1.0.0__package-a@1.0.0_package-a@1.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-e@1.0.0_package-a@1.0.0_package-b@1.0.0__package-a@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-f".to_string(),
- "package-f@1.0.0_package-a@1.0.0_package-b@1.0.0__package-a@1.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-f@1.0.0_package-a@1.0.0_package-b@1.0.0__package-a@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-a".to_string(),
- "package-a@1.0.0".to_string(),
- ), (
- "package-d".to_string(),
- "package-d@1.0.0_package-b@1.0.0__package-a@1.0.0_package-a@1.0.0".to_string(),
- )]),
- },
- ]
- );
- assert_eq!(
- package_reqs,
- vec![("package-a@1.0.0".to_string(), "package-a@1.0.0".to_string())]
- );
- }
-
- #[tokio::test]
- async fn resolve_dep_with_peer_deps_circular_3() {
- // a -> b -> c -> d -> c (peer)
- // -> e -> a (peer)
- let api = TestNpmRegistryApiInner::default();
- api.ensure_package_version("package-a", "1.0.0");
- api.ensure_package_version("package-b", "1.0.0");
- api.ensure_package_version("package-c", "1.0.0");
- api.ensure_package_version("package-d", "1.0.0");
- api.ensure_package_version("package-e", "1.0.0");
- api.add_dependency(("package-a", "1.0.0"), ("package-b", "1"));
- api.add_dependency(("package-b", "1.0.0"), ("package-c", "1"));
- api.add_dependency(("package-c", "1.0.0"), ("package-d", "1"));
- api.add_dependency(("package-d", "1.0.0"), ("package-e", "1"));
- api.add_peer_dependency(("package-d", "1.0.0"), ("package-c", "1"));
- api.add_peer_dependency(("package-e", "1.0.0"), ("package-a", "1"));
-
- let (packages, package_reqs) =
- run_resolver_and_get_output(api, vec!["npm:package-a@1.0.0"]).await;
- assert_eq!(
- packages,
- vec![
- TestNpmResolutionPackage {
- pkg_id: "package-a@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-b".to_string(),
- "package-b@1.0.0_package-a@1.0.0".to_string(),
- ),]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-b@1.0.0_package-a@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-c".to_string(),
- "package-c@1.0.0_package-a@1.0.0".to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-c@1.0.0_package-a@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-d".to_string(),
- "package-d@1.0.0_package-c@1.0.0__package-a@1.0.0_package-a@1.0.0"
- .to_string(),
- )]),
- },
- TestNpmResolutionPackage {
- pkg_id:
- "package-d@1.0.0_package-c@1.0.0__package-a@1.0.0_package-a@1.0.0"
- .to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([
- (
- "package-c".to_string(),
- "package-c@1.0.0_package-a@1.0.0".to_string(),
- ),
- (
- "package-e".to_string(),
- "package-e@1.0.0_package-a@1.0.0".to_string()
- ),
- ]),
- },
- TestNpmResolutionPackage {
- pkg_id: "package-e@1.0.0_package-a@1.0.0".to_string(),
- copy_index: 0,
- dependencies: BTreeMap::from([(
- "package-a".to_string(),
- "package-a@1.0.0".to_string()
- ),]),
- },
- ]
- );
- assert_eq!(
- package_reqs,
- vec![("package-a@1.0.0".to_string(), "package-a@1.0.0".to_string())]
- );
- }
-
- #[derive(Debug, PartialEq, Eq)]
- struct TestNpmResolutionPackage {
- pub pkg_id: String,
- pub copy_index: usize,
- pub dependencies: BTreeMap<String, String>,
- }
-
- async fn run_resolver_and_get_output(
- api: TestNpmRegistryApiInner,
- reqs: Vec<&str>,
- ) -> (Vec<TestNpmResolutionPackage>, Vec<(String, String)>) {
- let mut graph = Graph::default();
- let api = NpmRegistryApi::new_for_test(api);
- let mut resolver = GraphDependencyResolver::new(&mut graph, &api);
-
- for req in reqs {
- let req = NpmPackageReqReference::from_str(req).unwrap().req;
- resolver
- .add_package_req(&req, &api.package_info(&req.name).await.unwrap())
- .unwrap();
- }
-
- resolver.resolve_pending().await.unwrap();
- let snapshot = graph.into_snapshot(&api).await.unwrap();
-
- {
- let new_snapshot = Graph::from_snapshot(snapshot.clone())
- .unwrap()
- .into_snapshot(&api)
- .await
- .unwrap();
- assert_eq!(
- snapshot, new_snapshot,
- "recreated snapshot should be the same"
- );
- // create one again from the new snapshot
- let new_snapshot2 = Graph::from_snapshot(new_snapshot.clone())
- .unwrap()
- .into_snapshot(&api)
- .await
- .unwrap();
- assert_eq!(
- snapshot, new_snapshot2,
- "second recreated snapshot should be the same"
- );
- }
-
- let mut packages = snapshot.all_packages();
- packages.sort_by(|a, b| a.pkg_id.cmp(&b.pkg_id));
- let mut package_reqs = snapshot
- .package_reqs
- .into_iter()
- .map(|(a, b)| {
- (
- a.to_string(),
- snapshot.root_packages.get(&b).unwrap().as_serialized(),
- )
- })
- .collect::<Vec<_>>();
- package_reqs.sort_by(|a, b| a.0.to_string().cmp(&b.0.to_string()));
- let packages = packages
- .into_iter()
- .map(|pkg| TestNpmResolutionPackage {
- pkg_id: pkg.pkg_id.as_serialized(),
- copy_index: pkg.copy_index,
- dependencies: pkg
- .dependencies
- .into_iter()
- .map(|(key, value)| (key, value.as_serialized()))
- .collect(),
- })
- .collect();
-
- (packages, package_reqs)
- }
-}