summaryrefslogtreecommitdiff
path: root/cli/npm/resolvers/common.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cli/npm/resolvers/common.rs')
-rw-r--r--cli/npm/resolvers/common.rs94
1 files changed, 65 insertions, 29 deletions
diff --git a/cli/npm/resolvers/common.rs b/cli/npm/resolvers/common.rs
index b352229c1..e0705f12b 100644
--- a/cli/npm/resolvers/common.rs
+++ b/cli/npm/resolvers/common.rs
@@ -1,9 +1,11 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+use std::collections::HashMap;
use std::io::ErrorKind;
use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;
+use std::sync::Mutex;
use async_trait::async_trait;
use deno_ast::ModuleSpecifier;
@@ -54,6 +56,69 @@ pub trait NpmPackageFsResolver: Send + Sync {
) -> Result<(), AnyError>;
}
+#[derive(Debug)]
+pub struct RegistryReadPermissionChecker {
+ fs: Arc<dyn FileSystem>,
+ cache: Mutex<HashMap<PathBuf, PathBuf>>,
+ registry_path: PathBuf,
+}
+
+impl RegistryReadPermissionChecker {
+ pub fn new(fs: Arc<dyn FileSystem>, registry_path: PathBuf) -> Self {
+ Self {
+ fs,
+ registry_path,
+ cache: Default::default(),
+ }
+ }
+
+ pub fn ensure_registry_read_permission(
+ &self,
+ permissions: &dyn NodePermissions,
+ path: &Path,
+ ) -> Result<(), AnyError> {
+ // allow reading if it's in the node_modules
+ let is_path_in_node_modules = path.starts_with(&self.registry_path)
+ && path
+ .components()
+ .all(|c| !matches!(c, std::path::Component::ParentDir));
+
+ if is_path_in_node_modules {
+ let mut cache = self.cache.lock().unwrap();
+ let registry_path_canon = match cache.get(&self.registry_path) {
+ Some(canon) => canon.clone(),
+ None => {
+ let canon = self.fs.realpath_sync(&self.registry_path)?;
+ cache.insert(self.registry_path.to_path_buf(), canon.clone());
+ canon
+ }
+ };
+
+ let path_canon = match cache.get(path) {
+ Some(canon) => canon.clone(),
+ None => {
+ let canon = self.fs.realpath_sync(path);
+ if let Err(e) = &canon {
+ if e.kind() == ErrorKind::NotFound {
+ return Ok(());
+ }
+ }
+
+ let canon = canon?;
+ cache.insert(path.to_path_buf(), canon.clone());
+ canon
+ }
+ };
+
+ if path_canon.starts_with(registry_path_canon) {
+ return Ok(());
+ }
+ }
+
+ permissions.check_read(path)
+ }
+}
+
/// Caches all the packages in parallel.
pub async fn cache_packages(
mut packages: Vec<NpmResolutionPackage>,
@@ -90,35 +155,6 @@ pub async fn cache_packages(
Ok(())
}
-pub fn ensure_registry_read_permission(
- fs: &Arc<dyn FileSystem>,
- permissions: &dyn NodePermissions,
- registry_path: &Path,
- path: &Path,
-) -> Result<(), AnyError> {
- // allow reading if it's in the node_modules
- if path.starts_with(registry_path)
- && path
- .components()
- .all(|c| !matches!(c, std::path::Component::ParentDir))
- {
- // todo(dsherret): cache this?
- if let Ok(registry_path) = fs.realpath_sync(registry_path) {
- match fs.realpath_sync(path) {
- Ok(path) if path.starts_with(registry_path) => {
- return Ok(());
- }
- Err(e) if e.kind() == ErrorKind::NotFound => {
- return Ok(());
- }
- _ => {} // ignore
- }
- }
- }
-
- permissions.check_read(path)
-}
-
/// Gets the corresponding @types package for the provided package name.
pub fn types_package_name(package_name: &str) -> String {
debug_assert!(!package_name.starts_with("@types/"));