| '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; |
| }; |
| |
| /** |
| * 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), and `negated` (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 |
| */ |
| |
| module.exports = (input, options) => { |
| let opts = options || {}; |
| let length = input.length - 1; |
| let index = -1; |
| let start = 0; |
| let lastIndex = 0; |
| let isGlob = false; |
| let backslashes = false; |
| let negated = false; |
| let braces = 0; |
| let prev; |
| let code; |
| |
| let braceEscaped = false; |
| |
| let eos = () => index >= length; |
| let advance = () => { |
| prev = code; |
| return input.charCodeAt(++index); |
| }; |
| |
| while (index < length) { |
| code = advance(); |
| let next; |
| |
| if (code === CHAR_BACKWARD_SLASH) { |
| backslashes = true; |
| next = advance(); |
| |
| if (next === CHAR_LEFT_CURLY_BRACE) { |
| braceEscaped = true; |
| } |
| continue; |
| } |
| |
| if (braceEscaped === true || code === CHAR_LEFT_CURLY_BRACE) { |
| braces++; |
| |
| while (!eos() && (next = advance())) { |
| if (next === CHAR_BACKWARD_SLASH) { |
| backslashes = true; |
| next = advance(); |
| continue; |
| } |
| |
| if (next === CHAR_LEFT_CURLY_BRACE) { |
| braces++; |
| continue; |
| } |
| |
| if (!braceEscaped && next === CHAR_DOT && (next = advance()) === CHAR_DOT) { |
| isGlob = true; |
| break; |
| } |
| |
| if (!braceEscaped && next === CHAR_COMMA) { |
| isGlob = true; |
| break; |
| } |
| |
| if (next === CHAR_RIGHT_CURLY_BRACE) { |
| braces--; |
| if (braces === 0) { |
| braceEscaped = false; |
| break; |
| } |
| } |
| } |
| } |
| |
| if (code === CHAR_FORWARD_SLASH) { |
| if (prev === CHAR_DOT && index === (start + 1)) { |
| start += 2; |
| continue; |
| } |
| |
| lastIndex = index + 1; |
| continue; |
| } |
| |
| if (code === CHAR_ASTERISK) { |
| isGlob = true; |
| break; |
| } |
| |
| if (code === CHAR_ASTERISK || code === CHAR_QUESTION_MARK) { |
| isGlob = true; |
| break; |
| } |
| |
| if (code === CHAR_LEFT_SQUARE_BRACKET) { |
| while (!eos() && (next = advance())) { |
| if (next === CHAR_BACKWARD_SLASH) { |
| backslashes = true; |
| next = advance(); |
| continue; |
| } |
| |
| if (next === CHAR_RIGHT_SQUARE_BRACKET) { |
| isGlob = true; |
| break; |
| } |
| } |
| } |
| |
| let isExtglobChar = code === CHAR_PLUS |
| || code === CHAR_AT |
| || code === CHAR_EXCLAMATION_MARK; |
| |
| if (isExtglobChar && input.charCodeAt(index + 1) === CHAR_LEFT_PARENTHESES) { |
| isGlob = true; |
| break; |
| } |
| |
| if (code === CHAR_EXCLAMATION_MARK && index === start) { |
| negated = true; |
| start++; |
| continue; |
| } |
| |
| if (code === CHAR_LEFT_PARENTHESES) { |
| while (!eos() && (next = advance())) { |
| if (next === CHAR_BACKWARD_SLASH) { |
| backslashes = true; |
| next = advance(); |
| continue; |
| } |
| |
| if (next === CHAR_RIGHT_PARENTHESES) { |
| isGlob = true; |
| break; |
| } |
| } |
| } |
| |
| if (isGlob) { |
| break; |
| } |
| } |
| |
| let prefix = ''; |
| let orig = input; |
| let base = input; |
| let glob = ''; |
| |
| if (start > 0) { |
| prefix = input.slice(0, start); |
| input = input.slice(start); |
| lastIndex -= start; |
| } |
| |
| if (base && isGlob === true && lastIndex > 0) { |
| base = input.slice(0, lastIndex); |
| glob = input.slice(lastIndex); |
| } else if (isGlob === true) { |
| base = ''; |
| glob = input; |
| } else { |
| base = input; |
| } |
| |
| if (base && base !== '' && base !== '/' && base !== input) { |
| 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); |
| } |
| } |
| |
| return { prefix, input: orig, base, glob, negated, isGlob }; |
| }; |