Files
notification-elements-demo/node_modules/glob-to-regex.js/lib/index.js
Giuliano Silvestro 5d0c9ec7eb Initial commit: notification-elements-demo app
Interactive Angular 19 demo for @sda/notification-elements-ui with
6 sections: Bell & Feed, Notification Center, Inbox, Comments &
Threads, Mention Input, and Full-Featured layout. Includes mock
data, dark mode toggle, and real-time event log.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 21:49:19 +10:00

246 lines
8.1 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.toMatcher = exports.toRegex = void 0;
const escapeRe = (ch) => (/[.^$+{}()|\\]/.test(ch) ? `\\${ch}` : ch);
/**
* Parse an extended glob pattern like ?(a|b|c)
* Returns the regex string equivalent and the new index position
*/
const parseExtGlob = (pattern, startIdx, prefix, options) => {
let i = startIdx; // startIdx should be pointing at the character after '('
const parts = [];
let cur = '';
let depth = 1; // Track parenthesis depth for nested patterns
while (i < pattern.length && depth > 0) {
const ch = pattern[i];
if (ch === '(') {
depth++;
cur += ch;
i++;
}
else if (ch === ')') {
depth--;
if (depth === 0) {
// Found the closing parenthesis
parts.push(cur);
i++; // consume ')'
break;
}
else {
cur += ch;
i++;
}
}
else if (ch === '|' && depth === 1) {
// Pipe separator at top level of this extglob
parts.push(cur);
cur = '';
i++;
}
else {
cur += ch;
i++;
}
}
if (depth !== 0)
return; // Unclosed parenthesis
let alternatives = '';
const length = parts.length;
for (let j = 0; j < length; j++)
alternatives += (alternatives ? '|' : '') + (0, exports.toRegex)(parts[j], options).source.replace(/^\^/, '').replace(/\$$/, '');
switch (prefix) {
case '?': // zero or one
return [`(?:${alternatives})?`, i];
case '*': // zero or more
return [`(?:${alternatives})*`, i];
case '+': // one or more
return [`(?:${alternatives})+`, i];
case '@': // exactly one
return [`(?:${alternatives})`, i];
case '!': // none of (negative match)
// For negation, we need to match anything that doesn't match the pattern
// Use negative lookahead without consuming characters after
return [`(?!${alternatives})[^/]*`, i];
}
return;
};
/**
* 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
* - Extended globbing (when `extglob: true` option is set):
* - `?(pattern-list)` zero or one occurrence
* - `*(pattern-list)` zero or more occurrences
* - `+(pattern-list)` one or more occurrences
* - `@(pattern-list)` exactly one of the patterns
* - `!(pattern-list)` anything except the patterns
*/
const toRegex = (pattern, options) => {
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, options).source.replace(/^\^/, '').replace(/\$$/, '')).join('|');
return `(?:${alt})`;
};
const extglob = !!options?.extglob;
while (i < pattern.length) {
const char = pattern[i];
// Check for extended glob patterns when extglob is enabled
if (extglob && pattern[i + 1] === '(') {
if (char === '?' || char === '*' || char === '+' || char === '@' || char === '!') {
const result = parseExtGlob(pattern, i + 2, char, options);
if (result) {
regexStr += result[0];
i = result[1];
continue;
}
// If parse failed, fall through to normal handling
}
}
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;
}
}
const flags = options?.nocase ? 'i' : '';
return new RegExp('^' + regexStr + '$', flags);
};
exports.toRegex = toRegex;
const isRegExp = /^\/(.{1,4096})\/([gimsuy]{0,6})$/;
const toMatcher = (pattern, options) => {
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, options));
}
}
else {
regexes.push(pat);
}
}
return regexes.length
? new Function('p', 'return ' + regexes.map((r) => r + '.test(p)').join('||'))
: () => false;
};
exports.toMatcher = toMatcher;