@@ -624,28 +624,128 @@ export function getNormalizedPathComponents(path: string, currentDirectory: stri
624
624
}
625
625
626
626
/** @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 ) ;
629
719
}
630
720
631
721
/** @internal */
632
722
export function normalizePath ( path : string ) : string {
633
723
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 {
634
733
// Most paths don't require normalization
635
734
if ( ! relativePathSegmentRegExp . test ( path ) ) {
636
735
return path ;
637
736
}
638
737
// 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
+ }
640
742
if ( simplified !== path ) {
641
743
path = simplified ;
642
744
if ( ! relativePathSegmentRegExp . test ( path ) ) {
643
745
return path ;
644
746
}
645
747
}
646
- // Other paths require full normalization
647
- const normalized = getPathFromPathComponents ( reducePathComponents ( getPathComponents ( path ) ) ) ;
648
- return normalized && hasTrailingDirectorySeparator ( path ) ? ensureTrailingDirectorySeparator ( normalized ) : normalized ;
748
+ return undefined ;
649
749
}
650
750
651
751
function getPathWithoutRoot ( pathComponents : readonly string [ ] ) {
0 commit comments