1const minimatch = module.exports = (p, pattern, options = {}) => { 2 assertValidPattern(pattern) 3 4 // shortcut: comments match nothing. 5 if (!options.nocomment && pattern.charAt(0) === '#') { 6 return false 7 } 8 9 return new Minimatch(pattern, options).match(p) 10} 11 12module.exports = minimatch 13 14const path = require('./lib/path.js') 15minimatch.sep = path.sep 16 17const GLOBSTAR = Symbol('globstar **') 18minimatch.GLOBSTAR = GLOBSTAR 19const expand = require('brace-expansion') 20 21const plTypes = { 22 '!': { open: '(?:(?!(?:', close: '))[^/]*?)'}, 23 '?': { open: '(?:', close: ')?' }, 24 '+': { open: '(?:', close: ')+' }, 25 '*': { open: '(?:', close: ')*' }, 26 '@': { open: '(?:', close: ')' } 27} 28 29// any single thing other than / 30// don't need to escape / when using new RegExp() 31const qmark = '[^/]' 32 33// * => any number of characters 34const star = qmark + '*?' 35 36// ** when dots are allowed. Anything goes, except .. and . 37// not (^ or / followed by one or two dots followed by $ or /), 38// followed by anything, any number of times. 39const twoStarDot = '(?:(?!(?:\\\/|^)(?:\\.{1,2})($|\\\/)).)*?' 40 41// not a ^ or / followed by a dot, 42// followed by anything, any number of times. 43const twoStarNoDot = '(?:(?!(?:\\\/|^)\\.).)*?' 44 45// "abc" -> { a:true, b:true, c:true } 46const charSet = s => s.split('').reduce((set, c) => { 47 set[c] = true 48 return set 49}, {}) 50 51// characters that need to be escaped in RegExp. 52const reSpecials = charSet('().*{}+?[]^$\\!') 53 54// characters that indicate we have to add the pattern start 55const addPatternStartSet = charSet('[.(') 56 57// normalizes slashes. 58const slashSplit = /\/+/ 59 60minimatch.filter = (pattern, options = {}) => 61 (p, i, list) => minimatch(p, pattern, options) 62 63const ext = (a, b = {}) => { 64 const t = {} 65 Object.keys(a).forEach(k => t[k] = a[k]) 66 Object.keys(b).forEach(k => t[k] = b[k]) 67 return t 68} 69 70minimatch.defaults = def => { 71 if (!def || typeof def !== 'object' || !Object.keys(def).length) { 72 return minimatch 73 } 74 75 const orig = minimatch 76 77 const m = (p, pattern, options) => orig(p, pattern, ext(def, options)) 78 m.Minimatch = class Minimatch extends orig.Minimatch { 79 constructor (pattern, options) { 80 super(pattern, ext(def, options)) 81 } 82 } 83 m.Minimatch.defaults = options => orig.defaults(ext(def, options)).Minimatch 84 m.filter = (pattern, options) => orig.filter(pattern, ext(def, options)) 85 m.defaults = options => orig.defaults(ext(def, options)) 86 m.makeRe = (pattern, options) => orig.makeRe(pattern, ext(def, options)) 87 m.braceExpand = (pattern, options) => orig.braceExpand(pattern, ext(def, options)) 88 m.match = (list, pattern, options) => orig.match(list, pattern, ext(def, options)) 89 90 return m 91} 92 93 94 95 96 97// Brace expansion: 98// a{b,c}d -> abd acd 99// a{b,}c -> abc ac 100// a{0..3}d -> a0d a1d a2d a3d 101// a{b,c{d,e}f}g -> abg acdfg acefg 102// a{b,c}d{e,f}g -> abdeg acdeg abdeg abdfg 103// 104// Invalid sets are not expanded. 105// a{2..}b -> a{2..}b 106// a{b}c -> a{b}c 107minimatch.braceExpand = (pattern, options) => braceExpand(pattern, options) 108 109const braceExpand = (pattern, options = {}) => { 110 assertValidPattern(pattern) 111 112 // Thanks to Yeting Li <https://github.com/yetingli> for 113 // improving this regexp to avoid a ReDOS vulnerability. 114 if (options.nobrace || !/\{(?:(?!\{).)*\}/.test(pattern)) { 115 // shortcut. no need to expand. 116 return [pattern] 117 } 118 119 return expand(pattern) 120} 121 122const MAX_PATTERN_LENGTH = 1024 * 64 123const assertValidPattern = pattern => { 124 if (typeof pattern !== 'string') { 125 throw new TypeError('invalid pattern') 126 } 127 128 if (pattern.length > MAX_PATTERN_LENGTH) { 129 throw new TypeError('pattern is too long') 130 } 131} 132 133// parse a component of the expanded set. 134// At this point, no pattern may contain "/" in it 135// so we're going to return a 2d array, where each entry is the full 136// pattern, split on '/', and then turned into a regular expression. 137// A regexp is made at the end which joins each array with an 138// escaped /, and another full one which joins each regexp with |. 139// 140// Following the lead of Bash 4.1, note that "**" only has special meaning 141// when it is the *only* thing in a path portion. Otherwise, any series 142// of * is equivalent to a single *. Globstar behavior is enabled by 143// default, and can be disabled by setting options.noglobstar. 144const SUBPARSE = Symbol('subparse') 145 146minimatch.makeRe = (pattern, options) => 147 new Minimatch(pattern, options || {}).makeRe() 148 149minimatch.match = (list, pattern, options = {}) => { 150 const mm = new Minimatch(pattern, options) 151 list = list.filter(f => mm.match(f)) 152 if (mm.options.nonull && !list.length) { 153 list.push(pattern) 154 } 155 return list 156} 157 158// replace stuff like \* with * 159const globUnescape = s => s.replace(/\\(.)/g, '$1') 160const charUnescape = s => s.replace(/\\([^-\]])/g, '$1') 161const regExpEscape = s => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&') 162const braExpEscape = s => s.replace(/[[\]\\]/g, '\\$&') 163 164class Minimatch { 165 constructor (pattern, options) { 166 assertValidPattern(pattern) 167 168 if (!options) options = {} 169 170 this.options = options 171 this.set = [] 172 this.pattern = pattern 173 this.windowsPathsNoEscape = !!options.windowsPathsNoEscape || 174 options.allowWindowsEscape === false 175 if (this.windowsPathsNoEscape) { 176 this.pattern = this.pattern.replace(/\\/g, '/') 177 } 178 this.regexp = null 179 this.negate = false 180 this.comment = false 181 this.empty = false 182 this.partial = !!options.partial 183 184 // make the set of regexps etc. 185 this.make() 186 } 187 188 debug () {} 189 190 make () { 191 const pattern = this.pattern 192 const options = this.options 193 194 // empty patterns and comments match nothing. 195 if (!options.nocomment && pattern.charAt(0) === '#') { 196 this.comment = true 197 return 198 } 199 if (!pattern) { 200 this.empty = true 201 return 202 } 203 204 // step 1: figure out negation, etc. 205 this.parseNegate() 206 207 // step 2: expand braces 208 let set = this.globSet = this.braceExpand() 209 210 if (options.debug) this.debug = (...args) => console.error(...args) 211 212 this.debug(this.pattern, set) 213 214 // step 3: now we have a set, so turn each one into a series of path-portion 215 // matching patterns. 216 // These will be regexps, except in the case of "**", which is 217 // set to the GLOBSTAR object for globstar behavior, 218 // and will not contain any / characters 219 set = this.globParts = set.map(s => s.split(slashSplit)) 220 221 this.debug(this.pattern, set) 222 223 // glob --> regexps 224 set = set.map((s, si, set) => s.map(this.parse, this)) 225 226 this.debug(this.pattern, set) 227 228 // filter out everything that didn't compile properly. 229 set = set.filter(s => s.indexOf(false) === -1) 230 231 this.debug(this.pattern, set) 232 233 this.set = set 234 } 235 236 parseNegate () { 237 if (this.options.nonegate) return 238 239 const pattern = this.pattern 240 let negate = false 241 let negateOffset = 0 242 243 for (let i = 0; i < pattern.length && pattern.charAt(i) === '!'; i++) { 244 negate = !negate 245 negateOffset++ 246 } 247 248 if (negateOffset) this.pattern = pattern.slice(negateOffset) 249 this.negate = negate 250 } 251 252 // set partial to true to test if, for example, 253 // "/a/b" matches the start of "/*/b/*/d" 254 // Partial means, if you run out of file before you run 255 // out of pattern, then that's fine, as long as all 256 // the parts match. 257 matchOne (file, pattern, partial) { 258 var options = this.options 259 260 this.debug('matchOne', 261 { 'this': this, file: file, pattern: pattern }) 262 263 this.debug('matchOne', file.length, pattern.length) 264 265 for (var fi = 0, 266 pi = 0, 267 fl = file.length, 268 pl = pattern.length 269 ; (fi < fl) && (pi < pl) 270 ; fi++, pi++) { 271 this.debug('matchOne loop') 272 var p = pattern[pi] 273 var f = file[fi] 274 275 this.debug(pattern, p, f) 276 277 // should be impossible. 278 // some invalid regexp stuff in the set. 279 /* istanbul ignore if */ 280 if (p === false) return false 281 282 if (p === GLOBSTAR) { 283 this.debug('GLOBSTAR', [pattern, p, f]) 284 285 // "**" 286 // a/**/b/**/c would match the following: 287 // a/b/x/y/z/c 288 // a/x/y/z/b/c 289 // a/b/x/b/x/c 290 // a/b/c 291 // To do this, take the rest of the pattern after 292 // the **, and see if it would match the file remainder. 293 // If so, return success. 294 // If not, the ** "swallows" a segment, and try again. 295 // This is recursively awful. 296 // 297 // a/**/b/**/c matching a/b/x/y/z/c 298 // - a matches a 299 // - doublestar 300 // - matchOne(b/x/y/z/c, b/**/c) 301 // - b matches b 302 // - doublestar 303 // - matchOne(x/y/z/c, c) -> no 304 // - matchOne(y/z/c, c) -> no 305 // - matchOne(z/c, c) -> no 306 // - matchOne(c, c) yes, hit 307 var fr = fi 308 var pr = pi + 1 309 if (pr === pl) { 310 this.debug('** at the end') 311 // a ** at the end will just swallow the rest. 312 // We have found a match. 313 // however, it will not swallow /.x, unless 314 // options.dot is set. 315 // . and .. are *never* matched by **, for explosively 316 // exponential reasons. 317 for (; fi < fl; fi++) { 318 if (file[fi] === '.' || file[fi] === '..' || 319 (!options.dot && file[fi].charAt(0) === '.')) return false 320 } 321 return true 322 } 323 324 // ok, let's see if we can swallow whatever we can. 325 while (fr < fl) { 326 var swallowee = file[fr] 327 328 this.debug('\nglobstar while', file, fr, pattern, pr, swallowee) 329 330 // XXX remove this slice. Just pass the start index. 331 if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) { 332 this.debug('globstar found match!', fr, fl, swallowee) 333 // found a match. 334 return true 335 } else { 336 // can't swallow "." or ".." ever. 337 // can only swallow ".foo" when explicitly asked. 338 if (swallowee === '.' || swallowee === '..' || 339 (!options.dot && swallowee.charAt(0) === '.')) { 340 this.debug('dot detected!', file, fr, pattern, pr) 341 break 342 } 343 344 // ** swallows a segment, and continue. 345 this.debug('globstar swallow a segment, and continue') 346 fr++ 347 } 348 } 349 350 // no match was found. 351 // However, in partial mode, we can't say this is necessarily over. 352 // If there's more *pattern* left, then 353 /* istanbul ignore if */ 354 if (partial) { 355 // ran out of file 356 this.debug('\n>>> no match, partial?', file, fr, pattern, pr) 357 if (fr === fl) return true 358 } 359 return false 360 } 361 362 // something other than ** 363 // non-magic patterns just have to match exactly 364 // patterns with magic have been turned into regexps. 365 var hit 366 if (typeof p === 'string') { 367 hit = f === p 368 this.debug('string match', p, f, hit) 369 } else { 370 hit = f.match(p) 371 this.debug('pattern match', p, f, hit) 372 } 373 374 if (!hit) return false 375 } 376 377 // Note: ending in / means that we'll get a final "" 378 // at the end of the pattern. This can only match a 379 // corresponding "" at the end of the file. 380 // If the file ends in /, then it can only match a 381 // a pattern that ends in /, unless the pattern just 382 // doesn't have any more for it. But, a/b/ should *not* 383 // match "a/b/*", even though "" matches against the 384 // [^/]*? pattern, except in partial mode, where it might 385 // simply not be reached yet. 386 // However, a/b/ should still satisfy a/* 387 388 // now either we fell off the end of the pattern, or we're done. 389 if (fi === fl && pi === pl) { 390 // ran out of pattern and filename at the same time. 391 // an exact hit! 392 return true 393 } else if (fi === fl) { 394 // ran out of file, but still had pattern left. 395 // this is ok if we're doing the match as part of 396 // a glob fs traversal. 397 return partial 398 } else /* istanbul ignore else */ if (pi === pl) { 399 // ran out of pattern, still have file left. 400 // this is only acceptable if we're on the very last 401 // empty segment of a file with a trailing slash. 402 // a/* should match a/b/ 403 return (fi === fl - 1) && (file[fi] === '') 404 } 405 406 // should be unreachable. 407 /* istanbul ignore next */ 408 throw new Error('wtf?') 409 } 410 411 braceExpand () { 412 return braceExpand(this.pattern, this.options) 413 } 414 415 parse (pattern, isSub) { 416 assertValidPattern(pattern) 417 418 const options = this.options 419 420 // shortcuts 421 if (pattern === '**') { 422 if (!options.noglobstar) 423 return GLOBSTAR 424 else 425 pattern = '*' 426 } 427 if (pattern === '') return '' 428 429 let re = '' 430 let hasMagic = false 431 let escaping = false 432 // ? => one single character 433 const patternListStack = [] 434 const negativeLists = [] 435 let stateChar 436 let inClass = false 437 let reClassStart = -1 438 let classStart = -1 439 let cs 440 let pl 441 let sp 442 // . and .. never match anything that doesn't start with ., 443 // even when options.dot is set. However, if the pattern 444 // starts with ., then traversal patterns can match. 445 let dotTravAllowed = pattern.charAt(0) === '.' 446 let dotFileAllowed = options.dot || dotTravAllowed 447 const patternStart = () => 448 dotTravAllowed 449 ? '' 450 : dotFileAllowed 451 ? '(?!(?:^|\\/)\\.{1,2}(?:$|\\/))' 452 : '(?!\\.)' 453 const subPatternStart = (p) => 454 p.charAt(0) === '.' 455 ? '' 456 : options.dot 457 ? '(?!(?:^|\\/)\\.{1,2}(?:$|\\/))' 458 : '(?!\\.)' 459 460 461 const clearStateChar = () => { 462 if (stateChar) { 463 // we had some state-tracking character 464 // that wasn't consumed by this pass. 465 switch (stateChar) { 466 case '*': 467 re += star 468 hasMagic = true 469 break 470 case '?': 471 re += qmark 472 hasMagic = true 473 break 474 default: 475 re += '\\' + stateChar 476 break 477 } 478 this.debug('clearStateChar %j %j', stateChar, re) 479 stateChar = false 480 } 481 } 482 483 for (let i = 0, c; (i < pattern.length) && (c = pattern.charAt(i)); i++) { 484 this.debug('%s\t%s %s %j', pattern, i, re, c) 485 486 // skip over any that are escaped. 487 if (escaping) { 488 /* istanbul ignore next - completely not allowed, even escaped. */ 489 if (c === '/') { 490 return false 491 } 492 493 if (reSpecials[c]) { 494 re += '\\' 495 } 496 re += c 497 escaping = false 498 continue 499 } 500 501 switch (c) { 502 /* istanbul ignore next */ 503 case '/': { 504 // Should already be path-split by now. 505 return false 506 } 507 508 case '\\': 509 if (inClass && pattern.charAt(i + 1) === '-') { 510 re += c 511 continue 512 } 513 514 clearStateChar() 515 escaping = true 516 continue 517 518 // the various stateChar values 519 // for the "extglob" stuff. 520 case '?': 521 case '*': 522 case '+': 523 case '@': 524 case '!': 525 this.debug('%s\t%s %s %j <-- stateChar', pattern, i, re, c) 526 527 // all of those are literals inside a class, except that 528 // the glob [!a] means [^a] in regexp 529 if (inClass) { 530 this.debug(' in class') 531 if (c === '!' && i === classStart + 1) c = '^' 532 re += c 533 continue 534 } 535 536 // if we already have a stateChar, then it means 537 // that there was something like ** or +? in there. 538 // Handle the stateChar, then proceed with this one. 539 this.debug('call clearStateChar %j', stateChar) 540 clearStateChar() 541 stateChar = c 542 // if extglob is disabled, then +(asdf|foo) isn't a thing. 543 // just clear the statechar *now*, rather than even diving into 544 // the patternList stuff. 545 if (options.noext) clearStateChar() 546 continue 547 548 case '(': { 549 if (inClass) { 550 re += '(' 551 continue 552 } 553 554 if (!stateChar) { 555 re += '\\(' 556 continue 557 } 558 559 const plEntry = { 560 type: stateChar, 561 start: i - 1, 562 reStart: re.length, 563 open: plTypes[stateChar].open, 564 close: plTypes[stateChar].close, 565 } 566 this.debug(this.pattern, '\t', plEntry) 567 patternListStack.push(plEntry) 568 // negation is (?:(?!(?:js)(?:<rest>))[^/]*) 569 re += plEntry.open 570 // next entry starts with a dot maybe? 571 if (plEntry.start === 0 && plEntry.type !== '!') { 572 dotTravAllowed = true 573 re += subPatternStart(pattern.slice(i + 1)) 574 } 575 this.debug('plType %j %j', stateChar, re) 576 stateChar = false 577 continue 578 } 579 580 case ')': { 581 const plEntry = patternListStack[patternListStack.length - 1] 582 if (inClass || !plEntry) { 583 re += '\\)' 584 continue 585 } 586 patternListStack.pop() 587 588 // closing an extglob 589 clearStateChar() 590 hasMagic = true 591 pl = plEntry 592 // negation is (?:(?!js)[^/]*) 593 // The others are (?:<pattern>)<type> 594 re += pl.close 595 if (pl.type === '!') { 596 negativeLists.push(Object.assign(pl, { reEnd: re.length })) 597 } 598 continue 599 } 600 601 case '|': { 602 const plEntry = patternListStack[patternListStack.length - 1] 603 if (inClass || !plEntry) { 604 re += '\\|' 605 continue 606 } 607 608 clearStateChar() 609 re += '|' 610 // next subpattern can start with a dot? 611 if (plEntry.start === 0 && plEntry.type !== '!') { 612 dotTravAllowed = true 613 re += subPatternStart(pattern.slice(i + 1)) 614 } 615 continue 616 } 617 618 // these are mostly the same in regexp and glob 619 case '[': 620 // swallow any state-tracking char before the [ 621 clearStateChar() 622 623 if (inClass) { 624 re += '\\' + c 625 continue 626 } 627 628 inClass = true 629 classStart = i 630 reClassStart = re.length 631 re += c 632 continue 633 634 case ']': 635 // a right bracket shall lose its special 636 // meaning and represent itself in 637 // a bracket expression if it occurs 638 // first in the list. -- POSIX.2 2.8.3.2 639 if (i === classStart + 1 || !inClass) { 640 re += '\\' + c 641 continue 642 } 643 644 // split where the last [ was, make sure we don't have 645 // an invalid re. if so, re-walk the contents of the 646 // would-be class to re-translate any characters that 647 // were passed through as-is 648 // TODO: It would probably be faster to determine this 649 // without a try/catch and a new RegExp, but it's tricky 650 // to do safely. For now, this is safe and works. 651 cs = pattern.substring(classStart + 1, i) 652 try { 653 RegExp('[' + braExpEscape(charUnescape(cs)) + ']') 654 // looks good, finish up the class. 655 re += c 656 } catch (er) { 657 // out of order ranges in JS are errors, but in glob syntax, 658 // they're just a range that matches nothing. 659 re = re.substring(0, reClassStart) + '(?:$.)' // match nothing ever 660 } 661 hasMagic = true 662 inClass = false 663 continue 664 665 default: 666 // swallow any state char that wasn't consumed 667 clearStateChar() 668 669 if (reSpecials[c] && !(c === '^' && inClass)) { 670 re += '\\' 671 } 672 673 re += c 674 break 675 676 } // switch 677 } // for 678 679 // handle the case where we left a class open. 680 // "[abc" is valid, equivalent to "\[abc" 681 if (inClass) { 682 // split where the last [ was, and escape it 683 // this is a huge pita. We now have to re-walk 684 // the contents of the would-be class to re-translate 685 // any characters that were passed through as-is 686 cs = pattern.slice(classStart + 1) 687 sp = this.parse(cs, SUBPARSE) 688 re = re.substring(0, reClassStart) + '\\[' + sp[0] 689 hasMagic = hasMagic || sp[1] 690 } 691 692 // handle the case where we had a +( thing at the *end* 693 // of the pattern. 694 // each pattern list stack adds 3 chars, and we need to go through 695 // and escape any | chars that were passed through as-is for the regexp. 696 // Go through and escape them, taking care not to double-escape any 697 // | chars that were already escaped. 698 for (pl = patternListStack.pop(); pl; pl = patternListStack.pop()) { 699 let tail 700 tail = re.slice(pl.reStart + pl.open.length) 701 this.debug('setting tail', re, pl) 702 // maybe some even number of \, then maybe 1 \, followed by a | 703 tail = tail.replace(/((?:\\{2}){0,64})(\\?)\|/g, (_, $1, $2) => { 704 /* istanbul ignore else - should already be done */ 705 if (!$2) { 706 // the | isn't already escaped, so escape it. 707 $2 = '\\' 708 } 709 710 // need to escape all those slashes *again*, without escaping the 711 // one that we need for escaping the | character. As it works out, 712 // escaping an even number of slashes can be done by simply repeating 713 // it exactly after itself. That's why this trick works. 714 // 715 // I am sorry that you have to see this. 716 return $1 + $1 + $2 + '|' 717 }) 718 719 this.debug('tail=%j\n %s', tail, tail, pl, re) 720 const t = pl.type === '*' ? star 721 : pl.type === '?' ? qmark 722 : '\\' + pl.type 723 724 hasMagic = true 725 re = re.slice(0, pl.reStart) + t + '\\(' + tail 726 } 727 728 // handle trailing things that only matter at the very end. 729 clearStateChar() 730 if (escaping) { 731 // trailing \\ 732 re += '\\\\' 733 } 734 735 // only need to apply the nodot start if the re starts with 736 // something that could conceivably capture a dot 737 const addPatternStart = addPatternStartSet[re.charAt(0)] 738 739 // Hack to work around lack of negative lookbehind in JS 740 // A pattern like: *.!(x).!(y|z) needs to ensure that a name 741 // like 'a.xyz.yz' doesn't match. So, the first negative 742 // lookahead, has to look ALL the way ahead, to the end of 743 // the pattern. 744 for (let n = negativeLists.length - 1; n > -1; n--) { 745 const nl = negativeLists[n] 746 747 const nlBefore = re.slice(0, nl.reStart) 748 const nlFirst = re.slice(nl.reStart, nl.reEnd - 8) 749 let nlAfter = re.slice(nl.reEnd) 750 const nlLast = re.slice(nl.reEnd - 8, nl.reEnd) + nlAfter 751 752 // Handle nested stuff like *(*.js|!(*.json)), where open parens 753 // mean that we should *not* include the ) in the bit that is considered 754 // "after" the negated section. 755 const closeParensBefore = nlBefore.split(')').length 756 const openParensBefore = nlBefore.split('(').length - closeParensBefore 757 let cleanAfter = nlAfter 758 for (let i = 0; i < openParensBefore; i++) { 759 cleanAfter = cleanAfter.replace(/\)[+*?]?/, '') 760 } 761 nlAfter = cleanAfter 762 763 const dollar = nlAfter === '' && isSub !== SUBPARSE ? '(?:$|\\/)' : '' 764 765 re = nlBefore + nlFirst + nlAfter + dollar + nlLast 766 } 767 768 // if the re is not "" at this point, then we need to make sure 769 // it doesn't match against an empty path part. 770 // Otherwise a/* will match a/, which it should not. 771 if (re !== '' && hasMagic) { 772 re = '(?=.)' + re 773 } 774 775 if (addPatternStart) { 776 re = patternStart() + re 777 } 778 779 // parsing just a piece of a larger pattern. 780 if (isSub === SUBPARSE) { 781 return [re, hasMagic] 782 } 783 784 // if it's nocase, and the lcase/uppercase don't match, it's magic 785 if (options.nocase && !hasMagic) { 786 hasMagic = pattern.toUpperCase() !== pattern.toLowerCase() 787 } 788 789 // skip the regexp for non-magical patterns 790 // unescape anything in it, though, so that it'll be 791 // an exact match against a file etc. 792 if (!hasMagic) { 793 return globUnescape(pattern) 794 } 795 796 const flags = options.nocase ? 'i' : '' 797 try { 798 return Object.assign(new RegExp('^' + re + '$', flags), { 799 _glob: pattern, 800 _src: re, 801 }) 802 } catch (er) /* istanbul ignore next - should be impossible */ { 803 // If it was an invalid regular expression, then it can't match 804 // anything. This trick looks for a character after the end of 805 // the string, which is of course impossible, except in multi-line 806 // mode, but it's not a /m regex. 807 return new RegExp('$.') 808 } 809 } 810 811 makeRe () { 812 if (this.regexp || this.regexp === false) return this.regexp 813 814 // at this point, this.set is a 2d array of partial 815 // pattern strings, or "**". 816 // 817 // It's better to use .match(). This function shouldn't 818 // be used, really, but it's pretty convenient sometimes, 819 // when you just want to work with a regex. 820 const set = this.set 821 822 if (!set.length) { 823 this.regexp = false 824 return this.regexp 825 } 826 const options = this.options 827 828 const twoStar = options.noglobstar ? star 829 : options.dot ? twoStarDot 830 : twoStarNoDot 831 const flags = options.nocase ? 'i' : '' 832 833 // coalesce globstars and regexpify non-globstar patterns 834 // if it's the only item, then we just do one twoStar 835 // if it's the first, and there are more, prepend (\/|twoStar\/)? to next 836 // if it's the last, append (\/twoStar|) to previous 837 // if it's in the middle, append (\/|\/twoStar\/) to previous 838 // then filter out GLOBSTAR symbols 839 let re = set.map(pattern => { 840 pattern = pattern.map(p => 841 typeof p === 'string' ? regExpEscape(p) 842 : p === GLOBSTAR ? GLOBSTAR 843 : p._src 844 ).reduce((set, p) => { 845 if (!(set[set.length - 1] === GLOBSTAR && p === GLOBSTAR)) { 846 set.push(p) 847 } 848 return set 849 }, []) 850 pattern.forEach((p, i) => { 851 if (p !== GLOBSTAR || pattern[i-1] === GLOBSTAR) { 852 return 853 } 854 if (i === 0) { 855 if (pattern.length > 1) { 856 pattern[i+1] = '(?:\\\/|' + twoStar + '\\\/)?' + pattern[i+1] 857 } else { 858 pattern[i] = twoStar 859 } 860 } else if (i === pattern.length - 1) { 861 pattern[i-1] += '(?:\\\/|' + twoStar + ')?' 862 } else { 863 pattern[i-1] += '(?:\\\/|\\\/' + twoStar + '\\\/)' + pattern[i+1] 864 pattern[i+1] = GLOBSTAR 865 } 866 }) 867 return pattern.filter(p => p !== GLOBSTAR).join('/') 868 }).join('|') 869 870 // must match entire pattern 871 // ending in a * or ** will make it less strict. 872 re = '^(?:' + re + ')$' 873 874 // can match anything, as long as it's not this. 875 if (this.negate) re = '^(?!' + re + ').*$' 876 877 try { 878 this.regexp = new RegExp(re, flags) 879 } catch (ex) /* istanbul ignore next - should be impossible */ { 880 this.regexp = false 881 } 882 return this.regexp 883 } 884 885 match (f, partial = this.partial) { 886 this.debug('match', f, this.pattern) 887 // short-circuit in the case of busted things. 888 // comments, etc. 889 if (this.comment) return false 890 if (this.empty) return f === '' 891 892 if (f === '/' && partial) return true 893 894 const options = this.options 895 896 // windows: need to use /, not \ 897 if (path.sep !== '/') { 898 f = f.split(path.sep).join('/') 899 } 900 901 // treat the test path as a set of pathparts. 902 f = f.split(slashSplit) 903 this.debug(this.pattern, 'split', f) 904 905 // just ONE of the pattern sets in this.set needs to match 906 // in order for it to be valid. If negating, then just one 907 // match means that we have failed. 908 // Either way, return on the first hit. 909 910 const set = this.set 911 this.debug(this.pattern, 'set', set) 912 913 // Find the basename of the path by looking for the last non-empty segment 914 let filename 915 for (let i = f.length - 1; i >= 0; i--) { 916 filename = f[i] 917 if (filename) break 918 } 919 920 for (let i = 0; i < set.length; i++) { 921 const pattern = set[i] 922 let file = f 923 if (options.matchBase && pattern.length === 1) { 924 file = [filename] 925 } 926 const hit = this.matchOne(file, pattern, partial) 927 if (hit) { 928 if (options.flipNegate) return true 929 return !this.negate 930 } 931 } 932 933 // didn't get any hits. this is success if it's a negative 934 // pattern, failure otherwise. 935 if (options.flipNegate) return false 936 return this.negate 937 } 938 939 static defaults (def) { 940 return minimatch.defaults(def).Minimatch 941 } 942} 943 944minimatch.Minimatch = Minimatch 945