@@ -3339,18 +3339,23 @@ if (isWindows) {
33393339 } ;
33403340}
33413341
3342- function toAbsolutePathPreservingDots ( p ) {
3343- if ( ! pathModule . isAbsolute ( p ) ) {
3344- const root = splitRoot ( p ) ;
3345- if ( isWindows && root . length === 2 ) {
3346- return `${ pathModule . resolve ( root ) } ${ pathModule . sep } ${ StringPrototypeSlice ( p , root . length ) } ` ;
3347- }
3348- return `${ pathModule . resolve ( ) } ${ pathModule . sep } ${ p } ` ;
3349- }
3350- if ( isWindows && splitRoot ( p ) . length === 1 ) {
3351- return `${ pathModule . parse ( pathModule . resolve ( ) ) . root } ${ StringPrototypeSlice ( p , 1 ) } ` ;
3352- }
3353- return p ;
3342+ // realpath(3) applies `..` to the resolved parent, so on POSIX `.` and `..`
3343+ // are kept in the path while walking it. Windows applies `..` lexically, both
3344+ // in the path being opened and in symlink targets, so the path and the link
3345+ // targets are normalized up front there, matching `fs.realpathSync.native()`.
3346+ function toRealpathInput ( p ) {
3347+ if ( isWindows ) return pathModule . resolve ( p ) ;
3348+ return pathModule . isAbsolute ( p ) ? p : `${ pathModule . resolve ( ) } /${ p } ` ;
3349+ }
3350+
3351+ function joinRealpathLink ( previous , linkTarget ) {
3352+ if ( isWindows ) return pathModule . resolve ( previous , linkTarget ) ;
3353+ return pathModule . isAbsolute ( linkTarget ) ? linkTarget : `${ previous } ${ linkTarget } ` ;
3354+ }
3355+
3356+ function joinRealpathRemainder ( resolvedLink , remainder ) {
3357+ if ( isWindows ) return pathModule . resolve ( resolvedLink , remainder ) ;
3358+ return remainder . length === 0 ? resolvedLink : `${ resolvedLink } /${ remainder } ` ;
33543359}
33553360
33563361/**
@@ -3371,7 +3376,7 @@ function realpathSync(p, options) {
33713376 p += '' ;
33723377 }
33733378 validatePath ( p ) ;
3374- p = toAbsolutePathPreservingDots ( p ) ;
3379+ p = toRealpathInput ( p ) ;
33753380
33763381 // Check the resolved input path up front so that the lstat walk below cannot
33773382 // be used to probe the existence of paths outside the granted set.
@@ -3501,30 +3506,18 @@ function realpathSync(p, options) {
35013506 isFileType ( targetStats , S_IFSOCK ) ;
35023507 linkTarget = binding . readlink ( base , undefined ) ;
35033508 }
3504- const linkRoot = splitRoot ( linkTarget ) ;
3505- if ( pathModule . isAbsolute ( linkTarget ) ) {
3506- resolvedLink = isWindows && linkRoot . length === 1 ?
3507- pathModule . resolve ( previous , linkRoot ) +
3508- StringPrototypeSlice ( linkTarget , linkRoot . length ) :
3509- linkTarget ;
3510- } else if ( isWindows && linkRoot . length === 2 ) {
3511- resolvedLink = pathModule . resolve ( linkRoot ) +
3512- pathModule . sep + StringPrototypeSlice ( linkTarget , linkRoot . length ) ;
3513- } else {
3514- resolvedLink = `${ previous } ${ linkTarget } ` ;
3515- }
3509+ resolvedLink = joinRealpathLink ( previous , linkTarget ) ;
35163510
3517- if ( StringPrototypeIndexOf ( linkTarget , '..' ) === - 1 ) {
3511+ // On POSIX, a target containing `..` resolves relative to the path
3512+ // walked before it, so it cannot be cached by the link alone.
3513+ if ( isWindows || StringPrototypeIndexOf ( linkTarget , '..' ) === - 1 ) {
35183514 cache ?. set ( base , pathModule . resolve ( resolvedLink ) ) ;
35193515 }
35203516 if ( ! isWindows ) seenLinks . set ( id , linkTarget ) ;
35213517 }
35223518
35233519 // Resolve the link, then start over
3524- const remainder = StringPrototypeSlice ( p , pos ) ;
3525- p = remainder . length === 0 ?
3526- resolvedLink :
3527- `${ resolvedLink } ${ pathModule . sep } ${ remainder } ` ;
3520+ p = joinRealpathRemainder ( resolvedLink , StringPrototypeSlice ( p , pos ) ) ;
35283521
35293522 // Skip over roots
35303523 current = base = splitRoot ( p ) ;
@@ -3594,7 +3587,7 @@ function realpath(p, options, callback) {
35943587 p += '' ;
35953588 }
35963589 validatePath ( p ) ;
3597- p = toAbsolutePathPreservingDots ( p ) ;
3590+ p = toRealpathInput ( p ) ;
35983591
35993592 const seenLinks = new SafeMap ( ) ;
36003593 const knownHard = new SafeSet ( ) ;
@@ -3712,28 +3705,12 @@ function realpath(p, options, callback) {
37123705 function gotTarget ( err , target ) {
37133706 if ( err ) return callback ( err ) ;
37143707
3715- const linkRoot = splitRoot ( target ) ;
3716- let resolvedLink ;
3717- if ( pathModule . isAbsolute ( target ) ) {
3718- resolvedLink = isWindows && linkRoot . length === 1 ?
3719- pathModule . resolve ( previous , linkRoot ) +
3720- StringPrototypeSlice ( target , linkRoot . length ) :
3721- target ;
3722- } else if ( isWindows && linkRoot . length === 2 ) {
3723- resolvedLink = pathModule . resolve ( linkRoot ) +
3724- pathModule . sep + StringPrototypeSlice ( target , linkRoot . length ) ;
3725- } else {
3726- resolvedLink = `${ previous } ${ target } ` ;
3727- }
3728- gotResolvedLink ( resolvedLink ) ;
3708+ gotResolvedLink ( joinRealpathLink ( previous , target ) ) ;
37293709 }
37303710
37313711 function gotResolvedLink ( resolvedLink ) {
37323712 // Resolve the link, then start over
3733- const remainder = StringPrototypeSlice ( p , pos ) ;
3734- p = remainder . length === 0 ?
3735- resolvedLink :
3736- `${ resolvedLink } ${ pathModule . sep } ${ remainder } ` ;
3713+ p = joinRealpathRemainder ( resolvedLink , StringPrototypeSlice ( p , pos ) ) ;
37373714 current = base = splitRoot ( p ) ;
37383715 pos = current . length ;
37393716
0 commit comments