Skip to content

Commit

Permalink
filter out consecutive ** more efficiently
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
isaacs committed Jan 15, 2023
1 parent de4052a commit fd4d23d
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 26 deletions.
53 changes: 27 additions & 26 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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]
Expand Down
15 changes: 15 additions & 0 deletions test/consecutive-glob-stars.js
Original file line number Diff line number Diff line change
@@ -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', '**']])
}

0 comments on commit fd4d23d

Please # to comment.