minimatch 8.0.5 → 8.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cjs/index.js CHANGED
@@ -206,11 +206,13 @@ class Minimatch {
206
206
  isWindows;
207
207
  platform;
208
208
  windowsNoMagicRoot;
209
+ maxGlobstarRecursion;
209
210
  regexp;
210
211
  constructor(pattern, options = {}) {
211
212
  (0, assert_valid_pattern_js_1.assertValidPattern)(pattern);
212
213
  options = options || {};
213
214
  this.options = options;
215
+ this.maxGlobstarRecursion = options.maxGlobstarRecursion ?? 200;
214
216
  this.pattern = pattern;
215
217
  this.platform = options.platform || defaultPlatform;
216
218
  this.isWindows = this.platform === 'win32';
@@ -608,7 +610,8 @@ class Minimatch {
608
610
  // out of pattern, then that's fine, as long as all
609
611
  // the parts match.
610
612
  matchOne(file, pattern, partial = false) {
611
- const options = this.options;
613
+ let fileStartIndex = 0;
614
+ let patternStartIndex = 0;
612
615
  // a UNC pattern like //?/c:/* can match a path like c:/x
613
616
  // and vice versa
614
617
  if (this.isWindows) {
@@ -627,6 +630,8 @@ class Minimatch {
627
630
  const pd = pattern[3];
628
631
  if (fd.toLowerCase() === pd.toLowerCase()) {
629
632
  file[3] = pd;
633
+ fileStartIndex = 3;
634
+ patternStartIndex = 3;
630
635
  }
631
636
  }
632
637
  else if (patternUNC && typeof file[0] === 'string') {
@@ -634,14 +639,14 @@ class Minimatch {
634
639
  const fd = file[0];
635
640
  if (pd.toLowerCase() === fd.toLowerCase()) {
636
641
  pattern[3] = fd;
637
- pattern = pattern.slice(3);
642
+ patternStartIndex = 3;
638
643
  }
639
644
  }
640
645
  else if (fileUNC && typeof pattern[0] === 'string') {
641
646
  const fd = file[3];
642
647
  if (fd.toLowerCase() === pattern[0].toLowerCase()) {
643
648
  pattern[0] = fd;
644
- file = file.slice(3);
649
+ fileStartIndex = 3;
645
650
  }
646
651
  }
647
652
  }
@@ -651,102 +656,123 @@ class Minimatch {
651
656
  if (optimizationLevel >= 2) {
652
657
  file = this.levelTwoFileOptimize(file);
653
658
  }
654
- this.debug('matchOne', this, { file, pattern });
655
- this.debug('matchOne', file.length, pattern.length);
656
- for (var fi = 0, pi = 0, fl = file.length, pl = pattern.length; fi < fl && pi < pl; fi++, pi++) {
657
- this.debug('matchOne loop');
658
- var p = pattern[pi];
659
- var f = file[fi];
660
- this.debug(pattern, p, f);
661
- // should be impossible.
662
- // some invalid regexp stuff in the set.
663
- /* c8 ignore start */
664
- if (p === false) {
659
+ if (pattern.includes(exports.GLOBSTAR)) {
660
+ return this.#matchGlobstar(file, pattern, partial, fileStartIndex, patternStartIndex);
661
+ }
662
+ return this.#matchOne(file, pattern, partial, fileStartIndex, patternStartIndex);
663
+ }
664
+ #matchGlobstar(file, pattern, partial, fileIndex, patternIndex) {
665
+ const firstgs = pattern.indexOf(exports.GLOBSTAR, patternIndex);
666
+ const lastgs = pattern.lastIndexOf(exports.GLOBSTAR);
667
+ const [head, body, tail] = [
668
+ pattern.slice(patternIndex, firstgs),
669
+ pattern.slice(firstgs + 1, lastgs),
670
+ pattern.slice(lastgs + 1),
671
+ ];
672
+ if (head.length) {
673
+ const fileHead = file.slice(fileIndex, fileIndex + head.length);
674
+ if (!this.#matchOne(fileHead, head, partial, 0, 0))
675
+ return false;
676
+ fileIndex += head.length;
677
+ }
678
+ let fileTailMatch = 0;
679
+ if (tail.length) {
680
+ if (tail.length + fileIndex > file.length)
665
681
  return false;
682
+ let tailStart = file.length - tail.length;
683
+ if (this.#matchOne(file, tail, partial, tailStart, 0)) {
684
+ fileTailMatch = tail.length;
666
685
  }
667
- /* c8 ignore stop */
668
- if (p === exports.GLOBSTAR) {
669
- this.debug('GLOBSTAR', [pattern, p, f]);
670
- // "**"
671
- // a/**/b/**/c would match the following:
672
- // a/b/x/y/z/c
673
- // a/x/y/z/b/c
674
- // a/b/x/b/x/c
675
- // a/b/c
676
- // To do this, take the rest of the pattern after
677
- // the **, and see if it would match the file remainder.
678
- // If so, return success.
679
- // If not, the ** "swallows" a segment, and try again.
680
- // This is recursively awful.
681
- //
682
- // a/**/b/**/c matching a/b/x/y/z/c
683
- // - a matches a
684
- // - doublestar
685
- // - matchOne(b/x/y/z/c, b/**/c)
686
- // - b matches b
687
- // - doublestar
688
- // - matchOne(x/y/z/c, c) -> no
689
- // - matchOne(y/z/c, c) -> no
690
- // - matchOne(z/c, c) -> no
691
- // - matchOne(c, c) yes, hit
692
- var fr = fi;
693
- var pr = pi + 1;
694
- if (pr === pl) {
695
- this.debug('** at the end');
696
- // a ** at the end will just swallow the rest.
697
- // We have found a match.
698
- // however, it will not swallow /.x, unless
699
- // options.dot is set.
700
- // . and .. are *never* matched by **, for explosively
701
- // exponential reasons.
702
- for (; fi < fl; fi++) {
703
- if (file[fi] === '.' ||
704
- file[fi] === '..' ||
705
- (!options.dot && file[fi].charAt(0) === '.'))
706
- return false;
707
- }
708
- return true;
686
+ else {
687
+ if (file[file.length - 1] !== '' ||
688
+ fileIndex + tail.length === file.length) {
689
+ return false;
709
690
  }
710
- // ok, let's see if we can swallow whatever we can.
711
- while (fr < fl) {
712
- var swallowee = file[fr];
713
- this.debug('\nglobstar while', file, fr, pattern, pr, swallowee);
714
- // XXX remove this slice. Just pass the start index.
715
- if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) {
716
- this.debug('globstar found match!', fr, fl, swallowee);
717
- // found a match.
718
- return true;
719
- }
720
- else {
721
- // can't swallow "." or ".." ever.
722
- // can only swallow ".foo" when explicitly asked.
723
- if (swallowee === '.' ||
724
- swallowee === '..' ||
725
- (!options.dot && swallowee.charAt(0) === '.')) {
726
- this.debug('dot detected!', file, fr, pattern, pr);
727
- break;
728
- }
729
- // ** swallows a segment, and continue.
730
- this.debug('globstar swallow a segment, and continue');
731
- fr++;
732
- }
691
+ tailStart--;
692
+ if (!this.#matchOne(file, tail, partial, tailStart, 0))
693
+ return false;
694
+ fileTailMatch = tail.length + 1;
695
+ }
696
+ }
697
+ if (!body.length) {
698
+ let sawSome = !!fileTailMatch;
699
+ for (let i = fileIndex; i < file.length - fileTailMatch; i++) {
700
+ const f = String(file[i]);
701
+ sawSome = true;
702
+ if (f === '.' || f === '..' ||
703
+ (!this.options.dot && f.startsWith('.'))) {
704
+ return false;
733
705
  }
734
- // no match was found.
735
- // However, in partial mode, we can't say this is necessarily over.
736
- /* c8 ignore start */
737
- if (partial) {
738
- // ran out of file
739
- this.debug('\n>>> no match, partial?', file, fr, pattern, pr);
740
- if (fr === fl) {
741
- return true;
742
- }
706
+ }
707
+ return sawSome;
708
+ }
709
+ const bodySegments = [[[], 0]];
710
+ let currentBody = bodySegments[0];
711
+ let nonGsParts = 0;
712
+ const nonGsPartsSums = [0];
713
+ for (const b of body) {
714
+ if (b === exports.GLOBSTAR) {
715
+ nonGsPartsSums.push(nonGsParts);
716
+ currentBody = [[], 0];
717
+ bodySegments.push(currentBody);
718
+ }
719
+ else {
720
+ currentBody[0].push(b);
721
+ nonGsParts++;
722
+ }
723
+ }
724
+ let i = bodySegments.length - 1;
725
+ const fileLength = file.length - fileTailMatch;
726
+ for (const b of bodySegments) {
727
+ b[1] = fileLength - (nonGsPartsSums[i--] + b[0].length);
728
+ }
729
+ return !!this.#matchGlobStarBodySections(file, bodySegments, fileIndex, 0, partial, 0, !!fileTailMatch);
730
+ }
731
+ #matchGlobStarBodySections(file, bodySegments, fileIndex, bodyIndex, partial, globStarDepth, sawTail) {
732
+ const bs = bodySegments[bodyIndex];
733
+ if (!bs) {
734
+ for (let i = fileIndex; i < file.length; i++) {
735
+ sawTail = true;
736
+ const f = file[i];
737
+ if (f === '.' || f === '..' ||
738
+ (!this.options.dot && f.startsWith('.'))) {
739
+ return false;
743
740
  }
744
- /* c8 ignore stop */
741
+ }
742
+ return sawTail;
743
+ }
744
+ const [body, after] = bs;
745
+ while (fileIndex <= after) {
746
+ const m = this.#matchOne(file.slice(0, fileIndex + body.length), body, partial, fileIndex, 0);
747
+ if (m && globStarDepth < this.maxGlobstarRecursion) {
748
+ const sub = this.#matchGlobStarBodySections(file, bodySegments, fileIndex + body.length, bodyIndex + 1, partial, globStarDepth + 1, sawTail);
749
+ if (sub !== false)
750
+ return sub;
751
+ }
752
+ const f = file[fileIndex];
753
+ if (f === '.' || f === '..' ||
754
+ (!this.options.dot && f.startsWith('.'))) {
745
755
  return false;
746
756
  }
747
- // something other than **
748
- // non-magic patterns just have to match exactly
749
- // patterns with magic have been turned into regexps.
757
+ fileIndex++;
758
+ }
759
+ return null;
760
+ }
761
+ #matchOne(file, pattern, partial, fileIndex, patternIndex) {
762
+ let fi;
763
+ let pi;
764
+ let pl;
765
+ let fl;
766
+ for (fi = fileIndex, pi = patternIndex,
767
+ fl = file.length, pl = pattern.length; fi < fl && pi < pl; fi++, pi++) {
768
+ this.debug('matchOne loop');
769
+ let p = pattern[pi];
770
+ let f = file[fi];
771
+ this.debug(pattern, p, f);
772
+ /* c8 ignore start */
773
+ if (p === false || p === exports.GLOBSTAR)
774
+ return false;
775
+ /* c8 ignore stop */
750
776
  let hit;
751
777
  if (typeof p === 'string') {
752
778
  hit = f === p;
@@ -759,38 +785,17 @@ class Minimatch {
759
785
  if (!hit)
760
786
  return false;
761
787
  }
762
- // Note: ending in / means that we'll get a final ""
763
- // at the end of the pattern. This can only match a
764
- // corresponding "" at the end of the file.
765
- // If the file ends in /, then it can only match a
766
- // a pattern that ends in /, unless the pattern just
767
- // doesn't have any more for it. But, a/b/ should *not*
768
- // match "a/b/*", even though "" matches against the
769
- // [^/]*? pattern, except in partial mode, where it might
770
- // simply not be reached yet.
771
- // However, a/b/ should still satisfy a/*
772
- // now either we fell off the end of the pattern, or we're done.
773
788
  if (fi === fl && pi === pl) {
774
- // ran out of pattern and filename at the same time.
775
- // an exact hit!
776
789
  return true;
777
790
  }
778
791
  else if (fi === fl) {
779
- // ran out of file, but still had pattern left.
780
- // this is ok if we're doing the match as part of
781
- // a glob fs traversal.
782
792
  return partial;
783
793
  }
784
794
  else if (pi === pl) {
785
- // ran out of pattern, still have file left.
786
- // this is only acceptable if we're on the very last
787
- // empty segment of a file with a trailing slash.
788
- // a/* should match a/b/
789
795
  return fi === fl - 1 && file[fi] === '';
790
796
  /* c8 ignore start */
791
797
  }
792
798
  else {
793
- // should be unreachable.
794
799
  throw new Error('wtf?');
795
800
  }
796
801
  /* c8 ignore stop */