Repository: denoland/deno
Pull request: fix(install): Use relative symlinks in deno install

Fixes https://github.com/denoland/deno/issues/25161

Changed files:
filename: cli/npm/managed/resolvers/local.rs
patch: @@ -1048,42 +1048,50 @@ fn symlink_package_dir(
   // need to delete the previous symlink before creating a new one
   let _ignore = fs::remove_dir_all(new_path);
 
+  let old_path_relative =
+    crate::util::path::relative_path(new_parent, old_path)
+      .unwrap_or_else(|| old_path.to_path_buf());
+
   #[cfg(windows)]
-  return junction_or_symlink_dir(old_path, new_path);
+  {
+    junction_or_symlink_dir(&old_path_relative, old_path, new_path)
+  }
   #[cfg(not(windows))]
-  symlink_dir(old_path, new_path)
+  {
+    symlink_dir(&old_path_relative, new_path).map_err(Into::into)
+  }
 }
 
 #[cfg(windows)]
 fn junction_or_symlink_dir(
+  old_path_relative: &Path,
   old_path: &Path,
   new_path: &Path,
 ) -> Result<(), AnyError> {
-  use deno_core::anyhow::bail;
-  // Use junctions because they're supported on ntfs file systems without
-  // needing to elevate privileges on Windows
+  static USE_JUNCTIONS: std::sync::atomic::AtomicBool =
+    std::sync::atomic::AtomicBool::new(false);
+
+  if USE_JUNCTIONS.load(std::sync::atomic::Ordering::Relaxed) {
+    // Use junctions because they're supported on ntfs file systems without
+    // needing to elevate privileges on Windows.
+    // Note: junctions don't support relative paths, so we need to use the
+    // absolute path here.
+    return junction::create(old_path, new_path)
+      .context("Failed creating junction in node_modules folder");
+  }
 
-  match junction::create(old_path, new_path) {
+  match symlink_dir(old_path_relative, new_path) {
     Ok(()) => Ok(()),
-    Err(junction_err) => {
-      if cfg!(debug_assertions) {
-        // When running the tests, junctions should be created, but if not then
-        // surface this error.
-        log::warn!("Error creating junction. {:#}", junction_err);
-      }
-
-      match symlink_dir(old_path, new_path) {
-        Ok(()) => Ok(()),
-        Err(symlink_err) => bail!(
-          concat!(
-            "Failed creating junction and fallback symlink in node_modules folder.\n\n",
-            "{:#}\n\n{:#}",
-          ),
-          junction_err,
-          symlink_err,
-        ),
-      }
+    Err(symlink_err)
+      if symlink_err.kind() == std::io::ErrorKind::PermissionDenied =>
+    {
+      USE_JUNCTIONS.store(true, std::sync::atomic::Ordering::Relaxed);
+      junction::create(old_path, new_path).map_err(Into::into)
     }
+    Err(symlink_err) => Err(
+      AnyError::from(symlink_err)
+        .context("Failed creating symlink in node_modules folder"),
+    ),
   }
 }
 

filename: cli/util/fs.rs
patch: @@ -509,7 +509,7 @@ pub fn hard_link_dir_recursive(from: &Path, to: &Path) -> Result<(), AnyError> {
   Ok(())
 }
 
-pub fn symlink_dir(oldpath: &Path, newpath: &Path) -> Result<(), AnyError> {
+pub fn symlink_dir(oldpath: &Path, newpath: &Path) -> Result<(), Error> {
   let err_mapper = |err: Error| {
     Error::new(
       err.kind(),

filename: tests/specs/install/move_after_install/__test__.jsonc
patch: @@ -0,0 +1,28 @@
+{
+  "tempDir": true,
+  "envs": {
+    "DENO_FUTURE": "1"
+  },
+  "steps": [
+    {
+      "cwd": "./test-project",
+      "args": "install",
+      "output": "[WILDCARD]"
+    },
+    {
+      "cwd": "./test-project",
+      "args": "run -A main.mjs",
+      "output": "5\n"
+    },
+    {
+      "commandName": "mv",
+      "args": "test-project test-project-moved",
+      "output": ""
+    },
+    {
+      "cwd": "./test-project-moved",
+      "args": "run -A main.mjs",
+      "output": "5\n"
+    }
+  ]
+}

filename: tests/specs/install/move_after_install/test-project/main.mjs
patch: @@ -0,0 +1,4 @@
+import { getValue, setValue } from "@denotest/esm-basic";
+
+setValue(5);
+console.log(getValue());

filename: tests/specs/install/move_after_install/test-project/package.json
patch: @@ -0,0 +1,5 @@
+{
+  "dependencies": {
+    "@denotest/esm-basic": "*"
+  }
+}