opusdev/vector-similarity-api
1
1module.exports = minimatch2minimatch.Minimatch = Minimatch3 4var path = (function () { try { return require('path') } catch (e) {}}()) || {5 sep: '/'6}7minimatch.sep = path.sep8 9var GLOBSTAR = minimatch.GLOBSTAR = Minimatch.GLOBSTAR = {}10var expand = require('brace-expansion')11 12var plTypes = {13 '!': { open: '(?:(?!(?:', close: '))[^/]*?)'},14 '?': { open: '(?:', close: ')?' },15 '+': { open: '(?:', close: ')+' },16 '*': { open: '(?:', close: ')*' },17 '@': { open: '(?:', close: ')' }18}19 20// any single thing other than /21// don't need to escape / when using new RegExp()22var qmark = '[^/]'23 24// * => any number of characters25var star = qmark + '*?'26 27// ** when dots are allowed. Anything goes, except .. and .28// not (^ or / followed by one or two dots followed by $ or /),29// followed by anything, any number of times.30var twoStarDot = '(?:(?!(?:\\\/|^)(?:\\.{1,2})($|\\\/)).)*?'31 32// not a ^ or / followed by a dot,33// followed by anything, any number of times.34var twoStarNoDot = '(?:(?!(?:\\\/|^)\\.).)*?'35 36// characters that need to be escaped in RegExp.37var reSpecials = charSet('().*{}+?[]^$\\!')38 39// "abc" -> { a:true, b:true, c:true }40function charSet (s) {41 return s.split('').reduce(function (set, c) {42 set[c] = true43 return set44 }, {})45}46 47// normalizes slashes.48var slashSplit = /\/+/49 50minimatch.filter = filter51function filter (pattern, options) {52 options = options || {}53 return function (p, i, list) {54 return minimatch(p, pattern, options)55 }56}57 58function ext (a, b) {59 b = b || {}60 var t = {}61 Object.keys(a).forEach(function (k) {62 t[k] = a[k]63 })64 Object.keys(b).forEach(function (k) {65 t[k] = b[k]66 })67 return t68}69 70minimatch.defaults = function (def) {71 if (!def || typeof def !== 'object' || !Object.keys(def).length) {72 return minimatch73 }74 75 var orig = minimatch76 77 var m = function minimatch (p, pattern, options) {78 return orig(p, pattern, ext(def, options))79 }80 81 m.Minimatch = function Minimatch (pattern, options) {82 return new orig.Minimatch(pattern, ext(def, options))83 }84 m.Minimatch.defaults = function defaults (options) {85 return orig.defaults(ext(def, options)).Minimatch86 }87 88 m.filter = function filter (pattern, options) {89 return orig.filter(pattern, ext(def, options))90 }91 92 m.defaults = function defaults (options) {93 return orig.defaults(ext(def, options))94 }95 96 m.makeRe = function makeRe (pattern, options) {97 return orig.makeRe(pattern, ext(def, options))98 }99 100 m.braceExpand = function braceExpand (pattern, options) {101 return orig.braceExpand(pattern, ext(def, options))102 }103 104 m.match = function (list, pattern, options) {105 return orig.match(list, pattern, ext(def, options))106 }107 108 return m109}110 111Minimatch.defaults = function (def) {112 return minimatch.defaults(def).Minimatch113}114 115function minimatch (p, pattern, options) {116 assertValidPattern(pattern)117 118 if (!options) options = {}119 120 // shortcut: comments match nothing.121 if (!options.nocomment && pattern.charAt(0) === '#') {122 return false123 }124 125 return new Minimatch(pattern, options).match(p)126}127 128function Minimatch (pattern, options) {129 if (!(this instanceof Minimatch)) {130 return new Minimatch(pattern, options)131 }132 133 assertValidPattern(pattern)134 135 if (!options) options = {}136 137 pattern = pattern.trim()138 139 // windows support: need to use /, not \140 if (!options.allowWindowsEscape && path.sep !== '/') {141 pattern = pattern.split(path.sep).join('/')142 }143 144 this.options = options145 this.set = []146 this.pattern = pattern147 this.regexp = null148 this.negate = false149 this.comment = false150 this.empty = false151 this.partial = !!options.partial152 153 // make the set of regexps etc.154 this.make()155}156 157Minimatch.prototype.debug = function () {}158 159Minimatch.prototype.make = make160function make () {161 var pattern = this.pattern162 var options = this.options163 164 // empty patterns and comments match nothing.165 if (!options.nocomment && pattern.charAt(0) === '#') {166 this.comment = true167 return168 }169 if (!pattern) {170 this.empty = true171 return172 }173 174 // step 1: figure out negation, etc.175 this.parseNegate()176 177 // step 2: expand braces178 var set = this.globSet = this.braceExpand()179 180 if (options.debug) this.debug = function debug() { console.error.apply(console, arguments) }181 182 this.debug(this.pattern, set)183 184 // step 3: now we have a set, so turn each one into a series of path-portion185 // matching patterns.186 // These will be regexps, except in the case of "**", which is187 // set to the GLOBSTAR object for globstar behavior,188 // and will not contain any / characters189 set = this.globParts = set.map(function (s) {190 return s.split(slashSplit)191 })192 193 this.debug(this.pattern, set)194 195 // glob --> regexps196 set = set.map(function (s, si, set) {197 return s.map(this.parse, this)198 }, this)199 200 this.debug(this.pattern, set)201 202 // filter out everything that didn't compile properly.203 set = set.filter(function (s) {204 return s.indexOf(false) === -1205 })206 207 this.debug(this.pattern, set)208 209 this.set = set210}211 212Minimatch.prototype.parseNegate = parseNegate213function parseNegate () {214 var pattern = this.pattern215 var negate = false216 var options = this.options217 var negateOffset = 0218 219 if (options.nonegate) return220 221 for (var i = 0, l = pattern.length222 ; i < l && pattern.charAt(i) === '!'223 ; i++) {224 negate = !negate225 negateOffset++226 }227 228 if (negateOffset) this.pattern = pattern.substr(negateOffset)229 this.negate = negate230}231 232// Brace expansion:233// a{b,c}d -> abd acd234// a{b,}c -> abc ac235// a{0..3}d -> a0d a1d a2d a3d236// a{b,c{d,e}f}g -> abg acdfg acefg237// a{b,c}d{e,f}g -> abdeg acdeg abdeg abdfg238//239// Invalid sets are not expanded.240// a{2..}b -> a{2..}b241// a{b}c -> a{b}c242minimatch.braceExpand = function (pattern, options) {243 return braceExpand(pattern, options)244}245 246Minimatch.prototype.braceExpand = braceExpand247 248function braceExpand (pattern, options) {249 if (!options) {250 if (this instanceof Minimatch) {251 options = this.options252 } else {253 options = {}254 }255 }256 257 pattern = typeof pattern === 'undefined'258 ? this.pattern : pattern259 260 assertValidPattern(pattern)261 262 // Thanks to Yeting Li <https://github.com/yetingli> for263 // improving this regexp to avoid a ReDOS vulnerability.264 if (options.nobrace || !/\{(?:(?!\{).)*\}/.test(pattern)) {265 // shortcut. no need to expand.266 return [pattern]267 }268 269 return expand(pattern)270}271 272var MAX_PATTERN_LENGTH = 1024 * 64273var assertValidPattern = function (pattern) {274 if (typeof pattern !== 'string') {275 throw new TypeError('invalid pattern')276 }277 278 if (pattern.length > MAX_PATTERN_LENGTH) {279 throw new TypeError('pattern is too long')280 }281}282 283// parse a component of the expanded set.284// At this point, no pattern may contain "/" in it285// so we're going to return a 2d array, where each entry is the full286// pattern, split on '/', and then turned into a regular expression.287// A regexp is made at the end which joins each array with an288// escaped /, and another full one which joins each regexp with |.289//290// Following the lead of Bash 4.1, note that "**" only has special meaning291// when it is the *only* thing in a path portion. Otherwise, any series292// of * is equivalent to a single *. Globstar behavior is enabled by293// default, and can be disabled by setting options.noglobstar.294Minimatch.prototype.parse = parse295var SUBPARSE = {}296function parse (pattern, isSub) {297 assertValidPattern(pattern)298 299 var options = this.options300 301 // shortcuts302 if (pattern === '**') {303 if (!options.noglobstar)304 return GLOBSTAR305 else306 pattern = '*'307 }308 if (pattern === '') return ''309 310 var re = ''311 var hasMagic = !!options.nocase312 var escaping = false313 // ? => one single character314 var patternListStack = []315 var negativeLists = []316 var stateChar317 var inClass = false318 var reClassStart = -1319 var classStart = -1320 // . and .. never match anything that doesn't start with .,321 // even when options.dot is set.322 var patternStart = pattern.charAt(0) === '.' ? '' // anything323 // not (start or / followed by . or .. followed by / or end)324 : options.dot ? '(?!(?:^|\\\/)\\.{1,2}(?:$|\\\/))'325 : '(?!\\.)'326 var self = this327 328 function clearStateChar () {329 if (stateChar) {330 // we had some state-tracking character331 // that wasn't consumed by this pass.332 switch (stateChar) {333 case '*':334 re += star335 hasMagic = true336 break337 case '?':338 re += qmark339 hasMagic = true340 break341 default:342 re += '\\' + stateChar343 break344 }345 self.debug('clearStateChar %j %j', stateChar, re)346 stateChar = false347 }348 }349 350 for (var i = 0, len = pattern.length, c351 ; (i < len) && (c = pattern.charAt(i))352 ; i++) {353 this.debug('%s\t%s %s %j', pattern, i, re, c)354 355 // skip over any that are escaped.356 if (escaping && reSpecials[c]) {357 re += '\\' + c358 escaping = false359 continue360 }361 362 switch (c) {363 /* istanbul ignore next */364 case '/': {365 // completely not allowed, even escaped.366 // Should already be path-split by now.367 return false368 }369 370 case '\\':371 clearStateChar()372 escaping = true373 continue374 375 // the various stateChar values376 // for the "extglob" stuff.377 case '?':378 case '*':379 case '+':380 case '@':381 case '!':382 this.debug('%s\t%s %s %j <-- stateChar', pattern, i, re, c)383 384 // all of those are literals inside a class, except that385 // the glob [!a] means [^a] in regexp386 if (inClass) {387 this.debug(' in class')388 if (c === '!' && i === classStart + 1) c = '^'389 re += c390 continue391 }392 393 // if we already have a stateChar, then it means394 // that there was something like ** or +? in there.395 // Handle the stateChar, then proceed with this one.396 self.debug('call clearStateChar %j', stateChar)397 clearStateChar()398 stateChar = c399 // if extglob is disabled, then +(asdf|foo) isn't a thing.400 // just clear the statechar *now*, rather than even diving into401 // the patternList stuff.402 if (options.noext) clearStateChar()403 continue404 405 case '(':406 if (inClass) {407 re += '('408 continue409 }410 411 if (!stateChar) {412 re += '\\('413 continue414 }415 416 patternListStack.push({417 type: stateChar,418 start: i - 1,419 reStart: re.length,420 open: plTypes[stateChar].open,421 close: plTypes[stateChar].close422 })423 // negation is (?:(?!js)[^/]*)424 re += stateChar === '!' ? '(?:(?!(?:' : '(?:'425 this.debug('plType %j %j', stateChar, re)426 stateChar = false427 continue428 429 case ')':430 if (inClass || !patternListStack.length) {431 re += '\\)'432 continue433 }434 435 clearStateChar()436 hasMagic = true437 var pl = patternListStack.pop()438 // negation is (?:(?!js)[^/]*)439 // The others are (?:<pattern>)<type>440 re += pl.close441 if (pl.type === '!') {442 negativeLists.push(pl)443 }444 pl.reEnd = re.length445 continue446 447 case '|':448 if (inClass || !patternListStack.length || escaping) {449 re += '\\|'450 escaping = false451 continue452 }453 454 clearStateChar()455 re += '|'456 continue457 458 // these are mostly the same in regexp and glob459 case '[':460 // swallow any state-tracking char before the [461 clearStateChar()462 463 if (inClass) {464 re += '\\' + c465 continue466 }467 468 inClass = true469 classStart = i470 reClassStart = re.length471 re += c472 continue473 474 case ']':475 // a right bracket shall lose its special476 // meaning and represent itself in477 // a bracket expression if it occurs478 // first in the list. -- POSIX.2 2.8.3.2479 if (i === classStart + 1 || !inClass) {480 re += '\\' + c481 escaping = false482 continue483 }484 485 // handle the case where we left a class open.486 // "[z-a]" is valid, equivalent to "\[z-a\]"487 // split where the last [ was, make sure we don't have488 // an invalid re. if so, re-walk the contents of the489 // would-be class to re-translate any characters that490 // were passed through as-is491 // TODO: It would probably be faster to determine this492 // without a try/catch and a new RegExp, but it's tricky493 // to do safely. For now, this is safe and works.494 var cs = pattern.substring(classStart + 1, i)495 try {496 RegExp('[' + cs + ']')497 } catch (er) {498 // not a valid class!499 var sp = this.parse(cs, SUBPARSE)500 re = re.substr(0, reClassStart) + '\\[' + sp[0] + '\\]'501 hasMagic = hasMagic || sp[1]502 inClass = false503 continue504 }505 506 // finish up the class.507 hasMagic = true508 inClass = false509 re += c510 continue511 512 default:513 // swallow any state char that wasn't consumed514 clearStateChar()515 516 if (escaping) {517 // no need518 escaping = false519 } else if (reSpecials[c]520 && !(c === '^' && inClass)) {521 re += '\\'522 }523 524 re += c525 526 } // switch527 } // for528 529 // handle the case where we left a class open.530 // "[abc" is valid, equivalent to "\[abc"531 if (inClass) {532 // split where the last [ was, and escape it533 // this is a huge pita. We now have to re-walk534 // the contents of the would-be class to re-translate535 // any characters that were passed through as-is536 cs = pattern.substr(classStart + 1)537 sp = this.parse(cs, SUBPARSE)538 re = re.substr(0, reClassStart) + '\\[' + sp[0]539 hasMagic = hasMagic || sp[1]540 }541 542 // handle the case where we had a +( thing at the *end*543 // of the pattern.544 // each pattern list stack adds 3 chars, and we need to go through545 // and escape any | chars that were passed through as-is for the regexp.546 // Go through and escape them, taking care not to double-escape any547 // | chars that were already escaped.548 for (pl = patternListStack.pop(); pl; pl = patternListStack.pop()) {549 var tail = re.slice(pl.reStart + pl.open.length)550 this.debug('setting tail', re, pl)551 // maybe some even number of \, then maybe 1 \, followed by a |552 tail = tail.replace(/((?:\\{2}){0,64})(\\?)\|/g, function (_, $1, $2) {553 if (!$2) {554 // the | isn't already escaped, so escape it.555 $2 = '\\'556 }557 558 // need to escape all those slashes *again*, without escaping the559 // one that we need for escaping the | character. As it works out,560 // escaping an even number of slashes can be done by simply repeating561 // it exactly after itself. That's why this trick works.562 //563 // I am sorry that you have to see this.564 return $1 + $1 + $2 + '|'565 })566 567 this.debug('tail=%j\n %s', tail, tail, pl, re)568 var t = pl.type === '*' ? star569 : pl.type === '?' ? qmark570 : '\\' + pl.type571 572 hasMagic = true573 re = re.slice(0, pl.reStart) + t + '\\(' + tail574 }575 576 // handle trailing things that only matter at the very end.577 clearStateChar()578 if (escaping) {579 // trailing \\580 re += '\\\\'581 }582 583 // only need to apply the nodot start if the re starts with584 // something that could conceivably capture a dot585 var addPatternStart = false586 switch (re.charAt(0)) {587 case '[': case '.': case '(': addPatternStart = true588 }589 590 // Hack to work around lack of negative lookbehind in JS591 // A pattern like: *.!(x).!(y|z) needs to ensure that a name592 // like 'a.xyz.yz' doesn't match. So, the first negative593 // lookahead, has to look ALL the way ahead, to the end of594 // the pattern.595 for (var n = negativeLists.length - 1; n > -1; n--) {596 var nl = negativeLists[n]597 598 var nlBefore = re.slice(0, nl.reStart)599 var nlFirst = re.slice(nl.reStart, nl.reEnd - 8)600 var nlLast = re.slice(nl.reEnd - 8, nl.reEnd)601 var nlAfter = re.slice(nl.reEnd)602 603 nlLast += nlAfter604 605 // Handle nested stuff like *(*.js|!(*.json)), where open parens606 // mean that we should *not* include the ) in the bit that is considered607 // "after" the negated section.608 var openParensBefore = nlBefore.split('(').length - 1609 var cleanAfter = nlAfter610 for (i = 0; i < openParensBefore; i++) {611 cleanAfter = cleanAfter.replace(/\)[+*?]?/, '')612 }613 nlAfter = cleanAfter614 615 var dollar = ''616 if (nlAfter === '' && isSub !== SUBPARSE) {617 dollar = '$'618 }619 var newRe = nlBefore + nlFirst + nlAfter + dollar + nlLast620 re = newRe621 }622 623 // if the re is not "" at this point, then we need to make sure624 // it doesn't match against an empty path part.625 // Otherwise a/* will match a/, which it should not.626 if (re !== '' && hasMagic) {627 re = '(?=.)' + re628 }629 630 if (addPatternStart) {631 re = patternStart + re632 }633 634 // parsing just a piece of a larger pattern.635 if (isSub === SUBPARSE) {636 return [re, hasMagic]637 }638 639 // skip the regexp for non-magical patterns640 // unescape anything in it, though, so that it'll be641 // an exact match against a file etc.642 if (!hasMagic) {643 return globUnescape(pattern)644 }645 646 var flags = options.nocase ? 'i' : ''647 try {648 var regExp = new RegExp('^' + re + '$', flags)649 } catch (er) /* istanbul ignore next - should be impossible */ {650 // If it was an invalid regular expression, then it can't match651 // anything. This trick looks for a character after the end of652 // the string, which is of course impossible, except in multi-line653 // mode, but it's not a /m regex.654 return new RegExp('$.')655 }656 657 regExp._glob = pattern658 regExp._src = re659 660 return regExp661}662 663minimatch.makeRe = function (pattern, options) {664 return new Minimatch(pattern, options || {}).makeRe()665}666 667Minimatch.prototype.makeRe = makeRe668function makeRe () {669 if (this.regexp || this.regexp === false) return this.regexp670 671 // at this point, this.set is a 2d array of partial672 // pattern strings, or "**".673 //674 // It's better to use .match(). This function shouldn't675 // be used, really, but it's pretty convenient sometimes,676 // when you just want to work with a regex.677 var set = this.set678 679 if (!set.length) {680 this.regexp = false681 return this.regexp682 }683 var options = this.options684 685 var twoStar = options.noglobstar ? star686 : options.dot ? twoStarDot687 : twoStarNoDot688 var flags = options.nocase ? 'i' : ''689 690 var re = set.map(function (pattern) {691 return pattern.map(function (p) {692 return (p === GLOBSTAR) ? twoStar693 : (typeof p === 'string') ? regExpEscape(p)694 : p._src695 }).join('\\\/')696 }).join('|')697 698 // must match entire pattern699 // ending in a * or ** will make it less strict.700 re = '^(?:' + re + ')$'701 702 // can match anything, as long as it's not this.703 if (this.negate) re = '^(?!' + re + ').*$'704 705 try {706 this.regexp = new RegExp(re, flags)707 } catch (ex) /* istanbul ignore next - should be impossible */ {708 this.regexp = false709 }710 return this.regexp711}712 713minimatch.match = function (list, pattern, options) {714 options = options || {}715 var mm = new Minimatch(pattern, options)716 list = list.filter(function (f) {717 return mm.match(f)718 })719 if (mm.options.nonull && !list.length) {720 list.push(pattern)721 }722 return list723}724 725Minimatch.prototype.match = function match (f, partial) {726 if (typeof partial === 'undefined') partial = this.partial727 this.debug('match', f, this.pattern)728 // short-circuit in the case of busted things.729 // comments, etc.730 if (this.comment) return false731 if (this.empty) return f === ''732 733 if (f === '/' && partial) return true734 735 var options = this.options736 737 // windows: need to use /, not \738 if (path.sep !== '/') {739 f = f.split(path.sep).join('/')740 }741 742 // treat the test path as a set of pathparts.743 f = f.split(slashSplit)744 this.debug(this.pattern, 'split', f)745 746 // just ONE of the pattern sets in this.set needs to match747 // in order for it to be valid. If negating, then just one748 // match means that we have failed.749 // Either way, return on the first hit.750 751 var set = this.set752 this.debug(this.pattern, 'set', set)753 754 // Find the basename of the path by looking for the last non-empty segment755 var filename756 var i757 for (i = f.length - 1; i >= 0; i--) {758 filename = f[i]759 if (filename) break760 }761 762 for (i = 0; i < set.length; i++) {763 var pattern = set[i]764 var file = f765 if (options.matchBase && pattern.length === 1) {766 file = [filename]767 }768 var hit = this.matchOne(file, pattern, partial)769 if (hit) {770 if (options.flipNegate) return true771 return !this.negate772 }773 }774 775 // didn't get any hits. this is success if it's a negative776 // pattern, failure otherwise.777 if (options.flipNegate) return false778 return this.negate779}780 781// set partial to true to test if, for example,782// "/a/b" matches the start of "/*/b/*/d"783// Partial means, if you run out of file before you run784// out of pattern, then that's fine, as long as all785// the parts match.786Minimatch.prototype.matchOne = function (file, pattern, partial) {787 var options = this.options788 789 this.debug('matchOne',790 { 'this': this, file: file, pattern: pattern })791 792 this.debug('matchOne', file.length, pattern.length)793 794 for (var fi = 0,795 pi = 0,796 fl = file.length,797 pl = pattern.length798 ; (fi < fl) && (pi < pl)799 ; fi++, pi++) {800 this.debug('matchOne loop')801 var p = pattern[pi]802 var f = file[fi]803 804 this.debug(pattern, p, f)805 806 // should be impossible.807 // some invalid regexp stuff in the set.808 /* istanbul ignore if */809 if (p === false) return false810 811 if (p === GLOBSTAR) {812 this.debug('GLOBSTAR', [pattern, p, f])813 814 // "**"815 // a/**/b/**/c would match the following:816 // a/b/x/y/z/c817 // a/x/y/z/b/c818 // a/b/x/b/x/c819 // a/b/c820 // To do this, take the rest of the pattern after821 // the **, and see if it would match the file remainder.822 // If so, return success.823 // If not, the ** "swallows" a segment, and try again.824 // This is recursively awful.825 //826 // a/**/b/**/c matching a/b/x/y/z/c827 // - a matches a828 // - doublestar829 // - matchOne(b/x/y/z/c, b/**/c)830 // - b matches b831 // - doublestar832 // - matchOne(x/y/z/c, c) -> no833 // - matchOne(y/z/c, c) -> no834 // - matchOne(z/c, c) -> no835 // - matchOne(c, c) yes, hit836 var fr = fi837 var pr = pi + 1838 if (pr === pl) {839 this.debug('** at the end')840 // a ** at the end will just swallow the rest.841 // We have found a match.842 // however, it will not swallow /.x, unless843 // options.dot is set.844 // . and .. are *never* matched by **, for explosively845 // exponential reasons.846 for (; fi < fl; fi++) {847 if (file[fi] === '.' || file[fi] === '..' ||848 (!options.dot && file[fi].charAt(0) === '.')) return false849 }850 return true851 }852 853 // ok, let's see if we can swallow whatever we can.854 while (fr < fl) {855 var swallowee = file[fr]856 857 this.debug('\nglobstar while', file, fr, pattern, pr, swallowee)858 859 // XXX remove this slice. Just pass the start index.860 if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) {861 this.debug('globstar found match!', fr, fl, swallowee)862 // found a match.863 return true864 } else {865 // can't swallow "." or ".." ever.866 // can only swallow ".foo" when explicitly asked.867 if (swallowee === '.' || swallowee === '..' ||868 (!options.dot && swallowee.charAt(0) === '.')) {869 this.debug('dot detected!', file, fr, pattern, pr)870 break871 }872 873 // ** swallows a segment, and continue.874 this.debug('globstar swallow a segment, and continue')875 fr++876 }877 }878 879 // no match was found.880 // However, in partial mode, we can't say this is necessarily over.881 // If there's more *pattern* left, then882 /* istanbul ignore if */883 if (partial) {884 // ran out of file885 this.debug('\n>>> no match, partial?', file, fr, pattern, pr)886 if (fr === fl) return true887 }888 return false889 }890 891 // something other than **892 // non-magic patterns just have to match exactly893 // patterns with magic have been turned into regexps.894 var hit895 if (typeof p === 'string') {896 hit = f === p897 this.debug('string match', p, f, hit)898 } else {899 hit = f.match(p)900 this.debug('pattern match', p, f, hit)901 }902 903 if (!hit) return false904 }905 906 // Note: ending in / means that we'll get a final ""907 // at the end of the pattern. This can only match a908 // corresponding "" at the end of the file.909 // If the file ends in /, then it can only match a910 // a pattern that ends in /, unless the pattern just911 // doesn't have any more for it. But, a/b/ should *not*912 // match "a/b/*", even though "" matches against the913 // [^/]*? pattern, except in partial mode, where it might914 // simply not be reached yet.915 // However, a/b/ should still satisfy a/*916 917 // now either we fell off the end of the pattern, or we're done.918 if (fi === fl && pi === pl) {919 // ran out of pattern and filename at the same time.920 // an exact hit!921 return true922 } else if (fi === fl) {923 // ran out of file, but still had pattern left.924 // this is ok if we're doing the match as part of925 // a glob fs traversal.926 return partial927 } else /* istanbul ignore else */ if (pi === pl) {928 // ran out of pattern, still have file left.929 // this is only acceptable if we're on the very last930 // empty segment of a file with a trailing slash.931 // a/* should match a/b/932 return (fi === fl - 1) && (file[fi] === '')933 }934 935 // should be unreachable.936 /* istanbul ignore next */937 throw new Error('wtf?')938}939 940// replace stuff like \* with *941function globUnescape (s) {942 return s.replace(/\\(.)/g, '$1')943}944 945function regExpEscape (s) {946 return s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')947}948 