diff options
Diffstat (limited to 'cli/npm/resolution.rs')
-rw-r--r-- | cli/npm/resolution.rs | 425 |
1 files changed, 0 insertions, 425 deletions
diff --git a/cli/npm/resolution.rs b/cli/npm/resolution.rs deleted file mode 100644 index 3e76d5e85..000000000 --- a/cli/npm/resolution.rs +++ /dev/null @@ -1,425 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use std::collections::HashMap; -use std::collections::HashSet; -use std::sync::Arc; - -use deno_core::error::AnyError; -use deno_core::parking_lot::Mutex; -use deno_core::parking_lot::RwLock; -use deno_lockfile::NpmPackageDependencyLockfileInfo; -use deno_lockfile::NpmPackageLockfileInfo; -use deno_npm::registry::NpmPackageInfo; -use deno_npm::registry::NpmPackageVersionDistInfoIntegrity; -use deno_npm::registry::NpmRegistryApi; -use deno_npm::resolution::NpmPackageVersionResolutionError; -use deno_npm::resolution::NpmPackagesPartitioned; -use deno_npm::resolution::NpmResolutionError; -use deno_npm::resolution::NpmResolutionSnapshot; -use deno_npm::resolution::NpmResolutionSnapshotPendingResolver; -use deno_npm::resolution::NpmResolutionSnapshotPendingResolverOptions; -use deno_npm::resolution::PackageCacheFolderIdNotFoundError; -use deno_npm::resolution::PackageNotFoundFromReferrerError; -use deno_npm::resolution::PackageNvNotFoundError; -use deno_npm::resolution::PackageReqNotFoundError; -use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot; -use deno_npm::NpmPackageCacheFolderId; -use deno_npm::NpmPackageId; -use deno_npm::NpmResolutionPackage; -use deno_npm::NpmSystemInfo; -use deno_semver::package::PackageNv; -use deno_semver::package::PackageReq; -use deno_semver::VersionReq; - -use crate::args::Lockfile; -use crate::util::sync::TaskQueue; - -use super::registry::CliNpmRegistryApi; - -/// Handles updating and storing npm resolution in memory where the underlying -/// snapshot can be updated concurrently. Additionally handles updating the lockfile -/// based on changes to the resolution. -/// -/// This does not interact with the file system. -pub struct NpmResolution { - api: Arc<CliNpmRegistryApi>, - snapshot: RwLock<NpmResolutionSnapshot>, - update_queue: TaskQueue, - maybe_lockfile: Option<Arc<Mutex<Lockfile>>>, -} - -impl std::fmt::Debug for NpmResolution { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let snapshot = self.snapshot.read(); - f.debug_struct("NpmResolution") - .field("snapshot", &snapshot.as_valid_serialized().as_serialized()) - .finish() - } -} - -impl NpmResolution { - pub fn from_serialized( - api: Arc<CliNpmRegistryApi>, - initial_snapshot: Option<ValidSerializedNpmResolutionSnapshot>, - maybe_lockfile: Option<Arc<Mutex<Lockfile>>>, - ) -> Self { - let snapshot = - NpmResolutionSnapshot::new(initial_snapshot.unwrap_or_default()); - Self::new(api, snapshot, maybe_lockfile) - } - - pub fn new( - api: Arc<CliNpmRegistryApi>, - initial_snapshot: NpmResolutionSnapshot, - maybe_lockfile: Option<Arc<Mutex<Lockfile>>>, - ) -> Self { - Self { - api, - snapshot: RwLock::new(initial_snapshot), - update_queue: Default::default(), - maybe_lockfile, - } - } - - pub async fn add_package_reqs( - &self, - package_reqs: &[PackageReq], - ) -> Result<(), AnyError> { - // only allow one thread in here at a time - let _permit = self.update_queue.acquire().await; - let snapshot = add_package_reqs_to_snapshot( - &self.api, - package_reqs, - self.maybe_lockfile.clone(), - || self.snapshot.read().clone(), - ) - .await?; - - *self.snapshot.write() = snapshot; - Ok(()) - } - - pub async fn set_package_reqs( - &self, - package_reqs: &[PackageReq], - ) -> Result<(), AnyError> { - // only allow one thread in here at a time - let _permit = self.update_queue.acquire().await; - - let reqs_set = package_reqs.iter().collect::<HashSet<_>>(); - let snapshot = add_package_reqs_to_snapshot( - &self.api, - package_reqs, - self.maybe_lockfile.clone(), - || { - let snapshot = self.snapshot.read().clone(); - let has_removed_package = !snapshot - .package_reqs() - .keys() - .all(|req| reqs_set.contains(req)); - // if any packages were removed, we need to completely recreate the npm resolution snapshot - if has_removed_package { - snapshot.into_empty() - } else { - snapshot - } - }, - ) - .await?; - - *self.snapshot.write() = snapshot; - - Ok(()) - } - - pub async fn resolve_pending(&self) -> Result<(), AnyError> { - // only allow one thread in here at a time - let _permit = self.update_queue.acquire().await; - - let snapshot = add_package_reqs_to_snapshot( - &self.api, - &Vec::new(), - self.maybe_lockfile.clone(), - || self.snapshot.read().clone(), - ) - .await?; - - *self.snapshot.write() = snapshot; - - Ok(()) - } - - pub fn resolve_pkg_cache_folder_id_from_pkg_id( - &self, - id: &NpmPackageId, - ) -> Option<NpmPackageCacheFolderId> { - self - .snapshot - .read() - .package_from_id(id) - .map(|p| p.get_package_cache_folder_id()) - } - - pub fn resolve_pkg_id_from_pkg_cache_folder_id( - &self, - id: &NpmPackageCacheFolderId, - ) -> Result<NpmPackageId, PackageCacheFolderIdNotFoundError> { - self - .snapshot - .read() - .resolve_pkg_from_pkg_cache_folder_id(id) - .map(|pkg| pkg.id.clone()) - } - - pub fn resolve_package_from_package( - &self, - name: &str, - referrer: &NpmPackageCacheFolderId, - ) -> Result<NpmResolutionPackage, Box<PackageNotFoundFromReferrerError>> { - self - .snapshot - .read() - .resolve_package_from_package(name, referrer) - .cloned() - } - - /// Resolve a node package from a deno module. - pub fn resolve_pkg_id_from_pkg_req( - &self, - req: &PackageReq, - ) -> Result<NpmPackageId, PackageReqNotFoundError> { - self - .snapshot - .read() - .resolve_pkg_from_pkg_req(req) - .map(|pkg| pkg.id.clone()) - } - - pub fn resolve_pkg_reqs_from_pkg_id( - &self, - id: &NpmPackageId, - ) -> Vec<PackageReq> { - let snapshot = self.snapshot.read(); - let mut pkg_reqs = snapshot - .package_reqs() - .iter() - .filter(|(_, nv)| *nv == &id.nv) - .map(|(req, _)| req.clone()) - .collect::<Vec<_>>(); - pkg_reqs.sort(); // be deterministic - pkg_reqs - } - - pub fn resolve_pkg_id_from_deno_module( - &self, - id: &PackageNv, - ) -> Result<NpmPackageId, PackageNvNotFoundError> { - self - .snapshot - .read() - .resolve_package_from_deno_module(id) - .map(|pkg| pkg.id.clone()) - } - - /// Resolves a package requirement for deno graph. This should only be - /// called by deno_graph's NpmResolver or for resolving packages in - /// a package.json - pub fn resolve_pkg_req_as_pending( - &self, - pkg_req: &PackageReq, - ) -> Result<PackageNv, NpmPackageVersionResolutionError> { - // we should always have this because it should have been cached before here - let package_info = self.api.get_cached_package_info(&pkg_req.name).unwrap(); - self.resolve_pkg_req_as_pending_with_info(pkg_req, &package_info) - } - - /// Resolves a package requirement for deno graph. This should only be - /// called by deno_graph's NpmResolver or for resolving packages in - /// a package.json - pub fn resolve_pkg_req_as_pending_with_info( - &self, - pkg_req: &PackageReq, - package_info: &NpmPackageInfo, - ) -> Result<PackageNv, NpmPackageVersionResolutionError> { - debug_assert_eq!(pkg_req.name, package_info.name); - let mut snapshot = self.snapshot.write(); - let pending_resolver = get_npm_pending_resolver(&self.api); - let nv = pending_resolver.resolve_package_req_as_pending( - &mut snapshot, - pkg_req, - package_info, - )?; - Ok(nv) - } - - pub fn package_reqs(&self) -> HashMap<PackageReq, PackageNv> { - self.snapshot.read().package_reqs().clone() - } - - pub fn all_system_packages( - &self, - system_info: &NpmSystemInfo, - ) -> Vec<NpmResolutionPackage> { - self.snapshot.read().all_system_packages(system_info) - } - - pub fn all_system_packages_partitioned( - &self, - system_info: &NpmSystemInfo, - ) -> NpmPackagesPartitioned { - self - .snapshot - .read() - .all_system_packages_partitioned(system_info) - } - - pub fn has_packages(&self) -> bool { - !self.snapshot.read().is_empty() - } - - pub fn snapshot(&self) -> NpmResolutionSnapshot { - self.snapshot.read().clone() - } - - pub fn serialized_valid_snapshot( - &self, - ) -> ValidSerializedNpmResolutionSnapshot { - self.snapshot.read().as_valid_serialized() - } - - pub fn serialized_valid_snapshot_for_system( - &self, - system_info: &NpmSystemInfo, - ) -> ValidSerializedNpmResolutionSnapshot { - self - .snapshot - .read() - .as_valid_serialized_for_system(system_info) - } - - pub fn lock(&self, lockfile: &mut Lockfile) -> Result<(), AnyError> { - let snapshot = self.snapshot.read(); - populate_lockfile_from_snapshot(lockfile, &snapshot) - } -} - -async fn add_package_reqs_to_snapshot( - api: &CliNpmRegistryApi, - package_reqs: &[PackageReq], - maybe_lockfile: Option<Arc<Mutex<Lockfile>>>, - get_new_snapshot: impl Fn() -> NpmResolutionSnapshot, -) -> Result<NpmResolutionSnapshot, AnyError> { - let snapshot = get_new_snapshot(); - let snapshot = if !snapshot.has_pending() - && package_reqs - .iter() - .all(|req| snapshot.package_reqs().contains_key(req)) - { - log::debug!("Snapshot already up to date. Skipping pending resolution."); - snapshot - } else { - let pending_resolver = get_npm_pending_resolver(api); - let result = pending_resolver - .resolve_pending(snapshot, package_reqs) - .await; - api.clear_memory_cache(); - match result { - Ok(snapshot) => snapshot, - Err(NpmResolutionError::Resolution(err)) if api.mark_force_reload() => { - log::debug!("{err:#}"); - log::debug!("npm resolution failed. Trying again..."); - - // try again - let snapshot = get_new_snapshot(); - let result = pending_resolver - .resolve_pending(snapshot, package_reqs) - .await; - api.clear_memory_cache(); - // now surface the result after clearing the cache - result? - } - Err(err) => return Err(err.into()), - } - }; - - if let Some(lockfile_mutex) = maybe_lockfile { - let mut lockfile = lockfile_mutex.lock(); - populate_lockfile_from_snapshot(&mut lockfile, &snapshot)?; - } - - Ok(snapshot) -} - -fn get_npm_pending_resolver( - api: &CliNpmRegistryApi, -) -> NpmResolutionSnapshotPendingResolver<CliNpmRegistryApi> { - NpmResolutionSnapshotPendingResolver::new( - NpmResolutionSnapshotPendingResolverOptions { - api, - // WARNING: When bumping this version, check if anything needs to be - // updated in the `setNodeOnlyGlobalNames` call in 99_main_compiler.js - types_node_version_req: Some( - VersionReq::parse_from_npm("18.0.0 - 18.16.19").unwrap(), - ), - }, - ) -} - -fn populate_lockfile_from_snapshot( - lockfile: &mut Lockfile, - snapshot: &NpmResolutionSnapshot, -) -> Result<(), AnyError> { - for (package_req, nv) in snapshot.package_reqs() { - lockfile.insert_package_specifier( - format!("npm:{}", package_req), - format!( - "npm:{}", - snapshot - .resolve_package_from_deno_module(nv) - .unwrap() - .id - .as_serialized() - ), - ); - } - for package in snapshot.all_packages_for_every_system() { - lockfile - .check_or_insert_npm_package(npm_package_to_lockfile_info(package))?; - } - Ok(()) -} - -fn npm_package_to_lockfile_info( - pkg: &NpmResolutionPackage, -) -> NpmPackageLockfileInfo { - fn integrity_for_lockfile( - integrity: NpmPackageVersionDistInfoIntegrity, - ) -> String { - match integrity { - NpmPackageVersionDistInfoIntegrity::Integrity { - algorithm, - base64_hash, - } => format!("{}-{}", algorithm, base64_hash), - NpmPackageVersionDistInfoIntegrity::UnknownIntegrity(integrity) => { - integrity.to_string() - } - NpmPackageVersionDistInfoIntegrity::LegacySha1Hex(hex) => hex.to_string(), - } - } - - let dependencies = pkg - .dependencies - .iter() - .map(|(name, id)| NpmPackageDependencyLockfileInfo { - name: name.clone(), - id: id.as_serialized(), - }) - .collect(); - - NpmPackageLockfileInfo { - display_id: pkg.id.nv.to_string(), - serialized_id: pkg.id.as_serialized(), - integrity: integrity_for_lockfile(pkg.dist.integrity()), - dependencies, - } -} |