minimatch 9.0.6 → 9.0.8

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