diff --git a/minimatch.js b/minimatch.js index 772ddb81..6c8bfc35 100644 --- a/minimatch.js +++ b/minimatch.js @@ -440,11 +440,23 @@ class Minimatch { let pl let sp // . and .. never match anything that doesn't start with ., - // even when options.dot is set. - const patternStart = pattern.charAt(0) === '.' ? '' // anything - // not (start or / followed by . or .. followed by / or end) - : options.dot ? '(?!(?:^|\\\/)\\.{1,2}(?:$|\\\/))' - : '(?!\\.)' + // even when options.dot is set. However, if the pattern + // starts with ., then traversal patterns can match. + let dotTravAllowed = pattern.charAt(0) === '.' + let dotFileAllowed = options.dot || dotTravAllowed + const patternStart = () => + dotTravAllowed + ? '' + : dotFileAllowed + ? '(?!(?:^|\\/)\\.{1,2}(?:$|\\/))' + : '(?!\\.)' + const subPatternStart = (p) => + p.charAt(0) === '.' + ? '' + : options.dot + ? '(?!(?:^|\\/)\\.{1,2}(?:$|\\/))' + : '(?!\\.)' + const clearStateChar = () => { if (stateChar) { @@ -533,7 +545,7 @@ class Minimatch { if (options.noext) clearStateChar() continue - case '(': + case '(': { if (inClass) { re += '(' continue @@ -544,46 +556,64 @@ class Minimatch { continue } - patternListStack.push({ + const plEntry = { type: stateChar, start: i - 1, reStart: re.length, open: plTypes[stateChar].open, - close: plTypes[stateChar].close - }) - // negation is (?:(?!js)[^/]*) - re += stateChar === '!' ? '(?:(?!(?:' : '(?:' + close: plTypes[stateChar].close, + } + this.debug(this.pattern, '\t', plEntry) + patternListStack.push(plEntry) + // negation is (?:(?!(?:js)(?:))[^/]*) + re += plEntry.open + // next entry starts with a dot maybe? + if (plEntry.start === 0 && plEntry.type !== '!') { + dotTravAllowed = true + re += subPatternStart(pattern.slice(i + 1)) + } this.debug('plType %j %j', stateChar, re) stateChar = false - continue + continue + } - case ')': - if (inClass || !patternListStack.length) { + case ')': { + const plEntry = patternListStack[patternListStack.length - 1] + if (inClass || !plEntry) { re += '\\)' continue } + patternListStack.pop() + // closing an extglob clearStateChar() hasMagic = true - pl = patternListStack.pop() + pl = plEntry // negation is (?:(?!js)[^/]*) // The others are (?:) re += pl.close if (pl.type === '!') { - negativeLists.push(pl) + negativeLists.push(Object.assign(pl, { reEnd: re.length })) } - pl.reEnd = re.length - continue + continue + } - case '|': - if (inClass || !patternListStack.length) { + case '|': { + const plEntry = patternListStack[patternListStack.length - 1] + if (inClass || !plEntry) { re += '\\|' continue } clearStateChar() re += '|' - continue + // next subpattern can start with a dot? + if (plEntry.start === 0 && plEntry.type !== '!') { + dotTravAllowed = true + re += subPatternStart(pattern.slice(i + 1)) + } + continue + } // these are mostly the same in regexp and glob case '[': @@ -743,7 +773,7 @@ class Minimatch { } if (addPatternStart) { - re = patternStart + re + re = patternStart() + re } // parsing just a piece of a larger pattern. diff --git a/tap-snapshots/test/basic.js.test.cjs b/tap-snapshots/test/basic.js.test.cjs index becc8ff4..7dab7819 100644 --- a/tap-snapshots/test/basic.js.test.cjs +++ b/tap-snapshots/test/basic.js.test.cjs @@ -13,6 +13,10 @@ exports[`test/basic.js TAP basic tests > makeRe !!a* 1`] = ` /^(?:(?=.)a[^/]*?)$/ ` +exports[`test/basic.js TAP basic tests > makeRe !(.a|js)@(.*) 1`] = ` +/^(?:(?!\\.)(?=.)(?:(?!(?:\\.a|js)(?:\\.[^/]*?))[^/]*?)(?:\\.[^/]*?))$/ +` + exports[`test/basic.js TAP basic tests > makeRe !\\!a* 1`] = ` /^(?!^(?:(?=.)\\!a[^/]*?)$).*$/ ` @@ -33,20 +37,24 @@ exports[`test/basic.js TAP basic tests > makeRe #* 1`] = ` /^(?:(?=.)#[^/]*?)$/ ` +exports[`test/basic.js TAP basic tests > makeRe * 1`] = ` +/^(?:(?!(?:^|\\/)\\.{1,2}(?:$|\\/))(?=.)[^/]*?)$/ +` + exports[`test/basic.js TAP basic tests > makeRe *(a/b) 1`] = ` -/^(?:(?!\\.)(?=.)[^/]*?\\(a\\/b\\))$/ +/^(?:(?=.)[^/]*?\\((?!\\.)a\\/b\\))$/ ` exports[`test/basic.js TAP basic tests > makeRe *(a|{b),c)} 1`] = ` -/^(?:(?!\\.)(?=.)(?:a|b)*|(?!\\.)(?=.)(?:a|c)*)$/ +/^(?:(?=.)(?:(?!\\.)a|(?!\\.)b)*|(?=.)(?:(?!\\.)a|(?!\\.)c)*)$/ ` exports[`test/basic.js TAP basic tests > makeRe *(a|{b,c}) 1`] = ` -/^(?:(?!\\.)(?=.)(?:a|b)*|(?!\\.)(?=.)(?:a|c)*)$/ +/^(?:(?=.)(?:(?!\\.)a|(?!\\.)b)*|(?=.)(?:(?!\\.)a|(?!\\.)c)*)$/ ` exports[`test/basic.js TAP basic tests > makeRe *(a|{b|c,c}) 1`] = ` -/^(?:(?!\\.)(?=.)(?:a|b|c)*|(?!\\.)(?=.)(?:a|c)*)$/ +/^(?:(?=.)(?:(?!\\.)a|(?!\\.)b|(?!\\.)c)*|(?=.)(?:(?!\\.)a|(?!\\.)c)*)$/ ` exports[`test/basic.js TAP basic tests > makeRe *(a|{b|c,c}) 2`] = ` @@ -110,11 +118,15 @@ exports[`test/basic.js TAP basic tests > makeRe *c*?** 1`] = ` ` exports[`test/basic.js TAP basic tests > makeRe +(a)!(b)+(c) 1`] = ` -/^(?:(?!\\.)(?=.)(?:a)+(?:(?!(?:b)(?:c)+)[^/]*?)(?:c)+)$/ +/^(?:(?=.)(?:(?!\\.)a)+(?:(?!(?:b)(?:c)+)[^/]*?)(?:c)+)$/ ` exports[`test/basic.js TAP basic tests > makeRe +(a|*\\|c\\\\|d\\\\\\|e\\\\\\\\|f\\\\\\\\\\|g 1`] = ` -/^(?:(?=.)\\+\\(a\\|[^/]*?\\|c\\\\\\\\\\|d\\\\\\\\\\|e\\\\\\\\\\\\\\\\\\|f\\\\\\\\\\\\\\\\\\|g)$/ +/^(?:(?=.)\\+\\((?!\\.)a\\|(?!\\.)[^/]*?\\|c\\\\\\\\\\|(?!\\.)d\\\\\\\\\\|e\\\\\\\\\\\\\\\\\\|(?!\\.)f\\\\\\\\\\\\\\\\\\|g)$/ +` + +exports[`test/basic.js TAP basic tests > makeRe .* 1`] = ` +/^(?:(?=.)\\.[^/]*?)$/ ` exports[`test/basic.js TAP basic tests > makeRe /^root:/{s/^[^:]*:[^:]*:([^:]*).*$// 1`] = ` @@ -157,6 +169,42 @@ exports[`test/basic.js TAP basic tests > makeRe ??**********?****c 1`] = ` /^(?:(?!\\.)(?=.)[^/][^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/]*?[^/]*?c)$/ ` +exports[`test/basic.js TAP basic tests > makeRe @(*|.*) 1`] = ` +/^(?:(?=.)(?:(?!\\.)[^/]*?|\\.[^/]*?))$/ +` + +exports[`test/basic.js TAP basic tests > makeRe @(*|a) 1`] = ` +/^(?:(?=.)(?:(?!(?:^|\\/)\\.{1,2}(?:$|\\/))[^/]*?|(?!(?:^|\\/)\\.{1,2}(?:$|\\/))a))$/ +` + +exports[`test/basic.js TAP basic tests > makeRe @(.*) 1`] = ` +/^(?:(?=.)(?:\\.[^/]*?))$/ +` + +exports[`test/basic.js TAP basic tests > makeRe @(.*) 2`] = ` +/^(?:(?=.)(?:\\.[^/]*?))$/ +` + +exports[`test/basic.js TAP basic tests > makeRe @(.*|*) 1`] = ` +/^(?:(?=.)(?:\\.[^/]*?|(?!\\.)[^/]*?))$/ +` + +exports[`test/basic.js TAP basic tests > makeRe @(.*|js) 1`] = ` +/^(?:(?=.)(?:\\.[^/]*?|(?!\\.)js))$/ +` + +exports[`test/basic.js TAP basic tests > makeRe @(a|a[(])b 1`] = ` +/^(?:(?=.)(?:(?!\\.)a|(?!\\.)a[(])b)$/ +` + +exports[`test/basic.js TAP basic tests > makeRe @(a|a[)])b 1`] = ` +/^(?:(?=.)(?:(?!\\.)a|(?!\\.)a[\\)])b)$/ +` + +exports[`test/basic.js TAP basic tests > makeRe @(js|.*) 1`] = ` +/^(?:(?=.)(?:(?!\\.)js|\\.[^/]*?))$/ +` + exports[`test/basic.js TAP basic tests > makeRe X* 1`] = ` /^(?:(?=.)X[^/]*?)$/ ` @@ -422,9 +470,9 @@ exports[`test/basic.js TAP basic tests > makeRe {/?,*} 1`] = ` ` exports[`test/basic.js TAP basic tests > makeRe {a,*(b|c,d)} 1`] = ` -/^(?:a|(?!\\.)(?=.)[^/]*?\\(b\\|c|d\\))$/ +/^(?:a|(?=.)[^/]*?\\((?!\\.)b\\|(?!\\.)c|d\\))$/ ` exports[`test/basic.js TAP basic tests > makeRe {a,*(b|{c,d})} 1`] = ` -/^(?:a|(?!\\.)(?=.)(?:b|c)*|(?!\\.)(?=.)(?:b|d)*)$/ +/^(?:a|(?=.)(?:(?!\\.)b|(?!\\.)c)*|(?=.)(?:(?!\\.)b|(?!\\.)d)*)$/ ` diff --git a/test/patterns.js b/test/patterns.js index a74fb4ed..a4267191 100644 --- a/test/patterns.js +++ b/test/patterns.js @@ -291,6 +291,24 @@ module.exports = [ ] }, ['+(a)!(b)+(c)', ['ac', 'acc', 'adc']], + + 'https://github.com/isaacs/node-glob/issues/387', + () => (files = ['.a', '.a.js', '.js', 'a', 'a.js', 'js']), + ['.*', ['.a', '.a.js', '.js']], + ['*', ['.a', '.a.js', '.js', 'a', 'a.js', 'js'], { dot: true }], + ['@(*|.*)', ['.a', '.a.js', '.js', 'a', 'a.js', 'js']], + ['@(.*|*)', ['.a', '.a.js', '.js', 'a', 'a.js', 'js']], + ['@(*|a)', ['.a', '.a.js', '.js', 'a', 'a.js', 'js'], { dot: true }], + ['@(.*)', ['.a', '.a.js', '.js']], + ['@(.*)', ['.a', '.a.js', '.js'], { dot: true }], + ['@(js|.*)', ['js', '.a', '.a.js', '.js']], + ['@(.*|js)', ['js', '.a', '.a.js', '.js']], + // doesn't start at 0, no dice + // neg extglobs don't trigger this behavior. + ['!(.a|js)@(.*)', ['a.js'], { nonegate: true }], + () => files=['a(b', 'ab', 'a)b'], + ['@(a|a[(])b', ['a(b', 'ab']], + ['@(a|a[)])b', ['a)b', 'ab']], ] Object.defineProperty(module.exports, 'files', {