diff options
| author | Joel Kronqvist <joel.h.kronqvist@gmail.com> | 2022-03-05 19:02:27 +0200 | 
|---|---|---|
| committer | Joel Kronqvist <joel.h.kronqvist@gmail.com> | 2022-03-05 19:02:27 +0200 | 
| commit | 5d309ff52cd399a6b71968a6b9a70c8ac0b98981 (patch) | |
| tree | 360f7eb50f956e2367ef38fa1fc6ac7ac5258042 /node_modules/picomatch/lib | |
| parent | b500a50f1b97d93c98b36ed9a980f8188d648147 (diff) | |
| download | LYLLRuoka-5d309ff52cd399a6b71968a6b9a70c8ac0b98981.tar.gz LYLLRuoka-5d309ff52cd399a6b71968a6b9a70c8ac0b98981.zip  | |
Added node_modules for the updating to work properly.
Diffstat (limited to 'node_modules/picomatch/lib')
| -rw-r--r-- | node_modules/picomatch/lib/constants.js | 179 | ||||
| -rw-r--r-- | node_modules/picomatch/lib/parse.js | 1091 | ||||
| -rw-r--r-- | node_modules/picomatch/lib/picomatch.js | 342 | ||||
| -rw-r--r-- | node_modules/picomatch/lib/scan.js | 391 | ||||
| -rw-r--r-- | node_modules/picomatch/lib/utils.js | 64 | 
5 files changed, 2067 insertions, 0 deletions
diff --git a/node_modules/picomatch/lib/constants.js b/node_modules/picomatch/lib/constants.js new file mode 100644 index 0000000..a62ef38 --- /dev/null +++ b/node_modules/picomatch/lib/constants.js @@ -0,0 +1,179 @@ +'use strict'; + +const path = require('path'); +const WIN_SLASH = '\\\\/'; +const WIN_NO_SLASH = `[^${WIN_SLASH}]`; + +/** + * Posix glob regex + */ + +const DOT_LITERAL = '\\.'; +const PLUS_LITERAL = '\\+'; +const QMARK_LITERAL = '\\?'; +const SLASH_LITERAL = '\\/'; +const ONE_CHAR = '(?=.)'; +const QMARK = '[^/]'; +const END_ANCHOR = `(?:${SLASH_LITERAL}|$)`; +const START_ANCHOR = `(?:^|${SLASH_LITERAL})`; +const DOTS_SLASH = `${DOT_LITERAL}{1,2}${END_ANCHOR}`; +const NO_DOT = `(?!${DOT_LITERAL})`; +const NO_DOTS = `(?!${START_ANCHOR}${DOTS_SLASH})`; +const NO_DOT_SLASH = `(?!${DOT_LITERAL}{0,1}${END_ANCHOR})`; +const NO_DOTS_SLASH = `(?!${DOTS_SLASH})`; +const QMARK_NO_DOT = `[^.${SLASH_LITERAL}]`; +const STAR = `${QMARK}*?`; + +const POSIX_CHARS = { +  DOT_LITERAL, +  PLUS_LITERAL, +  QMARK_LITERAL, +  SLASH_LITERAL, +  ONE_CHAR, +  QMARK, +  END_ANCHOR, +  DOTS_SLASH, +  NO_DOT, +  NO_DOTS, +  NO_DOT_SLASH, +  NO_DOTS_SLASH, +  QMARK_NO_DOT, +  STAR, +  START_ANCHOR +}; + +/** + * Windows glob regex + */ + +const WINDOWS_CHARS = { +  ...POSIX_CHARS, + +  SLASH_LITERAL: `[${WIN_SLASH}]`, +  QMARK: WIN_NO_SLASH, +  STAR: `${WIN_NO_SLASH}*?`, +  DOTS_SLASH: `${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$)`, +  NO_DOT: `(?!${DOT_LITERAL})`, +  NO_DOTS: `(?!(?:^|[${WIN_SLASH}])${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`, +  NO_DOT_SLASH: `(?!${DOT_LITERAL}{0,1}(?:[${WIN_SLASH}]|$))`, +  NO_DOTS_SLASH: `(?!${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`, +  QMARK_NO_DOT: `[^.${WIN_SLASH}]`, +  START_ANCHOR: `(?:^|[${WIN_SLASH}])`, +  END_ANCHOR: `(?:[${WIN_SLASH}]|$)` +}; + +/** + * POSIX Bracket Regex + */ + +const POSIX_REGEX_SOURCE = { +  alnum: 'a-zA-Z0-9', +  alpha: 'a-zA-Z', +  ascii: '\\x00-\\x7F', +  blank: ' \\t', +  cntrl: '\\x00-\\x1F\\x7F', +  digit: '0-9', +  graph: '\\x21-\\x7E', +  lower: 'a-z', +  print: '\\x20-\\x7E ', +  punct: '\\-!"#$%&\'()\\*+,./:;<=>?@[\\]^_`{|}~', +  space: ' \\t\\r\\n\\v\\f', +  upper: 'A-Z', +  word: 'A-Za-z0-9_', +  xdigit: 'A-Fa-f0-9' +}; + +module.exports = { +  MAX_LENGTH: 1024 * 64, +  POSIX_REGEX_SOURCE, + +  // regular expressions +  REGEX_BACKSLASH: /\\(?![*+?^${}(|)[\]])/g, +  REGEX_NON_SPECIAL_CHARS: /^[^@![\].,$*+?^{}()|\\/]+/, +  REGEX_SPECIAL_CHARS: /[-*+?.^${}(|)[\]]/, +  REGEX_SPECIAL_CHARS_BACKREF: /(\\?)((\W)(\3*))/g, +  REGEX_SPECIAL_CHARS_GLOBAL: /([-*+?.^${}(|)[\]])/g, +  REGEX_REMOVE_BACKSLASH: /(?:\[.*?[^\\]\]|\\(?=.))/g, + +  // Replace globs with equivalent patterns to reduce parsing time. +  REPLACEMENTS: { +    '***': '*', +    '**/**': '**', +    '**/**/**': '**' +  }, + +  // Digits +  CHAR_0: 48, /* 0 */ +  CHAR_9: 57, /* 9 */ + +  // Alphabet chars. +  CHAR_UPPERCASE_A: 65, /* A */ +  CHAR_LOWERCASE_A: 97, /* a */ +  CHAR_UPPERCASE_Z: 90, /* Z */ +  CHAR_LOWERCASE_Z: 122, /* z */ + +  CHAR_LEFT_PARENTHESES: 40, /* ( */ +  CHAR_RIGHT_PARENTHESES: 41, /* ) */ + +  CHAR_ASTERISK: 42, /* * */ + +  // Non-alphabetic chars. +  CHAR_AMPERSAND: 38, /* & */ +  CHAR_AT: 64, /* @ */ +  CHAR_BACKWARD_SLASH: 92, /* \ */ +  CHAR_CARRIAGE_RETURN: 13, /* \r */ +  CHAR_CIRCUMFLEX_ACCENT: 94, /* ^ */ +  CHAR_COLON: 58, /* : */ +  CHAR_COMMA: 44, /* , */ +  CHAR_DOT: 46, /* . */ +  CHAR_DOUBLE_QUOTE: 34, /* " */ +  CHAR_EQUAL: 61, /* = */ +  CHAR_EXCLAMATION_MARK: 33, /* ! */ +  CHAR_FORM_FEED: 12, /* \f */ +  CHAR_FORWARD_SLASH: 47, /* / */ +  CHAR_GRAVE_ACCENT: 96, /* ` */ +  CHAR_HASH: 35, /* # */ +  CHAR_HYPHEN_MINUS: 45, /* - */ +  CHAR_LEFT_ANGLE_BRACKET: 60, /* < */ +  CHAR_LEFT_CURLY_BRACE: 123, /* { */ +  CHAR_LEFT_SQUARE_BRACKET: 91, /* [ */ +  CHAR_LINE_FEED: 10, /* \n */ +  CHAR_NO_BREAK_SPACE: 160, /* \u00A0 */ +  CHAR_PERCENT: 37, /* % */ +  CHAR_PLUS: 43, /* + */ +  CHAR_QUESTION_MARK: 63, /* ? */ +  CHAR_RIGHT_ANGLE_BRACKET: 62, /* > */ +  CHAR_RIGHT_CURLY_BRACE: 125, /* } */ +  CHAR_RIGHT_SQUARE_BRACKET: 93, /* ] */ +  CHAR_SEMICOLON: 59, /* ; */ +  CHAR_SINGLE_QUOTE: 39, /* ' */ +  CHAR_SPACE: 32, /*   */ +  CHAR_TAB: 9, /* \t */ +  CHAR_UNDERSCORE: 95, /* _ */ +  CHAR_VERTICAL_LINE: 124, /* | */ +  CHAR_ZERO_WIDTH_NOBREAK_SPACE: 65279, /* \uFEFF */ + +  SEP: path.sep, + +  /** +   * Create EXTGLOB_CHARS +   */ + +  extglobChars(chars) { +    return { +      '!': { type: 'negate', open: '(?:(?!(?:', close: `))${chars.STAR})` }, +      '?': { type: 'qmark', open: '(?:', close: ')?' }, +      '+': { type: 'plus', open: '(?:', close: ')+' }, +      '*': { type: 'star', open: '(?:', close: ')*' }, +      '@': { type: 'at', open: '(?:', close: ')' } +    }; +  }, + +  /** +   * Create GLOB_CHARS +   */ + +  globChars(win32) { +    return win32 === true ? WINDOWS_CHARS : POSIX_CHARS; +  } +}; diff --git a/node_modules/picomatch/lib/parse.js b/node_modules/picomatch/lib/parse.js new file mode 100644 index 0000000..58269d0 --- /dev/null +++ b/node_modules/picomatch/lib/parse.js @@ -0,0 +1,1091 @@ +'use strict'; + +const constants = require('./constants'); +const utils = require('./utils'); + +/** + * Constants + */ + +const { +  MAX_LENGTH, +  POSIX_REGEX_SOURCE, +  REGEX_NON_SPECIAL_CHARS, +  REGEX_SPECIAL_CHARS_BACKREF, +  REPLACEMENTS +} = constants; + +/** + * Helpers + */ + +const expandRange = (args, options) => { +  if (typeof options.expandRange === 'function') { +    return options.expandRange(...args, options); +  } + +  args.sort(); +  const value = `[${args.join('-')}]`; + +  try { +    /* eslint-disable-next-line no-new */ +    new RegExp(value); +  } catch (ex) { +    return args.map(v => utils.escapeRegex(v)).join('..'); +  } + +  return value; +}; + +/** + * 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; + +  const opts = { ...options }; +  const 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}`); +  } + +  const bos = { type: 'bos', value: '', output: opts.prepend || '' }; +  const tokens = [bos]; + +  const capture = opts.capture ? '' : '?:'; +  const 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}).)*?)`; +  }; + +  const nodot = opts.dot ? '' : NO_DOT; +  const qmarkNoDot = opts.dot ? QMARK : QMARK_NO_DOT; +  let star = opts.bash === true ? globstar(opts) : STAR; + +  if (opts.capture) { +    star = `(${star})`; +  } + +  // minimatch options support +  if (typeof opts.noext === 'boolean') { +    opts.noextglob = opts.noext; +  } + +  const state = { +    input, +    index: -1, +    start: 0, +    dot: opts.dot === true, +    consumed: '', +    output: '', +    prefix: '', +    backtrack: false, +    negated: false, +    brackets: 0, +    braces: 0, +    parens: 0, +    quotes: 0, +    globstar: false, +    tokens +  }; + +  input = utils.removePrefix(input, state); +  len = input.length; + +  const extglobs = []; +  const braces = []; +  const 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 remaining = () => input.slice(state.index + 1); +  const consume = (value = '', num = 0) => { +    state.consumed += value; +    state.index += num; +  }; + +  const append = token => { +    state.output += token.output != null ? token.output : token.value; +    consume(token.value); +  }; + +  const negate = () => { +    let count = 1; + +    while (peek() === '!' && (peek(2) !== '(' || peek(3) === '?')) { +      advance(); +      state.start++; +      count++; +    } + +    if (count % 2 === 0) { +      return false; +    } + +    state.negated = true; +    state.start++; +    return true; +  }; + +  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') { +      const isBrace = state.braces > 0 && (tok.type === 'comma' || tok.type === 'brace'); +      const isExtglob = tok.extglob === true || (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') { +      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; +      prev.output = (prev.output || '') + tok.value; +      return; +    } + +    tok.prev = prev; +    tokens.push(tok); +    prev = tok; +  }; + +  const extglobOpen = (type, value) => { +    const token = { ...EXTGLOB_CHARS[value], conditions: 1, inner: '' }; + +    token.prev = prev; +    token.parens = state.parens; +    token.output = state.output; +    const output = (opts.capture ? '(' : '') + token.open; + +    increment('parens'); +    push({ type, value, output: state.output ? '' : ONE_CHAR }); +    push({ type: 'paren', extglob: true, value: advance(), output }); +    extglobs.push(token); +  }; + +  const extglobClose = token => { +    let output = token.close + (opts.capture ? ')' : ''); +    let rest; + +    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(remaining())) { +        output = token.close = `)$))${extglobStar}`; +      } + +      if (token.inner.includes('*') && (rest = remaining()) && /^\.[^\\/.]+$/.test(rest)) { +        // Any non-magical string (`.ts`) or even nested expression (`.{ts,tsx}`) can follow after the closing parenthesis. +        // In this case, we need to parse the string and use it in the output of the original pattern. +        // Suitable patterns: `/!(*.d).ts`, `/!(*.d).{ts,tsx}`, `**/!(*-dbg).@(js)`. +        // +        // Disabling the `fastpaths` option due to a problem with parsing strings as `.ts` in the pattern like `**/!(*.d).ts`. +        const expression = parse(rest, { ...options, fastpaths: false }).output; + +        output = token.close = `)${expression})${extglobStar})`; +      } + +      if (token.prev.type === 'bos') { +        state.negatedExtglob = true; +      } +    } + +    push({ type: 'paren', extglob: true, value, output }); +    decrement('parens'); +  }; + +  /** +   * Fast paths +   */ + +  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 ? '\\' : ''); +        }); +      } +    } + +    if (output === input && opts.contains === true) { +      state.output = input; +      return state; +    } + +    state.output = utils.wrapOutput(output, state, options); +    return state; +  } + +  /** +   * Tokenize input until we reach end-of-string +   */ + +  while (!eos()) { +    value = advance(); + +    if (value === '\u0000') { +      continue; +    } + +    /** +     * Escaped characters +     */ + +    if (value === '\\') { +      const 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 +      const match = /^\\+/.exec(remaining()); +      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 === ':') { +        const inner = prev.value.slice(1); +        if (inner.includes('[')) { +          prev.posix = true; + +          if (inner.includes(':')) { +            const idx = prev.value.lastIndexOf('['); +            const pre = prev.value.slice(0, idx); +            const rest = prev.value.slice(idx + 2); +            const 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 === '(') { +      increment('parens'); +      push({ type: 'paren', value }); +      continue; +    } + +    if (value === ')') { +      if (state.parens === 0 && opts.strictBrackets === true) { +        throw new SyntaxError(syntaxError('opening', '(')); +      } + +      const 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; +    } + +    /** +     * Square brackets +     */ + +    if (value === '[') { +      if (opts.nobracket === true || !remaining().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'); + +      const 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; +      } + +      const 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) { +      increment('braces'); + +      const open = { +        type: 'brace', +        value, +        output: '(', +        outputIndex: state.output.length, +        tokensIndex: state.tokens.length +      }; + +      braces.push(open); +      push(open); +      continue; +    } + +    if (value === '}') { +      const brace = braces[braces.length - 1]; + +      if (opts.nobrace === true || !brace) { +        push({ type: 'text', value, output: value }); +        continue; +      } + +      let output = ')'; + +      if (brace.dots === true) { +        const arr = tokens.slice(); +        const 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; +      } + +      if (brace.comma !== true && brace.dots !== true) { +        const out = state.output.slice(0, brace.outputIndex); +        const toks = state.tokens.slice(brace.tokensIndex); +        brace.value = brace.output = '\\{'; +        value = output = '\\}'; +        state.output = out; +        for (const t of toks) { +          state.output += (t.output || t.value); +        } +      } + +      push({ type: 'brace', value, output }); +      decrement('braces'); +      braces.pop(); +      continue; +    } + +    /** +     * Pipes +     */ + +    if (value === '|') { +      if (extglobs.length > 0) { +        extglobs[extglobs.length - 1].conditions++; +      } +      push({ type: 'text', value }); +      continue; +    } + +    /** +     * Commas +     */ + +    if (value === ',') { +      let output = value; + +      const brace = braces[braces.length - 1]; +      if (brace && stack[stack.length - 1] === 'braces') { +        brace.comma = true; +        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 === state.start + 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; +        const brace = braces[braces.length - 1]; +        prev.type = 'dots'; +        prev.output += value; +        prev.value += value; +        brace.dots = true; +        continue; +      } + +      if ((state.braces + state.parens) === 0 && prev.type !== 'bos' && prev.type !== 'slash') { +        push({ type: 'text', value, output: DOT_LITERAL }); +        continue; +      } + +      push({ type: 'dot', value, output: DOT_LITERAL }); +      continue; +    } + +    /** +     * Question marks +     */ + +    if (value === '?') { +      const isGroup = prev && prev.value === '('; +      if (!isGroup && opts.noextglob !== true && peek() === '(' && peek(2) !== '?') { +        extglobOpen('qmark', value); +        continue; +      } + +      if (prev && prev.type === 'paren') { +        const 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 === '<' && !/<([!=]|\w+>)/.test(remaining()))) { +          output = `\\${value}`; +        } + +        push({ type: 'text', value, output }); +        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(); +        continue; +      } +    } + +    /** +     * Plus +     */ + +    if (value === '+') { +      if (opts.noextglob !== true && peek() === '(' && peek(2) !== '?') { +        extglobOpen('plus', value); +        continue; +      } + +      if ((prev && prev.value === '(') || opts.regex === false) { +        push({ type: 'plus', value, output: PLUS_LITERAL }); +        continue; +      } + +      if ((prev && (prev.type === 'bracket' || prev.type === 'paren' || prev.type === 'brace')) || state.parens > 0) { +        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', extglob: true, value, output: '' }); +        continue; +      } + +      push({ type: 'text', value }); +      continue; +    } + +    /** +     * Plain text +     */ + +    if (value !== '*') { +      if (value === '$' || value === '^') { +        value = `\\${value}`; +      } + +      const match = REGEX_NON_SPECIAL_CHARS.exec(remaining()); +      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.globstar = true; +      consume(value); +      continue; +    } + +    let rest = remaining(); +    if (opts.noextglob !== true && /^\([^?]/.test(rest)) { +      extglobOpen('star', value); +      continue; +    } + +    if (prev.type === 'star') { +      if (opts.noglobstar === true) { +        consume(value); +        continue; +      } + +      const prior = prev.prev; +      const before = prior.prev; +      const isStart = prior.type === 'slash' || prior.type === 'bos'; +      const afterStar = before && (before.type === 'star' || before.type === 'globstar'); + +      if (opts.bash === true && (!isStart || (rest[0] && rest[0] !== '/'))) { +        push({ type: 'star', value, output: '' }); +        continue; +      } + +      const isBrace = state.braces > 0 && (prior.type === 'comma' || prior.type === 'brace'); +      const 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 (rest.slice(0, 3) === '/**') { +        const after = input[state.index + 4]; +        if (after && after !== '/') { +          break; +        } +        rest = rest.slice(3); +        consume('/**', 3); +      } + +      if (prior.type === 'bos' && eos()) { +        prev.type = 'globstar'; +        prev.value += value; +        prev.output = globstar(opts); +        state.output = prev.output; +        state.globstar = true; +        consume(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) + (opts.strictSlashes ? ')' : '|$)'); +        prev.value += value; +        state.globstar = true; +        state.output += prior.output + prev.output; +        consume(value); +        continue; +      } + +      if (prior.type === 'slash' && prior.prev.type !== 'bos' && rest[0] === '/') { +        const end = rest[1] !== 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.globstar = true; + +        consume(value + advance()); + +        push({ type: 'slash', value: '/', output: '' }); +        continue; +      } + +      if (prior.type === 'bos' && rest[0] === '/') { +        prev.type = 'globstar'; +        prev.value += value; +        prev.output = `(?:^|${SLASH_LITERAL}|${globstar(opts)}${SLASH_LITERAL})`; +        state.output = prev.output; +        state.globstar = true; +        consume(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.globstar = true; +      consume(value); +      continue; +    } + +    const 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 (const 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) => { +  const opts = { ...options }; +  const max = typeof opts.maxLength === 'number' ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH; +  const len = input.length; +  if (len > max) { +    throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`); +  } + +  input = REPLACEMENTS[input] || input; +  const 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); + +  const nodot = opts.dot ? NO_DOTS : NO_DOT; +  const slashDot = opts.dot ? NO_DOTS_SLASH : NO_DOT; +  const capture = opts.capture ? '' : '?:'; +  const state = { negated: false, prefix: '' }; +  let star = opts.bash === true ? '.*?' : STAR; + +  if (opts.capture) { +    star = `(${star})`; +  } + +  const globstar = opts => { +    if (opts.noglobstar === true) return star; +    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: { +        const match = /^(.*?)\.(\w+)$/.exec(str); +        if (!match) return; + +        const source = create(match[1]); +        if (!source) return; + +        return source + DOT_LITERAL + match[2]; +      } +    } +  }; + +  const output = utils.removePrefix(input, state); +  let source = create(output); + +  if (source && opts.strictSlashes !== true) { +    source += `${SLASH_LITERAL}?`; +  } + +  return source; +}; + +module.exports = parse; diff --git a/node_modules/picomatch/lib/picomatch.js b/node_modules/picomatch/lib/picomatch.js new file mode 100644 index 0000000..782d809 --- /dev/null +++ b/node_modules/picomatch/lib/picomatch.js @@ -0,0 +1,342 @@ +'use strict'; + +const path = require('path'); +const scan = require('./scan'); +const parse = require('./parse'); +const utils = require('./utils'); +const constants = require('./constants'); +const isObject = val => val && typeof val === 'object' && !Array.isArray(val); + +/** + * Creates a matcher function from one or more glob patterns. The + * returned function takes a string to match as its first argument, + * and returns true if the string is a match. The returned matcher + * function also takes a boolean as the second argument that, when true, + * returns an object with additional information. + * + * ```js + * const picomatch = require('picomatch'); + * // picomatch(glob[, options]); + * + * const isMatch = picomatch('*.!(*a)'); + * console.log(isMatch('a.a')); //=> false + * console.log(isMatch('a.b')); //=> true + * ``` + * @name picomatch + * @param {String|Array} `globs` One or more glob patterns. + * @param {Object=} `options` + * @return {Function=} Returns a matcher function. + * @api public + */ + +const picomatch = (glob, options, returnState = false) => { +  if (Array.isArray(glob)) { +    const fns = glob.map(input => picomatch(input, options, returnState)); +    const arrayMatcher = str => { +      for (const isMatch of fns) { +        const state = isMatch(str); +        if (state) return state; +      } +      return false; +    }; +    return arrayMatcher; +  } + +  const isState = isObject(glob) && glob.tokens && glob.input; + +  if (glob === '' || (typeof glob !== 'string' && !isState)) { +    throw new TypeError('Expected pattern to be a non-empty string'); +  } + +  const opts = options || {}; +  const posix = utils.isWindows(options); +  const regex = isState +    ? picomatch.compileRe(glob, options) +    : picomatch.makeRe(glob, options, false, true); + +  const state = regex.state; +  delete regex.state; + +  let isIgnored = () => false; +  if (opts.ignore) { +    const ignoreOpts = { ...options, ignore: null, onMatch: null, onResult: null }; +    isIgnored = picomatch(opts.ignore, ignoreOpts, returnState); +  } + +  const matcher = (input, returnObject = false) => { +    const { isMatch, match, output } = picomatch.test(input, regex, options, { glob, posix }); +    const result = { glob, state, regex, posix, input, output, match, isMatch }; + +    if (typeof opts.onResult === 'function') { +      opts.onResult(result); +    } + +    if (isMatch === false) { +      result.isMatch = false; +      return returnObject ? result : false; +    } + +    if (isIgnored(input)) { +      if (typeof opts.onIgnore === 'function') { +        opts.onIgnore(result); +      } +      result.isMatch = false; +      return returnObject ? result : false; +    } + +    if (typeof opts.onMatch === 'function') { +      opts.onMatch(result); +    } +    return returnObject ? result : true; +  }; + +  if (returnState) { +    matcher.state = state; +  } + +  return matcher; +}; + +/** + * Test `input` with the given `regex`. This is used by the main + * `picomatch()` function to test the input string. + * + * ```js + * const picomatch = require('picomatch'); + * // picomatch.test(input, regex[, options]); + * + * console.log(picomatch.test('foo/bar', /^(?:([^/]*?)\/([^/]*?))$/)); + * // { isMatch: true, match: [ 'foo/', 'foo', 'bar' ], output: 'foo/bar' } + * ``` + * @param {String} `input` String to test. + * @param {RegExp} `regex` + * @return {Object} Returns an object with matching info. + * @api public + */ + +picomatch.test = (input, regex, options, { glob, posix } = {}) => { +  if (typeof input !== 'string') { +    throw new TypeError('Expected input to be a string'); +  } + +  if (input === '') { +    return { isMatch: false, output: '' }; +  } + +  const opts = options || {}; +  const format = opts.format || (posix ? utils.toPosixSlashes : null); +  let match = input === glob; +  let output = (match && format) ? format(input) : input; + +  if (match === false) { +    output = format ? format(input) : input; +    match = output === glob; +  } + +  if (match === false || opts.capture === true) { +    if (opts.matchBase === true || opts.basename === true) { +      match = picomatch.matchBase(input, regex, options, posix); +    } else { +      match = regex.exec(output); +    } +  } + +  return { isMatch: Boolean(match), match, output }; +}; + +/** + * Match the basename of a filepath. + * + * ```js + * const picomatch = require('picomatch'); + * // picomatch.matchBase(input, glob[, options]); + * console.log(picomatch.matchBase('foo/bar.js', '*.js'); // true + * ``` + * @param {String} `input` String to test. + * @param {RegExp|String} `glob` Glob pattern or regex created by [.makeRe](#makeRe). + * @return {Boolean} + * @api public + */ + +picomatch.matchBase = (input, glob, options, posix = utils.isWindows(options)) => { +  const regex = glob instanceof RegExp ? glob : picomatch.makeRe(glob, options); +  return regex.test(path.basename(input)); +}; + +/** + * Returns true if **any** of the given glob `patterns` match the specified `string`. + * + * ```js + * const picomatch = require('picomatch'); + * // picomatch.isMatch(string, patterns[, options]); + * + * console.log(picomatch.isMatch('a.a', ['b.*', '*.a'])); //=> true + * console.log(picomatch.isMatch('a.a', 'b.*')); //=> false + * ``` + * @param {String|Array} str The string to test. + * @param {String|Array} patterns One or more glob patterns to use for matching. + * @param {Object} [options] See available [options](#options). + * @return {Boolean} Returns true if any patterns match `str` + * @api public + */ + +picomatch.isMatch = (str, patterns, options) => picomatch(patterns, options)(str); + +/** + * Parse a glob pattern to create the source string for a regular + * expression. + * + * ```js + * const picomatch = require('picomatch'); + * const result = picomatch.parse(pattern[, options]); + * ``` + * @param {String} `pattern` + * @param {Object} `options` + * @return {Object} Returns an object with useful properties and output to be used as a regex source string. + * @api public + */ + +picomatch.parse = (pattern, options) => { +  if (Array.isArray(pattern)) return pattern.map(p => picomatch.parse(p, options)); +  return parse(pattern, { ...options, fastpaths: false }); +}; + +/** + * Scan a glob pattern to separate the pattern into segments. + * + * ```js + * const picomatch = require('picomatch'); + * // picomatch.scan(input[, options]); + * + * const result = picomatch.scan('!./foo/*.js'); + * console.log(result); + * { prefix: '!./', + *   input: '!./foo/*.js', + *   start: 3, + *   base: 'foo', + *   glob: '*.js', + *   isBrace: false, + *   isBracket: false, + *   isGlob: true, + *   isExtglob: false, + *   isGlobstar: false, + *   negated: true } + * ``` + * @param {String} `input` Glob pattern to scan. + * @param {Object} `options` + * @return {Object} Returns an object with + * @api public + */ + +picomatch.scan = (input, options) => scan(input, options); + +/** + * Compile a regular expression from the `state` object returned by the + * [parse()](#parse) method. + * + * @param {Object} `state` + * @param {Object} `options` + * @param {Boolean} `returnOutput` Intended for implementors, this argument allows you to return the raw output from the parser. + * @param {Boolean} `returnState` Adds the state to a `state` property on the returned regex. Useful for implementors and debugging. + * @return {RegExp} + * @api public + */ + +picomatch.compileRe = (state, options, returnOutput = false, returnState = false) => { +  if (returnOutput === true) { +    return state.output; +  } + +  const opts = options || {}; +  const prepend = opts.contains ? '' : '^'; +  const append = opts.contains ? '' : '$'; + +  let source = `${prepend}(?:${state.output})${append}`; +  if (state && state.negated === true) { +    source = `^(?!${source}).*$`; +  } + +  const regex = picomatch.toRegex(source, options); +  if (returnState === true) { +    regex.state = state; +  } + +  return regex; +}; + +/** + * Create a regular expression from a parsed glob pattern. + * + * ```js + * const picomatch = require('picomatch'); + * const state = picomatch.parse('*.js'); + * // picomatch.compileRe(state[, options]); + * + * console.log(picomatch.compileRe(state)); + * //=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/ + * ``` + * @param {String} `state` The object returned from the `.parse` method. + * @param {Object} `options` + * @param {Boolean} `returnOutput` Implementors may use this argument to return the compiled output, instead of a regular expression. This is not exposed on the options to prevent end-users from mutating the result. + * @param {Boolean} `returnState` Implementors may use this argument to return the state from the parsed glob with the returned regular expression. + * @return {RegExp} Returns a regex created from the given pattern. + * @api public + */ + +picomatch.makeRe = (input, options = {}, returnOutput = false, returnState = false) => { +  if (!input || typeof input !== 'string') { +    throw new TypeError('Expected a non-empty string'); +  } + +  let parsed = { negated: false, fastpaths: true }; + +  if (options.fastpaths !== false && (input[0] === '.' || input[0] === '*')) { +    parsed.output = parse.fastpaths(input, options); +  } + +  if (!parsed.output) { +    parsed = parse(input, options); +  } + +  return picomatch.compileRe(parsed, options, returnOutput, returnState); +}; + +/** + * Create a regular expression from the given regex source string. + * + * ```js + * const picomatch = require('picomatch'); + * // picomatch.toRegex(source[, options]); + * + * const { output } = picomatch.parse('*.js'); + * console.log(picomatch.toRegex(output)); + * //=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/ + * ``` + * @param {String} `source` Regular expression source string. + * @param {Object} `options` + * @return {RegExp} + * @api public + */ + +picomatch.toRegex = (source, options) => { +  try { +    const opts = options || {}; +    return new RegExp(source, opts.flags || (opts.nocase ? 'i' : '')); +  } catch (err) { +    if (options && options.debug === true) throw err; +    return /$^/; +  } +}; + +/** + * Picomatch constants. + * @return {Object} + */ + +picomatch.constants = constants; + +/** + * Expose "picomatch" + */ + +module.exports = picomatch; diff --git a/node_modules/picomatch/lib/scan.js b/node_modules/picomatch/lib/scan.js new file mode 100644 index 0000000..e59cd7a --- /dev/null +++ b/node_modules/picomatch/lib/scan.js @@ -0,0 +1,391 @@ +'use strict'; + +const utils = require('./utils'); +const { +  CHAR_ASTERISK,             /* * */ +  CHAR_AT,                   /* @ */ +  CHAR_BACKWARD_SLASH,       /* \ */ +  CHAR_COMMA,                /* , */ +  CHAR_DOT,                  /* . */ +  CHAR_EXCLAMATION_MARK,     /* ! */ +  CHAR_FORWARD_SLASH,        /* / */ +  CHAR_LEFT_CURLY_BRACE,     /* { */ +  CHAR_LEFT_PARENTHESES,     /* ( */ +  CHAR_LEFT_SQUARE_BRACKET,  /* [ */ +  CHAR_PLUS,                 /* + */ +  CHAR_QUESTION_MARK,        /* ? */ +  CHAR_RIGHT_CURLY_BRACE,    /* } */ +  CHAR_RIGHT_PARENTHESES,    /* ) */ +  CHAR_RIGHT_SQUARE_BRACKET  /* ] */ +} = require('./constants'); + +const isPathSeparator = code => { +  return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH; +}; + +const depth = token => { +  if (token.isPrefix !== true) { +    token.depth = token.isGlobstar ? Infinity : 1; +  } +}; + +/** + * Quickly scans a glob pattern and returns an object with a handful of + * useful properties, like `isGlob`, `path` (the leading non-glob, if it exists), + * `glob` (the actual pattern), `negated` (true if the path starts with `!` but not + * with `!(`) and `negatedExtglob` (true if the path starts with `!(`). + * + * ```js + * const pm = require('picomatch'); + * console.log(pm.scan('foo/bar/*.js')); + * { isGlob: true, input: 'foo/bar/*.js', base: 'foo/bar', glob: '*.js' } + * ``` + * @param {String} `str` + * @param {Object} `options` + * @return {Object} Returns an object with tokens and regex source string. + * @api public + */ + +const scan = (input, options) => { +  const opts = options || {}; + +  const length = input.length - 1; +  const scanToEnd = opts.parts === true || opts.scanToEnd === true; +  const slashes = []; +  const tokens = []; +  const parts = []; + +  let str = input; +  let index = -1; +  let start = 0; +  let lastIndex = 0; +  let isBrace = false; +  let isBracket = false; +  let isGlob = false; +  let isExtglob = false; +  let isGlobstar = false; +  let braceEscaped = false; +  let backslashes = false; +  let negated = false; +  let negatedExtglob = false; +  let finished = false; +  let braces = 0; +  let prev; +  let code; +  let token = { value: '', depth: 0, isGlob: false }; + +  const eos = () => index >= length; +  const peek = () => str.charCodeAt(index + 1); +  const advance = () => { +    prev = code; +    return str.charCodeAt(++index); +  }; + +  while (index < length) { +    code = advance(); +    let next; + +    if (code === CHAR_BACKWARD_SLASH) { +      backslashes = token.backslashes = true; +      code = advance(); + +      if (code === CHAR_LEFT_CURLY_BRACE) { +        braceEscaped = true; +      } +      continue; +    } + +    if (braceEscaped === true || code === CHAR_LEFT_CURLY_BRACE) { +      braces++; + +      while (eos() !== true && (code = advance())) { +        if (code === CHAR_BACKWARD_SLASH) { +          backslashes = token.backslashes = true; +          advance(); +          continue; +        } + +        if (code === CHAR_LEFT_CURLY_BRACE) { +          braces++; +          continue; +        } + +        if (braceEscaped !== true && code === CHAR_DOT && (code = advance()) === CHAR_DOT) { +          isBrace = token.isBrace = true; +          isGlob = token.isGlob = true; +          finished = true; + +          if (scanToEnd === true) { +            continue; +          } + +          break; +        } + +        if (braceEscaped !== true && code === CHAR_COMMA) { +          isBrace = token.isBrace = true; +          isGlob = token.isGlob = true; +          finished = true; + +          if (scanToEnd === true) { +            continue; +          } + +          break; +        } + +        if (code === CHAR_RIGHT_CURLY_BRACE) { +          braces--; + +          if (braces === 0) { +            braceEscaped = false; +            isBrace = token.isBrace = true; +            finished = true; +            break; +          } +        } +      } + +      if (scanToEnd === true) { +        continue; +      } + +      break; +    } + +    if (code === CHAR_FORWARD_SLASH) { +      slashes.push(index); +      tokens.push(token); +      token = { value: '', depth: 0, isGlob: false }; + +      if (finished === true) continue; +      if (prev === CHAR_DOT && index === (start + 1)) { +        start += 2; +        continue; +      } + +      lastIndex = index + 1; +      continue; +    } + +    if (opts.noext !== true) { +      const isExtglobChar = code === CHAR_PLUS +        || code === CHAR_AT +        || code === CHAR_ASTERISK +        || code === CHAR_QUESTION_MARK +        || code === CHAR_EXCLAMATION_MARK; + +      if (isExtglobChar === true && peek() === CHAR_LEFT_PARENTHESES) { +        isGlob = token.isGlob = true; +        isExtglob = token.isExtglob = true; +        finished = true; +        if (code === CHAR_EXCLAMATION_MARK && index === start) { +          negatedExtglob = true; +        } + +        if (scanToEnd === true) { +          while (eos() !== true && (code = advance())) { +            if (code === CHAR_BACKWARD_SLASH) { +              backslashes = token.backslashes = true; +              code = advance(); +              continue; +            } + +            if (code === CHAR_RIGHT_PARENTHESES) { +              isGlob = token.isGlob = true; +              finished = true; +              break; +            } +          } +          continue; +        } +        break; +      } +    } + +    if (code === CHAR_ASTERISK) { +      if (prev === CHAR_ASTERISK) isGlobstar = token.isGlobstar = true; +      isGlob = token.isGlob = true; +      finished = true; + +      if (scanToEnd === true) { +        continue; +      } +      break; +    } + +    if (code === CHAR_QUESTION_MARK) { +      isGlob = token.isGlob = true; +      finished = true; + +      if (scanToEnd === true) { +        continue; +      } +      break; +    } + +    if (code === CHAR_LEFT_SQUARE_BRACKET) { +      while (eos() !== true && (next = advance())) { +        if (next === CHAR_BACKWARD_SLASH) { +          backslashes = token.backslashes = true; +          advance(); +          continue; +        } + +        if (next === CHAR_RIGHT_SQUARE_BRACKET) { +          isBracket = token.isBracket = true; +          isGlob = token.isGlob = true; +          finished = true; +          break; +        } +      } + +      if (scanToEnd === true) { +        continue; +      } + +      break; +    } + +    if (opts.nonegate !== true && code === CHAR_EXCLAMATION_MARK && index === start) { +      negated = token.negated = true; +      start++; +      continue; +    } + +    if (opts.noparen !== true && code === CHAR_LEFT_PARENTHESES) { +      isGlob = token.isGlob = true; + +      if (scanToEnd === true) { +        while (eos() !== true && (code = advance())) { +          if (code === CHAR_LEFT_PARENTHESES) { +            backslashes = token.backslashes = true; +            code = advance(); +            continue; +          } + +          if (code === CHAR_RIGHT_PARENTHESES) { +            finished = true; +            break; +          } +        } +        continue; +      } +      break; +    } + +    if (isGlob === true) { +      finished = true; + +      if (scanToEnd === true) { +        continue; +      } + +      break; +    } +  } + +  if (opts.noext === true) { +    isExtglob = false; +    isGlob = false; +  } + +  let base = str; +  let prefix = ''; +  let glob = ''; + +  if (start > 0) { +    prefix = str.slice(0, start); +    str = str.slice(start); +    lastIndex -= start; +  } + +  if (base && isGlob === true && lastIndex > 0) { +    base = str.slice(0, lastIndex); +    glob = str.slice(lastIndex); +  } else if (isGlob === true) { +    base = ''; +    glob = str; +  } else { +    base = str; +  } + +  if (base && base !== '' && base !== '/' && base !== str) { +    if (isPathSeparator(base.charCodeAt(base.length - 1))) { +      base = base.slice(0, -1); +    } +  } + +  if (opts.unescape === true) { +    if (glob) glob = utils.removeBackslashes(glob); + +    if (base && backslashes === true) { +      base = utils.removeBackslashes(base); +    } +  } + +  const state = { +    prefix, +    input, +    start, +    base, +    glob, +    isBrace, +    isBracket, +    isGlob, +    isExtglob, +    isGlobstar, +    negated, +    negatedExtglob +  }; + +  if (opts.tokens === true) { +    state.maxDepth = 0; +    if (!isPathSeparator(code)) { +      tokens.push(token); +    } +    state.tokens = tokens; +  } + +  if (opts.parts === true || opts.tokens === true) { +    let prevIndex; + +    for (let idx = 0; idx < slashes.length; idx++) { +      const n = prevIndex ? prevIndex + 1 : start; +      const i = slashes[idx]; +      const value = input.slice(n, i); +      if (opts.tokens) { +        if (idx === 0 && start !== 0) { +          tokens[idx].isPrefix = true; +          tokens[idx].value = prefix; +        } else { +          tokens[idx].value = value; +        } +        depth(tokens[idx]); +        state.maxDepth += tokens[idx].depth; +      } +      if (idx !== 0 || value !== '') { +        parts.push(value); +      } +      prevIndex = i; +    } + +    if (prevIndex && prevIndex + 1 < input.length) { +      const value = input.slice(prevIndex + 1); +      parts.push(value); + +      if (opts.tokens) { +        tokens[tokens.length - 1].value = value; +        depth(tokens[tokens.length - 1]); +        state.maxDepth += tokens[tokens.length - 1].depth; +      } +    } + +    state.slashes = slashes; +    state.parts = parts; +  } + +  return state; +}; + +module.exports = scan; diff --git a/node_modules/picomatch/lib/utils.js b/node_modules/picomatch/lib/utils.js new file mode 100644 index 0000000..c3ca766 --- /dev/null +++ b/node_modules/picomatch/lib/utils.js @@ -0,0 +1,64 @@ +'use strict'; + +const path = require('path'); +const win32 = process.platform === 'win32'; +const { +  REGEX_BACKSLASH, +  REGEX_REMOVE_BACKSLASH, +  REGEX_SPECIAL_CHARS, +  REGEX_SPECIAL_CHARS_GLOBAL +} = require('./constants'); + +exports.isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val); +exports.hasRegexChars = str => REGEX_SPECIAL_CHARS.test(str); +exports.isRegexChar = str => str.length === 1 && exports.hasRegexChars(str); +exports.escapeRegex = str => str.replace(REGEX_SPECIAL_CHARS_GLOBAL, '\\$1'); +exports.toPosixSlashes = str => str.replace(REGEX_BACKSLASH, '/'); + +exports.removeBackslashes = str => { +  return str.replace(REGEX_REMOVE_BACKSLASH, match => { +    return match === '\\' ? '' : match; +  }); +}; + +exports.supportsLookbehinds = () => { +  const segs = process.version.slice(1).split('.').map(Number); +  if (segs.length === 3 && segs[0] >= 9 || (segs[0] === 8 && segs[1] >= 10)) { +    return true; +  } +  return false; +}; + +exports.isWindows = options => { +  if (options && typeof options.windows === 'boolean') { +    return options.windows; +  } +  return win32 === true || path.sep === '\\'; +}; + +exports.escapeLast = (input, char, lastIdx) => { +  const idx = input.lastIndexOf(char, lastIdx); +  if (idx === -1) return input; +  if (input[idx - 1] === '\\') return exports.escapeLast(input, char, idx - 1); +  return `${input.slice(0, idx)}\\${input.slice(idx)}`; +}; + +exports.removePrefix = (input, state = {}) => { +  let output = input; +  if (output.startsWith('./')) { +    output = output.slice(2); +    state.prefix = './'; +  } +  return output; +}; + +exports.wrapOutput = (input, state = {}, options = {}) => { +  const prepend = options.contains ? '' : '^'; +  const append = options.contains ? '' : '$'; + +  let output = `${prepend}(?:${input})${append}`; +  if (state.negated === true) { +    output = `(?:^(?!${output}).*$)`; +  } +  return output; +};  | 
