Repository: denoland/deno
Pull request: fix(task): do not error if node_modules folder not exists

Revealed https://github.com/denoland/deno/issues/23919

Closes #23914

Changed files:
filename: cli/npm/managed/resolvers/common.rs
patch: @@ -9,6 +9,7 @@ use std::sync::Mutex;
 
 use async_trait::async_trait;
 use deno_ast::ModuleSpecifier;
+use deno_core::anyhow::Context;
 use deno_core::error::AnyError;
 use deno_core::futures;
 use deno_core::unsync::spawn;
@@ -91,29 +92,31 @@ impl RegistryReadPermissionChecker {
 
     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 mut canonicalize =
+        |path: &Path| -> Result<Option<PathBuf>, AnyError> {
+          match cache.get(path) {
+            Some(canon) => Ok(Some(canon.clone())),
+            None => match self.fs.realpath_sync(path) {
+              Ok(canon) => {
+                cache.insert(path.to_path_buf(), canon.clone());
+                Ok(Some(canon))
+              }
+              Err(e) => {
+                if e.kind() == ErrorKind::NotFound {
+                  return Ok(None);
+                }
+                Err(AnyError::from(e)).with_context(|| {
+                  format!("failed canonicalizing '{}'", path.display())
+                })
+              }
+            },
           }
-
-          let canon = canon?;
-          cache.insert(path.to_path_buf(), canon.clone());
-          canon
-        }
+        };
+      let Some(registry_path_canon) = canonicalize(&self.registry_path)? else {
+        return Ok(()); // not exists, allow reading
+      };
+      let Some(path_canon) = canonicalize(path)? else {
+        return Ok(()); // not exists, allow reading
       };
 
       if path_canon.starts_with(registry_path_canon) {

filename: cli/tools/task.rs
patch: @@ -70,7 +70,8 @@ pub async fn execute_script(
       bail!("Only local configuration files are supported")
     };
     let cwd = match task_flags.cwd {
-      Some(path) => canonicalize_path(&PathBuf::from(path))?,
+      Some(path) => canonicalize_path(&PathBuf::from(path))
+        .context("failed canonicalizing --cwd")?,
       None => config_file_path.parent().unwrap().to_owned(),
     };
 

filename: tests/specs/task/node_modules_dir_false/__test__.jsonc
patch: @@ -0,0 +1,16 @@
+{
+  "tempDir": true,
+  "steps": [{
+    "args": "cache npm:@denotest/esm-basic",
+    "output": "[WILDCARD]"
+  }, {
+    "args": [
+      "eval",
+      "Deno.removeSync('./node_modules', { recursive: true });"
+    ],
+    "output": "[WILDCARD]"
+  }, {
+    "args": "task --quiet repro",
+    "output": "hi\n"
+  }]
+}

filename: tests/specs/task/node_modules_dir_false/deno.json
patch: @@ -0,0 +1,6 @@
+{
+  "nodeModulesDir": true,
+  "tasks": {
+    "repro": "echo hi"
+  }
+}