diff options
Diffstat (limited to 'cli/npm')
-rw-r--r-- | cli/npm/registry.rs | 44 | ||||
-rw-r--r-- | cli/npm/resolution.rs | 77 | ||||
-rw-r--r-- | cli/npm/resolvers/global.rs | 3 | ||||
-rw-r--r-- | cli/npm/resolvers/mod.rs | 11 |
4 files changed, 93 insertions, 42 deletions
diff --git a/cli/npm/registry.rs b/cli/npm/registry.rs index 0dcdb720a..b9bff09e6 100644 --- a/cli/npm/registry.rs +++ b/cli/npm/registry.rs @@ -5,6 +5,8 @@ use std::collections::HashSet; use std::fs; use std::io::ErrorKind; use std::path::PathBuf; +use std::sync::atomic::AtomicBool; +use std::sync::atomic::Ordering; use std::sync::Arc; use async_trait::async_trait; @@ -21,6 +23,7 @@ use deno_core::url::Url; use deno_core::TaskQueue; use deno_npm::registry::NpmPackageInfo; use deno_npm::registry::NpmRegistryApi; +use deno_npm::registry::NpmRegistryPackageInfoLoadError; use once_cell::sync::Lazy; use crate::args::CacheSetting; @@ -67,6 +70,7 @@ impl NpmRegistry { Self(Some(Arc::new(NpmRegistryApiInner { base_url, cache, + force_reload: AtomicBool::new(false), mem_cache: Default::default(), previously_reloaded_packages: Default::default(), http_client, @@ -96,6 +100,18 @@ impl NpmRegistry { &self.inner().base_url } + /// Marks that new requests for package information should retrieve it + /// from the npm registry + /// + /// Returns true if it was successfully set for the first time. + pub fn mark_force_reload(&self) -> bool { + // never force reload the registry information if reloading is disabled + if matches!(self.inner().cache.cache_setting(), CacheSetting::Only) { + return false; + } + !self.inner().force_reload.swap(true, Ordering::SeqCst) + } + fn inner(&self) -> &Arc<NpmRegistryApiInner> { // this panicking indicates a bug in the code where this // wasn't initialized @@ -108,17 +124,26 @@ static SYNC_DOWNLOAD_TASK_QUEUE: Lazy<TaskQueue> = #[async_trait] impl NpmRegistryApi for NpmRegistry { - async fn maybe_package_info( + async fn package_info( &self, name: &str, - ) -> Result<Option<Arc<NpmPackageInfo>>, AnyError> { - if should_sync_download() { + ) -> Result<Arc<NpmPackageInfo>, NpmRegistryPackageInfoLoadError> { + let result = if should_sync_download() { let inner = self.inner().clone(); SYNC_DOWNLOAD_TASK_QUEUE .queue(async move { inner.maybe_package_info(name).await }) .await } else { self.inner().maybe_package_info(name).await + }; + match result { + Ok(Some(info)) => Ok(info), + Ok(None) => Err(NpmRegistryPackageInfoLoadError::PackageNotExists { + package_name: name.to_string(), + }), + Err(err) => { + Err(NpmRegistryPackageInfoLoadError::LoadError(Arc::new(err))) + } } } } @@ -135,6 +160,7 @@ enum CacheItem { struct NpmRegistryApiInner { base_url: Url, cache: NpmCache, + force_reload: AtomicBool, mem_cache: Mutex<HashMap<String, CacheItem>>, previously_reloaded_packages: Mutex<HashSet<String>>, http_client: HttpClient, @@ -154,10 +180,10 @@ impl NpmRegistryApiInner { } Some(CacheItem::Pending(future)) => (false, future.clone()), None => { - if self.cache.cache_setting().should_use_for_npm_package(name) - // if this has been previously reloaded, then try loading from the - // file system cache - || !self.previously_reloaded_packages.lock().insert(name.to_string()) + if (self.cache.cache_setting().should_use_for_npm_package(name) && !self.force_reload()) + // if this has been previously reloaded, then try loading from the + // file system cache + || !self.previously_reloaded_packages.lock().insert(name.to_string()) { // attempt to load from the file cache if let Some(info) = self.load_file_cached_package_info(name) { @@ -203,6 +229,10 @@ impl NpmRegistryApiInner { } } + fn force_reload(&self) -> bool { + self.force_reload.load(Ordering::SeqCst) + } + fn load_file_cached_package_info( &self, name: &str, diff --git a/cli/npm/resolution.rs b/cli/npm/resolution.rs index 291acf4bc..d012b4f08 100644 --- a/cli/npm/resolution.rs +++ b/cli/npm/resolution.rs @@ -10,8 +10,13 @@ use deno_core::TaskQueue; use deno_lockfile::NpmPackageDependencyLockfileInfo; use deno_lockfile::NpmPackageLockfileInfo; use deno_npm::registry::NpmPackageInfo; +use deno_npm::resolution::NpmPackageVersionResolutionError; use deno_npm::resolution::NpmPackagesPartitioned; +use deno_npm::resolution::NpmResolutionError; use deno_npm::resolution::NpmResolutionSnapshot; +use deno_npm::resolution::PackageNotFoundFromReferrerError; +use deno_npm::resolution::PackageNvNotFoundError; +use deno_npm::resolution::PackageReqNotFoundError; use deno_npm::NpmPackageCacheFolderId; use deno_npm::NpmPackageId; use deno_npm::NpmResolutionPackage; @@ -70,13 +75,11 @@ impl NpmResolution { // only allow one thread in here at a time let _permit = inner.update_queue.acquire().await; - let snapshot = inner.snapshot.read().clone(); - let snapshot = add_package_reqs_to_snapshot( &inner.api, package_reqs, - snapshot, self.0.maybe_lockfile.clone(), + || inner.snapshot.read().clone(), ) .await?; @@ -91,24 +94,25 @@ impl NpmResolution { let inner = &self.0; // only allow one thread in here at a time let _permit = inner.update_queue.acquire().await; - let snapshot = inner.snapshot.read().clone(); - - let reqs_set = package_reqs.iter().collect::<HashSet<_>>(); - 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 - let snapshot = if has_removed_package { - NpmResolutionSnapshot::default() - } else { - snapshot - }; + + let reqs_set = package_reqs.iter().cloned().collect::<HashSet<_>>(); let snapshot = add_package_reqs_to_snapshot( &inner.api, package_reqs, - snapshot, self.0.maybe_lockfile.clone(), + || { + let snapshot = inner.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 { + NpmResolutionSnapshot::default() + } else { + snapshot + } + }, ) .await?; @@ -121,13 +125,12 @@ impl NpmResolution { let inner = &self.0; // only allow one thread in here at a time let _permit = inner.update_queue.acquire().await; - let snapshot = inner.snapshot.read().clone(); let snapshot = add_package_reqs_to_snapshot( &inner.api, Vec::new(), - snapshot, self.0.maybe_lockfile.clone(), + || inner.snapshot.read().clone(), ) .await?; @@ -139,7 +142,7 @@ impl NpmResolution { pub fn pkg_req_ref_to_nv_ref( &self, req_ref: NpmPackageReqReference, - ) -> Result<NpmPackageNvReference, AnyError> { + ) -> Result<NpmPackageNvReference, PackageReqNotFoundError> { let node_id = self.resolve_pkg_id_from_pkg_req(&req_ref.req)?; Ok(NpmPackageNvReference { nv: node_id.nv, @@ -163,7 +166,7 @@ impl NpmResolution { &self, name: &str, referrer: &NpmPackageCacheFolderId, - ) -> Result<NpmResolutionPackage, AnyError> { + ) -> Result<NpmResolutionPackage, Box<PackageNotFoundFromReferrerError>> { self .0 .snapshot @@ -176,7 +179,7 @@ impl NpmResolution { pub fn resolve_pkg_id_from_pkg_req( &self, req: &NpmPackageReq, - ) -> Result<NpmPackageId, AnyError> { + ) -> Result<NpmPackageId, PackageReqNotFoundError> { self .0 .snapshot @@ -188,7 +191,7 @@ impl NpmResolution { pub fn resolve_pkg_id_from_deno_module( &self, id: &NpmPackageNv, - ) -> Result<NpmPackageId, AnyError> { + ) -> Result<NpmPackageId, PackageNvNotFoundError> { self .0 .snapshot @@ -203,7 +206,7 @@ impl NpmResolution { pub fn resolve_package_req_as_pending( &self, pkg_req: &NpmPackageReq, - ) -> Result<NpmPackageNv, AnyError> { + ) -> Result<NpmPackageNv, NpmPackageVersionResolutionError> { // we should always have this because it should have been cached before here let package_info = self.0.api.get_cached_package_info(&pkg_req.name).unwrap(); @@ -217,7 +220,7 @@ impl NpmResolution { &self, pkg_req: &NpmPackageReq, package_info: &NpmPackageInfo, - ) -> Result<NpmPackageNv, AnyError> { + ) -> Result<NpmPackageNv, NpmPackageVersionResolutionError> { debug_assert_eq!(pkg_req.name, package_info.name); let inner = &self.0; let mut snapshot = inner.snapshot.write(); @@ -245,10 +248,14 @@ impl NpmResolution { async fn add_package_reqs_to_snapshot( api: &NpmRegistry, + // todo(18079): it should be possible to pass &[NpmPackageReq] in here + // and avoid all these clones, but the LSP complains because of its + // `Send` requirement package_reqs: Vec<NpmPackageReq>, - snapshot: NpmResolutionSnapshot, maybe_lockfile: Option<Arc<Mutex<Lockfile>>>, + get_new_snapshot: impl Fn() -> NpmResolutionSnapshot, ) -> Result<NpmResolutionSnapshot, AnyError> { + let snapshot = get_new_snapshot(); if !snapshot.has_pending() && package_reqs .iter() @@ -257,9 +264,23 @@ async fn add_package_reqs_to_snapshot( return Ok(snapshot); // already up to date } - let result = snapshot.resolve_pending(package_reqs, api).await; + let result = snapshot.resolve_pending(package_reqs.clone(), api).await; api.clear_memory_cache(); - let snapshot = result?; // propagate the error after clearing the memory cache + let snapshot = 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 = snapshot.resolve_pending(package_reqs, api).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(); diff --git a/cli/npm/resolvers/global.rs b/cli/npm/resolvers/global.rs index 518a9110a..810548e98 100644 --- a/cli/npm/resolvers/global.rs +++ b/cli/npm/resolvers/global.rs @@ -9,6 +9,7 @@ use async_trait::async_trait; use deno_ast::ModuleSpecifier; use deno_core::error::AnyError; use deno_core::url::Url; +use deno_npm::resolution::PackageNotFoundFromReferrerError; use deno_npm::NpmPackageCacheFolderId; use deno_npm::NpmPackageId; use deno_npm::NpmResolutionPackage; @@ -58,7 +59,7 @@ impl GlobalNpmPackageResolver { &self, package_name: &str, referrer_pkg_id: &NpmPackageCacheFolderId, - ) -> Result<NpmResolutionPackage, AnyError> { + ) -> Result<NpmResolutionPackage, Box<PackageNotFoundFromReferrerError>> { let types_name = types_package_name(package_name); self .resolution diff --git a/cli/npm/resolvers/mod.rs b/cli/npm/resolvers/mod.rs index c958743dc..a490dbf3f 100644 --- a/cli/npm/resolvers/mod.rs +++ b/cli/npm/resolvers/mod.rs @@ -15,6 +15,7 @@ use deno_core::parking_lot::Mutex; use deno_core::serde_json; use deno_core::url::Url; use deno_npm::resolution::NpmResolutionSnapshot; +use deno_npm::resolution::PackageReqNotFoundError; use deno_npm::NpmPackageId; use deno_runtime::deno_node::NodePermissions; use deno_runtime::deno_node::NodeResolutionMode; @@ -82,14 +83,14 @@ impl NpmPackageResolver { pub fn resolve_pkg_id_from_pkg_req( &self, req: &NpmPackageReq, - ) -> Result<NpmPackageId, AnyError> { + ) -> Result<NpmPackageId, PackageReqNotFoundError> { self.resolution.resolve_pkg_id_from_pkg_req(req) } pub fn pkg_req_ref_to_nv_ref( &self, req_ref: NpmPackageReqReference, - ) -> Result<NpmPackageNvReference, AnyError> { + ) -> Result<NpmPackageNvReference, PackageReqNotFoundError> { self.resolution.pkg_req_ref_to_nv_ref(req_ref) } @@ -225,10 +226,8 @@ impl NpmPackageResolver { &self, ) -> Result<(), AnyError> { // add and ensure this isn't added to the lockfile - self - .resolution - .add_package_reqs(vec![NpmPackageReq::from_str("@types/node").unwrap()]) - .await?; + let package_reqs = vec![NpmPackageReq::from_str("@types/node").unwrap()]; + self.resolution.add_package_reqs(package_reqs).await?; self.fs_resolver.cache_packages().await?; Ok(()) |