Socket
Socket
Sign inDemoInstall

picomatch

Package Overview
Dependencies
Maintainers
2
Versions
28
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

picomatch - npm Package Compare versions

Comparing version 1.1.2 to 1.2.0

lib/constants.js

2

index.js

@@ -0,1 +1,3 @@

'use strict';
module.exports = require('./lib/picomatch');

1395

lib/parse.js
'use strict';
const path = require('path');
const windows = () => process.platform === 'windows' || path.sep === '\\';
const MAX_LENGTH = 65536;
const utils = require('./utils');
const SPECIAL_CHAR_REGEX = /(\\?)((\W)(\3*))/g;
const NON_SPECIAL_CHAR_REGEX = /^[^@![\].,$*+?^{}()|\\/]+/;
const MAX_LENGTH = 1024 * 64;
/**
* Constants
*/
const START_ANCHOR = '(?:^|\\/)';
const END_ANCHOR = '(?:\\/|$)';
const DOT_LITERAL = '\\.';
const DOTS_SLASH = '\\.{1,2}' + END_ANCHOR;
const NO_DOT = '(?!\\.)';
const NO_DOT_SLASH = `(?!${DOTS_SLASH})`;
const ZERO_DOT_SLASH = `(?!\\.{0,1}${END_ANCHOR})`;
const NO_DOTS = '(?!(?:^|\\/)\\.{1,2}(?:$|\\/))';
const ONE_CHAR = '(?=.)';
const DOT_SLASH = '(?:^|\\./)';
const PLUS_LITERAL = '\\+';
const QMARK = '[^/]';
const QMARK_LITERAL = '\\?';
const QMARK_NO_DOT = '[^/.]';
const QMARK_WINDOWS = '[^\\\\/]';
const QMARK_WINDOWS_NO_DOT = '[^\\\\/.]';
const SLASH_LITERAL = '\\/';
const SLASH_LITERAL_WINDOWS = '[\\\\/]';
const STAR = `${QMARK}*?`;
const STAR_WINDOWS = `${QMARK_WINDOWS}*?`;
const POSIX = {
/**
* Extglobs
*/
const EXTGLOB_CHARS = {
'!': { type: 'negate', open: '(?:(?!(?:', close: `))${STAR})` },
'?': { type: 'qmark', open: '(?:', close: ')?' },
'+': { type: 'plus', open: '(?:', close: ')+' },
'*': { type: 'star', open: '(?:', close: ')*' },
'@': { type: 'at', open: '(?:', close: ')' }
};
/**
* POSIX Bracket Regex
*/
const POSIX_REGEX = {
alnum: 'a-zA-Z0-9',

@@ -28,716 +65,932 @@ alpha: 'a-zA-Z',

const nonWordRegex = /\W/g;
const endRegex = /\/\]?[*?]?$/;
const lookbehindRegex = /^([!=:]|<[!=])/;
const specialCharRegex = /^["`'^$*+-.?[\]{}()|]$/;
const bracketCloseRegex = /\]/;
const wildcardRegex = /^[*?]/;
const spaceRegex = /[bnvrts]/;
/**
* Replace globs with equivalent patterns to reduce parsing time.
*/
module.exports = (input, options) => {
const REPLACEMENTS = {
'***': '*',
'**/**': '**',
'**/**/**': '**'
};
/**
* Helpers
*/
const globstar = opts => {
let capture = opts.capture ? '' : '?:';
return `(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`;
};
const syntaxError = (place, char) => {
return `Missing ${place}: "${char}" - use "\\\\${char}" to match literal characters`;
};
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;
};
const parse = (input, options) => {
if (typeof input !== 'string') {
throw new TypeError('expected input to be a string');
throw new TypeError('Expected a string');
}
let opts = options || {};
let max = opts.maxLength || MAX_LENGTH;
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 RangeError(`input string must not be longer than ${max} bytes`);
throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`);
}
let orig = input;
let isWindows = opts.unixify !== false && (opts.unixify === true || windows() === true);
let STAR = isWindows ? '[^\\\\/]*?' : '[^\\/]*?';
let QMARK_NO_DOT = isWindows ? '[^\\\\/.]' : '[^\\/.]';
let QMARK = isWindows ? '[^\\\\/]' : '[^\\/]';
let slash = isWindows ? '[\\\\/]' : '\\/';
let GLOBSTAR_DOT = `(?:(?!(?:${slash}|^)(?:\\.{1,2})($|${slash})).)*?`;
let GLOBSTAR_NO_DOT = `(?:(?!(?:${slash}|^)\\.).)*?`;
let ONE_DOT = `(?!\\.{1,2}(?:${slash}|$))`;
let bos = { type: 'bos', value: '', output: opts.prepend || '' };
let tokens = [bos];
let prefix = opts.contains ? '' : '^';
let suffix = opts.contains ? '' : '$';
let wrap = str => `${prefix}(?:${str})${suffix}`;
let negate = str => `${prefix}(?!^${str}$).*${suffix}`;
let capture = opts.capture ? '' : '?:';
let nodot = opts.dot ? '' : NO_DOT;
let star = opts.bash === true ? globstar(opts) : STAR;
let qmarkNoDot = opts.dot ? QMARK : QMARK_NO_DOT;
let ast = { type: 'root', nodes: [], stash: [] };
let prepend = opts.prepend || '';
if (opts.capture) {
star = '(' + star + ')';
}
// minimatch options support
if (typeof opts.noext === 'boolean') {
opts.noextglob = opts.noext;
}
let state = {
ast,
segs: [{ value: '', depth: 1 }],
input,
index: -1,
start: 0,
consumed: '',
posix: opts.posix === true,
dot: opts.dot === true,
wrap
output: '',
backtrack: false,
brackets: 0,
braces: 0,
parens: 0,
quotes: 0,
tokens
};
let stack = [ast];
let extglobs = [];
let start = 0;
let i = -1;
let stack = [];
let block = state;
let prev = bos;
let value;
let _isParen,
after,
args,
block,
boundary,
brace,
bracket,
charClass,
first,
idx,
inner,
last,
left,
next,
node,
noglobstar,
paren,
posix,
prev,
qmark,
queue,
quoted,
slashes,
star,
stars,
stash,
value;
/**
* Tokenizing helpers
*/
const lastItem = (arr, n = 1) => arr[arr.length - n];
const lookbehind = (n = 1) => lastItem(stack, n);
const eos = () => i === len - 1;
const rest = () => !eos() ? input.slice(i + 1) : void 0;
const peek = (n = 1) => !eos() ? input[i + n] : void 0;
const advance = () => {
state.consumed += (input[i] || '');
return input[++i];
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 push = node => {
block = node;
stack.push(node);
const increment = type => {
state[type]++;
stack.push(type);
};
const isPunct = val => {
return val === '('
|| val === ')'
|| val === '{'
|| val === '}'
|| val === '|'
|| val === ','
|| val === '/';
const decrement = type => {
state[type]--;
stack.pop();
};
const isParen = () => {
if (block.type === 'paren' || block.type === 'brace') {
return isPunct(prev) && isPunct(next);
/**
* 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;
}
}
return false;
};
const append = (value, node, addsegment = true) => {
if (!block) block = lookbehind();
block.isGlob = state.isGlob;
block.stash.push(value);
if (state.parens && extglobs.length && tok.type !== 'paren' && !EXTGLOB_CHARS[tok.value]) {
let extglob = extglobs[extglobs.length - 1];
extglob.inner += tok.value;
}
if (node && block.nodes) {
block.nodes.push(node);
if (tok.value || tok.output) append(tok);
if (prev && prev.type === 'text' && tok.type === 'text') {
prev.value += tok.value;
return;
}
Reflect.defineProperty(tok, 'prev', { value: prev });
tokens.push(tok);
prev = tok;
};
const extglob = value => {
if (opts.noextglob === true) {
block.stash.push(value);
return;
const extglobOpen = (type, value) => {
let token = { ...EXTGLOB_CHARS[value], inner: '' };
Reflect.defineProperty(token, 'prev', { value: 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;
}
}
state.isGlob = true;
paren = opts.capture === true ? '(' : '(?:';
node = { type: 'paren', extglob: true, prefix: value, stash: [], nodes: [] };
if (node.prefix === '!' && opts.nonegate !== true) node.negated = true;
node.stash = value === '!' && opts.nonegate !== true ? [`${paren}(?!(?:`] : [paren];
Reflect.defineProperty(node, 'parent', { value: block });
block.length = block.stash.length;
block.nodes.push(node);
extglobs.push(node);
push(node);
advance();
push({ type: 'paren', extglob: true, value, output });
decrement('parens');
};
while (i < input.length - 1) {
if (opts.fastpaths !== false && !/(^[*!]|[/{[()\]}"])/.test(input)) {
let backslashes = false;
let output = input.replace(SPECIAL_CHAR_REGEX, (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 === '\\') {
if (opts.preserveBackslashes) {
append(value);
let next = peek();
if (next === '/' && opts.bash !== true) {
continue;
}
slashes = value;
let len = 1;
if (next === '.' || next === ';') {
continue;
}
if (opts.bash === true) {
next = peek();
append((eos() || !isSpecialChar(next) && !spaceRegex.test(next)) ? '\\\\' : '');
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 {
if (opts.unixify !== false) {
while ((next = peek()) === '\\' && !isSpecialChar(peek(2))) {
slashes += advance();
len++;
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[rest];
if (posix) {
prev.value = pre + posix;
state.backtrack = true;
advance();
if (!bos.output && tokens.indexOf(prev) === 1) {
bos.output = ONE_CHAR;
}
continue;
}
}
}
}
if (len > 2) {
slashes = (len % 2 === 0 || eos()) ? '\\\\' : ('\\' + advance());
append(slashes);
} else if (len === 2) {
append(slashes);
} else {
append((opts.unescapeRegex && len === 1 ? '' : slashes) + (eos() ? '\\' : advance()));
}
if ((value === '[' && peek() !== ':') || (value === '-' && peek() === ']')) {
value = '\\' + value;
}
prev = value;
if (value === ']' && (prev.value === '[' || prev.value === '[^')) {
value = '\\' + value;
}
if (opts.posix === true && value === '!' && prev.value === '[') {
value = '^';
}
prev.value += value;
append({ value });
continue;
}
block = lookbehind();
/**
* If we're inside a quoted string, continue
* until we reach the closing double quote.
*/
if (block.type === 'quote' && value !== '"' && value !== "'" && value !== '`') {
prev = value;
append(isSpecialChar(value) ? `\\${value}` : value);
if (state.quotes === 1 && value !== '"') {
value = utils.escapeRegex(value);
prev.value += value;
append({ value });
continue;
}
if (block.type === 'bracket' && value !== ']' && state.posix !== true) {
prev = value;
append(value);
/**
* Double quotes
*/
if (value === '"') {
state.quotes = state.quotes === 1 ? 0 : 1;
if (opts.keepQuotes === true) {
push({ type: 'text', value });
}
continue;
}
switch (value) {
case ':':
if (state.posix === true && prev === '[' && block.type === 'bracket' && lookbehind(2).type === 'bracket') {
posix = stack.pop();
posix.stash = [];
/**
* Parentheses
*/
while ((next = peek()) !== ':' && peek(2) !== ']') {
posix.stash.push(advance());
}
if (value === '(') {
push({ type: 'paren', value });
increment('parens');
continue;
}
advance();
advance();
stash = posix.stash.join('');
inner = POSIX[stash] || stash;
block = lookbehind();
block.stash.push(inner);
block.posix = true;
break;
}
if (value === ')') {
if (state.parens === 0 && opts.strictBrackets === true) {
throw new SyntaxError(syntaxError('opening', '('));
}
append(value);
break;
case '[':
if (!(rest() || '').includes(']')) {
append('\\[');
break;
}
let extglob = extglobs[extglobs.length - 1];
if (extglob && state.parens === extglob.parens + 1) {
extglobClose(extglobs.pop());
continue;
}
state.isGlob = true;
node = { type: 'bracket', stash: [value], nodes: [] };
if (peek() === ']') node.stash.push(`\\${advance()}`);
push(node);
break;
case ']':
if (block.type !== 'bracket') {
if (opts.strictBrackets === true) {
throw new Error('Missing closing: "]" - use "\\\\[" to match literal brackets');
}
append('\\]');
break;
}
push({ type: 'paren', value, output: state.parens ? ')' : '\\)' });
decrement('parens');
continue;
}
first = block.stash[1];
if (first === '!' && state.posix === true) {
first = block.stash[1] = '^';
}
/**
* Brackets
*/
if (block.stash.length === 1 || (block.stash.length === 2 && first === '^')) {
append('\\]');
break;
if (value === '[') {
if (opts.nobracket === true || !input.slice(state.index + 1).includes(']')) {
if (opts.nobracket !== true && opts.strictBrackets === true) {
throw new SyntaxError(syntaxError('closing', ']'));
}
bracket = stack.pop();
block = lookbehind(1);
next = peek();
value = '\\' + value;
} else {
increment('brackets');
}
if (first === '^' && !bracket.stash.includes('/') && next !== void 0) {
bracket.stash.push(isWindows ? '\\\\/' : '/');
}
push({ type: 'bracket', value });
continue;
}
stash = bracket.stash.slice(1);
if (value === ']') {
if (opts.nobracket === true || (prev && prev.type === 'bracket' && prev.value.length === 1)) {
push({ type: 'text', value, output: '\\' + value });
continue;
}
if (bracket.posix === true) {
block.posix = true;
append(`[${stash.join('')}]`);
break;
if (state.brackets === 0) {
if (opts.strictBrackets === true) {
throw new SyntaxError(syntaxError('opening', '['));
}
if (block.posix === true && block.type !== 'root') {
stash.unshift('\\[');
stash = block.stash.concat(stash);
inner = stash.join('') + ']';
stack.pop();
block = lookbehind();
append(inner);
break;
}
push({ type: 'text', value, output: '\\' + value });
continue;
}
inner = stash.join('');
left = inner.replace(/\W/g, '\\$&');
append(`(?:\\[${left}\\]|[${inner}])`);
break;
case '(':
if (!(rest() || '').includes(')')) {
if (opts.strictBrackets === true) {
throw new Error('Missing closing: ")" - use "\\)" to match literal parens');
}
append('\\(');
break;
}
decrement('brackets');
state.isGlob = true;
node = { type: 'paren', stash: [value], nodes: [] };
Reflect.defineProperty(node, 'parent', { value: block });
block.nodes.push(node);
push(node);
break;
case ')':
if (lookbehind(1).type !== 'paren') {
if (opts.strictBrackets === true) {
throw new Error('Missing opening: "(" - use "\\(" to match literal parens');
}
append('\\)');
break;
}
let prevValue = prev.value.slice(1);
if (prev.posix !== true && prevValue[0] === '^' && !prevValue.includes('/')) {
value = '/' + value;
}
paren = stack.pop();
block = lookbehind(1);
next = peek();
prev.value += value;
append({ value });
if (paren.negated && !paren.nodes.length && block.negated && block.length === 1 && next === ')') {
block.converted = true;
paren.stash[0] = block.stash[0] = '(?:';
paren.prefix = block.prefix = '@';
}
// when literal brackets are explicitly disabled
// assume we should match with a regex character class
if (opts.literalBrackets === false || utils.hasRegexChars(prevValue)) {
continue;
}
inner = paren.stash.join('');
let escaped = utils.escapeRegex(prev.value);
state.output = state.output.slice(0, -prev.value.length);
if (paren.prefix) {
state.isGlob = true;
boundary = eos() || (rest()[0] === ')' && block.prefix !== '!' && opts.nonegate !== true) ? '$' : '';
// 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;
}
extglobs.pop();
// when the user specifies nothing, try to match both
prev.value = `(${capture}${escaped}|${prev.value})`;
state.output += prev.value;
continue;
}
if (block.close === false) {
append(inner);
break;
}
}
/**
* Braces
*/
switch (paren.prefix) {
case '!':
star = STAR;
if (value === '{' && opts.nobrace !== true) {
push({ type: 'brace', value, output: '(' });
increment('braces');
continue;
}
if (paren.negated || opts.bash || block.nodes.length) {
if (paren.stash.includes(slash) || !paren.nodes.some(node => node.type === 'star')) {
star = '.*?';
}
}
if (value === '}') {
if (opts.nobrace === true || state.braces === 0) {
push({ type: 'text', value, output: '\\' + value });
continue;
}
append(`${inner})${boundary})${star})`);
let output = ')';
if (block.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;
case '*':
case '+':
case '?':
append(`${inner})${paren.prefix}`);
break;
case '@':
default: {
append(`${inner})`);
break;
}
if (arr[i].type !== 'dots') {
range.unshift(arr[i].value);
}
}
break;
case '{':
if (block.type === 'bracket') {
append(value);
break;
}
if (opts.nobrace === true) {
append('\\' + value);
break;
}
output = expandRange(range, opts);
state.backtrack = true;
}
state.isGlob = state.brace = true;
node = { type: 'brace', stash: [value], nodes: [] };
block.nodes.push(node);
push(node);
break;
case '}':
if (block.type === 'bracket') {
append(value);
break;
}
push({ type: 'brace', value, output });
decrement('braces');
continue;
}
if (opts.nobrace === true || block.type !== 'brace') {
append('\\}');
break;
}
/**
* Commas
*/
brace = stack.pop();
block = lookbehind(1);
if (value === ',') {
let output = value;
if (opts.noquantifiers !== true && brace.quantifier === true) {
append(`{${brace.stash.slice(1).join('')}}`);
break;
}
if (state.braces > 0 && stack[stack.length - 1] === 'braces') {
output = '|';
}
if (typeof opts.expandBrace === 'function') {
brace.stash.push('}');
append(opts.expandBrace(...brace.stash));
break;
}
push({ type: 'comma', value, output });
continue;
}
if (brace.stash.includes('..')) {
inner = brace.stash.filter(v => v !== '{');
idx = inner.indexOf('..');
args = [inner.slice(0, idx).join(''), inner.slice(idx + 1).join('')];
/**
* Slashes
*/
if (typeof opts.expandRange === 'function') {
append(opts.expandRange(...args));
break;
}
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;
}
charClass = `[${args.join('-')}]`;
try {
/* eslint-disable no-new */
new RegExp(charClass);
} catch (ex) {
charClass = `[${args.map(v => `\\${v}`).join('-')}]`;
}
push({ type: 'slash', value, output: SLASH_LITERAL });
continue;
}
append(charClass);
break;
}
/**
* Dots
*/
append(`(${brace.stash.slice(1).join('')})`);
break;
case '!':
if (block.type === 'bracket' && state.posix === true && prev === '[') {
append('^');
break;
}
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;
block.dots = true;
continue;
}
if (opts.noextglob !== true && peek() === '(') {
extglob(value);
break;
}
push({ type: 'dot', value, output: DOT_LITERAL });
continue;
}
if (i === start && opts.nonegate !== true) {
start++;
state.isGlob = true;
state.negated = true;
state.wrap = negate;
state.segs[state.segs.length - 1].negated = true;
break;
}
/**
* Question marks
*/
append(value);
break;
case '*':
if (block.type === 'bracket' && state.posix === true) {
append(value);
break;
if (value === '?') {
if (prev && prev.type === 'paren') {
let next = peek();
let output = value;
if (next === '<' && parseInt(process.version.slice(1), 10) < 10) {
throw new Error('Node.js v10 or higher is required for regex lookbehinds');
}
if (prev === ']' && state.posix === true || opts.bash === false) {
append(value);
break;
if (prev.value === '(' && !/[!=<:]/.test(next) || (next === '<' && !/[!=]/.test(peek(2)))) {
output = '\\' + value;
}
let consumed = state.consumed;
state.isGlob = true;
next = peek();
push({ type: 'text', value, output });
continue;
}
if (opts.noextglob !== true && next === '(' && peek(2) !== '?') {
extglob(value);
break;
}
if (opts.noextglob !== true && peek() === '(' && peek(2) !== '?') {
extglobOpen('qmark', value);
continue;
}
stars = value;
while ((next = peek()) === '*' && peek(2) !== '(') stars += advance();
if (opts.dot !== true && (prev.type === 'slash' || prev.type === 'bos')) {
push({ type: 'qmark', value, output: QMARK_NO_DOT });
continue;
}
if (prev === '!' && i === 2) prev = '';
noglobstar = opts.noglobstar === true
|| stars.length > 2
|| (!(_isParen = isParen()) && next && next !== '/')
|| (!_isParen && prev && prev !== '/');
push({ type: 'qmark', value, output: QMARK });
continue;
}
if (noglobstar) stars = '*';
if (stars === '**' && block.type === 'root' && (next === '/' || !next)) {
if (prev === '/') {
block.nodes.pop();
block.stash.pop();
append(`(?:${opts.relaxSlashes ? '' : ONE_CHAR}|${slash}`);
queue = ')';
} else {
append('(?:');
if (next === '/') {
if (!opts.dot && wildcardRegex.test(input[i + 2])) {
append(NO_DOT);
queue = `|${slash + NO_DOT}|${NO_DOT})`;
} else {
queue = `${slash}|${ONE_CHAR})`;
}
} else {
queue = ')';
}
advance();
}
/**
* Exclamation
*/
if (value === '!') {
if (opts.noextglob !== true && peek() === '(') {
if (peek(2) !== '?' || !/[!=<:]/.test(peek(3))) {
extglobOpen('negate', value);
continue;
}
}
if (opts.dot !== true) {
if (!state.dot && (i === start || prev === '/')) {
idx = Math.max(block.stash.lastIndexOf(slash), 0);
after = block.stash.slice(idx);
if (opts.nonegate !== true && state.index === 0) {
negate(state);
continue;
}
}
if (!after.includes(NO_DOT)) {
append(NO_DOT);
}
/**
* Plus
*/
if (stars.length === 1 && !after.includes(ONE_CHAR)) {
if (next || opts.allowEmpty !== false) {
append(ONE_CHAR);
}
}
}
} else if ((!prev || prev === '/') && prev !== '.') {
append(ONE_DOT);
}
if (value === '+') {
if (opts.noextglob !== true && peek() === '(' && peek(2) !== '?') {
extglobOpen('plus', value);
continue;
}
after = void 0;
last = state.segs[state.segs.length - 1];
if (prev && (prev.type === 'bracket' || prev.type === 'paren' || prev.type === 'brace')) {
let output = prev.extglob === true ? '\\' + value : value;
push({ type: 'plus', value, output });
continue;
}
if (stars === '**') {
// globstars
state.globstar = true;
append(opts.dot || next === '.' || state.negated ? GLOBSTAR_DOT : GLOBSTAR_NO_DOT, { type: 'globstar' });
last.globstar = true;
last.depth = Infinity;
if (queue) append(queue);
} else {
// single stars
append(opts.bash ? '.*?' : STAR, { type: 'star' });
last.depth = opts.bash ? Infinity : 1;
last.star = true;
}
// use regex behavior inside parens
if (state.parens > 0 && opts.regex !== false) {
push({ type: 'plus', value });
continue;
}
break;
case '?':
if (block.type === 'bracket' && state.posix === true) {
append(value);
break;
}
push({ type: 'plus', value: PLUS_LITERAL });
continue;
}
if (opts.noextglob !== true && peek() === '(' && peek(2) !== '?') {
extglob(value);
break;
}
/**
* Plain text
*/
// support lookbehinds
if (opts.lookbehinds !== false && prev === '(') {
let match = lookbehindRegex.exec(rest());
if (match) {
if (match[1] === '<!' || match[1] === '<=') {
if (parseInt(process.version.slice(1), 10) < 10) {
throw new Error('Node.js v10 or higher is required for regex lookbehinds');
}
}
state.isGlob = true;
append(value);
break;
}
}
if (value === '@') {
if (opts.noextglob !== true && peek() === '(' && peek(2) !== '?') {
push({ type: 'at', value, output: '' });
continue;
}
if (opts.qmarkLiteral === true) {
append('\\?');
break;
}
push({ type: 'text', value });
continue;
}
state.isGlob = true;
qmark = value;
while ((next = peek()) === '?') qmark += advance();
/**
* Plain text
*/
if ((i === start || prev === '/') && opts.dot !== true) {
append(QMARK_NO_DOT);
} else {
append(opts.bash ? '.' : QMARK);
}
if (value !== '*') {
if (value === '$' || value === '^') {
value = '\\' + value;
}
if (qmark.length > 1) {
append(`{${qmark.length}}`);
}
break;
case '@':
if (block.type === 'bracket' && state.posix === true) {
append(value);
break;
}
let match = NON_SPECIAL_CHAR_REGEX.exec(input.slice(state.index + 1));
if (match) {
value += match[0];
state.index += match[0].length;
}
if (opts.noextglob !== true && peek() === '(' && peek(2) !== '?') {
extglob(value);
break;
}
push({ type: 'text', value });
continue;
}
append(value);
break;
case '+':
if (block.type === 'bracket' && state.posix === true) {
append(value);
break;
}
/**
* Stars
*/
if (opts.noextglob !== true && peek() === '(' && peek(2) !== '?') {
extglob(value);
break;
}
if (block.type !== 'paren' && prev !== ']' && prev !== '}' && prev !== ')') {
append('\\');
}
append('+');
break;
case '/':
append(slash, { type: 'slash' });
if (peek() === '!' && peek(2) === '(') {
append(NO_DOT + ONE_CHAR);
}
state.dot = false;
break;
case '.':
if (i === start && peek() === '/') {
start += 2;
state.prefix = './';
advance();
if (!opts.prepend) {
append(DOT_SLASH);
}
break;
}
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 (block.type === 'bracket' && state.posix === true) {
append(value);
break;
}
if (opts.noextglob !== true && peek() === '(' && peek(2) !== '?') {
extglobOpen('star', value);
continue;
}
if (block.type === 'brace') {
while ((next = peek()) === '.') value += advance();
if (value.length > 1) {
append(value);
break;
}
}
if (prev.type === 'star') {
if (opts.noglobstar === true) {
state.consumed += value;
continue;
}
if ((i === start || prev === '/' || prev === '}' || prev === ')')) {
state.dot = true;
}
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');
append('\\.');
break;
case ',':
node = { type: 'comma', value: block.type === 'brace' ? '|' : value };
append(node.value, node);
break;
case '|':
append(value);
break;
case '"':
case "'":
case '`':
if (block.type === 'bracket' && state.posix === true) {
append(value);
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 (block.type === 'quote' && block.stash[0] === value) {
quoted = stack.pop();
if (prior.type === 'bos' && eos()) {
prev.type = 'globstar';
prev.value += value;
prev.output = globstar(opts);
state.output = prev.output;
state.consumed += value;
continue;
}
if (opts.keepQuotes !== true) {
quoted.stash = quoted.stash.slice(1);
} else {
quoted.stash.push(value);
}
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;
block = lookbehind(1);
append(quoted.stash.join(''));
} else {
block = { type: 'quote', stash: [value], nodes: [] };
push(block);
}
break;
default: {
if (block.type === 'bracket') {
append(value);
break;
}
prev.type = 'globstar';
prev.output = globstar(opts) + '|$)';
prev.value += value;
node = { type: 'text', value: isSpecialChar(value) ? `\\${value}` : value };
append(node.value);
break;
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;
}
prev = value;
}
state.consumed += input[i] || '';
let token = { type: 'star', value, output: star };
// escape unclosed brackets
while (stack.length > 1) {
node = stack.pop();
if (opts.bash === true) {
token.output = '.*?';
if (prev.type === 'bos' || prev.type === 'slash') {
token.output = nodot + token.output;
}
push(token);
continue;
}
let close = { paren: ')', brace: '}', bracket: ']' };
if (opts.strictBrackets === true && close.hasOwnProperty(node.type)) {
let c = close[node.type];
throw new Error(`Missing closing: "${c}" - use "\\\\${c}" to match literal ${node.type}s`);
if (prev && (prev.type === 'bracket' || prev.type === 'paren') && opts.regex === true) {
token.output = value;
push(token);
continue;
}
block = lookbehind();
append(node.stash.join('').replace(nonWordRegex, '\\$&'), null, false);
if (state.index === state.start || prev.type === 'slash' || prev.type === 'dot') {
if (prev.type === 'dot') {
state.output += ZERO_DOT_SLASH;
prev.output += ZERO_DOT_SLASH;
} else if (opts.dot === true) {
state.output += NO_DOT_SLASH;
prev.output += NO_DOT_SLASH;
} else {
state.output += nodot;
prev.output += nodot;
}
if (peek() !== '*') {
state.output += ONE_CHAR;
prev.output += ONE_CHAR;
}
}
push(token);
}
idx = ast.stash.indexOf(slash);
if (idx === -1) idx = ast.stash.length;
while (state.brackets > 0) {
if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', ']'));
state.output = utils.escapeLast(state.output, '[');
decrement('brackets');
}
if (state.globstar !== true && !ast.stash.slice(0, idx).includes(ONE_CHAR) && isSpecialChar(orig[0])) {
ast.stash.unshift(ONE_CHAR);
while (state.parens > 0) {
if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', ')'));
state.output = utils.escapeLast(state.output, '(');
decrement('parens');
}
last = ast.nodes[ast.nodes.length - 1] || {};
while (state.braces > 0) {
if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', '}'));
state.output = utils.escapeLast(state.output, '{');
decrement('braces');
}
if (last.type === 'star' || (opts.relaxSlashes === true && !endRegex.test(orig))) {
append(slash + '?', null, false);
if (opts.strictSlashes !== true && (prev.type === 'star' || prev.type === 'bracket')) {
push({ type: 'maybe_slash', value: '', output: '\\/?' });
}
state.output = prepend + ast.stash.join('');
state.source = state.wrap(state.output);
// 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;
};
function isSpecialChar(ch) {
return ch !== '' && typeof ch === 'string' && specialCharRegex.test(ch);
}
/**
* 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 star = opts.bash === true ? '.*?' : STAR;
let nodot = opts.dot ? NO_DOTS : NO_DOT;
let slashDot = opts.dot ? NO_DOT_SLASH : NO_DOT;
let qmarkNoDot = opts.dot ? QMARK : QMARK_NO_DOT;
switch (input) {
case '*':
return `${nodot}${ONE_CHAR}${star}`;
case '.*':
return `\\.${ONE_CHAR}${star}`;
case '*.*':
return `${nodot}${star}\\.${ONE_CHAR}${star}`;
case '*/*':
return `${nodot}${star}${SLASH_LITERAL}${ONE_CHAR}${slashDot}${star}`;
case '**':
return nodot + globstar(opts);
case '**/*':
return `(?:${nodot}${globstar(opts)}\\/)?${slashDot}${ONE_CHAR}${star}`;
case '**/*.*':
return `(?:${nodot}${globstar(opts)}\\/)?${nodot}${star}\\.${ONE_CHAR}${star}`;
case '**/.*':
return `(?:${nodot}${globstar(opts)}\\/)?\\.${ONE_CHAR}${star}`;
default: {
let match = /^(.*?)(?:\.(\w+))$/.exec(input);
if (!match) return;
let source = parse.fastpaths(match[1], options);
if (!source) return;
return source + DOT_LITERAL + match[2];
}
}
};
module.exports = parse;
'use strict';
const path = require('path');
const windows = () => process.platform === 'windows' || path.sep === '\\';
const normalize = str => str.replace(/^\.[\\/]/, '');
const unixify = str => str.replace(/\\+/g, '/');
const scan = require('./scan');
const parse = require('./parse');
const scan = require('./scan');
const utils = require('./utils');
const toPosixSlashes = str => str.replace(/\\/g, '/');

@@ -29,66 +28,103 @@ /**

const picomatch = (patterns, options) => {
if (Array.isArray(patterns)) {
let fns = patterns.map(p => picomatch(p, options));
return (...args) => fns.some(fn => fn(...args));
const picomatch = (glob, options, returnState = false) => {
if (Array.isArray(glob)) {
let fns = glob.map(input => picomatch(input, options, returnState));
return str => {
for (let isMatch of fns) {
let state = isMatch(str);
if (state) return state;
}
return false;
};
}
let matcher = (pattern, opts = {}) => {
if (typeof pattern !== 'string') {
throw new TypeError('expected pattern to be a string');
}
if (typeof glob !== 'string' || glob === '') {
throw new TypeError('Expected pattern to be a non-empty string');
}
if (pattern === '') {
return str => opts.bash ? str === pattern : false;
}
let opts = options || {};
let posix = utils.isWindows(options);
let regex = picomatch.makeRe(glob, options, false, true);
let state = regex.state;
delete regex.state;
let isIgnored = () => false;
let isWin = isWindows(opts);
let negated = false;
let isIgnored = () => false;
if (opts.ignore) {
isIgnored = picomatch(opts.ignore, { ...options, ignore: null, onMatch: null }, returnState);
}
if (opts.nonegate !== true && pattern[0] === '!' && pattern[1] !== '(') {
pattern = pattern.slice(1);
negated = true;
const matcher = (input, returnObject = false) => {
let { isMatch, match, output } = picomatch.test(input, regex, options, { glob, posix });
let result = { glob, state, regex, posix, input, output, match, isMatch };
if (typeof opts.onResult === 'function') {
opts.onResult(result);
}
if (opts.normalize !== false) {
pattern = normalize(pattern);
if (isMatch === false) {
result.isMatch = false;
return returnObject ? result : false;
}
if (opts.ignore) {
let ignore = picomatch(opts.ignore, { ...opts, ignore: null });
isIgnored = str => ignore(str, true, true);
if (isIgnored(input)) {
if (typeof opts.onIgnore === 'function') {
opts.onIgnore(result);
}
result.isMatch = false;
return returnObject ? result : false;
}
let test = str => !isIgnored(str) && pattern === str;
if (/[+*?[({})\]\\]/.test(pattern)) {
let regex = picomatch.makeRe(pattern, opts);
test = str => !isIgnored(str) && (pattern === str || regex.test(str));
if (typeof opts.onMatch === 'function') {
opts.onMatch(result);
}
return returnObject ? result : true;
};
let isMatch = (str, isUnixified = false, isNormalized = false) => {
if (typeof opts.onData === 'function') str = opts.onData(str);
if (str === pattern) return true;
if (isNormalized !== true && opts.normalize !== false) str = normalize(str);
if (str === pattern) return true;
if (opts.matchBase) {
return test(path.basename(str));
}
return test((!isUnixified && isWin) ? unixify(str) : str);
};
if (returnState) {
matcher.state = state;
}
return negated ? str => !isMatch(str) : isMatch;
};
return matcher;
};
if (options && typeof options.onMatch === 'function') {
let fn = matcher;
matcher = (patterns, opts) => {
let isMatch = fn(patterns, opts);
return str => isMatch(str) ? opts.onMatch(str) : false;
};
picomatch.test = (input, regex, options, { glob, posix } = {}) => {
if (typeof input !== 'string') {
throw new TypeError('Expected input to be a string');
}
return precompile('matcher', patterns, options, matcher);
if (input === '') {
return { isMatch: false, output: '' };
}
let opts = options || {};
let format = opts.format || (posix ? 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: !!match, match, output };
};
picomatch.matchBase = (input, pattern, options, posix = utils.isWindows(options)) => {
let regex = pattern instanceof RegExp ? pattern : picomatch.makeRe(pattern, options);
if (posix) {
input = path.posix.basename(input);
} else {
input = path.basename(input);
}
return !!regex.exec(input);
};
/**

@@ -104,3 +140,3 @@ * Returns true if **any** of the given glob `patterns` match the specified `string`.

* ```
* @param {String|Array} str The string to test.
* @param {String|Array} str The string to test.
* @param {String|Array} patterns One or more glob patterns to use for matching.

@@ -112,33 +148,5 @@ * @param {Object} [options] See available [options](#options).

picomatch.isMatch = (str, pattern, options, unixified, normalized) => {
let isMatch = precompile('isMatch', pattern, options, picomatch);
return isMatch(str, unixified, normalized);
};
picomatch.isMatch = (str, patterns, options) => picomatch(patterns, options)(str);
/**
* Create a regular expression from the given glob `pattern`.
*
* ```js
* const pm = require('picomatch');
* pm.makeRe(pattern[, options]);
*
* console.log(pm.makeRe('*.js'));
* //=> /^(?:(\.[\\\/])?(?!\.)(?=.)[^\/]*?\.js)$/
* ```
* @param {String} `pattern` A glob pattern to convert to regex.
* @param {Object} `options`
* @return {RegExp} Returns a regex created from the given pattern.
* @api public
*/
picomatch.makeRe = (input, options) => {
let makeRe = (pattern, opts = {}) => {
let flags = opts.flags || (opts.nocase ? 'i' : '');
let state = picomatch.parse(pattern, opts);
return toRegex(state.source, flags);
};
return precompile('makeRe', input, options, makeRe);
};
/**
* Parse a glob pattern to create the source string for a regular

@@ -157,3 +165,3 @@ * expression.

picomatch.parse = parse;
picomatch.parse = (input, options) => parse(input, options);

@@ -174,3 +182,3 @@ /**

picomatch.scan = scan;
picomatch.scan = (input, options) => scan(input, options);

@@ -188,4 +196,12 @@ /**

picomatch.split = (pattern, options) => {
let { parent, glob } = scan(pattern, options);
return [ parent, glob ];
let state = scan(pattern, options);
let cwd = options && options.cwd ? options.cwd : process.cwd();
let base = state.base;
if (base[0] === '/') base = base.slice(1);
return {
base: state.base,
glob: state.glob,
cwd: path.resolve(cwd, state.base)
};
};

@@ -203,3 +219,3 @@

let glob = args.pop();
let base = unixify(path.posix.join(...args));
let base = toPosixSlashes(path.posix.join(...args));
return path.posix.join(base, glob);

@@ -218,3 +234,3 @@ };

let glob = args.pop();
let base = unixify(path.posix.resolve(...args));
let base = toPosixSlashes(path.posix.resolve(...args));
return path.posix.join(base, glob);

@@ -224,54 +240,78 @@ };

/**
* Clear the picomatch cache that is used for precompiled regular expressions.
* Precompiling can be completely disabled by setting `nocache` to true.
* Create a regular expression from the given glob `pattern`.
*
* ```js
* picomatch.clearCache();
* const pm = require('picomatch');
* pm.makeRe(pattern[, options]);
*
* console.log(pm.makeRe('*.js'));
* //=> /^(?:(\.[\\\/])?(?!\.)(?=.)[^\/]*?\.js)$/
* ```
* @return {undefined}
* @param {String} `pattern` A glob pattern to convert to regex.
* @param {Object} `options`
* @return {RegExp} Returns a regex created from the given pattern.
* @api public
*/
picomatch.clearCache = () => {
picomatch.cache = {};
};
picomatch.makeRe = (input, options, returnOutput = false, returnState = false) => {
if (!input || typeof input !== 'string') {
throw new TypeError('Expected a non-empty string');
}
/**
* Helpers
*/
let opts = options || {};
let prefix = opts.contains ? '' : '^';
let suffix = opts.contains ? '' : '$';
let state = { negated: false, fastpaths: true };
let output;
function precompile(method, pattern, options, fn) {
if (picomatch.nocache === true || options && options.nocache === true) {
return fn(pattern, options);
if (input.startsWith('./')) input = input.slice(2);
if (opts.fastpaths !== false && (input[0] === '.' || input[0] === '*')) {
output = parse.fastpaths(input, options);
if (output && opts.strictSlashes !== true) {
output += '\\/?';
}
}
if (picomatch.cache === void 0) picomatch.cache = {};
let key = createKey(method, pattern, options);
let res = picomatch.cache[key];
if (res === void 0) {
res = picomatch.cache[key] = fn(pattern, options);
if (output === void 0 && !/[-![$*+?^{}(|)\\\]]/.test(input)) {
output = input.replace(/([./])/g, '\\$1');
}
return res;
}
function createKey(method, pattern, options) {
let key = `method="${method}";pattern="${pattern}"`;
if (!options) return key;
let val = '';
for (let k of Object.keys(options)) val += `${k}:${options[k]};`;
if (val) key += `;options=${val}`;
return key;
}
if (output === void 0) {
state = picomatch.parse(input, options);
output = state.output;
}
function toRegex(value, flags) {
if (returnOutput === true) {
return output;
}
let source = `${prefix}(?:${output})${suffix}`;
if (state && state.negated === true) {
source = `^(?!${source}).*$`;
}
let regex = picomatch.toRegex(source, options);
if (returnState === true) {
regex.state = state;
}
return regex;
};
picomatch.toRegex = (source, options) => {
try {
return new RegExp(value, flags);
} catch (err) { /* ignore error */ }
return new RegExp(value.replace(/\W/g, '\\$&'), flags);
}
let opts = options || {};
return new RegExp(source, opts.flags || (opts.nocase ? 'i' : ''));
} catch (err) {
if (options && options.debug === true) throw err;
return /$^/;
}
};
function isWindows(options = {}) {
return options.unixify !== false && (options.unixify === true || windows() === true);
}
/**
* Initialize the nocache property
*/
picomatch.nocache = false;
module.exports = picomatch;
'use strict';
const path = require('path');
const windows = () => process.platform === 'windows' || path.sep === '\\';
const cache = {};
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_LOWERCASE_A, /* a */
CHAR_LOWERCASE_Z, /* z */
CHAR_PLUS, /* + */
CHAR_QUESTION_MARK, /* ? */
CHAR_RIGHT_CURLY_BRACE, /* } */
CHAR_RIGHT_PARENTHESES, /* ) */
CHAR_RIGHT_SQUARE_BRACKET, /* ] */
CHAR_UPPERCASE_A, /* A */
CHAR_UPPERCASE_Z /* Z */
} = require('./constants');
const isPathSeparator = code => {
return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH;
};
/**

@@ -15,3 +37,3 @@ * Quickly scans a glob pattern and returns an object with a handful of

* console.log(pm.scan('foo/bar/*.js'));
* { isGlob: true, input: 'foo/bar/*.js', path: 'foo/bar', parts: [ 'foo', 'bar' ], glob: '*.js' }
* { isGlob: true, input: 'foo/bar/*.js', base: 'foo/bar', glob: '*.js' }
* ```

@@ -24,175 +46,184 @@ * @param {String} `str`

const scan = (input, options = {}) => {
let terminated = false;
let isWin = isWindows(options);
let state = { input, base: '', pattern: '', parts: void 0, isGlob: false };
let string = input;
let slash = false;
let stack = [];
let stash = [''];
module.exports = (input, options) => {
let opts = options || {};
let length = input.length - 1;
let index = -1;
let start = 0;
let end = input.length - 1;
let i = -1;
let lastIndex = 0;
let isGlob = false;
let backslashes = false;
let negated = false;
let braces = 0;
let prev;
let code;
let append = value => {
if (value === '/') {
stash.push('');
} else if (value) {
stash[stash.length - 1] += value;
}
let braceEscaped = false;
let eos = () => index >= length;
let advance = () => {
prev = code;
return input.charCodeAt(++index);
};
let terminate = value => {
if (terminated) {
append(value);
return;
}
while (index < length) {
code = advance();
let next;
let temp = stash.slice();
terminated = true;
state.isGlob = true;
state.pattern = stash.pop() + value + (eos() ? '' : string.slice(i + 1));
if (code === CHAR_BACKWARD_SLASH) {
backslashes = true;
next = advance();
while (stash.length && stash[stash.length - 1].slice(-1) === '.') {
state.pattern = stash.pop() + '/' + state.pattern;
if (next === CHAR_LEFT_CURLY_BRACE) {
braceEscaped = true;
}
continue;
}
state.base = stash.join('/');
state.parts = stash;
stash = temp;
if (braceEscaped === true || code === CHAR_LEFT_CURLY_BRACE) {
braces++;
if (options.segments !== true) {
i = string.length;
} else {
append(value);
}
};
while (!eos() && (next = advance())) {
if (next === CHAR_BACKWARD_SLASH) {
backslashes = true;
next = advance();
continue;
}
let closeIndex = (value, start) => {
let pair = { '[': ']', '(': ')', '{': '}' };
let idx = string.indexOf(pair[value], start);
if (idx > -1 && string[idx - 1] === '\\') {
idx = closeIndex(value, idx + 1);
}
return idx;
};
if (next === CHAR_LEFT_CURLY_BRACE) {
braces++;
continue;
}
let eos = () => i >= end;
let peek = (n = 1) => string[i + n];
let advance = () => string[++i];
for (; i < string.length - 1;) {
let value = advance();
let char;
switch (value) {
case '\\':
char = advance();
if (isWin && char === value) {
if (i === start) slash = true;
append('/');
if (!braceEscaped && next === CHAR_DOT && (next = advance()) === CHAR_DOT) {
isGlob = true;
break;
}
append((char === value ? value : '') + char);
break;
case '/':
if (i === start) slash = true;
append(value);
break;
case '[':
case '{':
case '(':
stack.push({ value });
if (closeIndex(value, i + 1) > -1) {
terminate(value);
if (!braceEscaped && next === CHAR_COMMA) {
isGlob = true;
break;
}
append(value);
break;
case ']':
case '}':
case ')':
stack.pop();
append(value);
break;
case '.':
char = peek();
if (i === start) {
if (char === '/') {
state.prefix = './';
start += 2;
advance();
if (next === CHAR_RIGHT_CURLY_BRACE) {
braces--;
if (braces === 0) {
braceEscaped = false;
break;
}
if (char === void 0) {
state.base = string;
return state;
}
}
append(value);
break;
case '?':
case '+':
terminate(value);
break;
case '*':
while (!eos() && peek() === '*') value += advance();
terminate(value);
break;
case '!':
if (i === start && options.nonegate !== true && peek() !== '(') {
start++;
state.negated = true;
}
}
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;
}
append(value);
break;
case '@':
if (peek() === '(') {
terminate(value);
}
}
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;
}
append(value);
break;
default: {
append(value);
break;
}
}
if (isGlob) {
break;
}
}
if (!state.pattern) state.base = stash.join('/');
if (state.base === '' && slash === true) {
state.parts[state.parts.length - 1] += '/';
state.base = '/';
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 (state.parts === void 0) {
state.parts = stash;
if (isGlob === true && lastIndex > 0) {
base = input.slice(0, lastIndex);
glob = input.slice(lastIndex);
} else if (isGlob === true) {
base = '';
glob = input;
} else {
base = input;
}
return state;
};
if (base !== '' && base !== '/' && base !== input) {
if (isPathSeparator(base.charCodeAt(base.length - 1))) {
base = base.slice(0, base.length - 1);
}
}
function precompile(input, options, fn) {
if (options && options.nocache === true) {
return fn(input, options);
if (opts.unescape === true) {
if (glob) glob = removeBackslashes(glob);
if (base && backslashes === true) {
base = removeBackslashes(base);
}
}
let key = createKey(input, options);
return cache[key] || (cache[key] = fn(input, options));
}
function createKey(input, options) {
let key = `method="scan";pattern="${input}"`;
if (!options) return key;
let val = '';
for (let k of Object.keys(options)) val += `${k}:${options[k]};`;
if (val) key += `;options=${val}`;
return key;
}
return { prefix, input: orig, base, glob, negated, isGlob };
};
function isWindows(options = {}) {
return options.unixify !== false && (options.unixify === true || windows() === true);
function removeBackslashes(str) {
return str.replace(/(?:\[.*?(?<!\\)\]|\\(?=.))/g, match => {
return match === '\\' ? '' : match;
});
}
module.exports = (input, options) => precompile(input, options, scan);
{
"name": "picomatch",
"description": "Blazing fast and accurate glob matcher written in JavaScript, with no dependencies and full support for standard and extended Bash glob features, including braces, extglobs, POSIX brackets, and regular expressions.",
"version": "1.1.2",
"version": "1.2.0",
"homepage": "https://github.com/folder/picomatch",

@@ -25,11 +25,7 @@ "author": "Jon Schlinkert (https://github.com/jonschlinkert)",

"devDependencies": {
"ansi-colors": "^3.2.1",
"bash-match": "^1.0.2",
"fill-range": "^6.0.0",
"gulp-format-md": "^1.0.0",
"minimatch": "^3.0.4",
"minimist": "^1.2.0",
"mocha": "^3.5.3",
"multimatch": "^2.1.0",
"nyc": "^13.0.1"
"gulp-format-md": "^2.0.0",
"mocha": "^6.0.2",
"nyc": "^13.3.0",
"time-require": "github:jonschlinkert/time-require"
},

@@ -53,17 +49,18 @@ "keywords": [

},
"related": {
"list": [
"micromatch",
"braces"
]
},
"reflinks": [
"bash",
"fill-range",
"micromatch",
"minimatch",
"braces",
"expand-brackets",
"extglob",
"micromatch",
"minimatch",
"nanomatch",
"picomatch"
],
"related": {
"list": ["micromatch", "braces"]
}
]
}
}
}

@@ -31,3 +31,3 @@ <h1 align="center">Picomatch</h1>

* **Fast** - Loads in about 2ms (that's several times faster than a [single frame of a HD movie](http://www.endmemo.com/sconvert/framespersecondframespermillisecond.php) at 60fps)
* **Performant** - Optional precompiling to speed up repeat matching (like when watching files)
* **Performant** - Use the returned matcher function to speed up repeat matching (like when watching files)
* **Accurate matching** - Using wildcards (`*` and `?`), globstars (`**`) for nested directories, [advanced globbing](#advanced-globbing) with extglobs, braces, and POSIX brackets, and support for escaping special characters with `\` or quotes.

@@ -73,28 +73,128 @@ * **Well tested** - Thousands of unit tests

| --- | --- | --- | --- |
| `bash` | `boolean` | `false` | Follow bash matching rules more strictly - disallows backslashes as escape characters. |
| `dot` | `boolean` | `false` | Enable dotfile matching. By default, dotfiles are ignored unless a `.` is explicitly defined in the pattern, or `options.dot` is true |
| `expandBrace` | `function` | `undefined` | Function to be called on brace patterns as an alternative to the built-in functionality. The function receives the entire brace pattern including the enclosing braces as its only argument, and it must return a string to be used in the generated regex. |
| `expandRange` | `function` | `undefined` | Custom function for expanding ranges in brace patterns, such as `{a..z}`. The function receives the range values as two arguments, and it must return a string to be used in the generated regex. It's recommended that returned strings be wrapped in parentheses. This option is overridden by the `braces` option. |
| `failglob` | `boolean` | `false` | Throws an error if no matches are found. Based on the bash option of the same name. |
| `flags` | `boolean` | `undefined` | Regex flags to use in the generated regex. If defined, the `nocase` option will be overridden. |
| `ignore` | `array\|string` | `undefined` | One or more glob patterns for excluding strings that should not be matched from the result. |
| `keepQuotes` | `boolean` | `false` | Retain quotes in the generated regex, since quotes may also be used as an alternative to backslashes. |
| `lookbehinds` | `boolean` | `true` | Support regex positive and negative lookbehinds. Note that you must be using Node 8.1.10 or higher to enable regex lookbehinds. |
| `matchBase` | `boolean` | `false` | If set, then patterns without slashes will be matched against the basename of the path if it contains slashes. For example, `a?b` would match the path `/xyz/123/acb`, but not `/xyz/acb/123`. |
| `maxLength` | `boolean` | `65536` | Limit the max length of the input string. An error is thrown if the input string is longer than this value. |
| `nobrace` | `boolean` | `false` | Disabled brace matching. Thus, `{a,b}` and `{1..3}` would be treated as literals. |
| `nocase` | `boolean` | `false` | Make matching case-insensitive. Equivalent to the regex `i` flag. Note that this option is overridden by the `flags` option. |
| `nodupes` | `boolean` | `true` | Deprecated, use `nounique` instead. This option will be removed in a future major release. By default duplicates are removed. Disable uniquification by setting this option to false. |
| `noextglob` | `boolean` | `false` | Disable support for matching with extglobs (like `+(a\|b)`) |
| `noglobstar` | `boolean` | `false` | Disable support for matching nested directories with globstars (`**`) |
| `nonegate` | `boolean` | `false` | Disable support for negating with leading `!` |
| `noquantifiers` | `boolean` | `false` | Disable support for regex quantifiers (like `a{1,2}`) and treat them as brace patterns to be expanded. |
| `normalize` | `boolean` | `false` | Normalize returned paths to remove leading `./` |
| `posix` | `boolean` | `false` | Support POSX character classes ("posix brackets"). |
| `prepend` | `boolean` | `undefined` | String to prepend to the generated regex used for matching. |
| `strictBrackets` | `boolean` | `undefined` | Throw an error if brackets, braces, or parens are imbalanced. |
| `strictSlashes` | `boolean` | `undefined` | Strictly enforce leading and trailing slashes. |
| `unescapeRegex` | `boolean` | `undefined` | Remove backslashes preceding escaped characters in the returned regular expression. By default, backslashes are retained. |
| `unixify` | `boolean` | `undefined` | Convert all slashes in the list to match (not in the glob pattern itself) to forward slashes. |
| `basename` | `boolean` | `false` | If set, then patterns without slashes will be matched against the basename of the path if it contains slashes. For example, `a?b` would match the path `/xyz/123/acb`, but not `/xyz/acb/123`. |
| `bash` | `boolean` | `false` | Follow bash matching rules more strictly - disallows backslashes as escape characters, and treats single stars as globstars (`**`). |
| `capture` | `boolean` | `undefined` | Return regex matches in supporting methods. |
| `contains` | `boolean` | `undefined` | Allows glob to match any part of the given string(s). |
| `cwd` | `string` | `process.cwd()` | Current working directory. Used by `picomatch.split()` |
| `debug` | `boolean` | `undefined` | Debug regular expressions when an error is thrown. |
| `dot` | `boolean` | `false` | Enable dotfile matching. By default, dotfiles are ignored unless a `.` is explicitly defined in the pattern, or `options.dot` is true |
| `expandRange` | `function` | `undefined` | Custom function for expanding ranges in brace patterns, such as `{a..z}`. The function receives the range values as two arguments, and it must return a string to be used in the generated regex. It's recommended that returned strings be wrapped in parentheses. This option is overridden by the `expandBrace` option. |
| `failglob` | `boolean` | `false` | Throws an error if no matches are found. Based on the bash option of the same name. |
| `fastpaths` | `boolean` | `true` | To speed up processing, full parsing is skipped for a handful common glob patterns. Disable this behavior by setting this option to `false`. |
| `flags` | `boolean` | `undefined` | Regex flags to use in the generated regex. If defined, the `nocase` option will be overridden. |
| [format](#optionsformat) | `function` | `undefined` | Custom function for formatting the returned string. This is useful for removing leading slashes, converting Windows paths to Posix paths, etc. |
| `ignore` | `array\|string` | `undefined` | One or more glob patterns for excluding strings that should not be matched from the result. |
| `keepQuotes` | `boolean` | `false` | Retain quotes in the generated regex, since quotes may also be used as an alternative to backslashes. |
| `literalBrackets` | `boolean` | `undefined` | When `true`, brackets in the glob pattern will be escaped so that only literal brackets will be matched. |
| `lookbehinds` | `boolean` | `true` | Support regex positive and negative lookbehinds. Note that you must be using Node 8.1.10 or higher to enable regex lookbehinds. |
| `matchBase` | `boolean` | `false` | Alias for `basename` |
| `maxLength` | `boolean` | `65536` | Limit the max length of the input string. An error is thrown if the input string is longer than this value. |
| `nobrace` | `boolean` | `false` | Disable brace matching, so that `{a,b}` and `{1..3}` would be treated as literal characters. |
| `nobracket` | `boolean` | `undefined` | Disable matching with regex brackets. |
| `nocase` | `boolean` | `false` | Make matching case-insensitive. Equivalent to the regex `i` flag. Note that this option is overridden by the `flags` option. |
| `nodupes` | `boolean` | `true` | Deprecated, use `nounique` instead. This option will be removed in a future major release. By default duplicates are removed. Disable uniquification by setting this option to false. |
| `noext` | `boolean` | `false` | Alias for `noextglob` |
| `noextglob` | `boolean` | `false` | Disable support for matching with extglobs (like `+(a\|b)`) |
| `noglobstar` | `boolean` | `false` | Disable support for matching nested directories with globstars (`**`) |
| `nonegate` | `boolean` | `false` | Disable support for negating with leading `!` |
| `noquantifiers` | `boolean` | `false` | Disable support for regex quantifiers (like `a{1,2}`) and treat them as brace patterns to be expanded. |
| [onIgnore](#optionsonIgnore) | `function` | `undefined` | Function to be called on ignored items. |
| [onMatch](#optionsonMatch) | `function` | `undefined` | Function to be called on matched items. |
| [onResult](#optionsonResult) | `function` | `undefined` | Function to be called on all items, regardless of whether or not they are matched or ignored. |
| `posix` | `boolean` | `false` | Support POSX character classes ("posix brackets"). |
| `posixSlashes` | `boolean` | `undefined` | Convert all slashes in file paths to forward slashes. This does not convert slashes in the glob pattern itself |
| `prepend` | `boolean` | `undefined` | String to prepend to the generated regex used for matching. |
| `regex` | `boolean` | `false` | Use regular expression rules for `+` (instead of matching literal `+`), and for stars that follow closing parentheses or brackets (as in `)*` and `]*`). |
| `strictBrackets` | `boolean` | `undefined` | Throw an error if brackets, braces, or parens are imbalanced. |
| `strictSlashes` | `boolean` | `undefined` | When true, picomatch won't match trailing slashes with single stars. |
| `unescape` | `boolean` | `undefined` | Remove backslashes preceding escaped characters in the glob pattern. By default, backslashes are retained. |
| `unixify` | `boolean` | `undefined` | Alias for `posixSlashes`, for backwards compatitibility. |
### options.expandRange
**Type**: `function`
**Default**: `undefined`
Custom function for expanding ranges in brace patterns. The [fill-range](https://github.com/jonschlinkert/fill-range) library is ideal for this purpose, or you can use custom code to do whatever you need.
**Example**
The following example shows how to create a glob that matches a folder
```js
const fill = require('fill-range');
const regex = pm.makeRe('foo/{01..25}/bar', {
expandRange(a, b) {
return `(${fill(a, b, { toRegex: true })})`;
}
});
console.log(regex);
//=> /^(?:foo\/((?:0[1-9]|1[0-9]|2[0-5]))\/bar)$/
console.log(regex.test('foo/00/bar')) // false
console.log(regex.test('foo/01/bar')) // true
console.log(regex.test('foo/10/bar')) // true
console.log(regex.test('foo/22/bar')) // true
console.log(regex.test('foo/25/bar')) // true
console.log(regex.test('foo/26/bar')) // false
```
### options.format
**Type**: `function`
**Default**: `undefined`
Custom function for formatting strings before they're matched.
**Example**
```js
// strip leading './' from strings
const format = str => str.replace(/^\.\//, '');
const isMatch = picomatch('foo/*.js', { format });
console.log(isMatch('./foo/bar.js')); //=> true
```
### options.onMatch
```js
const onMatch = ({ glob, regex, input, output }) => {
console.log({ glob, regex, input, output });
};
const isMatch = picomatch('*', { onMatch });
isMatch('foo');
isMatch('bar');
isMatch('baz');
```
### options.onIgnore
```js
const onIgnore = ({ glob, regex, input, output }) => {
console.log({ glob, regex, input, output });
};
const isMatch = picomatch('*', { onIgnore, ignore: 'f*' });
isMatch('foo');
isMatch('bar');
isMatch('baz');
```
### options.onResult
```js
const onResult = ({ glob, regex, input, output }) => {
console.log({ glob, regex, input, output });
};
const isMatch = picomatch('*', { onResult, ignore: 'f*' });
isMatch('foo');
isMatch('bar');
isMatch('baz');
```
<br>

@@ -129,7 +229,42 @@ <br>

* extglobs (todo)
* POSIX brackets
* [extglobs](#extglobs)
* [POSIX brackets](#posix-brackets)
* brace expansion (todo)
* regular expressions (todo)
### Extglobs
| **Pattern** | **Description** |
| --- | --- | --- |
| `@(pattern)` | Match _only one_ consecutive occurrence of `pattern` |
| `*(pattern)` | Match _zero or more_ consecutive occurrences of `pattern` |
| `+(pattern)` | Match _one or more_ consecutive occurrences of `pattern` |
| `?(pattern)` | Match _zero or **one**_ consecutive occurrences of `pattern` |
| `!(pattern)` | Match _anything but_ `pattern` |
**Examples**
```js
const pm = require('picomatch');
// *(pattern) matches ZERO or more of "pattern"
console.log(pm.isMatch('a', 'a*(z)')); // true
console.log(pm.isMatch('az', 'a*(z)')); // true
console.log(pm.isMatch('azzz', 'a*(z)')); // true
// +(pattern) matches ONE or more of "pattern"
console.log(pm.isMatch('a', 'a*(z)')); // true
console.log(pm.isMatch('az', 'a*(z)')); // true
console.log(pm.isMatch('azzz', 'a*(z)')); // true
// supports multiple extglobs
console.log(pm.isMatch('foo.bar', '!(foo).!(bar)')); // false
console.log(pm.isMatch('foo.bar', '!(foo).!(bar)')); // false
console.log(pm.isMatch('foo.bar', '!(foo).!(bar)')); // false
console.log(pm.isMatch('foo.bar', '!(foo).!(bar)')); // true
// supports nested extglobs
console.log(pm.isMatch('foo.bar', '!(!(foo)).!(!(bar))')); // true
```
### POSIX brackets

@@ -167,2 +302,10 @@

## Brace Expansion
TODO
## Regular Expressions
TODO
## Matching special characters as literals

@@ -190,6 +333,2 @@

Comparison to other libraries.
### Feature comparison
The following table shows which features are supported by [minimatch](https://github.com/isaacs/minimatch), [micromatch](https://github.com/micromatch/micromatch), [picomatch](https://github.com/folder/picomatch), [nanomatch](https://github.com/micromatch/nanomatch), [extglob](https://github.com/micromatch/extglob), [braces](https://github.com/micromatch/braces), and [expand-brackets](https://github.com/micromatch/expand-brackets).

@@ -199,10 +338,10 @@

| --- | --- | --- | --- | --- | --- | --- | --- |
| wildcard matching (`*?+`) | ✔ | ✔ | ✔ | ✔ | - | - | - |
| advancing globbing | ✔ | ✔ | ✔ | - | - | - | - |
| brace _matching_ | ✔ | ✔ | ✔ | - | - | ✔ | - |
| brace _expansion_ | ✔ | ✔ | - | - | - | ✔ | - |
| extglobs | partial | ✔ | ✔ | - | ✔ | - | - |
| posix brackets | - | ✔ | ✔ | - | - | - | ✔ |
| regular expression syntax | - | ✔ | ✔ | ✔ | ✔ | - | ✔ |
| file system operations | - | - | - | - | - | - | - |
| Wildcard matching (`*?+`) | ✔ | ✔ | ✔ | ✔ | - | - | - |
| Advancing globbing | ✔ | ✔ | ✔ | - | - | - | - |
| Brace _matching_ | ✔ | ✔ | ✔ | - | - | ✔ | - |
| Brace _expansion_ | ✔ | ✔ | - | - | - | ✔ | - |
| Extglobs | partial | ✔ | ✔ | - | ✔ | - | - |
| Posix brackets | - | ✔ | ✔ | - | - | - | ✔ |
| Regular expression syntax | - | ✔ | ✔ | ✔ | ✔ | - | ✔ |
| File system operations | - | - | - | - | - | - | - |

@@ -232,5 +371,50 @@ <br>

### Benchmarks
```
# .makeRe star
picomatch x 1,993,050 ops/sec ±0.51% (91 runs sampled)
minimatch x 627,206 ops/sec ±1.96% (87 runs sampled))
# .makeRe star; dot=true
picomatch x 1,436,640 ops/sec ±0.62% (91 runs sampled)
minimatch x 525,876 ops/sec ±0.60% (88 runs sampled)
# .makeRe globstar
picomatch x 1,592,742 ops/sec ±0.42% (90 runs sampled)
minimatch x 962,043 ops/sec ±1.76% (91 runs sampled)d)
# .makeRe globstars
picomatch x 1,615,199 ops/sec ±0.35% (94 runs sampled)
minimatch x 477,179 ops/sec ±1.33% (91 runs sampled)
# .makeRe with leading star
picomatch x 1,220,856 ops/sec ±0.40% (92 runs sampled)
minimatch x 453,564 ops/sec ±1.43% (94 runs sampled)
# .makeRe - braces
picomatch x 117,916 ops/sec ±2.03% (80 runs sampled))
minimatch x 108,573 ops/sec ±1.63% (91 runs sampled))
```
<br>
<br>
## Philosophies
The goal of this library is to be blazing fast, without compromising on accuracy.
**Accuracy**
The number one of goal of this libary is accuracy. However, it's not unusual for different glob implementations to have different rules for matching behavior, even with simple wildcard matching. It gets increasingly more complicated when combinations of different features are combined, like when extglobs are combined with globstars, braces, slashes, and so on: `!(**/{a,b,*/c})`.
Thus, given that there is no cannonical glob specification to use as a single source of truth when differences of opinion arise regarding behavior, sometimes we have to implement our best judgement and rely on feedback from users to make improvements.
**Performance**
Although this library performs well in benchmarks, and in most cases it's faster than other popular libraries we benchmarked against, we will always choose accuracy over performance. It's not helpful to anyone if our library is faster at returning the wrong answer.
<br>
<br>
## About

@@ -237,0 +421,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc