@@ -9,7 +9,7 @@ use std::{
99 sync:: Arc ,
1010} ;
1111
12- use camino:: { Utf8Path , Utf8PathBuf } ;
12+ use camino:: { Utf8Component , Utf8Path , Utf8PathBuf } ;
1313use dashmap:: { DashMap , DashSet } ;
1414use futures:: future:: BoxFuture ;
1515use rustc_hash:: FxHasher ;
@@ -18,7 +18,6 @@ use tokio::sync::OnceCell as OnceLock;
1818use crate :: {
1919 context:: ResolveContext as Ctx ,
2020 package_json:: { off_to_location, PackageJson } ,
21- path:: PathUtil ,
2221 resolver_path:: { hash_path, ResolverPath } ,
2322 FileMetadata , FileSystem , JSONError , ResolveError , ResolveOptions , TsConfig ,
2423} ;
@@ -272,10 +271,18 @@ impl CachedPathImpl {
272271 . map ( |path| Some ( Utf8PathBuf :: from_path_buf ( path) . expect ( "path should be UTF-8" ) ) ) ;
273272 }
274273 if let Some ( parent) = self . parent ( ) {
275- let parent_path = parent. realpath ( fs) . await ?;
276- return Ok ( Some (
277- parent_path. normalize_with ( self . path . strip_prefix ( parent. path ( ) ) . unwrap ( ) ) ,
278- ) ) ;
274+ let mut real_path = parent. realpath ( fs) . await ?;
275+ // Unnormalized paths (e.g. from alias values or absolute specifiers) can
276+ // end in `..`, where `file_name()` returns `None` and would silently drop
277+ // the component; POSIX semantics pop it after the parent is resolved.
278+ match self . path . components ( ) . next_back ( ) {
279+ Some ( Utf8Component :: Normal ( segment) ) => real_path. push ( segment) ,
280+ Some ( Utf8Component :: ParentDir ) => {
281+ real_path. pop ( ) ;
282+ }
283+ _ => { }
284+ }
285+ return Ok ( Some ( real_path) ) ;
279286 }
280287 Ok ( None )
281288 } )
0 commit comments