From 7147925bedab3b5078f4b05a08bf47573443257e Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Tue, 12 Nov 2024 10:39:51 +0900 Subject: [PATCH] fix: use same UNC path normalization logic with libuv --- Cargo.lock | 7 ---- Cargo.toml | 1 - fixtures/pnpm/longfilename/index.js | 3 ++ fixtures/pnpm/longfilename/package.json | 7 ++++ fixtures/pnpm/package.json | 1 + pnpm-lock.yaml | 8 +++++ src/file_system.rs | 43 ++++++++++++++++++++++--- tests/resolve_test.rs | 10 ++++++ 8 files changed, 67 insertions(+), 13 deletions(-) create mode 100644 fixtures/pnpm/longfilename/index.js create mode 100644 fixtures/pnpm/longfilename/package.json diff --git a/Cargo.lock b/Cargo.lock index cc9ac699..41419914 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -340,12 +340,6 @@ dependencies = [ "litrs", ] -[[package]] -name = "dunce" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" - [[package]] name = "either" version = "1.13.0" @@ -688,7 +682,6 @@ dependencies = [ "criterion2", "dashmap", "document-features", - "dunce", "indexmap 2.6.0", "json-strip-comments", "normalize-path", diff --git a/Cargo.toml b/Cargo.toml index 182640a1..56984336 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,7 +70,6 @@ serde_json = { version = "1", features = [ "preserve_order", ] } # preserve_order: package_json.exports requires order such as `["require", "import", "default"]` rustc-hash = { version = "2" } -dunce = "1" # Normalize Windows paths to the most compatible format, avoiding UNC where possible once_cell = "1" # Use `std::sync::OnceLock::get_or_try_init` when it is stable. thiserror = "1" json-strip-comments = "1" diff --git a/fixtures/pnpm/longfilename/index.js b/fixtures/pnpm/longfilename/index.js new file mode 100644 index 00000000..7f0f7ce6 --- /dev/null +++ b/fixtures/pnpm/longfilename/index.js @@ -0,0 +1,3 @@ +const test = 'hello world' + +export default test diff --git a/fixtures/pnpm/longfilename/package.json b/fixtures/pnpm/longfilename/package.json new file mode 100644 index 00000000..21b5e76f --- /dev/null +++ b/fixtures/pnpm/longfilename/package.json @@ -0,0 +1,7 @@ +{ + "name": "@oxc-resolver/test-longfilename-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "private": true, + "version": "0.0.0", + "main": "index.js", + "type": "module" +} diff --git a/fixtures/pnpm/package.json b/fixtures/pnpm/package.json index 12fd900b..577d8a3c 100644 --- a/fixtures/pnpm/package.json +++ b/fixtures/pnpm/package.json @@ -3,6 +3,7 @@ "version": "1.0.0", "private": true, "devDependencies": { + "@oxc-resolver/test-longfilename-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa": "file:./longfilename", "axios": "1.6.2", "ipaddr.js": "2.2.0", "postcss": "8.4.33", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e6f5501b..30d0e6f6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,6 +29,9 @@ importers: fixtures/pnpm: devDependencies: + '@oxc-resolver/test-longfilename-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa': + specifier: file:./longfilename + version: file:fixtures/pnpm/longfilename axios: specifier: 1.6.2 version: 1.6.2 @@ -546,6 +549,9 @@ packages: '@octokit/types@13.6.1': resolution: {integrity: sha512-PHZE9Z+kWXb23Ndik8MKPirBPziOc0D2/3KH1P+6jK5nGWe96kadZuE4jev2/Jq7FvIfTlT2Ltg8Fv2x1v0a5g==} + '@oxc-resolver/test-longfilename-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@file:fixtures/pnpm/longfilename': + resolution: {directory: fixtures/pnpm/longfilename, type: directory} + '@rollup/pluginutils@4.2.1': resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==} engines: {node: '>= 8.0.0'} @@ -1907,6 +1913,8 @@ snapshots: dependencies: '@octokit/openapi-types': 22.2.0 + '@oxc-resolver/test-longfilename-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@file:fixtures/pnpm/longfilename': {} + '@rollup/pluginutils@4.2.1': dependencies: estree-walker: 2.0.2 diff --git a/src/file_system.rs b/src/file_system.rs index 0ba7da73..0f7f2911 100644 --- a/src/file_system.rs +++ b/src/file_system.rs @@ -1,12 +1,17 @@ use std::{ fs, io, - path::{Path, PathBuf}, + path::{Component, Path, PathBuf, Prefix}, }; use cfg_if::cfg_if; #[cfg(feature = "yarn_pnp")] use pnp::fs::{LruZipCache, VPath, VPathInfo, ZipCache}; +#[cfg(windows)] +const UNC_PATH_PREFIX: &str = "\\\\?\\UNC\\"; +#[cfg(windows)] +const LONG_PATH_PREFIX: &str = "\\\\?\\"; + /// File System abstraction used for `ResolverGeneric` pub trait FileSystem: Send + Sync { /// See [std::fs::read_to_string] @@ -165,13 +170,13 @@ impl FileSystem for FileSystemOs { if #[cfg(feature = "yarn_pnp")] { match VPath::from(path)? { VPath::Zip(info) => { - dunce::canonicalize(info.physical_base_path().join(info.zip_path)) + node_compatible_raw_canonicalize(info.physical_base_path().join(info.zip_path)) } - VPath::Virtual(info) => dunce::canonicalize(info.physical_base_path()), - VPath::Native(path) => dunce::canonicalize(path), + VPath::Virtual(info) => node_compatible_raw_canonicalize(info.physical_base_path()), + VPath::Native(path) => node_compatible_raw_canonicalize(path), } } else if #[cfg(windows)] { - dunce::canonicalize(path) + node_compatible_raw_canonicalize(path) } else { use std::path::Component; let mut path_buf = path.to_path_buf(); @@ -227,3 +232,31 @@ fn metadata() { ); let _ = meta; } + +fn node_compatible_raw_canonicalize>(path: P) -> io::Result { + cfg_if! { + if #[cfg(windows)] { + // same logic with https://github.com/libuv/libuv/blob/d4ab6fbba4669935a6bc23645372dfe4ac29ab39/src/win/fs.c#L2774-L2784 + let canonicalized = fs::canonicalize(path)?; + let first_component = canonicalized.components().next(); + match first_component { + Some(Component::Prefix(prefix)) => { + match prefix.kind() { + Prefix::VerbatimUNC(_, _) => { + Ok(canonicalized.to_str().and_then(|s| s.get(UNC_PATH_PREFIX.len()..)).map(PathBuf::from).unwrap_or(canonicalized)) + } + Prefix::VerbatimDisk(_) => { + Ok(canonicalized.to_str().and_then(|s| s.get(LONG_PATH_PREFIX.len()..)).map(PathBuf::from).unwrap_or(canonicalized)) + } + _ => { + Ok(canonicalized) + } + } + } + _ => Ok(canonicalized), + } + } else { + fs::canonicalize(path) + } + } +} diff --git a/tests/resolve_test.rs b/tests/resolve_test.rs index 3c250569..8b4341d1 100644 --- a/tests/resolve_test.rs +++ b/tests/resolve_test.rs @@ -205,3 +205,13 @@ fn nested_symlinks() { Ok(dir.join("nm/index.js")) ); } + +#[test] +fn windows_symlinked_longfilename() { + let dir = dir(); + let path = dir.join("fixtures/pnpm"); + let module_path = dir.join("node_modules/.pnpm/@oxc-resolver+test-longfilename-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_m464apeldykmdsyzlfhtrggk24/node_modules/@oxc-resolver/test-longfilename-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/index.js"); + + let resolution = Resolver::new(ResolveOptions::default()).resolve(&path, "@oxc-resolver/test-longfilename-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").map(|r| r.full_path()); + assert_eq!(resolution, Ok(module_path)); +}