@@ -14,6 +14,57 @@ const minimatch = (p, pattern, options = {}) => {
14
14
} ;
15
15
exports . minimatch = minimatch ;
16
16
exports . 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
+ } ;
17
68
/* c8 ignore start */
18
69
const platform = typeof process === 'object' && process
19
70
? ( typeof process . env === 'object' &&
@@ -213,14 +264,53 @@ class Minimatch {
213
264
// and will not contain any / characters
214
265
const rawGlobParts = this . globSet . map ( s => this . slashSplit ( s ) ) ;
215
266
// 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
+ }
220
308
set . push ( part ) ;
221
- }
222
- return set ;
223
- } , [ ] ) ) ;
309
+ return set ;
310
+ } , [ ] ) ;
311
+ return parts . length === 0 ? [ '' ] : parts ;
312
+ } ) ;
313
+ }
224
314
this . debug ( this . pattern , this . globParts ) ;
225
315
// glob --> regexps
226
316
let set = this . globParts . map ( ( s , _ , __ ) => s . map ( ss => this . parse ( ss ) ) ) ;
@@ -458,6 +548,39 @@ class Minimatch {
458
548
}
459
549
if ( pattern === '' )
460
550
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
+ }
461
584
let re = '' ;
462
585
let hasMagic = false ;
463
586
let escaping = false ;
@@ -776,7 +899,7 @@ class Minimatch {
776
899
return [ re , hasMagic ] ;
777
900
}
778
901
// 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 ) {
780
903
hasMagic = pattern . toUpperCase ( ) !== pattern . toLowerCase ( ) ;
781
904
}
782
905
// skip the regexp for non-magical patterns
@@ -787,10 +910,17 @@ class Minimatch {
787
910
}
788
911
const flags = options . nocase ? 'i' : '' ;
789
912
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 ) ;
794
924
/* c8 ignore start */
795
925
}
796
926
catch ( er ) {
0 commit comments