168 lines
5.2 KiB
JavaScript
168 lines
5.2 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.toMatcher = exports.toRegex = void 0;
|
|
const escapeRe = (ch) => (/[.^$+{}()|\\]/.test(ch) ? `\\${ch}` : ch);
|
|
/**
|
|
* Convert a glob pattern to a regular expression
|
|
*
|
|
* Supports:
|
|
* - `/` to separate path segments
|
|
* - `*` to match zero or more characters in a path segment
|
|
* - `?` to match one character in a path segment
|
|
* - `**` to match any number of path segments, including none
|
|
* - `{}` to group conditions (e.g. `{html,txt}`)
|
|
* - `[abc]`, `[a-z]`, `[!a-z]`, `[!abc]` character classes
|
|
*/
|
|
const toRegex = (pattern) => {
|
|
let regexStr = '';
|
|
let i = 0;
|
|
// Helper to parse a brace group like {a,b,c}. No nesting support.
|
|
const parseBraceGroup = () => {
|
|
// Assume current char is '{'
|
|
i++; // skip '{'
|
|
const parts = [];
|
|
let cur = '';
|
|
let closed = false;
|
|
while (i < pattern.length) {
|
|
const ch = pattern[i];
|
|
if (ch === '}') {
|
|
parts.push(cur);
|
|
i++; // consume '}'
|
|
closed = true;
|
|
break;
|
|
}
|
|
if (ch === ',') {
|
|
parts.push(cur);
|
|
cur = '';
|
|
i++;
|
|
continue;
|
|
}
|
|
cur += ch;
|
|
i++;
|
|
}
|
|
if (!closed) {
|
|
// treat as literal '{...'
|
|
return '\\{' + escapeRe(cur);
|
|
}
|
|
// Convert each part recursively to support globs inside braces
|
|
const alt = parts.map((p) => (0, exports.toRegex)(p).source.replace(/^\^/, '').replace(/\$$/, '')).join('|');
|
|
return `(?:${alt})`;
|
|
};
|
|
while (i < pattern.length) {
|
|
const char = pattern[i];
|
|
switch (char) {
|
|
case '*': {
|
|
// Check for double star **
|
|
if (pattern[i + 1] === '*') {
|
|
// Collapse consecutive * beyond two (e.g., *** -> **)
|
|
let j = i + 2;
|
|
while (pattern[j] === '*')
|
|
j++;
|
|
// If followed by a slash, make it optional to allow zero segments
|
|
if (pattern[j] === '/') {
|
|
regexStr += '(?:.*/)?';
|
|
i = j + 1; // consume **/
|
|
}
|
|
else {
|
|
regexStr += '.*';
|
|
i = j; // consume **
|
|
}
|
|
}
|
|
else {
|
|
regexStr += '[^/]*';
|
|
i++;
|
|
}
|
|
break;
|
|
}
|
|
case '?':
|
|
regexStr += '[^/]';
|
|
i++;
|
|
break;
|
|
case '[': {
|
|
// Copy character class as-is with support for leading '!'
|
|
let cls = '[';
|
|
i++;
|
|
if (i < pattern.length && pattern[i] === '!') {
|
|
cls += '^';
|
|
i++;
|
|
}
|
|
// if first after [ or [^ is ']' include it literally
|
|
if (i < pattern.length && pattern[i] === ']') {
|
|
cls += ']';
|
|
i++;
|
|
}
|
|
while (i < pattern.length && pattern[i] !== ']') {
|
|
const ch = pattern[i];
|
|
// Escape backslash inside class
|
|
cls += ch === '\\' ? '\\\\' : ch;
|
|
i++;
|
|
}
|
|
if (i < pattern.length && pattern[i] === ']') {
|
|
cls += ']';
|
|
i++;
|
|
}
|
|
else {
|
|
// Unclosed class -> treat '[' literally
|
|
regexStr += '\\[';
|
|
continue;
|
|
}
|
|
regexStr += cls;
|
|
break;
|
|
}
|
|
case '{': {
|
|
regexStr += parseBraceGroup();
|
|
break;
|
|
}
|
|
case '/':
|
|
regexStr += '/';
|
|
i++;
|
|
break;
|
|
case '.':
|
|
case '^':
|
|
case '$':
|
|
case '+':
|
|
case '(':
|
|
case ')':
|
|
case '|':
|
|
case '\\':
|
|
regexStr += `\\${char}`;
|
|
i++;
|
|
break;
|
|
default:
|
|
regexStr += char;
|
|
i++;
|
|
break;
|
|
}
|
|
}
|
|
return new RegExp('^' + regexStr + '$');
|
|
};
|
|
exports.toRegex = toRegex;
|
|
const isRegExp = /^\/(.{1,4096})\/([gimsuy]{0,6})$/;
|
|
const toMatcher = (pattern) => {
|
|
const regexes = [];
|
|
const patterns = Array.isArray(pattern) ? pattern : [pattern];
|
|
for (const pat of patterns) {
|
|
if (typeof pat === 'string') {
|
|
const match = isRegExp.exec(pat);
|
|
if (match) {
|
|
const [, expr, flags] = match;
|
|
regexes.push(new RegExp(expr, flags));
|
|
}
|
|
else {
|
|
regexes.push((0, exports.toRegex)(pat));
|
|
}
|
|
}
|
|
else {
|
|
regexes.push(pat);
|
|
}
|
|
}
|
|
const length = regexes.length;
|
|
return (path) => {
|
|
for (let i = 0; i < length; i++)
|
|
if (regexes[i].test(path))
|
|
return true;
|
|
return false;
|
|
};
|
|
};
|
|
exports.toMatcher = toMatcher;
|