@@ -624,28 +624,128 @@ export function getNormalizedPathComponents(path: string, currentDirectory: stri
624624}
625625
626626/** @internal */
627- export function getNormalizedAbsolutePath ( fileName : string , currentDirectory : string | undefined ) : string {
628- return getPathFromPathComponents ( getNormalizedPathComponents ( fileName , currentDirectory ) ) ;
627+ export function getNormalizedAbsolutePath ( path : string , currentDirectory : string | undefined ) : string {
628+ let rootLength = getRootLength ( path ) ;
629+ if ( rootLength === 0 && currentDirectory ) {
630+ path = combinePaths ( currentDirectory , path ) ;
631+ rootLength = getRootLength ( path ) ;
632+ }
633+ else {
634+ // combinePaths normalizes slashes, so not necessary in the other branch
635+ path = normalizeSlashes ( path ) ;
636+ }
637+
638+ const simpleNormalized = simpleNormalizePath ( path ) ;
639+ if ( simpleNormalized !== undefined ) {
640+ return simpleNormalized . length > rootLength ? removeTrailingDirectorySeparator ( simpleNormalized ) : simpleNormalized ;
641+ }
642+
643+ const length = path . length ;
644+ const root = path . substring ( 0 , rootLength ) ;
645+ // `normalized` is only initialized once `path` is determined to be non-normalized
646+ let normalized ;
647+ let index = rootLength ;
648+ let segmentStart = index ;
649+ let normalizedUpTo = index ;
650+ let seenNonDotDotSegment = rootLength !== 0 ;
651+ while ( index < length ) {
652+ // At beginning of segment
653+ segmentStart = index ;
654+ let ch = path . charCodeAt ( index ) ;
655+ while ( ch === CharacterCodes . slash && index + 1 < length ) {
656+ index ++ ;
657+ ch = path . charCodeAt ( index ) ;
658+ }
659+ if ( index > segmentStart ) {
660+ // Seen superfluous separator
661+ normalized ??= path . substring ( 0 , segmentStart - 1 ) ;
662+ segmentStart = index ;
663+ }
664+ // Past any superfluous separators
665+ let segmentEnd = path . indexOf ( directorySeparator , index + 1 ) ;
666+ if ( segmentEnd === - 1 ) {
667+ segmentEnd = length ;
668+ }
669+ const segmentLength = segmentEnd - segmentStart ;
670+ if ( segmentLength === 1 && path . charCodeAt ( index ) === CharacterCodes . dot ) {
671+ // "." segment (skip)
672+ normalized ??= path . substring ( 0 , normalizedUpTo ) ;
673+ }
674+ else if ( segmentLength === 2 && path . charCodeAt ( index ) === CharacterCodes . dot && path . charCodeAt ( index + 1 ) === CharacterCodes . dot ) {
675+ // ".." segment
676+ if ( ! seenNonDotDotSegment ) {
677+ if ( normalized !== undefined ) {
678+ normalized += normalized . length === rootLength ? ".." : "/.." ;
679+ }
680+ else {
681+ normalizedUpTo = index + 2 ;
682+ }
683+ }
684+ else if ( normalized === undefined ) {
685+ if ( normalizedUpTo - 2 >= 0 ) {
686+ normalized = path . substring ( 0 , Math . max ( rootLength , path . lastIndexOf ( directorySeparator , normalizedUpTo - 2 ) ) ) ;
687+ }
688+ else {
689+ normalized = path . substring ( 0 , normalizedUpTo ) ;
690+ }
691+ }
692+ else {
693+ const lastSlash = normalized . lastIndexOf ( directorySeparator ) ;
694+ if ( lastSlash !== - 1 ) {
695+ normalized = normalized . substring ( 0 , Math . max ( rootLength , lastSlash ) ) ;
696+ }
697+ else {
698+ normalized = root ;
699+ }
700+ if ( normalized . length === rootLength ) {
701+ seenNonDotDotSegment = rootLength !== 0 ;
702+ }
703+ }
704+ }
705+ else if ( normalized !== undefined ) {
706+ if ( normalized . length !== rootLength ) {
707+ normalized += directorySeparator ;
708+ }
709+ seenNonDotDotSegment = true ;
710+ normalized += path . substring ( segmentStart , segmentEnd ) ;
711+ }
712+ else {
713+ seenNonDotDotSegment = true ;
714+ normalizedUpTo = segmentEnd ;
715+ }
716+ index = segmentEnd + 1 ;
717+ }
718+ return normalized ?? ( length > rootLength ? removeTrailingDirectorySeparator ( path ) : path ) ;
629719}
630720
631721/** @internal */
632722export function normalizePath ( path : string ) : string {
633723 path = normalizeSlashes ( path ) ;
724+ let normalized = simpleNormalizePath ( path ) ;
725+ if ( normalized !== undefined ) {
726+ return normalized ;
727+ }
728+ normalized = getNormalizedAbsolutePath ( path , "" ) ;
729+ return normalized && hasTrailingDirectorySeparator ( path ) ? ensureTrailingDirectorySeparator ( normalized ) : normalized ;
730+ }
731+
732+ function simpleNormalizePath ( path : string ) : string | undefined {
634733 // Most paths don't require normalization
635734 if ( ! relativePathSegmentRegExp . test ( path ) ) {
636735 return path ;
637736 }
638737 // Some paths only require cleanup of `/./` or leading `./`
639- const simplified = path . replace ( / \/ \. \/ / g, "/" ) . replace ( / ^ \. \/ / , "" ) ;
738+ let simplified = path . replace ( / \/ \. \/ / g, "/" ) ;
739+ if ( simplified . startsWith ( "./" ) ) {
740+ simplified = simplified . slice ( 2 ) ;
741+ }
640742 if ( simplified !== path ) {
641743 path = simplified ;
642744 if ( ! relativePathSegmentRegExp . test ( path ) ) {
643745 return path ;
644746 }
645747 }
646- // Other paths require full normalization
647- const normalized = getPathFromPathComponents ( reducePathComponents ( getPathComponents ( path ) ) ) ;
648- return normalized && hasTrailingDirectorySeparator ( path ) ? ensureTrailingDirectorySeparator ( normalized ) : normalized ;
748+ return undefined ;
649749}
650750
651751function getPathWithoutRoot ( pathComponents : readonly string [ ] ) {
0 commit comments