@jsxtools/glob
Version:
convert glob patterns into regular expressions
291 lines (214 loc) • 4.67 kB
JavaScript
/**
* Convert a glob pattern into a Regular Expression
* @param {String} glob - Glob pattern
* @return {RegExp} Regular Expression
*/
module.exports = function glob(glob) {
// Metacharacters
var AT = '@';
var BACKSLASH = '\\';
var BANG = '!';
var CARET = '^';
var COLON = ':';
var COMMA = ',';
var CURLY_OPEN = '{';
var CURLY_SHUT = '}';
var DOLLAR = '$';
var DOT = '.';
var EQUAL = '=';
var PAREN_OPEN = '(';
var PAREN_SHUT = ')';
var PIPE = '|';
var PLUS = '+';
var QUERY = '?';
var SLASH = '/';
var SQUARE_OPEN = '[';
var SQUARE_SHUT = ']';
var STAR = '*';
// Regular Expression sources for path segments
var GLOBSTAR_SEGMENT = '((?:[^/]*(?:/|$))*)';
var WILDCARD_SEGMENT = '([^/]*)';
/** @type {String} source of the Regular Expression to return */
var source = '';
// 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;
var inRange = false;
/** @type {Array<String>} - stack (to be kept out of scope) */
var ext = [];
/** @type {Number} index of the current character in the glob pattern */
var i = 0;
/** @type {String} current character in the glob pattern */
var char;
/** @type {String} next character in the glob pattern */
var nextChar;
for (; i < glob.length; ++i) {
char = glob[i];
nextChar = glob[i + 1];
// escape the following symbols
if (
char === BACKSLASH ||
char === DOLLAR ||
char === CARET ||
char === DOT ||
char === EQUAL
) {
source += BACKSLASH + char;
continue;
}
if (char === SLASH) {
source += BACKSLASH + char;
continue;
}
if (char === PAREN_OPEN) {
if (ext.length) {
source += char;
continue;
}
source += BACKSLASH + char;
continue;
}
if (char === PAREN_SHUT) {
if (ext.length) {
source += char;
var type = ext.pop();
source += (
type === AT
? '{1}'
: type === BANG
? WILDCARD_SEGMENT
: type
);
continue;
}
source += BACKSLASH + char;
continue;
}
if (char === PIPE) {
if (ext.length) {
source += char;
continue;
}
source += BACKSLASH + char;
continue;
}
if (char === PLUS) {
if (nextChar === PAREN_OPEN) {
ext.push(char);
continue;
}
source += BACKSLASH + char;
continue;
}
if (
char === AT &&
nextChar === PAREN_OPEN
) {
ext.push(char);
continue;
}
if (char === BANG) {
if (inRange) {
source += CARET;
continue;
}
if (nextChar === PAREN_OPEN) {
ext.push(char);
source += '(?!';
++i;
continue;
}
source += BACKSLASH + char;
continue;
}
if (char === QUERY) {
if (nextChar === PAREN_OPEN) {
ext.push(char);
} else {
source += DOT;
}
continue;
}
if (char === SQUARE_OPEN) {
if (
inRange &&
nextChar === COLON
) {
++i;
/** @type {String} - range or class of characters */
var value = '';
while (glob[++i] !== COLON) {
value += glob[i];
}
if (value === 'alnum') {
source += '(\\w|\\d)';
} else if (value === 'space') {
source += '\\s';
} else if (value === 'digit') {
source += '\\d';
}
// skip the closing `:` handled in the while loop above
++i;
continue;
}
inRange = true;
source += char;
continue;
}
if (char === SQUARE_SHUT) {
inRange = false;
source += char;
continue;
}
if (char === CURLY_OPEN) {
inGroup = true;
source += PAREN_OPEN;
continue;
}
if (char === CURLY_SHUT) {
inGroup = false;
source += PAREN_SHUT;
continue;
}
if (char === COMMA) {
if (inGroup) {
source += PIPE;
continue;
}
source += BACKSLASH + char;
continue;
}
if (char === STAR) {
if (nextChar === PAREN_OPEN) {
ext.push(char);
continue;
}
// move over consecutive stars and store the previous and next characters
/** @type {String} previous character in the glob pattern */
var prevChar = glob[i - 1];
/** @type {Number} truthy number when matching a globstar pattern */
var isGlobstar;
while (glob[i + 1] === STAR) {
isGlobstar = ++i;
}
nextChar = glob[i + 1];
if (
!isGlobstar ||
prevChar !== SLASH &&
prevChar !== undefined ||
nextChar !== SLASH &&
nextChar !== undefined
) {
// not a globstar; match one path segment
source += WILDCARD_SEGMENT;
} else {
// is a globstar; match zero or more path segments
source += GLOBSTAR_SEGMENT;
++i;
}
continue;
}
source += char;
}
return RegExp(CARET + source + DOLLAR, 'i');
}