Spaces:
Runtime error
Runtime error
| const isWin = process.platform === 'win32'; | |
| const SEP = isWin ? `\\\\+` : `\\/`; | |
| const SEP_ESC = isWin ? `\\\\` : `/`; | |
| const GLOBSTAR = `((?:[^/]*(?:/|$))*)`; | |
| const WILDCARD = `([^/]*)`; | |
| const GLOBSTAR_SEGMENT = `((?:[^${SEP_ESC}]*(?:${SEP_ESC}|$))*)`; | |
| const WILDCARD_SEGMENT = `([^${SEP_ESC}]*)`; | |
| /** | |
| * Convert any glob pattern to a JavaScript Regexp object | |
| * @param {String} glob Glob pattern to convert | |
| * @param {Object} opts Configuration object | |
| * @param {Boolean} [opts.extended=false] Support advanced ext globbing | |
| * @param {Boolean} [opts.globstar=false] Support globstar | |
| * @param {Boolean} [opts.strict=true] be laissez faire about mutiple slashes | |
| * @param {Boolean} [opts.filepath=''] Parse as filepath for extra path related features | |
| * @param {String} [opts.flags=''] RegExp globs | |
| * @returns {Object} converted object with string, segments and RegExp object | |
| */ | |
| function globrex(glob, {extended = false, globstar = false, strict = false, filepath = false, flags = ''} = {}) { | |
| let regex = ''; | |
| let segment = ''; | |
| let path = { regex: '', segments: [] }; | |
| // If we are doing extended matching, this boolean is true when we are inside | |
| // a group (eg {*.html,*.js}), and false otherwise. | |
| let inGroup = false; | |
| let inRange = false; | |
| // extglob stack. Keep track of scope | |
| const ext = []; | |
| // Helper function to build string and segments | |
| function add(str, {split, last, only}={}) { | |
| if (only !== 'path') regex += str; | |
| if (filepath && only !== 'regex') { | |
| path.regex += (str === '\\/' ? SEP : str); | |
| if (split) { | |
| if (last) segment += str; | |
| if (segment !== '') { | |
| if (!flags.includes('g')) segment = `^${segment}$`; // change it 'includes' | |
| path.segments.push(new RegExp(segment, flags)); | |
| } | |
| segment = ''; | |
| } else { | |
| segment += str; | |
| } | |
| } | |
| } | |
| let c, n; | |
| for (let i = 0; i < glob.length; i++) { | |
| c = glob[i]; | |
| n = glob[i + 1]; | |
| if (['\\', '$', '^', '.', '='].includes(c)) { | |
| add(`\\${c}`); | |
| continue; | |
| } | |
| if (c === '/') { | |
| add(`\\${c}`, {split: true}); | |
| if (n === '/' && !strict) regex += '?'; | |
| continue; | |
| } | |
| if (c === '(') { | |
| if (ext.length) { | |
| add(c); | |
| continue; | |
| } | |
| add(`\\${c}`); | |
| continue; | |
| } | |
| if (c === ')') { | |
| if (ext.length) { | |
| add(c); | |
| let type = ext.pop(); | |
| if (type === '@') { | |
| add('{1}'); | |
| } else if (type === '!') { | |
| add('([^\/]*)'); | |
| } else { | |
| add(type); | |
| } | |
| continue; | |
| } | |
| add(`\\${c}`); | |
| continue; | |
| } | |
| if (c === '|') { | |
| if (ext.length) { | |
| add(c); | |
| continue; | |
| } | |
| add(`\\${c}`); | |
| continue; | |
| } | |
| if (c === '+') { | |
| if (n === '(' && extended) { | |
| ext.push(c); | |
| continue; | |
| } | |
| add(`\\${c}`); | |
| continue; | |
| } | |
| if (c === '@' && extended) { | |
| if (n === '(') { | |
| ext.push(c); | |
| continue; | |
| } | |
| } | |
| if (c === '!') { | |
| if (extended) { | |
| if (inRange) { | |
| add('^'); | |
| continue | |
| } | |
| if (n === '(') { | |
| ext.push(c); | |
| add('(?!'); | |
| i++; | |
| continue; | |
| } | |
| add(`\\${c}`); | |
| continue; | |
| } | |
| add(`\\${c}`); | |
| continue; | |
| } | |
| if (c === '?') { | |
| if (extended) { | |
| if (n === '(') { | |
| ext.push(c); | |
| } else { | |
| add('.'); | |
| } | |
| continue; | |
| } | |
| add(`\\${c}`); | |
| continue; | |
| } | |
| if (c === '[') { | |
| if (inRange && n === ':') { | |
| i++; // skip [ | |
| let value = ''; | |
| while(glob[++i] !== ':') value += glob[i]; | |
| if (value === 'alnum') add('(\\w|\\d)'); | |
| else if (value === 'space') add('\\s'); | |
| else if (value === 'digit') add('\\d'); | |
| i++; // skip last ] | |
| continue; | |
| } | |
| if (extended) { | |
| inRange = true; | |
| add(c); | |
| continue; | |
| } | |
| add(`\\${c}`); | |
| continue; | |
| } | |
| if (c === ']') { | |
| if (extended) { | |
| inRange = false; | |
| add(c); | |
| continue; | |
| } | |
| add(`\\${c}`); | |
| continue; | |
| } | |
| if (c === '{') { | |
| if (extended) { | |
| inGroup = true; | |
| add('('); | |
| continue; | |
| } | |
| add(`\\${c}`); | |
| continue; | |
| } | |
| if (c === '}') { | |
| if (extended) { | |
| inGroup = false; | |
| add(')'); | |
| continue; | |
| } | |
| add(`\\${c}`); | |
| continue; | |
| } | |
| if (c === ',') { | |
| if (inGroup) { | |
| add('|'); | |
| continue; | |
| } | |
| add(`\\${c}`); | |
| continue; | |
| } | |
| if (c === '*') { | |
| if (n === '(' && extended) { | |
| ext.push(c); | |
| continue; | |
| } | |
| // Move over all consecutive "*"'s. | |
| // Also store the previous and next characters | |
| let prevChar = glob[i - 1]; | |
| let starCount = 1; | |
| while (glob[i + 1] === '*') { | |
| starCount++; | |
| i++; | |
| } | |
| let nextChar = glob[i + 1]; | |
| if (!globstar) { | |
| // globstar is disabled, so treat any number of "*" as one | |
| add('.*'); | |
| } else { | |
| // globstar is enabled, so determine if this is a globstar segment | |
| let 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 | |
| add(GLOBSTAR, {only:'regex'}); | |
| add(GLOBSTAR_SEGMENT, {only:'path', last:true, split:true}); | |
| i++; // move over the "/" | |
| } else { | |
| // it's not a globstar, so only match one path segment | |
| add(WILDCARD, {only:'regex'}); | |
| add(WILDCARD_SEGMENT, {only:'path'}); | |
| } | |
| } | |
| continue; | |
| } | |
| add(c); | |
| } | |
| // When regexp 'g' flag is specified don't | |
| // constrain the regular expression with ^ & $ | |
| if (!flags.includes('g')) { | |
| regex = `^${regex}$`; | |
| segment = `^${segment}$`; | |
| if (filepath) path.regex = `^${path.regex}$`; | |
| } | |
| const result = {regex: new RegExp(regex, flags)}; | |
| // Push the last segment | |
| if (filepath) { | |
| path.segments.push(new RegExp(segment, flags)); | |
| path.regex = new RegExp(path.regex, flags); | |
| path.globstar = new RegExp(!flags.includes('g') ? `^${GLOBSTAR_SEGMENT}$` : GLOBSTAR_SEGMENT, flags); | |
| result.path = path; | |
| } | |
| return result; | |
| } | |
| module.exports = globrex; | |