From a44c83a3d66a576e93f642ec475ea505215a8d62 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Tue, 27 Sep 2022 18:02:35 -0400 Subject: [PATCH] fix(npm): use ntfs junctions in node_modules folder on Windows (#16061) --- Cargo.lock | 11 +++++++++++ cli/Cargo.toml | 1 + cli/npm/resolvers/local.rs | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index a7bfecc65b..b6bbbf31a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -848,6 +848,7 @@ dependencies = [ "indexmap", "indicatif", "jsonc-parser", + "junction", "libc", "log 0.4.17", "mitata", @@ -2493,6 +2494,16 @@ dependencies = [ "serde_json", ] +[[package]] +name = "junction" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be39922b087cecaba4e2d5592dedfc8bda5d4a5a1231f143337cca207950b61d" +dependencies = [ + "scopeguard", + "winapi 0.3.9", +] + [[package]] name = "kernel32-sys" version = "0.2.2" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 0663c02ff8..22929fd21f 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -111,6 +111,7 @@ zstd = '=0.11.2' [target.'cfg(windows)'.dependencies] fwdansi = "=1.1.0" +junction = "=0.2.0" winapi = { version = "=0.3.9", features = ["knownfolders", "mswsock", "objbase", "shlobj", "tlhelp32", "winbase", "winerror", "winsock2"] } [dev-dependencies] diff --git a/cli/npm/resolvers/local.rs b/cli/npm/resolvers/local.rs index 35223f1aaf..fa2ad4275d 100644 --- a/cli/npm/resolvers/local.rs +++ b/cli/npm/resolvers/local.rs @@ -323,9 +323,44 @@ fn symlink_package_dir( // need to delete the previous symlink before creating a new one let _ignore = fs::remove_dir_all(new_path); + + #[cfg(windows)] + return junction_or_symlink_dir(old_path, new_path); + #[cfg(not(windows))] fs_util::symlink_dir(old_path, new_path) } +#[cfg(windows)] +fn junction_or_symlink_dir( + old_path: &Path, + new_path: &Path, +) -> Result<(), AnyError> { + // Use junctions because they're supported on ntfs file systems without + // needing to elevate privileges on Windows + match junction::create(old_path, new_path) { + Ok(()) => Ok(()), + Err(junction_err) => { + if cfg!(debug) { + // When running the tests, junctions should be created, but if not then + // surface this error. + log::warn!("Error creating junction. {:#}", junction_err); + } + + match fs_util::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, + ), + } + } + } +} + fn join_package_name(path: &Path, package_name: &str) -> PathBuf { let mut path = path.to_path_buf(); // ensure backslashes are used on windows