1import expand from 'brace-expansion'; 2import { assertValidPattern } from './assert-valid-pattern.js'; 3import { AST } from './ast.js'; 4import { escape } from './escape.js'; 5import { unescape } from './unescape.js'; 6export const minimatch = (p, pattern, options = {}) => { 7 assertValidPattern(pattern); 8 // shortcut: comments match nothing. 9 if (!options.nocomment && pattern.charAt(0) === '#') { 10 return false; 11 } 12 return new Minimatch(pattern, options).match(p); 13}; 14// Optimized checking for the most common glob patterns. 15const starDotExtRE = /^\*+([^+@!?\*\[\(]*)$/; 16const starDotExtTest = (ext) => (f) => !f.startsWith('.') && f.endsWith(ext); 17const starDotExtTestDot = (ext) => (f) => f.endsWith(ext); 18const starDotExtTestNocase = (ext) => { 19 ext = ext.toLowerCase(); 20 return (f) => !f.startsWith('.') && f.toLowerCase().endsWith(ext); 21}; 22const starDotExtTestNocaseDot = (ext) => { 23 ext = ext.toLowerCase(); 24 return (f) => f.toLowerCase().endsWith(ext); 25}; 26const starDotStarRE = /^\*+\.\*+$/; 27const starDotStarTest = (f) => !f.startsWith('.') && f.includes('.'); 28const starDotStarTestDot = (f) => f !== '.' && f !== '..' && f.includes('.'); 29const dotStarRE = /^\.\*+$/; 30const dotStarTest = (f) => f !== '.' && f !== '..' && f.startsWith('.'); 31const starRE = /^\*+$/; 32const starTest = (f) => f.length !== 0 && !f.startsWith('.'); 33const starTestDot = (f) => f.length !== 0 && f !== '.' && f !== '..'; 34const qmarksRE = /^\?+([^+@!?\*\[\(]*)?$/; 35const qmarksTestNocase = ([$0, ext = '']) => { 36 const noext = qmarksTestNoExt([$0]); 37 if (!ext) 38 return noext; 39 ext = ext.toLowerCase(); 40 return (f) => noext(f) && f.toLowerCase().endsWith(ext); 41}; 42const qmarksTestNocaseDot = ([$0, ext = '']) => { 43 const noext = qmarksTestNoExtDot([$0]); 44 if (!ext) 45 return noext; 46 ext = ext.toLowerCase(); 47 return (f) => noext(f) && f.toLowerCase().endsWith(ext); 48}; 49const qmarksTestDot = ([$0, ext = '']) => { 50 const noext = qmarksTestNoExtDot([$0]); 51 return !ext ? noext : (f) => noext(f) && f.endsWith(ext); 52}; 53const qmarksTest = ([$0, ext = '']) => { 54 const noext = qmarksTestNoExt([$0]); 55 return !ext ? noext : (f) => noext(f) && f.endsWith(ext); 56}; 57const qmarksTestNoExt = ([$0]) => { 58 const len = $0.length; 59 return (f) => f.length === len && !f.startsWith('.'); 60}; 61const qmarksTestNoExtDot = ([$0]) => { 62 const len = $0.length; 63 return (f) => f.length === len && f !== '.' && f !== '..'; 64}; 65/* c8 ignore start */ 66const defaultPlatform = (typeof process === 'object' && process 67 ? (typeof process.env === 'object' && 68 process.env && 69 process.env.__MINIMATCH_TESTING_PLATFORM__) || 70 process.platform 71 : 'posix'); 72const path = { 73 win32: { sep: '\\' }, 74 posix: { sep: '/' }, 75}; 76/* c8 ignore stop */ 77export const sep = defaultPlatform === 'win32' ? path.win32.sep : path.posix.sep; 78minimatch.sep = sep; 79export const GLOBSTAR = Symbol('globstar **'); 80minimatch.GLOBSTAR = GLOBSTAR; 81// any single thing other than / 82// don't need to escape / when using new RegExp() 83const qmark = '[^/]'; 84// * => any number of characters 85const star = qmark + '*?'; 86// ** when dots are allowed. Anything goes, except .. and . 87// not (^ or / followed by one or two dots followed by $ or /), 88// followed by anything, any number of times. 89const twoStarDot = '(?:(?!(?:\\/|^)(?:\\.{1,2})($|\\/)).)*?'; 90// not a ^ or / followed by a dot, 91// followed by anything, any number of times. 92const twoStarNoDot = '(?:(?!(?:\\/|^)\\.).)*?'; 93export const filter = (pattern, options = {}) => (p) => minimatch(p, pattern, options); 94minimatch.filter = filter; 95const ext = (a, b = {}) => Object.assign({}, a, b); 96export const defaults = (def) => { 97 if (!def || typeof def !== 'object' || !Object.keys(def).length) { 98 return minimatch; 99 } 100 const orig = minimatch; 101 const m = (p, pattern, options = {}) => orig(p, pattern, ext(def, options)); 102 return Object.assign(m, { 103 Minimatch: class Minimatch extends orig.Minimatch { 104 constructor(pattern, options = {}) { 105 super(pattern, ext(def, options)); 106 } 107 static defaults(options) { 108 return orig.defaults(ext(def, options)).Minimatch; 109 } 110 }, 111 AST: class AST extends orig.AST { 112 /* c8 ignore start */ 113 constructor(type, parent, options = {}) { 114 super(type, parent, ext(def, options)); 115 } 116 /* c8 ignore stop */ 117 static fromGlob(pattern, options = {}) { 118 return orig.AST.fromGlob(pattern, ext(def, options)); 119 } 120 }, 121 unescape: (s, options = {}) => orig.unescape(s, ext(def, options)), 122 escape: (s, options = {}) => orig.escape(s, ext(def, options)), 123 filter: (pattern, options = {}) => orig.filter(pattern, ext(def, options)), 124 defaults: (options) => orig.defaults(ext(def, options)), 125 makeRe: (pattern, options = {}) => orig.makeRe(pattern, ext(def, options)), 126 braceExpand: (pattern, options = {}) => orig.braceExpand(pattern, ext(def, options)), 127 match: (list, pattern, options = {}) => orig.match(list, pattern, ext(def, options)), 128 sep: orig.sep, 129 GLOBSTAR: GLOBSTAR, 130 }); 131}; 132minimatch.defaults = defaults; 133// Brace expansion: 134// a{b,c}d -> abd acd 135// a{b,}c -> abc ac 136// a{0..3}d -> a0d a1d a2d a3d 137// a{b,c{d,e}f}g -> abg acdfg acefg 138// a{b,c}d{e,f}g -> abdeg acdeg abdeg abdfg 139// 140// Invalid sets are not expanded. 141// a{2..}b -> a{2..}b 142// a{b}c -> a{b}c 143export const braceExpand = (pattern, options = {}) => { 144 assertValidPattern(pattern); 145 // Thanks to Yeting Li <https://github.com/yetingli> for 146 // improving this regexp to avoid a ReDOS vulnerability. 147 if (options.nobrace || !/\{(?:(?!\{).)*\}/.test(pattern)) { 148 // shortcut. no need to expand. 149 return [pattern]; 150 } 151 return expand(pattern); 152}; 153minimatch.braceExpand = braceExpand; 154// parse a component of the expanded set. 155// At this point, no pattern may contain "/" in it 156// so we're going to return a 2d array, where each entry is the full 157// pattern, split on '/', and then turned into a regular expression. 158// A regexp is made at the end which joins each array with an 159// escaped /, and another full one which joins each regexp with |. 160// 161// Following the lead of Bash 4.1, note that "**" only has special meaning 162// when it is the *only* thing in a path portion. Otherwise, any series 163// of * is equivalent to a single *. Globstar behavior is enabled by 164// default, and can be disabled by setting options.noglobstar. 165export const makeRe = (pattern, options = {}) => new Minimatch(pattern, options).makeRe(); 166minimatch.makeRe = makeRe; 167export const match = (list, pattern, options = {}) => { 168 const mm = new Minimatch(pattern, options); 169 list = list.filter(f => mm.match(f)); 170 if (mm.options.nonull && !list.length) { 171 list.push(pattern); 172 } 173 return list; 174}; 175minimatch.match = match; 176// replace stuff like \* with * 177const globMagic = /[?*]|[+@!]\(.*?\)|\[|\]/; 178const regExpEscape = (s) => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); 179export class Minimatch { 180 options; 181 set; 182 pattern; 183 windowsPathsNoEscape; 184 nonegate; 185 negate; 186 comment; 187 empty; 188 preserveMultipleSlashes; 189 partial; 190 globSet; 191 globParts; 192 nocase; 193 isWindows; 194 platform; 195 windowsNoMagicRoot; 196 regexp; 197 constructor(pattern, options = {}) { 198 assertValidPattern(pattern); 199 options = options || {}; 200 this.options = options; 201 this.pattern = pattern; 202 this.platform = options.platform || defaultPlatform; 203 this.isWindows = this.platform === 'win32'; 204 this.windowsPathsNoEscape = 205 !!options.windowsPathsNoEscape || options.allowWindowsEscape === false; 206 if (this.windowsPathsNoEscape) { 207 this.pattern = this.pattern.replace(/\\/g, '/'); 208 } 209 this.preserveMultipleSlashes = !!options.preserveMultipleSlashes; 210 this.regexp = null; 211 this.negate = false; 212 this.nonegate = !!options.nonegate; 213 this.comment = false; 214 this.empty = false; 215 this.partial = !!options.partial; 216 this.nocase = !!this.options.nocase; 217 this.windowsNoMagicRoot = 218 options.windowsNoMagicRoot !== undefined 219 ? options.windowsNoMagicRoot 220 : !!(this.isWindows && this.nocase); 221 this.globSet = []; 222 this.globParts = []; 223 this.set = []; 224 // make the set of regexps etc. 225 this.make(); 226 } 227 hasMagic() { 228 if (this.options.magicalBraces && this.set.length > 1) { 229 return true; 230 } 231 for (const pattern of this.set) { 232 for (const part of pattern) { 233 if (typeof part !== 'string') 234 return true; 235 } 236 } 237 return false; 238 } 239 debug(..._) { } 240 make() { 241 const pattern = this.pattern; 242 const options = this.options; 243 // empty patterns and comments match nothing. 244 if (!options.nocomment && pattern.charAt(0) === '#') { 245 this.comment = true; 246 return; 247 } 248 if (!pattern) { 249 this.empty = true; 250 return; 251 } 252 // step 1: figure out negation, etc. 253 this.parseNegate(); 254 // step 2: expand braces 255 this.globSet = [...new Set(this.braceExpand())]; 256 if (options.debug) { 257 this.debug = (...args) => console.error(...args); 258 } 259 this.debug(this.pattern, this.globSet); 260 // step 3: now we have a set, so turn each one into a series of 261 // path-portion matching patterns. 262 // These will be regexps, except in the case of "**", which is 263 // set to the GLOBSTAR object for globstar behavior, 264 // and will not contain any / characters 265 // 266 // First, we preprocess to make the glob pattern sets a bit simpler 267 // and deduped. There are some perf-killing patterns that can cause 268 // problems with a glob walk, but we can simplify them down a bit. 269 const rawGlobParts = this.globSet.map(s => this.slashSplit(s)); 270 this.globParts = this.preprocess(rawGlobParts); 271 this.debug(this.pattern, this.globParts); 272 // glob --> regexps 273 let set = this.globParts.map((s, _, __) => { 274 if (this.isWindows && this.windowsNoMagicRoot) { 275 // check if it's a drive or unc path. 276 const isUNC = s[0] === '' && 277 s[1] === '' && 278 (s[2] === '?' || !globMagic.test(s[2])) && 279 !globMagic.test(s[3]); 280 const isDrive = /^[a-z]:/i.test(s[0]); 281 if (isUNC) { 282 return [...s.slice(0, 4), ...s.slice(4).map(ss => this.parse(ss))]; 283 } 284 else if (isDrive) { 285 return [s[0], ...s.slice(1).map(ss => this.parse(ss))]; 286 } 287 } 288 return s.map(ss => this.parse(ss)); 289 }); 290 this.debug(this.pattern, set); 291 // filter out everything that didn't compile properly. 292 this.set = set.filter(s => s.indexOf(false) === -1); 293 // do not treat the ? in UNC paths as magic 294 if (this.isWindows) { 295 for (let i = 0; i < this.set.length; i++) { 296 const p = this.set[i]; 297 if (p[0] === '' && 298 p[1] === '' && 299 this.globParts[i][2] === '?' && 300 typeof p[3] === 'string' && 301 /^[a-z]:$/i.test(p[3])) { 302 p[2] = '?'; 303 } 304 } 305 } 306 this.debug(this.pattern, this.set); 307 } 308 // various transforms to equivalent pattern sets that are 309 // faster to process in a filesystem walk. The goal is to 310 // eliminate what we can, and push all ** patterns as far 311 // to the right as possible, even if it increases the number 312 // of patterns that we have to process. 313 preprocess(globParts) { 314 // if we're not in globstar mode, then turn all ** into * 315 if (this.options.noglobstar) { 316 for (let i = 0; i < globParts.length; i++) { 317 for (let j = 0; j < globParts[i].length; j++) { 318 if (globParts[i][j] === '**') { 319 globParts[i][j] = '*'; 320 } 321 } 322 } 323 } 324 const { optimizationLevel = 1 } = this.options; 325 if (optimizationLevel >= 2) { 326 // aggressive optimization for the purpose of fs walking 327 globParts = this.firstPhasePreProcess(globParts); 328 globParts = this.secondPhasePreProcess(globParts); 329 } 330 else if (optimizationLevel >= 1) { 331 // just basic optimizations to remove some .. parts 332 globParts = this.levelOneOptimize(globParts); 333 } 334 else { 335 globParts = this.adjascentGlobstarOptimize(globParts); 336 } 337 return globParts; 338 } 339 // just get rid of adjascent ** portions 340 adjascentGlobstarOptimize(globParts) { 341 return globParts.map(parts => { 342 let gs = -1; 343 while (-1 !== (gs = parts.indexOf('**', gs + 1))) { 344 let i = gs; 345 while (parts[i + 1] === '**') { 346 i++; 347 } 348 if (i !== gs) { 349 parts.splice(gs, i - gs); 350 } 351 } 352 return parts; 353 }); 354 } 355 // get rid of adjascent ** and resolve .. portions 356 levelOneOptimize(globParts) { 357 return globParts.map(parts => { 358 parts = parts.reduce((set, part) => { 359 const prev = set[set.length - 1]; 360 if (part === '**' && prev === '**') { 361 return set; 362 } 363 if (part === '..') { 364 if (prev && prev !== '..' && prev !== '.' && prev !== '**') { 365 set.pop(); 366 return set; 367 } 368 } 369 set.push(part); 370 return set; 371 }, []); 372 return parts.length === 0 ? [''] : parts; 373 }); 374 } 375 levelTwoFileOptimize(parts) { 376 if (!Array.isArray(parts)) { 377 parts = this.slashSplit(parts); 378 } 379 let didSomething = false; 380 do { 381 didSomething = false; 382 // <pre>/<e>/<rest> -> <pre>/<rest> 383 if (!this.preserveMultipleSlashes) { 384 for (let i = 1; i < parts.length - 1; i++) { 385 const p = parts[i]; 386 // don't squeeze out UNC patterns 387 if (i === 1 && p === '' && parts[0] === '') 388 continue; 389 if (p === '.' || p === '') { 390 didSomething = true; 391 parts.splice(i, 1); 392 i--; 393 } 394 } 395 if (parts[0] === '.' && 396 parts.length === 2 && 397 (parts[1] === '.' || parts[1] === '')) { 398 didSomething = true; 399 parts.pop(); 400 } 401 } 402 // <pre>/<p>/../<rest> -> <pre>/<rest> 403 let dd = 0; 404 while (-1 !== (dd = parts.indexOf('..', dd + 1))) { 405 const p = parts[dd - 1]; 406 if (p && p !== '.' && p !== '..' && p !== '**') { 407 didSomething = true; 408 parts.splice(dd - 1, 2); 409 dd -= 2; 410 } 411 } 412 } while (didSomething); 413 return parts.length === 0 ? [''] : parts; 414 } 415 // First phase: single-pattern processing 416 // <pre> is 1 or more portions 417 // <rest> is 1 or more portions 418 // <p> is any portion other than ., .., '', or ** 419 // <e> is . or '' 420 // 421 // **/.. is *brutal* for filesystem walking performance, because 422 // it effectively resets the recursive walk each time it occurs, 423 // and ** cannot be reduced out by a .. pattern part like a regexp 424 // or most strings (other than .., ., and '') can be. 425 // 426 // <pre>/**/../<p>/<p>/<rest> -> {<pre>/../<p>/<p>/<rest>,<pre>/**/<p>/<p>/<rest>} 427 // <pre>/<e>/<rest> -> <pre>/<rest> 428 // <pre>/<p>/../<rest> -> <pre>/<rest> 429 // **/**/<rest> -> **/<rest> 430 // 431 // **/*/<rest> -> */**/<rest> <== not valid because ** doesn't follow 432 // this WOULD be allowed if ** did follow symlinks, or * didn't 433 firstPhasePreProcess(globParts) { 434 let didSomething = false; 435 do { 436 didSomething = false; 437 // <pre>/**/../<p>/<p>/<rest> -> {<pre>/../<p>/<p>/<rest>,<pre>/**/<p>/<p>/<rest>} 438 for (let parts of globParts) { 439 let gs = -1; 440 while (-1 !== (gs = parts.indexOf('**', gs + 1))) { 441 let gss = gs; 442 while (parts[gss + 1] === '**') { 443 // <pre>/**/**/<rest> -> <pre>/**/<rest> 444 gss++; 445 } 446 // eg, if gs is 2 and gss is 4, that means we have 3 ** 447 // parts, and can remove 2 of them. 448 if (gss > gs) { 449 parts.splice(gs + 1, gss - gs); 450 } 451 let next = parts[gs + 1]; 452 const p = parts[gs + 2]; 453 const p2 = parts[gs + 3]; 454 if (next !== '..') 455 continue; 456 if (!p || 457 p === '.' || 458 p === '..' || 459 !p2 || 460 p2 === '.' || 461 p2 === '..') { 462 continue; 463 } 464 didSomething = true; 465 // edit parts in place, and push the new one 466 parts.splice(gs, 1); 467 const other = parts.slice(0); 468 other[gs] = '**'; 469 globParts.push(other); 470 gs--; 471 } 472 // <pre>/<e>/<rest> -> <pre>/<rest> 473 if (!this.preserveMultipleSlashes) { 474 for (let i = 1; i < parts.length - 1; i++) { 475 const p = parts[i]; 476 // don't squeeze out UNC patterns 477 if (i === 1 && p === '' && parts[0] === '') 478 continue; 479 if (p === '.' || p === '') { 480 didSomething = true; 481 parts.splice(i, 1); 482 i--; 483 } 484 } 485 if (parts[0] === '.' && 486 parts.length === 2 && 487 (parts[1] === '.' || parts[1] === '')) { 488 didSomething = true; 489 parts.pop(); 490 } 491 } 492 // <pre>/<p>/../<rest> -> <pre>/<rest> 493 let dd = 0; 494 while (-1 !== (dd = parts.indexOf('..', dd + 1))) { 495 const p = parts[dd - 1]; 496 if (p && p !== '.' && p !== '..' && p !== '**') { 497 didSomething = true; 498 const needDot = dd === 1 && parts[dd + 1] === '**'; 499 const splin = needDot ? ['.'] : []; 500 parts.splice(dd - 1, 2, ...splin); 501 if (parts.length === 0) 502 parts.push(''); 503 dd -= 2; 504 } 505 } 506 } 507 } while (didSomething); 508 return globParts; 509 } 510 // second phase: multi-pattern dedupes 511 // {<pre>/*/<rest>,<pre>/<p>/<rest>} -> <pre>/*/<rest> 512 // {<pre>/<rest>,<pre>/<rest>} -> <pre>/<rest> 513 // {<pre>/**/<rest>,<pre>/<rest>} -> <pre>/**/<rest> 514 // 515 // {<pre>/**/<rest>,<pre>/**/<p>/<rest>} -> <pre>/**/<rest> 516 // ^-- not valid because ** doens't follow symlinks 517 secondPhasePreProcess(globParts) { 518 for (let i = 0; i < globParts.length - 1; i++) { 519 for (let j = i + 1; j < globParts.length; j++) { 520 const matched = this.partsMatch(globParts[i], globParts[j], !this.preserveMultipleSlashes); 521 if (!matched) 522 continue; 523 globParts[i] = matched; 524 globParts[j] = []; 525 } 526 } 527 return globParts.filter(gs => gs.length); 528 } 529 partsMatch(a, b, emptyGSMatch = false) { 530 let ai = 0; 531 let bi = 0; 532 let result = []; 533 let which = ''; 534 while (ai < a.length && bi < b.length) { 535 if (a[ai] === b[bi]) { 536 result.push(which === 'b' ? b[bi] : a[ai]); 537 ai++; 538 bi++; 539 } 540 else if (emptyGSMatch && a[ai] === '**' && b[bi] === a[ai + 1]) { 541 result.push(a[ai]); 542 ai++; 543 } 544 else if (emptyGSMatch && b[bi] === '**' && a[ai] === b[bi + 1]) { 545 result.push(b[bi]); 546 bi++; 547 } 548 else if (a[ai] === '*' && 549 b[bi] && 550 (this.options.dot || !b[bi].startsWith('.')) && 551 b[bi] !== '**') { 552 if (which === 'b') 553 return false; 554 which = 'a'; 555 result.push(a[ai]); 556 ai++; 557 bi++; 558 } 559 else if (b[bi] === '*' && 560 a[ai] && 561 (this.options.dot || !a[ai].startsWith('.')) && 562 a[ai] !== '**') { 563 if (which === 'a') 564 return false; 565 which = 'b'; 566 result.push(b[bi]); 567 ai++; 568 bi++; 569 } 570 else { 571 return false; 572 } 573 } 574 // if we fall out of the loop, it means they two are identical 575 // as long as their lengths match 576 return a.length === b.length && result; 577 } 578 parseNegate() { 579 if (this.nonegate) 580 return; 581 const pattern = this.pattern; 582 let negate = false; 583 let negateOffset = 0; 584 for (let i = 0; i < pattern.length && pattern.charAt(i) === '!'; i++) { 585 negate = !negate; 586 negateOffset++; 587 } 588 if (negateOffset) 589 this.pattern = pattern.slice(negateOffset); 590 this.negate = negate; 591 } 592 // set partial to true to test if, for example, 593 // "/a/b" matches the start of "/*/b/*/d" 594 // Partial means, if you run out of file before you run 595 // out of pattern, then that's fine, as long as all 596 // the parts match. 597 matchOne(file, pattern, partial = false) { 598 const options = this.options; 599 // UNC paths like //?/X:/... can match X:/... and vice versa 600 // Drive letters in absolute drive or unc paths are always compared 601 // case-insensitively. 602 if (this.isWindows) { 603 const fileDrive = typeof file[0] === 'string' && /^[a-z]:$/i.test(file[0]); 604 const fileUNC = !fileDrive && 605 file[0] === '' && 606 file[1] === '' && 607 file[2] === '?' && 608 /^[a-z]:$/i.test(file[3]); 609 const patternDrive = typeof pattern[0] === 'string' && /^[a-z]:$/i.test(pattern[0]); 610 const patternUNC = !patternDrive && 611 pattern[0] === '' && 612 pattern[1] === '' && 613 pattern[2] === '?' && 614 typeof pattern[3] === 'string' && 615 /^[a-z]:$/i.test(pattern[3]); 616 const fdi = fileUNC ? 3 : fileDrive ? 0 : undefined; 617 const pdi = patternUNC ? 3 : patternDrive ? 0 : undefined; 618 if (typeof fdi === 'number' && typeof pdi === 'number') { 619 const [fd, pd] = [file[fdi], pattern[pdi]]; 620 if (fd.toLowerCase() === pd.toLowerCase()) { 621 pattern[pdi] = fd; 622 if (pdi > fdi) { 623 pattern = pattern.slice(pdi); 624 } 625 else if (fdi > pdi) { 626 file = file.slice(fdi); 627 } 628 } 629 } 630 } 631 // resolve and reduce . and .. portions in the file as well. 632 // dont' need to do the second phase, because it's only one string[] 633 const { optimizationLevel = 1 } = this.options; 634 if (optimizationLevel >= 2) { 635 file = this.levelTwoFileOptimize(file); 636 } 637 this.debug('matchOne', this, { file, pattern }); 638 this.debug('matchOne', file.length, pattern.length); 639 for (var fi = 0, pi = 0, fl = file.length, pl = pattern.length; fi < fl && pi < pl; fi++, pi++) { 640 this.debug('matchOne loop'); 641 var p = pattern[pi]; 642 var f = file[fi]; 643 this.debug(pattern, p, f); 644 // should be impossible. 645 // some invalid regexp stuff in the set. 646 /* c8 ignore start */ 647 if (p === false) { 648 return false; 649 } 650 /* c8 ignore stop */ 651 if (p === GLOBSTAR) { 652 this.debug('GLOBSTAR', [pattern, p, f]); 653 // "**" 654 // a/**/b/**/c would match the following: 655 // a/b/x/y/z/c 656 // a/x/y/z/b/c 657 // a/b/x/b/x/c 658 // a/b/c 659 // To do this, take the rest of the pattern after 660 // the **, and see if it would match the file remainder. 661 // If so, return success. 662 // If not, the ** "swallows" a segment, and try again. 663 // This is recursively awful. 664 // 665 // a/**/b/**/c matching a/b/x/y/z/c 666 // - a matches a 667 // - doublestar 668 // - matchOne(b/x/y/z/c, b/**/c) 669 // - b matches b 670 // - doublestar 671 // - matchOne(x/y/z/c, c) -> no 672 // - matchOne(y/z/c, c) -> no 673 // - matchOne(z/c, c) -> no 674 // - matchOne(c, c) yes, hit 675 var fr = fi; 676 var pr = pi + 1; 677 if (pr === pl) { 678 this.debug('** at the end'); 679 // a ** at the end will just swallow the rest. 680 // We have found a match. 681 // however, it will not swallow /.x, unless 682 // options.dot is set. 683 // . and .. are *never* matched by **, for explosively 684 // exponential reasons. 685 for (; fi < fl; fi++) { 686 if (file[fi] === '.' || 687 file[fi] === '..' || 688 (!options.dot && file[fi].charAt(0) === '.')) 689 return false; 690 } 691 return true; 692 } 693 // ok, let's see if we can swallow whatever we can. 694 while (fr < fl) { 695 var swallowee = file[fr]; 696 this.debug('\nglobstar while', file, fr, pattern, pr, swallowee); 697 // XXX remove this slice. Just pass the start index. 698 if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) { 699 this.debug('globstar found match!', fr, fl, swallowee); 700 // found a match. 701 return true; 702 } 703 else { 704 // can't swallow "." or ".." ever. 705 // can only swallow ".foo" when explicitly asked. 706 if (swallowee === '.' || 707 swallowee === '..' || 708 (!options.dot && swallowee.charAt(0) === '.')) { 709 this.debug('dot detected!', file, fr, pattern, pr); 710 break; 711 } 712 // ** swallows a segment, and continue. 713 this.debug('globstar swallow a segment, and continue'); 714 fr++; 715 } 716 } 717 // no match was found. 718 // However, in partial mode, we can't say this is necessarily over. 719 /* c8 ignore start */ 720 if (partial) { 721 // ran out of file 722 this.debug('\n>>> no match, partial?', file, fr, pattern, pr); 723 if (fr === fl) { 724 return true; 725 } 726 } 727 /* c8 ignore stop */ 728 return false; 729 } 730 // something other than ** 731 // non-magic patterns just have to match exactly 732 // patterns with magic have been turned into regexps. 733 let hit; 734 if (typeof p === 'string') { 735 hit = f === p; 736 this.debug('string match', p, f, hit); 737 } 738 else { 739 hit = p.test(f); 740 this.debug('pattern match', p, f, hit); 741 } 742 if (!hit) 743 return false; 744 } 745 // Note: ending in / means that we'll get a final "" 746 // at the end of the pattern. This can only match a 747 // corresponding "" at the end of the file. 748 // If the file ends in /, then it can only match a 749 // a pattern that ends in /, unless the pattern just 750 // doesn't have any more for it. But, a/b/ should *not* 751 // match "a/b/*", even though "" matches against the 752 // [^/]*? pattern, except in partial mode, where it might 753 // simply not be reached yet. 754 // However, a/b/ should still satisfy a/* 755 // now either we fell off the end of the pattern, or we're done. 756 if (fi === fl && pi === pl) { 757 // ran out of pattern and filename at the same time. 758 // an exact hit! 759 return true; 760 } 761 else if (fi === fl) { 762 // ran out of file, but still had pattern left. 763 // this is ok if we're doing the match as part of 764 // a glob fs traversal. 765 return partial; 766 } 767 else if (pi === pl) { 768 // ran out of pattern, still have file left. 769 // this is only acceptable if we're on the very last 770 // empty segment of a file with a trailing slash. 771 // a/* should match a/b/ 772 return fi === fl - 1 && file[fi] === ''; 773 /* c8 ignore start */ 774 } 775 else { 776 // should be unreachable. 777 throw new Error('wtf?'); 778 } 779 /* c8 ignore stop */ 780 } 781 braceExpand() { 782 return braceExpand(this.pattern, this.options); 783 } 784 parse(pattern) { 785 assertValidPattern(pattern); 786 const options = this.options; 787 // shortcuts 788 if (pattern === '**') 789 return GLOBSTAR; 790 if (pattern === '') 791 return ''; 792 // far and away, the most common glob pattern parts are 793 // *, *.*, and *.<ext> Add a fast check method for those. 794 let m; 795 let fastTest = null; 796 if ((m = pattern.match(starRE))) { 797 fastTest = options.dot ? starTestDot : starTest; 798 } 799 else if ((m = pattern.match(starDotExtRE))) { 800 fastTest = (options.nocase 801 ? options.dot 802 ? starDotExtTestNocaseDot 803 : starDotExtTestNocase 804 : options.dot 805 ? starDotExtTestDot 806 : starDotExtTest)(m[1]); 807 } 808 else if ((m = pattern.match(qmarksRE))) { 809 fastTest = (options.nocase 810 ? options.dot 811 ? qmarksTestNocaseDot 812 : qmarksTestNocase 813 : options.dot 814 ? qmarksTestDot 815 : qmarksTest)(m); 816 } 817 else if ((m = pattern.match(starDotStarRE))) { 818 fastTest = options.dot ? starDotStarTestDot : starDotStarTest; 819 } 820 else if ((m = pattern.match(dotStarRE))) { 821 fastTest = dotStarTest; 822 } 823 const re = AST.fromGlob(pattern, this.options).toMMPattern(); 824 return fastTest ? Object.assign(re, { test: fastTest }) : re; 825 } 826 makeRe() { 827 if (this.regexp || this.regexp === false) 828 return this.regexp; 829 // at this point, this.set is a 2d array of partial 830 // pattern strings, or "**". 831 // 832 // It's better to use .match(). This function shouldn't 833 // be used, really, but it's pretty convenient sometimes, 834 // when you just want to work with a regex. 835 const set = this.set; 836 if (!set.length) { 837 this.regexp = false; 838 return this.regexp; 839 } 840 const options = this.options; 841 const twoStar = options.noglobstar 842 ? star 843 : options.dot 844 ? twoStarDot 845 : twoStarNoDot; 846 const flags = new Set(options.nocase ? ['i'] : []); 847 // regexpify non-globstar patterns 848 // if ** is only item, then we just do one twoStar 849 // if ** is first, and there are more, prepend (\/|twoStar\/)? to next 850 // if ** is last, append (\/twoStar|) to previous 851 // if ** is in the middle, append (\/|\/twoStar\/) to previous 852 // then filter out GLOBSTAR symbols 853 let re = set 854 .map(pattern => { 855 const pp = pattern.map(p => { 856 if (p instanceof RegExp) { 857 for (const f of p.flags.split('')) 858 flags.add(f); 859 } 860 return typeof p === 'string' 861 ? regExpEscape(p) 862 : p === GLOBSTAR 863 ? GLOBSTAR 864 : p._src; 865 }); 866 pp.forEach((p, i) => { 867 const next = pp[i + 1]; 868 const prev = pp[i - 1]; 869 if (p !== GLOBSTAR || prev === GLOBSTAR) { 870 return; 871 } 872 if (prev === undefined) { 873 if (next !== undefined && next !== GLOBSTAR) { 874 pp[i + 1] = '(?:\\/|' + twoStar + '\\/)?' + next; 875 } 876 else { 877 pp[i] = twoStar; 878 } 879 } 880 else if (next === undefined) { 881 pp[i - 1] = prev + '(?:\\/|' + twoStar + ')?'; 882 } 883 else if (next !== GLOBSTAR) { 884 pp[i - 1] = prev + '(?:\\/|\\/' + twoStar + '\\/)' + next; 885 pp[i + 1] = GLOBSTAR; 886 } 887 }); 888 return pp.filter(p => p !== GLOBSTAR).join('/'); 889 }) 890 .join('|'); 891 // need to wrap in parens if we had more than one thing with |, 892 // otherwise only the first will be anchored to ^ and the last to $ 893 const [open, close] = set.length > 1 ? ['(?:', ')'] : ['', '']; 894 // must match entire pattern 895 // ending in a * or ** will make it less strict. 896 re = '^' + open + re + close + '$'; 897 // can match anything, as long as it's not this. 898 if (this.negate) 899 re = '^(?!' + re + ').+$'; 900 try { 901 this.regexp = new RegExp(re, [...flags].join('')); 902 /* c8 ignore start */ 903 } 904 catch (ex) { 905 // should be impossible 906 this.regexp = false; 907 } 908 /* c8 ignore stop */ 909 return this.regexp; 910 } 911 slashSplit(p) { 912 // if p starts with // on windows, we preserve that 913 // so that UNC paths aren't broken. Otherwise, any number of 914 // / characters are coalesced into one, unless 915 // preserveMultipleSlashes is set to true. 916 if (this.preserveMultipleSlashes) { 917 return p.split('/'); 918 } 919 else if (this.isWindows && /^\/\/[^\/]+/.test(p)) { 920 // add an extra '' for the one we lose 921 return ['', ...p.split(/\/+/)]; 922 } 923 else { 924 return p.split(/\/+/); 925 } 926 } 927 match(f, partial = this.partial) { 928 this.debug('match', f, this.pattern); 929 // short-circuit in the case of busted things. 930 // comments, etc. 931 if (this.comment) { 932 return false; 933 } 934 if (this.empty) { 935 return f === ''; 936 } 937 if (f === '/' && partial) { 938 return true; 939 } 940 const options = this.options; 941 // windows: need to use /, not \ 942 if (this.isWindows) { 943 f = f.split('\\').join('/'); 944 } 945 // treat the test path as a set of pathparts. 946 const ff = this.slashSplit(f); 947 this.debug(this.pattern, 'split', ff); 948 // just ONE of the pattern sets in this.set needs to match 949 // in order for it to be valid. If negating, then just one 950 // match means that we have failed. 951 // Either way, return on the first hit. 952 const set = this.set; 953 this.debug(this.pattern, 'set', set); 954 // Find the basename of the path by looking for the last non-empty segment 955 let filename = ff[ff.length - 1]; 956 if (!filename) { 957 for (let i = ff.length - 2; !filename && i >= 0; i--) { 958 filename = ff[i]; 959 } 960 } 961 for (let i = 0; i < set.length; i++) { 962 const pattern = set[i]; 963 let file = ff; 964 if (options.matchBase && pattern.length === 1) { 965 file = [filename]; 966 } 967 const hit = this.matchOne(file, pattern, partial); 968 if (hit) { 969 if (options.flipNegate) { 970 return true; 971 } 972 return !this.negate; 973 } 974 } 975 // didn't get any hits. this is success if it's a negative 976 // pattern, failure otherwise. 977 if (options.flipNegate) { 978 return false; 979 } 980 return this.negate; 981 } 982 static defaults(def) { 983 return minimatch.defaults(def).Minimatch; 984 } 985} 986/* c8 ignore start */ 987export { AST } from './ast.js'; 988export { escape } from './escape.js'; 989export { unescape } from './unescape.js'; 990/* c8 ignore stop */ 991minimatch.AST = AST; 992minimatch.Minimatch = Minimatch; 993minimatch.escape = escape; 994minimatch.unescape = unescape; 995//# sourceMappingURL=index.js.map