@@ -157,7 +157,9 @@ minimatch.match = (list, pattern, options = {}) => {
157
157
158
158
// replace stuff like \* with *
159
159
const globUnescape = s => s . replace ( / \\ ( .) / g, '$1' )
160
+ const charUnescape = s => s . replace ( / \\ ( [ ^ - \] ] ) / g, '$1' )
160
161
const regExpEscape = s => s . replace ( / [ - [ \] { } ( ) * + ? . , \\ ^ $ | # \s ] / g, '\\$&' )
162
+ const braExpEscape = s => s . replace ( / [ [ \] \\ ] / g, '\\$&' )
161
163
162
164
class Minimatch {
163
165
constructor ( pattern , options ) {
@@ -425,7 +427,7 @@ class Minimatch {
425
427
if ( pattern === '' ) return ''
426
428
427
429
let re = ''
428
- let hasMagic = ! ! options . nocase
430
+ let hasMagic = false
429
431
let escaping = false
430
432
// ? => one single character
431
433
const patternListStack = [ ]
@@ -438,11 +440,23 @@ class Minimatch {
438
440
let pl
439
441
let sp
440
442
// . and .. never match anything that doesn't start with .,
441
- // even when options.dot is set.
442
- const patternStart = pattern . charAt ( 0 ) === '.' ? '' // anything
443
- // not (start or / followed by . or .. followed by / or end)
444
- : options . dot ? '(?!(?:^|\\\/)\\.{1,2}(?:$|\\\/))'
445
- : '(?!\\.)'
443
+ // even when options.dot is set. However, if the pattern
444
+ // starts with ., then traversal patterns can match.
445
+ let dotTravAllowed = pattern . charAt ( 0 ) === '.'
446
+ let dotFileAllowed = options . dot || dotTravAllowed
447
+ const patternStart = ( ) =>
448
+ dotTravAllowed
449
+ ? ''
450
+ : dotFileAllowed
451
+ ? '(?!(?:^|\\/)\\.{1,2}(?:$|\\/))'
452
+ : '(?!\\.)'
453
+ const subPatternStart = ( p ) =>
454
+ p . charAt ( 0 ) === '.'
455
+ ? ''
456
+ : options . dot
457
+ ? '(?!(?:^|\\/)\\.{1,2}(?:$|\\/))'
458
+ : '(?!\\.)'
459
+
446
460
447
461
const clearStateChar = ( ) => {
448
462
if ( stateChar ) {
@@ -492,6 +506,11 @@ class Minimatch {
492
506
}
493
507
494
508
case '\\' :
509
+ if ( inClass && pattern . charAt ( i + 1 ) === '-' ) {
510
+ re += c
511
+ continue
512
+ }
513
+
495
514
clearStateChar ( )
496
515
escaping = true
497
516
continue
@@ -526,7 +545,7 @@ class Minimatch {
526
545
if ( options . noext ) clearStateChar ( )
527
546
continue
528
547
529
- case '(' :
548
+ case '(' : {
530
549
if ( inClass ) {
531
550
re += '('
532
551
continue
@@ -537,46 +556,64 @@ class Minimatch {
537
556
continue
538
557
}
539
558
540
- patternListStack . push ( {
559
+ const plEntry = {
541
560
type : stateChar ,
542
561
start : i - 1 ,
543
562
reStart : re . length ,
544
563
open : plTypes [ stateChar ] . open ,
545
- close : plTypes [ stateChar ] . close
546
- } )
547
- // negation is (?:(?!js)[^/]*)
548
- re += stateChar === '!' ? '(?:(?!(?:' : '(?:'
564
+ close : plTypes [ stateChar ] . close ,
565
+ }
566
+ this . debug ( this . pattern , '\t' , plEntry )
567
+ patternListStack . push ( plEntry )
568
+ // negation is (?:(?!(?:js)(?:<rest>))[^/]*)
569
+ re += plEntry . open
570
+ // next entry starts with a dot maybe?
571
+ if ( plEntry . start === 0 && plEntry . type !== '!' ) {
572
+ dotTravAllowed = true
573
+ re += subPatternStart ( pattern . slice ( i + 1 ) )
574
+ }
549
575
this . debug ( 'plType %j %j' , stateChar , re )
550
576
stateChar = false
551
- continue
577
+ continue
578
+ }
552
579
553
- case ')' :
554
- if ( inClass || ! patternListStack . length ) {
580
+ case ')' : {
581
+ const plEntry = patternListStack [ patternListStack . length - 1 ]
582
+ if ( inClass || ! plEntry ) {
555
583
re += '\\)'
556
584
continue
557
585
}
586
+ patternListStack . pop ( )
558
587
588
+ // closing an extglob
559
589
clearStateChar ( )
560
590
hasMagic = true
561
- pl = patternListStack . pop ( )
591
+ pl = plEntry
562
592
// negation is (?:(?!js)[^/]*)
563
593
// The others are (?:<pattern>)<type>
564
594
re += pl . close
565
595
if ( pl . type === '!' ) {
566
- negativeLists . push ( pl )
596
+ negativeLists . push ( Object . assign ( pl , { reEnd : re . length } ) )
567
597
}
568
- pl . reEnd = re . length
569
- continue
598
+ continue
599
+ }
570
600
571
- case '|' :
572
- if ( inClass || ! patternListStack . length ) {
601
+ case '|' : {
602
+ const plEntry = patternListStack [ patternListStack . length - 1 ]
603
+ if ( inClass || ! plEntry ) {
573
604
re += '\\|'
574
605
continue
575
606
}
576
607
577
608
clearStateChar ( )
578
609
re += '|'
579
- continue
610
+ // next subpattern can start with a dot?
611
+ if ( plEntry . start === 0 && plEntry . type !== '!' ) {
612
+ dotTravAllowed = true
613
+ re += subPatternStart ( pattern . slice ( i + 1 ) )
614
+ }
615
+ continue
616
+ }
580
617
581
618
// these are mostly the same in regexp and glob
582
619
case '[' :
@@ -604,8 +641,6 @@ class Minimatch {
604
641
continue
605
642
}
606
643
607
- // handle the case where we left a class open.
608
- // "[z-a]" is valid, equivalent to "\[z-a\]"
609
644
// split where the last [ was, make sure we don't have
610
645
// an invalid re. if so, re-walk the contents of the
611
646
// would-be class to re-translate any characters that
@@ -615,20 +650,16 @@ class Minimatch {
615
650
// to do safely. For now, this is safe and works.
616
651
cs = pattern . substring ( classStart + 1 , i )
617
652
try {
618
- RegExp ( '[' + cs + ']' )
653
+ RegExp ( '[' + braExpEscape ( charUnescape ( cs ) ) + ']' )
654
+ // looks good, finish up the class.
655
+ re += c
619
656
} catch ( er ) {
620
- // not a valid class!
621
- sp = this . parse ( cs , SUBPARSE )
622
- re = re . substring ( 0 , reClassStart ) + '\\[' + sp [ 0 ] + '\\]'
623
- hasMagic = hasMagic || sp [ 1 ]
624
- inClass = false
625
- continue
657
+ // out of order ranges in JS are errors, but in glob syntax,
658
+ // they're just a range that matches nothing.
659
+ re = re . substring ( 0 , reClassStart ) + '(?:$.)' // match nothing ever
626
660
}
627
-
628
- // finish up the class.
629
661
hasMagic = true
630
662
inClass = false
631
- re += c
632
663
continue
633
664
634
665
default :
@@ -721,14 +752,16 @@ class Minimatch {
721
752
// Handle nested stuff like *(*.js|!(*.json)), where open parens
722
753
// mean that we should *not* include the ) in the bit that is considered
723
754
// "after" the negated section.
724
- const openParensBefore = nlBefore . split ( '(' ) . length - 1
755
+ const closeParensBefore = nlBefore . split ( ')' ) . length
756
+ const openParensBefore = nlBefore . split ( '(' ) . length - closeParensBefore
725
757
let cleanAfter = nlAfter
726
758
for ( let i = 0 ; i < openParensBefore ; i ++ ) {
727
759
cleanAfter = cleanAfter . replace ( / \) [ + * ? ] ? / , '' )
728
760
}
729
761
nlAfter = cleanAfter
730
762
731
- const dollar = nlAfter === '' && isSub !== SUBPARSE ? '$' : ''
763
+ const dollar = nlAfter === '' && isSub !== SUBPARSE ? '(?:$|\\/)' : ''
764
+
732
765
re = nlBefore + nlFirst + nlAfter + dollar + nlLast
733
766
}
734
767
@@ -740,14 +773,19 @@ class Minimatch {
740
773
}
741
774
742
775
if ( addPatternStart ) {
743
- re = patternStart + re
776
+ re = patternStart ( ) + re
744
777
}
745
778
746
779
// parsing just a piece of a larger pattern.
747
780
if ( isSub === SUBPARSE ) {
748
781
return [ re , hasMagic ]
749
782
}
750
783
784
+ // if it's nocase, and the lcase/uppercase don't match, it's magic
785
+ if ( options . nocase && ! hasMagic ) {
786
+ hasMagic = pattern . toUpperCase ( ) !== pattern . toLowerCase ( )
787
+ }
788
+
751
789
// skip the regexp for non-magical patterns
752
790
// unescape anything in it, though, so that it'll be
753
791
// an exact match against a file etc.
0 commit comments