From fd4d23d583dc521151697677f07b65a3b01acb07 Mon Sep 17 00:00:00 2001 From: isaacs Date: Sun, 15 Jan 2023 15:03:59 -0800 Subject: [PATCH] filter out consecutive ** more efficiently Previously, consecutive globstars were being properly stripped out of makeRe, but leaving them in there in the normal minimatch set geometrically increases the amount of work that has to be done when glob gets a pattern like 'a/**/**/b'. Better to strip them out from the initial pattern, and never have to worry about it again. --- src/index.ts | 53 +++++++++++++++++----------------- test/consecutive-glob-stars.js | 15 ++++++++++ 2 files changed, 42 insertions(+), 26 deletions(-) create mode 100644 test/consecutive-glob-stars.js diff --git a/src/index.ts b/src/index.ts index 85826622..f9d822f9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -309,12 +309,24 @@ export class Minimatch { // These will be regexps, except in the case of "**", which is // set to the GLOBSTAR object for globstar behavior, // and will not contain any / characters - const globParts = (this.globParts = globSet.map(s => s.split(slashSplit))) + const rawGlobParts = globSet.map(s => s.split(slashSplit)) + + // consecutive globstars are an unncessary perf killer + this.globParts = this.options.noglobstar + ? rawGlobParts + : rawGlobParts.map(parts => + parts.reduce((set: string[], part) => { + if (part !== '**' || set[set.length - 1] !== '**') { + set.push(part) + } + return set + }, []) + ) - this.debug(this.pattern, globParts) + this.debug(this.pattern, this.globParts) // glob --> regexps - let set = globParts.map((s, _, __) => s.map(ss => this.parse(ss))) + let set = this.globParts.map((s, _, __) => s.map(ss => this.parse(ss))) this.debug(this.pattern, set) @@ -921,32 +933,21 @@ export class Minimatch { : twoStarNoDot const flags = options.nocase ? 'i' : '' - // coalesce globstars and regexpify non-globstar patterns - // if it's the only item, then we just do one twoStar - // if it's the first, and there are more, prepend (\/|twoStar\/)? to next - // if it's the last, append (\/twoStar|) to previous - // if it's in the middle, append (\/|\/twoStar\/) to previous + // regexpify non-globstar patterns + // if ** is only item, then we just do one twoStar + // if ** is first, and there are more, prepend (\/|twoStar\/)? to next + // if ** is last, append (\/twoStar|) to previous + // if ** is in the middle, append (\/|\/twoStar\/) to previous // then filter out GLOBSTAR symbols let re = set .map(pattern => { - const pp: (string | typeof GLOBSTAR)[] = pattern - .map(p => - typeof p === 'string' - ? regExpEscape(p) - : p === GLOBSTAR - ? GLOBSTAR - : p._src - ) - .reduce((set: (string | typeof GLOBSTAR)[], p) => { - if ( - (set[set.length - 1] === GLOBSTAR && p === GLOBSTAR) || - p === undefined - ) { - return set - } - set.push(p) - return set - }, []) + const pp: (string | typeof GLOBSTAR)[] = pattern.map(p => + typeof p === 'string' + ? regExpEscape(p) + : p === GLOBSTAR + ? GLOBSTAR + : p._src + ) as (string | typeof GLOBSTAR)[] pp.forEach((p, i) => { const next = pp[i + 1] const prev = pp[i - 1] diff --git a/test/consecutive-glob-stars.js b/test/consecutive-glob-stars.js new file mode 100644 index 00000000..cd5555aa --- /dev/null +++ b/test/consecutive-glob-stars.js @@ -0,0 +1,15 @@ +const t = require('tap') +const {Minimatch, GLOBSTAR} = require('../') + +const patterns = [] +for (const a of ['**', '**/**', '**/**/**']) { + for (const b of['**', '**/**', '**/**/**']) { + for (const c of ['**', '**/**', '**/**/**']) { + patterns.push(`x/${a}/y/${b}/z/${c}`) + } + } +} + +for (const m of patterns.map(p => new Minimatch(p))) { + t.same(m.globParts, [['x', '**', 'y', '**', 'z', '**']]) +}