diff options
Diffstat (limited to 'cli/npm/resolution/snapshot.rs')
-rw-r--r-- | cli/npm/resolution/snapshot.rs | 151 |
1 files changed, 68 insertions, 83 deletions
diff --git a/cli/npm/resolution/snapshot.rs b/cli/npm/resolution/snapshot.rs index c717e74a8..bdc204ce8 100644 --- a/cli/npm/resolution/snapshot.rs +++ b/cli/npm/resolution/snapshot.rs @@ -8,21 +8,19 @@ use deno_core::anyhow::anyhow; use deno_core::anyhow::bail; use deno_core::anyhow::Context; use deno_core::error::AnyError; -use deno_core::futures; use deno_core::parking_lot::Mutex; +use deno_graph::npm::NpmPackageNv; use deno_graph::npm::NpmPackageReq; use deno_graph::semver::VersionReq; use serde::Deserialize; use serde::Serialize; use crate::args::Lockfile; -use crate::npm::cache::should_sync_download; use crate::npm::cache::NpmPackageCacheFolderId; use crate::npm::registry::NpmPackageVersionDistInfo; use crate::npm::registry::NpmRegistryApi; -use crate::npm::registry::RealNpmRegistryApi; -use super::NpmPackageNodeId; +use super::NpmPackageId; use super::NpmResolutionPackage; /// Packages partitioned by if they are "copy" packages or not. @@ -44,13 +42,17 @@ impl NpmPackagesPartitioned { } } -#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)] pub struct NpmResolutionSnapshot { + /// The unique package requirements map to a single npm package name and version. #[serde(with = "map_to_vec")] - pub(super) package_reqs: HashMap<NpmPackageReq, NpmPackageNodeId>, - pub(super) packages_by_name: HashMap<String, Vec<NpmPackageNodeId>>, + pub(super) package_reqs: HashMap<NpmPackageReq, NpmPackageNv>, + // Each root level npm package name and version maps to an exact npm package node id. #[serde(with = "map_to_vec")] - pub(super) packages: HashMap<NpmPackageNodeId, NpmResolutionPackage>, + pub(super) root_packages: HashMap<NpmPackageNv, NpmPackageId>, + pub(super) packages_by_name: HashMap<String, Vec<NpmPackageId>>, + #[serde(with = "map_to_vec")] + pub(super) packages: HashMap<NpmPackageId, NpmResolutionPackage>, } // This is done so the maps with non-string keys get serialized and deserialized as vectors. @@ -98,25 +100,24 @@ impl NpmResolutionSnapshot { &self, req: &NpmPackageReq, ) -> Result<&NpmResolutionPackage, AnyError> { - match self.package_reqs.get(req) { - Some(id) => Ok(self.packages.get(id).unwrap()), + match self + .package_reqs + .get(req) + .and_then(|nv| self.root_packages.get(nv)) + .and_then(|id| self.packages.get(id)) + { + Some(id) => Ok(id), None => bail!("could not find npm package directory for '{}'", req), } } - pub fn top_level_packages(&self) -> Vec<NpmPackageNodeId> { - self - .package_reqs - .values() - .cloned() - .collect::<HashSet<_>>() - .into_iter() - .collect::<Vec<_>>() + pub fn top_level_packages(&self) -> Vec<NpmPackageId> { + self.root_packages.values().cloned().collect::<Vec<_>>() } pub fn package_from_id( &self, - id: &NpmPackageNodeId, + id: &NpmPackageId, ) -> Option<&NpmResolutionPackage> { self.packages.get(id) } @@ -129,13 +130,13 @@ impl NpmResolutionSnapshot { // todo(dsherret): do we need an additional hashmap to get this quickly? let referrer_package = self .packages_by_name - .get(&referrer.name) + .get(&referrer.nv.name) .and_then(|packages| { packages .iter() - .filter(|p| p.version == referrer.version) - .filter_map(|id| { - let package = self.packages.get(id)?; + .filter(|p| p.nv.version == referrer.nv.version) + .filter_map(|node_id| { + let package = self.packages.get(node_id)?; if package.copy_index == referrer.copy_index { Some(package) } else { @@ -153,7 +154,7 @@ impl NpmResolutionSnapshot { return Ok(self.packages.get(id).unwrap()); } - if referrer_package.id.name == name { + if referrer_package.pkg_id.nv.name == name { return Ok(referrer_package); } @@ -198,19 +199,19 @@ impl NpmResolutionSnapshot { &self, name: &str, version_req: &VersionReq, - ) -> Option<NpmPackageNodeId> { + ) -> Option<NpmPackageId> { // todo(dsherret): this is not exactly correct because some ids // will be better than others due to peer dependencies - let mut maybe_best_id: Option<&NpmPackageNodeId> = None; - if let Some(ids) = self.packages_by_name.get(name) { - for id in ids { - if version_req.matches(&id.version) { + let mut maybe_best_id: Option<&NpmPackageId> = None; + if let Some(node_ids) = self.packages_by_name.get(name) { + for node_id in node_ids.iter() { + if version_req.matches(&node_id.nv.version) { let is_best_version = maybe_best_id .as_ref() - .map(|best_id| best_id.version.cmp(&id.version).is_lt()) + .map(|best_id| best_id.nv.version.cmp(&node_id.nv.version).is_lt()) .unwrap_or(true); if is_best_version { - maybe_best_id = Some(id); + maybe_best_id = Some(node_id); } } } @@ -220,11 +221,12 @@ impl NpmResolutionSnapshot { pub async fn from_lockfile( lockfile: Arc<Mutex<Lockfile>>, - api: &RealNpmRegistryApi, + api: &NpmRegistryApi, ) -> Result<Self, AnyError> { - let mut package_reqs: HashMap<NpmPackageReq, NpmPackageNodeId>; - let mut packages_by_name: HashMap<String, Vec<NpmPackageNodeId>>; - let mut packages: HashMap<NpmPackageNodeId, NpmResolutionPackage>; + let mut package_reqs: HashMap<NpmPackageReq, NpmPackageNv>; + let mut root_packages: HashMap<NpmPackageNv, NpmPackageId>; + let mut packages_by_name: HashMap<String, Vec<NpmPackageId>>; + let mut packages: HashMap<NpmPackageId, NpmResolutionPackage>; let mut copy_index_resolver: SnapshotPackageCopyIndexResolver; { @@ -233,6 +235,8 @@ impl NpmResolutionSnapshot { // pre-allocate collections package_reqs = HashMap::with_capacity(lockfile.content.npm.specifiers.len()); + root_packages = + HashMap::with_capacity(lockfile.content.npm.specifiers.len()); let packages_len = lockfile.content.npm.packages.len(); packages = HashMap::with_capacity(packages_len); packages_by_name = HashMap::with_capacity(packages_len); // close enough @@ -244,31 +248,32 @@ impl NpmResolutionSnapshot { for (key, value) in &lockfile.content.npm.specifiers { let package_req = NpmPackageReq::from_str(key) .with_context(|| format!("Unable to parse npm specifier: {key}"))?; - let package_id = NpmPackageNodeId::from_serialized(value)?; - package_reqs.insert(package_req, package_id.clone()); + let package_id = NpmPackageId::from_serialized(value)?; + package_reqs.insert(package_req, package_id.nv.clone()); + root_packages.insert(package_id.nv.clone(), package_id.clone()); verify_ids.insert(package_id.clone()); } // then the packages for (key, value) in &lockfile.content.npm.packages { - let package_id = NpmPackageNodeId::from_serialized(key)?; + let package_id = NpmPackageId::from_serialized(key)?; // collect the dependencies let mut dependencies = HashMap::default(); packages_by_name - .entry(package_id.name.to_string()) + .entry(package_id.nv.name.to_string()) .or_default() .push(package_id.clone()); for (name, specifier) in &value.dependencies { - let dep_id = NpmPackageNodeId::from_serialized(specifier)?; + let dep_id = NpmPackageId::from_serialized(specifier)?; dependencies.insert(name.to_string(), dep_id.clone()); verify_ids.insert(dep_id); } let package = NpmResolutionPackage { - id: package_id.clone(), + pkg_id: package_id.clone(), copy_index: copy_index_resolver.resolve(&package_id), // temporary dummy value dist: NpmPackageVersionDistInfo::default(), @@ -288,40 +293,20 @@ impl NpmResolutionSnapshot { } } - let mut unresolved_tasks = Vec::with_capacity(packages_by_name.len()); - - // cache the package names in parallel in the registry api - // unless synchronous download should occur - if should_sync_download() { - let mut package_names = packages_by_name.keys().collect::<Vec<_>>(); - package_names.sort(); - for package_name in package_names { - api.package_info(package_name).await?; - } - } else { - for package_name in packages_by_name.keys() { - let package_name = package_name.clone(); - let api = api.clone(); - unresolved_tasks.push(tokio::task::spawn(async move { - api.package_info(&package_name).await?; - Result::<_, AnyError>::Ok(()) - })); - } - } - for result in futures::future::join_all(unresolved_tasks).await { - result??; - } + api + .cache_in_parallel(packages_by_name.keys().cloned().collect()) + .await?; // ensure the dist is set for each package for package in packages.values_mut() { // this will read from the memory cache now let version_info = match api - .package_version_info(&package.id.name, &package.id.version) + .package_version_info(&package.pkg_id.nv) .await? { Some(version_info) => version_info, None => { - bail!("could not find '{}' specified in the lockfile. Maybe try again with --reload", package.id.display()); + bail!("could not find '{}' specified in the lockfile. Maybe try again with --reload", package.pkg_id.nv); } }; package.dist = version_info.dist; @@ -329,6 +314,7 @@ impl NpmResolutionSnapshot { Ok(Self { package_reqs, + root_packages, packages_by_name, packages, }) @@ -336,8 +322,8 @@ impl NpmResolutionSnapshot { } pub struct SnapshotPackageCopyIndexResolver { - packages_to_copy_index: HashMap<NpmPackageNodeId, usize>, - package_name_version_to_copy_count: HashMap<(String, String), usize>, + packages_to_copy_index: HashMap<NpmPackageId, usize>, + package_name_version_to_copy_count: HashMap<NpmPackageNv, usize>, } impl SnapshotPackageCopyIndexResolver { @@ -349,7 +335,7 @@ impl SnapshotPackageCopyIndexResolver { } pub fn from_map_with_capacity( - mut packages_to_copy_index: HashMap<NpmPackageNodeId, usize>, + mut packages_to_copy_index: HashMap<NpmPackageId, usize>, capacity: usize, ) -> Self { let mut package_name_version_to_copy_count = @@ -358,9 +344,9 @@ impl SnapshotPackageCopyIndexResolver { packages_to_copy_index.reserve(capacity - packages_to_copy_index.len()); } - for (id, index) in &packages_to_copy_index { + for (node_id, index) in &packages_to_copy_index { let entry = package_name_version_to_copy_count - .entry((id.name.to_string(), id.version.to_string())) + .entry(node_id.nv.clone()) .or_insert(0); if *entry < *index { *entry = *index; @@ -372,18 +358,18 @@ impl SnapshotPackageCopyIndexResolver { } } - pub fn resolve(&mut self, id: &NpmPackageNodeId) -> usize { - if let Some(index) = self.packages_to_copy_index.get(id) { + pub fn resolve(&mut self, node_id: &NpmPackageId) -> usize { + if let Some(index) = self.packages_to_copy_index.get(node_id) { *index } else { let index = *self .package_name_version_to_copy_count - .entry((id.name.to_string(), id.version.to_string())) + .entry(node_id.nv.clone()) .and_modify(|count| { *count += 1; }) .or_insert(0); - self.packages_to_copy_index.insert(id.clone(), index); + self.packages_to_copy_index.insert(node_id.clone(), index); index } } @@ -422,24 +408,24 @@ mod tests { SnapshotPackageCopyIndexResolver::with_capacity(10); assert_eq!( copy_index_resolver - .resolve(&NpmPackageNodeId::from_serialized("package@1.0.0").unwrap()), + .resolve(&NpmPackageId::from_serialized("package@1.0.0").unwrap()), 0 ); assert_eq!( copy_index_resolver - .resolve(&NpmPackageNodeId::from_serialized("package@1.0.0").unwrap()), + .resolve(&NpmPackageId::from_serialized("package@1.0.0").unwrap()), 0 ); assert_eq!( copy_index_resolver.resolve( - &NpmPackageNodeId::from_serialized("package@1.0.0_package-b@1.0.0") + &NpmPackageId::from_serialized("package@1.0.0_package-b@1.0.0") .unwrap() ), 1 ); assert_eq!( copy_index_resolver.resolve( - &NpmPackageNodeId::from_serialized( + &NpmPackageId::from_serialized( "package@1.0.0_package-b@1.0.0__package-c@2.0.0" ) .unwrap() @@ -448,15 +434,14 @@ mod tests { ); assert_eq!( copy_index_resolver.resolve( - &NpmPackageNodeId::from_serialized("package@1.0.0_package-b@1.0.0") + &NpmPackageId::from_serialized("package@1.0.0_package-b@1.0.0") .unwrap() ), 1 ); assert_eq!( - copy_index_resolver.resolve( - &NpmPackageNodeId::from_serialized("package-b@1.0.0").unwrap() - ), + copy_index_resolver + .resolve(&NpmPackageId::from_serialized("package-b@1.0.0").unwrap()), 0 ); } |