|  | 'use strict'; | 
|  |  | 
|  | const utils = require('./utils'); | 
|  | const constants = require('./constants'); | 
|  |  | 
|  | /** | 
|  | * Constants | 
|  | */ | 
|  |  | 
|  | const { | 
|  | MAX_LENGTH, | 
|  | POSIX_REGEX_SOURCE, | 
|  | REGEX_NON_SPECIAL_CHAR, | 
|  | REGEX_SPECIAL_CHARS_BACKREF, | 
|  | REPLACEMENTS | 
|  | } = constants; | 
|  |  | 
|  | /** | 
|  | * Helpers | 
|  | */ | 
|  |  | 
|  | const expandRange = (args, options) => { | 
|  | if (typeof options.expandRange === 'function') { | 
|  | return options.expandRange(...args, options); | 
|  | } | 
|  |  | 
|  | args.sort(); | 
|  | let value = `[${args.join('-')}]`; | 
|  |  | 
|  | try { | 
|  | /* eslint-disable no-new */ | 
|  | new RegExp(value); | 
|  | } catch (ex) { | 
|  | return args.map(v => utils.escapeRegex(v)).join('..'); | 
|  | } | 
|  |  | 
|  | return value; | 
|  | }; | 
|  |  | 
|  | const negate = state => { | 
|  | let count = 1; | 
|  |  | 
|  | while (state.peek() === '!' && (state.peek(2) !== '(' || state.peek(3) === '?')) { | 
|  | state.advance(); | 
|  | state.start++; | 
|  | count++; | 
|  | } | 
|  |  | 
|  | if (count % 2 === 0) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | state.negated = true; | 
|  | state.start++; | 
|  | return true; | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Create the message for a syntax error | 
|  | */ | 
|  |  | 
|  | const syntaxError = (type, char) => { | 
|  | return `Missing ${type}: "${char}" - use "\\\\${char}" to match literal characters`; | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Parse the given input string. | 
|  | * @param {String} input | 
|  | * @param {Object} options | 
|  | * @return {Object} | 
|  | */ | 
|  |  | 
|  | const parse = (input, options) => { | 
|  | if (typeof input !== 'string') { | 
|  | throw new TypeError('Expected a string'); | 
|  | } | 
|  |  | 
|  | input = REPLACEMENTS[input] || input; | 
|  |  | 
|  | let opts = { ...options }; | 
|  | let max = typeof opts.maxLength === 'number' ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH; | 
|  | let len = input.length; | 
|  | if (len > max) { | 
|  | throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`); | 
|  | } | 
|  |  | 
|  | let bos = { type: 'bos', value: '', output: opts.prepend || '' }; | 
|  | let tokens = [bos]; | 
|  |  | 
|  | let capture = opts.capture ? '' : '?:'; | 
|  | let win32 = utils.isWindows(options); | 
|  |  | 
|  | // create constants based on platform, for windows or posix | 
|  | const PLATFORM_CHARS = constants.globChars(win32); | 
|  | const EXTGLOB_CHARS = constants.extglobChars(PLATFORM_CHARS); | 
|  |  | 
|  | const { | 
|  | DOT_LITERAL, | 
|  | PLUS_LITERAL, | 
|  | SLASH_LITERAL, | 
|  | ONE_CHAR, | 
|  | DOTS_SLASH, | 
|  | NO_DOT, | 
|  | NO_DOT_SLASH, | 
|  | NO_DOTS_SLASH, | 
|  | QMARK, | 
|  | QMARK_NO_DOT, | 
|  | STAR, | 
|  | START_ANCHOR | 
|  | } = PLATFORM_CHARS; | 
|  |  | 
|  | const globstar = (opts) => { | 
|  | return `(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`; | 
|  | }; | 
|  |  | 
|  | let nodot = opts.dot ? '' : NO_DOT; | 
|  | let star = opts.bash === true ? globstar(opts) : STAR; | 
|  | let qmarkNoDot = opts.dot ? QMARK : QMARK_NO_DOT; | 
|  |  | 
|  | if (opts.capture) { | 
|  | star = `(${star})`; | 
|  | } | 
|  |  | 
|  | // minimatch options support | 
|  | if (typeof opts.noext === 'boolean') { | 
|  | opts.noextglob = opts.noext; | 
|  | } | 
|  |  | 
|  | let state = { | 
|  | index: -1, | 
|  | start: 0, | 
|  | consumed: '', | 
|  | output: '', | 
|  | backtrack: false, | 
|  | brackets: 0, | 
|  | braces: 0, | 
|  | parens: 0, | 
|  | quotes: 0, | 
|  | tokens | 
|  | }; | 
|  |  | 
|  | let extglobs = []; | 
|  | let stack = []; | 
|  | let prev = bos; | 
|  | let value; | 
|  |  | 
|  | /** | 
|  | * Tokenizing helpers | 
|  | */ | 
|  |  | 
|  | const eos = () => state.index === len - 1; | 
|  | const peek = state.peek = (n = 1) => input[state.index + n]; | 
|  | const advance = state.advance = () => input[++state.index]; | 
|  | const append = token => { | 
|  | state.output += token.output != null ? token.output : token.value; | 
|  | state.consumed += token.value || ''; | 
|  | }; | 
|  |  | 
|  | const increment = type => { | 
|  | state[type]++; | 
|  | stack.push(type); | 
|  | }; | 
|  |  | 
|  | const decrement = type => { | 
|  | state[type]--; | 
|  | stack.pop(); | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Push tokens onto the tokens array. This helper speeds up | 
|  | * tokenizing by 1) helping us avoid backtracking as much as possible, | 
|  | * and 2) helping us avoid creating extra tokens when consecutive | 
|  | * characters are plain text. This improves performance and simplifies | 
|  | * lookbehinds. | 
|  | */ | 
|  |  | 
|  | const push = tok => { | 
|  | if (prev.type === 'globstar') { | 
|  | let isBrace = state.braces > 0 && (tok.type === 'comma' || tok.type === 'brace'); | 
|  | let isExtglob = extglobs.length && (tok.type === 'pipe' || tok.type === 'paren'); | 
|  | if (tok.type !== 'slash' && tok.type !== 'paren' && !isBrace && !isExtglob) { | 
|  | state.output = state.output.slice(0, -prev.output.length); | 
|  | prev.type = 'star'; | 
|  | prev.value = '*'; | 
|  | prev.output = star; | 
|  | state.output += prev.output; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (extglobs.length && tok.type !== 'paren' && !EXTGLOB_CHARS[tok.value]) { | 
|  | extglobs[extglobs.length - 1].inner += tok.value; | 
|  | } | 
|  |  | 
|  | if (tok.value || tok.output) append(tok); | 
|  | if (prev && prev.type === 'text' && tok.type === 'text') { | 
|  | prev.value += tok.value; | 
|  | return; | 
|  | } | 
|  |  | 
|  | tok.prev = prev; | 
|  | tokens.push(tok); | 
|  | prev = tok; | 
|  | }; | 
|  |  | 
|  | const extglobOpen = (type, value) => { | 
|  | let token = { ...EXTGLOB_CHARS[value], conditions: 1, inner: '' }; | 
|  |  | 
|  | token.prev = prev; | 
|  | token.parens = state.parens; | 
|  | token.output = state.output; | 
|  | let output = (opts.capture ? '(' : '') + token.open; | 
|  |  | 
|  | push({ type, value, output: state.output ? '' : ONE_CHAR }); | 
|  | push({ type: 'paren', extglob: true, value: advance(), output }); | 
|  | increment('parens'); | 
|  | extglobs.push(token); | 
|  | }; | 
|  |  | 
|  | const extglobClose = token => { | 
|  | let output = token.close + (opts.capture ? ')' : ''); | 
|  |  | 
|  | if (token.type === 'negate') { | 
|  | let extglobStar = star; | 
|  |  | 
|  | if (token.inner && token.inner.length > 1 && token.inner.includes('/')) { | 
|  | extglobStar = globstar(opts); | 
|  | } | 
|  |  | 
|  | if (extglobStar !== star || eos() || /^\)+$/.test(input.slice(state.index + 1))) { | 
|  | output = token.close = ')$))' + extglobStar; | 
|  | } | 
|  |  | 
|  | if (token.prev.type === 'bos' && eos()) { | 
|  | state.negatedExtglob = true; | 
|  | } | 
|  | } | 
|  |  | 
|  | push({ type: 'paren', extglob: true, value, output }); | 
|  | decrement('parens'); | 
|  | }; | 
|  |  | 
|  | if (opts.fastpaths !== false && !/(^[*!]|[/{[()\]}"])/.test(input)) { | 
|  | let backslashes = false; | 
|  |  | 
|  | let output = input.replace(REGEX_SPECIAL_CHARS_BACKREF, (m, esc, chars, first, rest, index) => { | 
|  | if (first === '\\') { | 
|  | backslashes = true; | 
|  | return m; | 
|  | } | 
|  |  | 
|  | if (first === '?') { | 
|  | if (esc) { | 
|  | return esc + first + (rest ? QMARK.repeat(rest.length) : ''); | 
|  | } | 
|  | if (index === 0) { | 
|  | return qmarkNoDot + (rest ? QMARK.repeat(rest.length) : ''); | 
|  | } | 
|  | return QMARK.repeat(chars.length); | 
|  | } | 
|  |  | 
|  | if (first === '.') { | 
|  | return DOT_LITERAL.repeat(chars.length); | 
|  | } | 
|  |  | 
|  | if (first === '*') { | 
|  | if (esc) { | 
|  | return esc + first + (rest ? star : ''); | 
|  | } | 
|  | return star; | 
|  | } | 
|  | return esc ? m : '\\' + m; | 
|  | }); | 
|  |  | 
|  | if (backslashes === true) { | 
|  | if (opts.unescape === true) { | 
|  | output = output.replace(/\\/g, ''); | 
|  | } else { | 
|  | output = output.replace(/\\+/g, m => { | 
|  | return m.length % 2 === 0 ? '\\\\' : (m ? '\\' : ''); | 
|  | }); | 
|  | } | 
|  | } | 
|  |  | 
|  | state.output = output; | 
|  | return state; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Tokenize input until we reach end-of-string | 
|  | */ | 
|  |  | 
|  | while (!eos()) { | 
|  | value = advance(); | 
|  |  | 
|  | if (value === '\u0000') { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Escaped characters | 
|  | */ | 
|  |  | 
|  | if (value === '\\') { | 
|  | let next = peek(); | 
|  |  | 
|  | if (next === '/' && opts.bash !== true) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (next === '.' || next === ';') { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (!next) { | 
|  | value += '\\'; | 
|  | push({ type: 'text', value }); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | // collapse slashes to reduce potential for exploits | 
|  | let match = /^\\+/.exec(input.slice(state.index + 1)); | 
|  | let slashes = 0; | 
|  |  | 
|  | if (match && match[0].length > 2) { | 
|  | slashes = match[0].length; | 
|  | state.index += slashes; | 
|  | if (slashes % 2 !== 0) { | 
|  | value += '\\'; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (opts.unescape === true) { | 
|  | value = advance() || ''; | 
|  | } else { | 
|  | value += advance() || ''; | 
|  | } | 
|  |  | 
|  | if (state.brackets === 0) { | 
|  | push({ type: 'text', value }); | 
|  | continue; | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * If we're inside a regex character class, continue | 
|  | * until we reach the closing bracket. | 
|  | */ | 
|  |  | 
|  | if (state.brackets > 0 && (value !== ']' || prev.value === '[' || prev.value === '[^')) { | 
|  | if (opts.posix !== false && value === ':') { | 
|  | let inner = prev.value.slice(1); | 
|  | if (inner.includes('[')) { | 
|  | prev.posix = true; | 
|  |  | 
|  | if (inner.includes(':')) { | 
|  | let idx = prev.value.lastIndexOf('['); | 
|  | let pre = prev.value.slice(0, idx); | 
|  | let rest = prev.value.slice(idx + 2); | 
|  | let posix = POSIX_REGEX_SOURCE[rest]; | 
|  | if (posix) { | 
|  | prev.value = pre + posix; | 
|  | state.backtrack = true; | 
|  | advance(); | 
|  |  | 
|  | if (!bos.output && tokens.indexOf(prev) === 1) { | 
|  | bos.output = ONE_CHAR; | 
|  | } | 
|  | continue; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if ((value === '[' && peek() !== ':') || (value === '-' && peek() === ']')) { | 
|  | value = '\\' + value; | 
|  | } | 
|  |  | 
|  | if (value === ']' && (prev.value === '[' || prev.value === '[^')) { | 
|  | value = '\\' + value; | 
|  | } | 
|  |  | 
|  | if (opts.posix === true && value === '!' && prev.value === '[') { | 
|  | value = '^'; | 
|  | } | 
|  |  | 
|  | prev.value += value; | 
|  | append({ value }); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * If we're inside a quoted string, continue | 
|  | * until we reach the closing double quote. | 
|  | */ | 
|  |  | 
|  | if (state.quotes === 1 && value !== '"') { | 
|  | value = utils.escapeRegex(value); | 
|  | prev.value += value; | 
|  | append({ value }); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Double quotes | 
|  | */ | 
|  |  | 
|  | if (value === '"') { | 
|  | state.quotes = state.quotes === 1 ? 0 : 1; | 
|  | if (opts.keepQuotes === true) { | 
|  | push({ type: 'text', value }); | 
|  | } | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Parentheses | 
|  | */ | 
|  |  | 
|  | if (value === '(') { | 
|  | push({ type: 'paren', value }); | 
|  | increment('parens'); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (value === ')') { | 
|  | if (state.parens === 0 && opts.strictBrackets === true) { | 
|  | throw new SyntaxError(syntaxError('opening', '(')); | 
|  | } | 
|  |  | 
|  | let extglob = extglobs[extglobs.length - 1]; | 
|  | if (extglob && state.parens === extglob.parens + 1) { | 
|  | extglobClose(extglobs.pop()); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | push({ type: 'paren', value, output: state.parens ? ')' : '\\)' }); | 
|  | decrement('parens'); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Brackets | 
|  | */ | 
|  |  | 
|  | if (value === '[') { | 
|  | if (opts.nobracket === true || !input.slice(state.index + 1).includes(']')) { | 
|  | if (opts.nobracket !== true && opts.strictBrackets === true) { | 
|  | throw new SyntaxError(syntaxError('closing', ']')); | 
|  | } | 
|  |  | 
|  | value = '\\' + value; | 
|  | } else { | 
|  | increment('brackets'); | 
|  | } | 
|  |  | 
|  | push({ type: 'bracket', value }); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (value === ']') { | 
|  | if (opts.nobracket === true || (prev && prev.type === 'bracket' && prev.value.length === 1)) { | 
|  | push({ type: 'text', value, output: '\\' + value }); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (state.brackets === 0) { | 
|  | if (opts.strictBrackets === true) { | 
|  | throw new SyntaxError(syntaxError('opening', '[')); | 
|  | } | 
|  |  | 
|  | push({ type: 'text', value, output: '\\' + value }); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | decrement('brackets'); | 
|  |  | 
|  | let prevValue = prev.value.slice(1); | 
|  | if (prev.posix !== true && prevValue[0] === '^' && !prevValue.includes('/')) { | 
|  | value = '/' + value; | 
|  | } | 
|  |  | 
|  | prev.value += value; | 
|  | append({ value }); | 
|  |  | 
|  | // when literal brackets are explicitly disabled | 
|  | // assume we should match with a regex character class | 
|  | if (opts.literalBrackets === false || utils.hasRegexChars(prevValue)) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | let escaped = utils.escapeRegex(prev.value); | 
|  | state.output = state.output.slice(0, -prev.value.length); | 
|  |  | 
|  | // when literal brackets are explicitly enabled | 
|  | // assume we should escape the brackets to match literal characters | 
|  | if (opts.literalBrackets === true) { | 
|  | state.output += escaped; | 
|  | prev.value = escaped; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | // when the user specifies nothing, try to match both | 
|  | prev.value = `(${capture}${escaped}|${prev.value})`; | 
|  | state.output += prev.value; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Braces | 
|  | */ | 
|  |  | 
|  | if (value === '{' && opts.nobrace !== true) { | 
|  | push({ type: 'brace', value, output: '(' }); | 
|  | increment('braces'); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (value === '}') { | 
|  | if (opts.nobrace === true || state.braces === 0) { | 
|  | push({ type: 'text', value, output: '\\' + value }); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | let output = ')'; | 
|  |  | 
|  | if (state.dots === true) { | 
|  | let arr = tokens.slice(); | 
|  | let range = []; | 
|  |  | 
|  | for (let i = arr.length - 1; i >= 0; i--) { | 
|  | tokens.pop(); | 
|  | if (arr[i].type === 'brace') { | 
|  | break; | 
|  | } | 
|  | if (arr[i].type !== 'dots') { | 
|  | range.unshift(arr[i].value); | 
|  | } | 
|  | } | 
|  |  | 
|  | output = expandRange(range, opts); | 
|  | state.backtrack = true; | 
|  | } | 
|  |  | 
|  | push({ type: 'brace', value, output }); | 
|  | decrement('braces'); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Pipes | 
|  | */ | 
|  |  | 
|  | if (value === '|') { | 
|  | if (extglobs.length > 0) { | 
|  | extglobs[extglobs.length - 1].conditions++; | 
|  | } | 
|  | push({ type: 'text', value }); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Commas | 
|  | */ | 
|  |  | 
|  | if (value === ',') { | 
|  | let output = value; | 
|  |  | 
|  | if (state.braces > 0 && stack[stack.length - 1] === 'braces') { | 
|  | output = '|'; | 
|  | } | 
|  |  | 
|  | push({ type: 'comma', value, output }); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Slashes | 
|  | */ | 
|  |  | 
|  | if (value === '/') { | 
|  | // if the beginning of the glob is "./", advance the start | 
|  | // to the current index, and don't add the "./" characters | 
|  | // to the state. This greatly simplifies lookbehinds when | 
|  | // checking for BOS characters like "!" and "." (not "./") | 
|  | if (prev.type === 'dot' && state.index === 1) { | 
|  | state.start = state.index + 1; | 
|  | state.consumed = ''; | 
|  | state.output = ''; | 
|  | tokens.pop(); | 
|  | prev = bos; // reset "prev" to the first token | 
|  | continue; | 
|  | } | 
|  |  | 
|  | push({ type: 'slash', value, output: SLASH_LITERAL }); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Dots | 
|  | */ | 
|  |  | 
|  | if (value === '.') { | 
|  | if (state.braces > 0 && prev.type === 'dot') { | 
|  | if (prev.value === '.') prev.output = DOT_LITERAL; | 
|  | prev.type = 'dots'; | 
|  | prev.output += value; | 
|  | prev.value += value; | 
|  | state.dots = true; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | push({ type: 'dot', value, output: DOT_LITERAL }); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Question marks | 
|  | */ | 
|  |  | 
|  | if (value === '?') { | 
|  | if (prev && prev.type === 'paren') { | 
|  | let next = peek(); | 
|  | let output = value; | 
|  |  | 
|  | if (next === '<' && !utils.supportsLookbehinds()) { | 
|  | throw new Error('Node.js v10 or higher is required for regex lookbehinds'); | 
|  | } | 
|  |  | 
|  | if (prev.value === '(' && !/[!=<:]/.test(next) || (next === '<' && !/[!=]/.test(peek(2)))) { | 
|  | output = '\\' + value; | 
|  | } | 
|  |  | 
|  | push({ type: 'text', value, output }); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (opts.noextglob !== true && peek() === '(' && peek(2) !== '?') { | 
|  | extglobOpen('qmark', value); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (opts.dot !== true && (prev.type === 'slash' || prev.type === 'bos')) { | 
|  | push({ type: 'qmark', value, output: QMARK_NO_DOT }); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | push({ type: 'qmark', value, output: QMARK }); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Exclamation | 
|  | */ | 
|  |  | 
|  | if (value === '!') { | 
|  | if (opts.noextglob !== true && peek() === '(') { | 
|  | if (peek(2) !== '?' || !/[!=<:]/.test(peek(3))) { | 
|  | extglobOpen('negate', value); | 
|  | continue; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (opts.nonegate !== true && state.index === 0) { | 
|  | negate(state); | 
|  | continue; | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Plus | 
|  | */ | 
|  |  | 
|  | if (value === '+') { | 
|  | if (opts.noextglob !== true && peek() === '(' && peek(2) !== '?') { | 
|  | extglobOpen('plus', value); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (prev && (prev.type === 'bracket' || prev.type === 'paren' || prev.type === 'brace')) { | 
|  | let output = prev.extglob === true ? '\\' + value : value; | 
|  | push({ type: 'plus', value, output }); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | // use regex behavior inside parens | 
|  | if (state.parens > 0 && opts.regex !== false) { | 
|  | push({ type: 'plus', value }); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | push({ type: 'plus', value: PLUS_LITERAL }); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Plain text | 
|  | */ | 
|  |  | 
|  | if (value === '@') { | 
|  | if (opts.noextglob !== true && peek() === '(' && peek(2) !== '?') { | 
|  | push({ type: 'at', value, output: '' }); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | push({ type: 'text', value }); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Plain text | 
|  | */ | 
|  |  | 
|  | if (value !== '*') { | 
|  | if (value === '$' || value === '^') { | 
|  | value = '\\' + value; | 
|  | } | 
|  |  | 
|  | let match = REGEX_NON_SPECIAL_CHAR.exec(input.slice(state.index + 1)); | 
|  | if (match) { | 
|  | value += match[0]; | 
|  | state.index += match[0].length; | 
|  | } | 
|  |  | 
|  | push({ type: 'text', value }); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Stars | 
|  | */ | 
|  |  | 
|  | if (prev && (prev.type === 'globstar' || prev.star === true)) { | 
|  | prev.type = 'star'; | 
|  | prev.star = true; | 
|  | prev.value += value; | 
|  | prev.output = star; | 
|  | state.backtrack = true; | 
|  | state.consumed += value; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (opts.noextglob !== true && peek() === '(' && peek(2) !== '?') { | 
|  | extglobOpen('star', value); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (prev.type === 'star') { | 
|  | if (opts.noglobstar === true) { | 
|  | state.consumed += value; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | let prior = prev.prev; | 
|  | let before = prior.prev; | 
|  | let isStart = prior.type === 'slash' || prior.type === 'bos'; | 
|  | let afterStar = before && (before.type === 'star' || before.type === 'globstar'); | 
|  |  | 
|  | if (opts.bash === true && (!isStart || (!eos() && peek() !== '/'))) { | 
|  | push({ type: 'star', value, output: '' }); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | let isBrace = state.braces > 0 && (prior.type === 'comma' || prior.type === 'brace'); | 
|  | let isExtglob = extglobs.length && (prior.type === 'pipe' || prior.type === 'paren'); | 
|  | if (!isStart && prior.type !== 'paren' && !isBrace && !isExtglob) { | 
|  | push({ type: 'star', value, output: '' }); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | // strip consecutive `/**/` | 
|  | while (input.slice(state.index + 1, state.index + 4) === '/**') { | 
|  | let after = input[state.index + 4]; | 
|  | if (after && after !== '/') { | 
|  | break; | 
|  | } | 
|  | state.consumed += '/**'; | 
|  | state.index += 3; | 
|  | } | 
|  |  | 
|  | if (prior.type === 'bos' && eos()) { | 
|  | prev.type = 'globstar'; | 
|  | prev.value += value; | 
|  | prev.output = globstar(opts); | 
|  | state.output = prev.output; | 
|  | state.consumed += value; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (prior.type === 'slash' && prior.prev.type !== 'bos' && !afterStar && eos()) { | 
|  | state.output = state.output.slice(0, -(prior.output + prev.output).length); | 
|  | prior.output = '(?:' + prior.output; | 
|  |  | 
|  | prev.type = 'globstar'; | 
|  | prev.output = globstar(opts) + '|$)'; | 
|  | prev.value += value; | 
|  |  | 
|  | state.output += prior.output + prev.output; | 
|  | state.consumed += value; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | let next = peek(); | 
|  | if (prior.type === 'slash' && prior.prev.type !== 'bos' && next === '/') { | 
|  | let end = peek(2) !== void 0 ? '|$' : ''; | 
|  |  | 
|  | state.output = state.output.slice(0, -(prior.output + prev.output).length); | 
|  | prior.output = '(?:' + prior.output; | 
|  |  | 
|  | prev.type = 'globstar'; | 
|  | prev.output = `${globstar(opts)}${SLASH_LITERAL}|${SLASH_LITERAL}${end})`; | 
|  | prev.value += value; | 
|  |  | 
|  | state.output += prior.output + prev.output; | 
|  | state.consumed += value + advance(); | 
|  |  | 
|  | push({ type: 'slash', value, output: '' }); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (prior.type === 'bos' && next === '/') { | 
|  | prev.type = 'globstar'; | 
|  | prev.value += value; | 
|  | prev.output = `(?:^|${SLASH_LITERAL}|${globstar(opts)}${SLASH_LITERAL})`; | 
|  | state.output = prev.output; | 
|  | state.consumed += value + advance(); | 
|  | push({ type: 'slash', value, output: '' }); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | // remove single star from output | 
|  | state.output = state.output.slice(0, -prev.output.length); | 
|  |  | 
|  | // reset previous token to globstar | 
|  | prev.type = 'globstar'; | 
|  | prev.output = globstar(opts); | 
|  | prev.value += value; | 
|  |  | 
|  | // reset output with globstar | 
|  | state.output += prev.output; | 
|  | state.consumed += value; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | let token = { type: 'star', value, output: star }; | 
|  |  | 
|  | if (opts.bash === true) { | 
|  | token.output = '.*?'; | 
|  | if (prev.type === 'bos' || prev.type === 'slash') { | 
|  | token.output = nodot + token.output; | 
|  | } | 
|  | push(token); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (prev && (prev.type === 'bracket' || prev.type === 'paren') && opts.regex === true) { | 
|  | token.output = value; | 
|  | push(token); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (state.index === state.start || prev.type === 'slash' || prev.type === 'dot') { | 
|  | if (prev.type === 'dot') { | 
|  | state.output += NO_DOT_SLASH; | 
|  | prev.output += NO_DOT_SLASH; | 
|  |  | 
|  | } else if (opts.dot === true) { | 
|  | state.output += NO_DOTS_SLASH; | 
|  | prev.output += NO_DOTS_SLASH; | 
|  |  | 
|  | } else { | 
|  | state.output += nodot; | 
|  | prev.output += nodot; | 
|  | } | 
|  |  | 
|  | if (peek() !== '*') { | 
|  | state.output += ONE_CHAR; | 
|  | prev.output += ONE_CHAR; | 
|  | } | 
|  | } | 
|  |  | 
|  | push(token); | 
|  | } | 
|  |  | 
|  | while (state.brackets > 0) { | 
|  | if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', ']')); | 
|  | state.output = utils.escapeLast(state.output, '['); | 
|  | decrement('brackets'); | 
|  | } | 
|  |  | 
|  | while (state.parens > 0) { | 
|  | if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', ')')); | 
|  | state.output = utils.escapeLast(state.output, '('); | 
|  | decrement('parens'); | 
|  | } | 
|  |  | 
|  | while (state.braces > 0) { | 
|  | if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', '}')); | 
|  | state.output = utils.escapeLast(state.output, '{'); | 
|  | decrement('braces'); | 
|  | } | 
|  |  | 
|  | if (opts.strictSlashes !== true && (prev.type === 'star' || prev.type === 'bracket')) { | 
|  | push({ type: 'maybe_slash', value: '', output: `${SLASH_LITERAL}?` }); | 
|  | } | 
|  |  | 
|  | // rebuild the output if we had to backtrack at any point | 
|  | if (state.backtrack === true) { | 
|  | state.output = ''; | 
|  |  | 
|  | for (let token of state.tokens) { | 
|  | state.output += token.output != null ? token.output : token.value; | 
|  |  | 
|  | if (token.suffix) { | 
|  | state.output += token.suffix; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return state; | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Fast paths for creating regular expressions for common glob patterns. | 
|  | * This can significantly speed up processing and has very little downside | 
|  | * impact when none of the fast paths match. | 
|  | */ | 
|  |  | 
|  | parse.fastpaths = (input, options) => { | 
|  | let opts = { ...options }; | 
|  | let max = typeof opts.maxLength === 'number' ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH; | 
|  | let len = input.length; | 
|  | if (len > max) { | 
|  | throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`); | 
|  | } | 
|  |  | 
|  | input = REPLACEMENTS[input] || input; | 
|  | let win32 = utils.isWindows(options); | 
|  |  | 
|  | // create constants based on platform, for windows or posix | 
|  | const { | 
|  | DOT_LITERAL, | 
|  | SLASH_LITERAL, | 
|  | ONE_CHAR, | 
|  | DOTS_SLASH, | 
|  | NO_DOT, | 
|  | NO_DOTS, | 
|  | NO_DOTS_SLASH, | 
|  | STAR, | 
|  | START_ANCHOR | 
|  | } = constants.globChars(win32); | 
|  |  | 
|  | let capture = opts.capture ? '' : '?:'; | 
|  | let star = opts.bash === true ? '.*?' : STAR; | 
|  | let nodot = opts.dot ? NO_DOTS : NO_DOT; | 
|  | let slashDot = opts.dot ? NO_DOTS_SLASH : NO_DOT; | 
|  |  | 
|  | if (opts.capture) { | 
|  | star = `(${star})`; | 
|  | } | 
|  |  | 
|  | const globstar = (opts) => { | 
|  | return `(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`; | 
|  | }; | 
|  |  | 
|  | const create = str => { | 
|  | switch (str) { | 
|  | case '*': | 
|  | return `${nodot}${ONE_CHAR}${star}`; | 
|  |  | 
|  | case '.*': | 
|  | return `${DOT_LITERAL}${ONE_CHAR}${star}`; | 
|  |  | 
|  | case '*.*': | 
|  | return `${nodot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`; | 
|  |  | 
|  | case '*/*': | 
|  | return `${nodot}${star}${SLASH_LITERAL}${ONE_CHAR}${slashDot}${star}`; | 
|  |  | 
|  | case '**': | 
|  | return nodot + globstar(opts); | 
|  |  | 
|  | case '**/*': | 
|  | return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${ONE_CHAR}${star}`; | 
|  |  | 
|  | case '**/*.*': | 
|  | return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`; | 
|  |  | 
|  | case '**/.*': | 
|  | return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${DOT_LITERAL}${ONE_CHAR}${star}`; | 
|  |  | 
|  | default: { | 
|  | let match = /^(.*?)\.(\w+)$/.exec(str); | 
|  | if (!match) return; | 
|  |  | 
|  | let source = create(match[1], options); | 
|  | if (!source) return; | 
|  |  | 
|  | return source + DOT_LITERAL + match[2]; | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | let output = create(input); | 
|  | if (output && opts.strictSlashes !== true) { | 
|  | output += `${SLASH_LITERAL}?`; | 
|  | } | 
|  |  | 
|  | return output; | 
|  | }; | 
|  |  | 
|  | module.exports = parse; |