work.suroh.tk/node_modules/picomatch/lib/scan.js

223 lines
5.0 KiB
JavaScript

'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) => {
const opts = options || {};
const 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;
const eos = () => index >= length;
const 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;
}
}
}
const isExtglobChar = code === CHAR_PLUS
|| code === CHAR_AT
|| code === CHAR_EXCLAMATION_MARK;
if (isExtglobChar && input.charCodeAt(index + 1) === CHAR_LEFT_PARENTHESES) {
isGlob = true;
break;
}
if (!opts.nonegate && code === CHAR_EXCLAMATION_MARK && index === start) {
negated = true;
start++;
continue;
}
if (opts.noparen !== true && code === CHAR_LEFT_PARENTHESES) {
while (!eos() && (code = advance())) {
if (code === CHAR_BACKWARD_SLASH) {
backslashes = true;
code = advance();
continue;
}
if (code === CHAR_RIGHT_PARENTHESES) {
isGlob = true;
break;
}
}
}
if (isGlob) {
break;
}
}
if (opts.noext === true) {
isGlob = false;
}
let prefix = '';
const 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 };
};