@@ -399,21 +399,8 @@ win32.parse = function(pathString) {
399399win32 . sep = '\\' ;
400400win32 . delimiter = ';' ;
401401
402-
403- // Split a filename into [root, dir, basename, ext], unix version
404- // 'root' is just a slash, or nothing.
405- const splitPathRe =
406- / ^ ( \/ ? | ) ( [ \s \S ] * ?) ( (?: \. { 1 , 2 } | [ ^ \/ ] + ?| ) ( \. [ ^ . \/ ] * | ) ) (?: [ \/ ] * ) $ / ;
407402var posix = { } ;
408403
409-
410- function posixSplitPath ( filename ) {
411- const out = splitPathRe . exec ( filename ) ;
412- out . shift ( ) ;
413- return out ;
414- }
415-
416-
417404// path.resolve([from ...], to)
418405// posix version
419406posix . resolve = function ( ) {
@@ -527,39 +514,159 @@ posix._makeLong = function(path) {
527514
528515
529516posix . dirname = function ( path ) {
530- const result = posixSplitPath ( path ) ;
531- const root = result [ 0 ] ;
532- var dir = result [ 1 ] ;
533-
534- if ( ! root && ! dir ) {
535- // No dirname whatsoever
517+ if ( path . length === 0 )
536518 return '.' ;
519+ var code = path . charCodeAt ( 0 ) ;
520+ var hasRoot = ( code === 47 ) ;
521+ var end = - 1 ;
522+ var matchedSlash = true ;
523+ for ( var i = path . length - 1 ; i >= 1 ; -- i ) {
524+ code = path . charCodeAt ( i ) ;
525+ if ( code === 47 ) {
526+ if ( ! matchedSlash ) {
527+ end = i ;
528+ break ;
529+ }
530+ } else {
531+ // We saw the first non-path separator
532+ matchedSlash = false ;
533+ }
537534 }
538535
539- if ( dir ) {
540- // It has a dirname, strip trailing slash
541- dir = dir . substr ( 0 , dir . length - 1 ) ;
542- }
543-
544- return root + dir ;
536+ if ( end === - 1 )
537+ return hasRoot ? '/' : '.' ;
538+ if ( hasRoot && end === 1 )
539+ return '//' ;
540+ return path . slice ( 0 , end ) ;
545541} ;
546542
547543
548544posix . basename = function ( path , ext ) {
549545 if ( ext !== undefined && typeof ext !== 'string' )
550546 throw new TypeError ( 'ext must be a string' ) ;
551547
552- var f = posixSplitPath ( path ) [ 2 ] ;
548+ var start = 0 ;
549+ var end = - 1 ;
550+ var matchedSlash = true ;
551+ var i ;
552+
553+ if ( ext !== undefined && ext . length > 0 && ext . length <= path . length ) {
554+ if ( ext . length === path . length && ext === path )
555+ return '' ;
556+ var extIdx = ext . length - 1 ;
557+ var firstNonSlashEnd = - 1 ;
558+ for ( i = path . length - 1 ; i >= 0 ; -- i ) {
559+ const code = path . charCodeAt ( i ) ;
560+ if ( code === 47 /*/*/ ) {
561+ // If we reached a path separator that was not part of a set of path
562+ // separators at the end of the string, stop now
563+ if ( ! matchedSlash ) {
564+ start = i + 1 ;
565+ break ;
566+ }
567+ } else {
568+ if ( firstNonSlashEnd === - 1 ) {
569+ // We saw the first non-path separator, remember this index in case
570+ // we need it if the extension ends up not matching
571+ matchedSlash = false ;
572+ firstNonSlashEnd = i + 1 ;
573+ }
574+ if ( extIdx >= 0 ) {
575+ // Try to match the explicit extension
576+ if ( code === ext . charCodeAt ( extIdx ) ) {
577+ if ( -- extIdx === - 1 ) {
578+ // We matched the extension, so mark this as the end of our path
579+ // component
580+ end = i ;
581+ }
582+ } else {
583+ // Extension does not match, so our result is the entire path
584+ // component
585+ extIdx = - 1 ;
586+ end = firstNonSlashEnd ;
587+ }
588+ }
589+ }
590+ }
591+
592+ if ( start === end )
593+ end = firstNonSlashEnd ;
594+ else if ( end === - 1 )
595+ end = path . length ;
596+ return path . slice ( start , end ) ;
597+ } else {
598+ for ( i = path . length - 1 ; i >= 0 ; -- i ) {
599+ if ( path . charCodeAt ( i ) === 47 /*/*/ ) {
600+ // If we reached a path separator that was not part of a set of path
601+ // separators at the end of the string, stop now
602+ if ( ! matchedSlash ) {
603+ start = i + 1 ;
604+ break ;
605+ }
606+ } else if ( end === - 1 ) {
607+ // We saw the first non-path separator, mark this as the end of our
608+ // path component
609+ matchedSlash = false ;
610+ end = i + 1 ;
611+ }
612+ }
553613
554- if ( ext && f . substr ( - 1 * ext . length ) === ext ) {
555- f = f . substr ( 0 , f . length - ext . length ) ;
614+ if ( end === - 1 )
615+ return '' ;
616+ return path . slice ( start , end ) ;
556617 }
557- return f ;
558618} ;
559619
560620
561621posix . extname = function ( path ) {
562- return posixSplitPath ( path ) [ 3 ] ;
622+ var startDot = - 1 ;
623+ var startPart = 0 ;
624+ var end = - 1 ;
625+ var matchedSlash = true ;
626+ // Track the state of characters (if any) we see before our first dot and
627+ // after any path separator we find
628+ var preDotState = 0 ;
629+ for ( var i = path . length - 1 ; i >= 0 ; -- i ) {
630+ const code = path . charCodeAt ( i ) ;
631+ if ( code === 47 ) {
632+ // If we reached a path separator that was not part of a set of path
633+ // separators at the end of the string, stop now
634+ if ( ! matchedSlash ) {
635+ startPart = i + 1 ;
636+ break ;
637+ }
638+ continue ;
639+ }
640+ if ( end === - 1 ) {
641+ // We saw the first non-path separator, mark this as the end of our
642+ // extension
643+ matchedSlash = false ;
644+ end = i + 1 ;
645+ }
646+ if ( code === 46 ) {
647+ // If this is our first dot, mark it as the start of our extension
648+ if ( startDot === - 1 )
649+ startDot = i ;
650+ else if ( preDotState !== 1 )
651+ preDotState = 1 ;
652+ } else if ( startDot !== - 1 ) {
653+ // We saw a non-dot and non-path separator before our dot, so we should
654+ // have a good chance at having a non-empty extension
655+ preDotState = - 1 ;
656+ }
657+ }
658+
659+ if ( startDot === - 1 ||
660+ end === - 1 ||
661+ // We saw a non-dot character immediately before the dot
662+ preDotState === 0 ||
663+ // The (right-most) trimmed path component is exactly '..'
664+ ( preDotState === 1 &&
665+ startDot === end - 1 &&
666+ startDot === startPart + 1 ) ) {
667+ return '' ;
668+ }
669+ return path . slice ( startDot , end ) ;
563670} ;
564671
565672
@@ -587,15 +694,90 @@ posix.format = function(pathObject) {
587694
588695posix . parse = function ( pathString ) {
589696 assertPath ( pathString ) ;
697+ var ret = { root : '' , dir : '' , base : '' , ext : '' , name : '' } ;
698+ if ( pathString . length === 0 )
699+ return ret ;
700+ var code = pathString . charCodeAt ( 0 ) ;
701+ var isAbsolute = ( code === 47 ) ;
702+ var start ;
703+ if ( isAbsolute ) {
704+ ret . root = '/' ;
705+ start = 1 ;
706+ } else {
707+ start = 0 ;
708+ }
709+ var startDot = - 1 ;
710+ var startPart = 0 ;
711+ var end = - 1 ;
712+ var matchedSlash = true ;
713+ var i = pathString . length - 1 ;
714+
715+ // Track the state of characters (if any) we see before our first dot and
716+ // after any path separator we find
717+ var preDotState = 0 ;
718+
719+ // Get non-dir info
720+ for ( ; i >= start ; -- i ) {
721+ code = pathString . charCodeAt ( i ) ;
722+ if ( code === 47 ) {
723+ // If we reached a path separator that was not part of a set of path
724+ // separators at the end of the string, stop now
725+ if ( ! matchedSlash ) {
726+ startPart = i + 1 ;
727+ break ;
728+ }
729+ continue ;
730+ }
731+ if ( end === - 1 ) {
732+ // We saw the first non-path separator, mark this as the end of our
733+ // extension
734+ matchedSlash = false ;
735+ end = i + 1 ;
736+ }
737+ if ( code === 46 ) {
738+ // If this is our first dot, mark it as the start of our extension
739+ if ( startDot === - 1 )
740+ startDot = i ;
741+ else if ( preDotState !== 1 )
742+ preDotState = 1 ;
743+ } else if ( startDot !== - 1 ) {
744+ // We saw a non-dot and non-path separator before our dot, so we should
745+ // have a good chance at having a non-empty extension
746+ preDotState = - 1 ;
747+ }
748+ }
590749
591- var allParts = posixSplitPath ( pathString ) ;
592- return {
593- root : allParts [ 0 ] ,
594- dir : allParts [ 0 ] + allParts [ 1 ] . slice ( 0 , - 1 ) ,
595- base : allParts [ 2 ] ,
596- ext : allParts [ 3 ] ,
597- name : allParts [ 2 ] . slice ( 0 , allParts [ 2 ] . length - allParts [ 3 ] . length )
598- } ;
750+ if ( startDot === - 1 ||
751+ end === - 1 ||
752+ // We saw a non-dot character immediately before the dot
753+ preDotState === 0 ||
754+ // The (right-most) trimmed path component is exactly '..'
755+ ( preDotState === 1 &&
756+ startDot === end - 1 &&
757+ startDot === startPart + 1 ) ) {
758+ if ( end !== - 1 ) {
759+ if ( startPart === 0 && isAbsolute )
760+ ret . base = ret . name = pathString . slice ( 1 , end ) ;
761+ else
762+ ret . base = ret . name = pathString . slice ( startPart , end ) ;
763+ }
764+ } else {
765+ if ( startPart === 0 && isAbsolute ) {
766+ ret . name = pathString . slice ( 1 , startDot ) ;
767+ ret . base = pathString . slice ( 1 , end ) ;
768+ } else {
769+ ret . name = pathString . slice ( startPart , startDot ) ;
770+ ret . base = pathString . slice ( startPart , end ) ;
771+ }
772+ ret . ext = pathString . slice ( startDot , end ) ;
773+ }
774+
775+ if ( startPart > 0 )
776+ ret . dir = pathString . slice ( 0 , startPart - 1 ) ;
777+ else if ( isAbsolute )
778+ ret . dir = '/' ;
779+
780+ return ret ;
599781} ;
600782
601783
0 commit comments