minimatch 8.0.5 → 8.0.7

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,127 @@ 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] = partial ? [
668
+ pattern.slice(patternIndex, firstgs),
669
+ pattern.slice(firstgs + 1),
670
+ [],
671
+ ] : [
672
+ pattern.slice(patternIndex, firstgs),
673
+ pattern.slice(firstgs + 1, lastgs),
674
+ pattern.slice(lastgs + 1),
675
+ ];
676
+ if (head.length) {
677
+ const fileHead = file.slice(fileIndex, fileIndex + head.length);
678
+ if (!this.#matchOne(fileHead, head, partial, 0, 0))
665
679
  return false;
680
+ fileIndex += head.length;
681
+ }
682
+ let fileTailMatch = 0;
683
+ if (tail.length) {
684
+ if (tail.length + fileIndex > file.length)
685
+ return false;
686
+ let tailStart = file.length - tail.length;
687
+ if (this.#matchOne(file, tail, partial, tailStart, 0)) {
688
+ fileTailMatch = tail.length;
666
689
  }
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;
690
+ else {
691
+ if (file[file.length - 1] !== '' ||
692
+ fileIndex + tail.length === file.length) {
693
+ return false;
709
694
  }
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
- }
695
+ tailStart--;
696
+ if (!this.#matchOne(file, tail, partial, tailStart, 0))
697
+ return false;
698
+ fileTailMatch = tail.length + 1;
699
+ }
700
+ }
701
+ if (!body.length) {
702
+ let sawSome = !!fileTailMatch;
703
+ for (let i = fileIndex; i < file.length - fileTailMatch; i++) {
704
+ const f = String(file[i]);
705
+ sawSome = true;
706
+ if (f === '.' || f === '..' ||
707
+ (!this.options.dot && f.startsWith('.'))) {
708
+ return false;
733
709
  }
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
- }
710
+ }
711
+ return partial || sawSome;
712
+ }
713
+ const bodySegments = [[[], 0]];
714
+ let currentBody = bodySegments[0];
715
+ let nonGsParts = 0;
716
+ const nonGsPartsSums = [0];
717
+ for (const b of body) {
718
+ if (b === exports.GLOBSTAR) {
719
+ nonGsPartsSums.push(nonGsParts);
720
+ currentBody = [[], 0];
721
+ bodySegments.push(currentBody);
722
+ }
723
+ else {
724
+ currentBody[0].push(b);
725
+ nonGsParts++;
726
+ }
727
+ }
728
+ let i = bodySegments.length - 1;
729
+ const fileLength = file.length - fileTailMatch;
730
+ for (const b of bodySegments) {
731
+ b[1] = fileLength - (nonGsPartsSums[i--] + b[0].length);
732
+ }
733
+ return !!this.#matchGlobStarBodySections(file, bodySegments, fileIndex, 0, partial, 0, !!fileTailMatch);
734
+ }
735
+ #matchGlobStarBodySections(file, bodySegments, fileIndex, bodyIndex, partial, globStarDepth, sawTail) {
736
+ const bs = bodySegments[bodyIndex];
737
+ if (!bs) {
738
+ for (let i = fileIndex; i < file.length; i++) {
739
+ sawTail = true;
740
+ const f = file[i];
741
+ if (f === '.' || f === '..' ||
742
+ (!this.options.dot && f.startsWith('.'))) {
743
+ return false;
743
744
  }
744
- /* c8 ignore stop */
745
+ }
746
+ return sawTail;
747
+ }
748
+ const [body, after] = bs;
749
+ while (fileIndex <= after) {
750
+ const m = this.#matchOne(file.slice(0, fileIndex + body.length), body, partial, fileIndex, 0);
751
+ if (m && globStarDepth < this.maxGlobstarRecursion) {
752
+ const sub = this.#matchGlobStarBodySections(file, bodySegments, fileIndex + body.length, bodyIndex + 1, partial, globStarDepth + 1, sawTail);
753
+ if (sub !== false)
754
+ return sub;
755
+ }
756
+ const f = file[fileIndex];
757
+ if (f === '.' || f === '..' ||
758
+ (!this.options.dot && f.startsWith('.'))) {
745
759
  return false;
746
760
  }
747
- // something other than **
748
- // non-magic patterns just have to match exactly
749
- // patterns with magic have been turned into regexps.
761
+ fileIndex++;
762
+ }
763
+ return partial || null;
764
+ }
765
+ #matchOne(file, pattern, partial, fileIndex, patternIndex) {
766
+ let fi;
767
+ let pi;
768
+ let pl;
769
+ let fl;
770
+ for (fi = fileIndex, pi = patternIndex,
771
+ fl = file.length, pl = pattern.length; fi < fl && pi < pl; fi++, pi++) {
772
+ this.debug('matchOne loop');
773
+ let p = pattern[pi];
774
+ let f = file[fi];
775
+ this.debug(pattern, p, f);
776
+ /* c8 ignore start */
777
+ if (p === false || p === exports.GLOBSTAR)
778
+ return false;
779
+ /* c8 ignore stop */
750
780
  let hit;
751
781
  if (typeof p === 'string') {
752
782
  hit = f === p;
@@ -759,38 +789,17 @@ class Minimatch {
759
789
  if (!hit)
760
790
  return false;
761
791
  }
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
792
  if (fi === fl && pi === pl) {
774
- // ran out of pattern and filename at the same time.
775
- // an exact hit!
776
793
  return true;
777
794
  }
778
795
  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
796
  return partial;
783
797
  }
784
798
  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
799
  return fi === fl - 1 && file[fi] === '';
790
800
  /* c8 ignore start */
791
801
  }
792
802
  else {
793
- // should be unreachable.
794
803
  throw new Error('wtf?');
795
804
  }
796
805
  /* c8 ignore stop */