@@ -14,6 +14,57 @@ const minimatch = (p, pattern, options = {}) => {
1414} ;
1515exports . minimatch = minimatch ;
1616exports . default = exports . minimatch ;
17+ // Optimized checking for the most common glob patterns.
18+ const starDotExtRE = / ^ \* + ( [ ^ + @ ! ? \* \[ \( ] * ) $ / ;
19+ const starDotExtTest = ( ext ) => ( f ) => ! f . startsWith ( '.' ) && f . endsWith ( ext ) ;
20+ const starDotExtTestDot = ( ext ) => ( f ) => f . endsWith ( ext ) ;
21+ const starDotExtTestNocase = ( ext ) => {
22+ ext = ext . toLowerCase ( ) ;
23+ return ( f ) => ! f . startsWith ( '.' ) && f . toLowerCase ( ) . endsWith ( ext ) ;
24+ } ;
25+ const starDotExtTestNocaseDot = ( ext ) => {
26+ ext = ext . toLowerCase ( ) ;
27+ return ( f ) => f . toLowerCase ( ) . endsWith ( ext ) ;
28+ } ;
29+ const starDotStarRE = / ^ \* + \. \* + $ / ;
30+ const starDotStarTest = ( f ) => ! f . startsWith ( '.' ) && f . includes ( '.' ) ;
31+ const starDotStarTestDot = ( f ) => f !== '.' && f !== '..' && f . includes ( '.' ) ;
32+ const dotStarRE = / ^ \. \* + $ / ;
33+ const dotStarTest = ( f ) => f !== '.' && f !== '..' && f . startsWith ( '.' ) ;
34+ const starRE = / ^ \* + $ / ;
35+ const starTest = ( f ) => f . length !== 0 && ! f . startsWith ( '.' ) ;
36+ const starTestDot = ( f ) => f . length !== 0 && f !== '.' && f !== '..' ;
37+ const qmarksRE = / ^ \? + ( [ ^ + @ ! ? \* \[ \( ] * ) ? $ / ;
38+ const qmarksTestNocase = ( [ $0 , ext = '' ] ) => {
39+ const noext = qmarksTestNoExt ( [ $0 ] ) ;
40+ if ( ! ext )
41+ return noext ;
42+ ext = ext . toLowerCase ( ) ;
43+ return ( f ) => noext ( f ) && f . toLowerCase ( ) . endsWith ( ext ) ;
44+ } ;
45+ const qmarksTestNocaseDot = ( [ $0 , ext = '' ] ) => {
46+ const noext = qmarksTestNoExtDot ( [ $0 ] ) ;
47+ if ( ! ext )
48+ return noext ;
49+ ext = ext . toLowerCase ( ) ;
50+ return ( f ) => noext ( f ) && f . toLowerCase ( ) . endsWith ( ext ) ;
51+ } ;
52+ const qmarksTestDot = ( [ $0 , ext = '' ] ) => {
53+ const noext = qmarksTestNoExtDot ( [ $0 ] ) ;
54+ return ! ext ? noext : ( f ) => noext ( f ) && f . endsWith ( ext ) ;
55+ } ;
56+ const qmarksTest = ( [ $0 , ext = '' ] ) => {
57+ const noext = qmarksTestNoExt ( [ $0 ] ) ;
58+ return ! ext ? noext : ( f ) => noext ( f ) && f . endsWith ( ext ) ;
59+ } ;
60+ const qmarksTestNoExt = ( [ $0 ] ) => {
61+ const len = $0 . length ;
62+ return ( f ) => f . length === len && ! f . startsWith ( '.' ) ;
63+ } ;
64+ const qmarksTestNoExtDot = ( [ $0 ] ) => {
65+ const len = $0 . length ;
66+ return ( f ) => f . length === len && f !== '.' && f !== '..' ;
67+ } ;
1768/* c8 ignore start */
1869const platform = typeof process === 'object' && process
1970 ? ( typeof process . env === 'object' &&
@@ -213,14 +264,53 @@ class Minimatch {
213264 // and will not contain any / characters
214265 const rawGlobParts = this . globSet . map ( s => this . slashSplit ( s ) ) ;
215266 // consecutive globstars are an unncessary perf killer
216- this . globParts = this . options . noglobstar
217- ? rawGlobParts
218- : rawGlobParts . map ( parts => parts . reduce ( ( set , part ) => {
219- if ( part !== '**' || set [ set . length - 1 ] !== '**' ) {
267+ // also, **/*/... is equivalent to */**/..., so swap all of those
268+ // this turns a pattern like **/*/**/*/x into */*/**/x
269+ // and a pattern like **/x/**/*/y becomes **/x/*/**/y
270+ // the *later* we can push the **, the more efficient it is,
271+ // because we can avoid having to do a recursive walk until
272+ // the walked tree is as shallow as possible.
273+ // Note that this is only true up to the last pattern, though, because
274+ // a/*/** will only match a/b if b is a dir, but a/**/* will match a/b
275+ // regardless, since it's "0 or more path segments" if it's not final.
276+ if ( this . options . noglobstar ) {
277+ // ** is * anyway
278+ this . globParts = rawGlobParts ;
279+ }
280+ else {
281+ // do this swap BEFORE the reduce, so that we can turn a string
282+ // of **/*/**/* into */*/**/** and then reduce the **'s into one
283+ for ( const parts of rawGlobParts ) {
284+ let swapped ;
285+ do {
286+ swapped = false ;
287+ for ( let i = 0 ; i < parts . length - 1 ; i ++ ) {
288+ if ( parts [ i ] === '*' && parts [ i - 1 ] === '**' ) {
289+ parts [ i ] = '**' ;
290+ parts [ i - 1 ] = '*' ;
291+ swapped = true ;
292+ }
293+ }
294+ } while ( swapped ) ;
295+ }
296+ this . globParts = rawGlobParts . map ( parts => {
297+ parts = parts . reduce ( ( set , part ) => {
298+ const prev = set [ set . length - 1 ] ;
299+ if ( part === '**' && prev === '**' ) {
300+ return set ;
301+ }
302+ if ( part === '..' ) {
303+ if ( prev && prev !== '..' && prev !== '.' && prev !== '**' ) {
304+ set . pop ( ) ;
305+ return set ;
306+ }
307+ }
220308 set . push ( part ) ;
221- }
222- return set ;
223- } , [ ] ) ) ;
309+ return set ;
310+ } , [ ] ) ;
311+ return parts . length === 0 ? [ '' ] : parts ;
312+ } ) ;
313+ }
224314 this . debug ( this . pattern , this . globParts ) ;
225315 // glob --> regexps
226316 let set = this . globParts . map ( ( s , _ , __ ) => s . map ( ss => this . parse ( ss ) ) ) ;
@@ -458,6 +548,39 @@ class Minimatch {
458548 }
459549 if ( pattern === '' )
460550 return '' ;
551+ // far and away, the most common glob pattern parts are
552+ // *, *.*, and *.<ext> Add a fast check method for those.
553+ let m ;
554+ let fastTest = null ;
555+ if ( isSub !== SUBPARSE ) {
556+ if ( ( m = pattern . match ( starRE ) ) ) {
557+ fastTest = options . dot ? starTestDot : starTest ;
558+ }
559+ else if ( ( m = pattern . match ( starDotExtRE ) ) ) {
560+ fastTest = ( options . nocase
561+ ? options . dot
562+ ? starDotExtTestNocaseDot
563+ : starDotExtTestNocase
564+ : options . dot
565+ ? starDotExtTestDot
566+ : starDotExtTest ) ( m [ 1 ] ) ;
567+ }
568+ else if ( ( m = pattern . match ( qmarksRE ) ) ) {
569+ fastTest = ( options . nocase
570+ ? options . dot
571+ ? qmarksTestNocaseDot
572+ : qmarksTestNocase
573+ : options . dot
574+ ? qmarksTestDot
575+ : qmarksTest ) ( m ) ;
576+ }
577+ else if ( ( m = pattern . match ( starDotStarRE ) ) ) {
578+ fastTest = options . dot ? starDotStarTestDot : starDotStarTest ;
579+ }
580+ else if ( ( m = pattern . match ( dotStarRE ) ) ) {
581+ fastTest = dotStarTest ;
582+ }
583+ }
461584 let re = '' ;
462585 let hasMagic = false ;
463586 let escaping = false ;
@@ -776,7 +899,7 @@ class Minimatch {
776899 return [ re , hasMagic ] ;
777900 }
778901 // if it's nocase, and the lcase/uppercase don't match, it's magic
779- if ( options . nocase && ! hasMagic ) {
902+ if ( options . nocase && ! hasMagic && ! options . nocaseMagicOnly ) {
780903 hasMagic = pattern . toUpperCase ( ) !== pattern . toLowerCase ( ) ;
781904 }
782905 // skip the regexp for non-magical patterns
@@ -787,10 +910,17 @@ class Minimatch {
787910 }
788911 const flags = options . nocase ? 'i' : '' ;
789912 try {
790- return Object . assign ( new RegExp ( '^' + re + '$' , flags ) , {
791- _glob : pattern ,
792- _src : re ,
793- } ) ;
913+ const ext = fastTest
914+ ? {
915+ _glob : pattern ,
916+ _src : re ,
917+ test : fastTest ,
918+ }
919+ : {
920+ _glob : pattern ,
921+ _src : re ,
922+ } ;
923+ return Object . assign ( new RegExp ( '^' + re + '$' , flags ) , ext ) ;
794924 /* c8 ignore start */
795925 }
796926 catch ( er ) {
0 commit comments