You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
130 lines
3.4 KiB
130 lines
3.4 KiB
module.exports = function (glob, opts) { |
|
if (typeof glob !== 'string') { |
|
throw new TypeError('Expected a string'); |
|
} |
|
|
|
var str = String(glob); |
|
|
|
// The regexp we are building, as a string. |
|
var reStr = ""; |
|
|
|
// Whether we are matching so called "extended" globs (like bash) and should |
|
// support single character matching, matching ranges of characters, group |
|
// matching, etc. |
|
var extended = opts ? !!opts.extended : false; |
|
|
|
// When globstar is _false_ (default), '/foo/*' is translated a regexp like |
|
// '^\/foo\/.*$' which will match any string beginning with '/foo/' |
|
// When globstar is _true_, '/foo/*' is translated to regexp like |
|
// '^\/foo\/[^/]*$' which will match any string beginning with '/foo/' BUT |
|
// which does not have a '/' to the right of it. |
|
// E.g. with '/foo/*' these will match: '/foo/bar', '/foo/bar.txt' but |
|
// these will not '/foo/bar/baz', '/foo/bar/baz.txt' |
|
// Lastely, when globstar is _true_, '/foo/**' is equivelant to '/foo/*' when |
|
// globstar is _false_ |
|
var globstar = opts ? !!opts.globstar : false; |
|
|
|
// If we are doing extended matching, this boolean is true when we are inside |
|
// a group (eg {*.html,*.js}), and false otherwise. |
|
var inGroup = false; |
|
|
|
// RegExp flags (eg "i" ) to pass in to RegExp constructor. |
|
var flags = opts && typeof( opts.flags ) === "string" ? opts.flags : ""; |
|
|
|
var c; |
|
for (var i = 0, len = str.length; i < len; i++) { |
|
c = str[i]; |
|
|
|
switch (c) { |
|
case "/": |
|
case "$": |
|
case "^": |
|
case "+": |
|
case ".": |
|
case "(": |
|
case ")": |
|
case "=": |
|
case "!": |
|
case "|": |
|
reStr += "\\" + c; |
|
break; |
|
|
|
case "?": |
|
if (extended) { |
|
reStr += "."; |
|
break; |
|
} |
|
|
|
case "[": |
|
case "]": |
|
if (extended) { |
|
reStr += c; |
|
break; |
|
} |
|
|
|
case "{": |
|
if (extended) { |
|
inGroup = true; |
|
reStr += "("; |
|
break; |
|
} |
|
|
|
case "}": |
|
if (extended) { |
|
inGroup = false; |
|
reStr += ")"; |
|
break; |
|
} |
|
|
|
case ",": |
|
if (inGroup) { |
|
reStr += "|"; |
|
break; |
|
} |
|
reStr += "\\" + c; |
|
break; |
|
|
|
case "*": |
|
// Move over all consecutive "*"'s. |
|
// Also store the previous and next characters |
|
var prevChar = str[i - 1]; |
|
var starCount = 1; |
|
while(str[i + 1] === "*") { |
|
starCount++; |
|
i++; |
|
} |
|
var nextChar = str[i + 1]; |
|
|
|
if (!globstar) { |
|
// globstar is disabled, so treat any number of "*" as one |
|
reStr += ".*"; |
|
} else { |
|
// globstar is enabled, so determine if this is a globstar segment |
|
var isGlobstar = starCount > 1 // multiple "*"'s |
|
&& (prevChar === "/" || prevChar === undefined) // from the start of the segment |
|
&& (nextChar === "/" || nextChar === undefined) // to the end of the segment |
|
|
|
if (isGlobstar) { |
|
// it's a globstar, so match zero or more path segments |
|
reStr += "((?:[^/]*(?:\/|$))*)"; |
|
i++; // move over the "/" |
|
} else { |
|
// it's not a globstar, so only match one path segment |
|
reStr += "([^/]*)"; |
|
} |
|
} |
|
break; |
|
|
|
default: |
|
reStr += c; |
|
} |
|
} |
|
|
|
// When regexp 'g' flag is specified don't |
|
// constrain the regular expression with ^ & $ |
|
if (!flags || !~flags.indexOf('g')) { |
|
reStr = "^" + reStr + "$"; |
|
} |
|
|
|
return new RegExp(reStr, flags); |
|
};
|
|
|