'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 }; };