better-auth
Version:
The most comprehensive authentication framework for TypeScript.
108 lines (107 loc) • 4.34 kB
JavaScript
//#region src/utils/wildcard.ts
/**
* Escapes a character if it has a special meaning in regular expressions
* and returns the character as is if it doesn't
*/
function escapeRegExpChar(char) {
if (char === "-" || char === "^" || char === "$" || char === "+" || char === "." || char === "(" || char === ")" || char === "|" || char === "[" || char === "]" || char === "{" || char === "}" || char === "*" || char === "?" || char === "\\") return `\\${char}`;
else return char;
}
/**
* Escapes all characters in a given string that have a special meaning in regular expressions
*/
function escapeRegExpString(str) {
let result = "";
for (let i = 0; i < str.length; i++) result += escapeRegExpChar(str[i]);
return result;
}
/**
* Transforms one or more glob patterns into a RegExp pattern
*/
function transform(pattern, separator = true) {
if (Array.isArray(pattern)) return `(?:${pattern.map((p) => `^${transform(p, separator)}$`).join("|")})`;
let separatorSplitter = "";
let separatorMatcher = "";
let wildcard = ".";
if (separator === true) {
separatorSplitter = "/";
separatorMatcher = "[/\\\\]";
wildcard = "[^/\\\\]";
} else if (separator) {
separatorSplitter = separator;
separatorMatcher = escapeRegExpString(separatorSplitter);
if (separatorMatcher.length > 1) {
separatorMatcher = `(?:${separatorMatcher})`;
wildcard = `((?!${separatorMatcher}).)`;
} else wildcard = `[^${separatorMatcher}]`;
}
const requiredSeparator = separator ? `${separatorMatcher}+?` : "";
const optionalSeparator = separator ? `${separatorMatcher}*?` : "";
const segments = separator ? pattern.split(separatorSplitter) : [pattern];
let result = "";
for (let s = 0; s < segments.length; s++) {
const segment = segments[s];
const nextSegment = segments[s + 1];
let currentSeparator = "";
if (!segment && s > 0) continue;
if (separator) if (s === segments.length - 1) currentSeparator = optionalSeparator;
else if (nextSegment !== "**") currentSeparator = requiredSeparator;
else currentSeparator = "";
if (separator && segment === "**") {
if (currentSeparator) {
result += s === 0 ? "" : currentSeparator;
result += `(?:${wildcard}*?${currentSeparator})*?`;
}
continue;
}
for (let c = 0; c < segment.length; c++) {
const char = segment[c];
if (char === "\\") {
if (c < segment.length - 1) {
result += escapeRegExpChar(segment[c + 1]);
c++;
}
} else if (char === "?") result += wildcard;
else if (char === "*") result += `${wildcard}*?`;
else result += escapeRegExpChar(char);
}
result += currentSeparator;
}
return result;
}
function isMatch(regexp, sample) {
if (typeof sample !== "string") throw new TypeError(`Sample must be a string, but ${typeof sample} given`);
return regexp.test(sample);
}
/**
* Compiles one or more glob patterns into a RegExp and returns an isMatch function.
* The isMatch function takes a sample string as its only argument and returns `true`
* if the string matches the pattern(s).
*
* ```js
* wildcardMatch('src/*.js')('src/index.js') //=> true
* ```
*
* ```js
* const isMatch = wildcardMatch('*.example.com', '.')
* isMatch('foo.example.com') //=> true
* isMatch('foo.bar.com') //=> false
* ```
*/
function wildcardMatch(pattern, options) {
if (typeof pattern !== "string" && !Array.isArray(pattern)) throw new TypeError(`The first argument must be a single pattern string or an array of patterns, but ${typeof pattern} given`);
if (typeof options === "string" || typeof options === "boolean") options = { separator: options };
if (arguments.length === 2 && !(typeof options === "undefined" || typeof options === "object" && options !== null && !Array.isArray(options))) throw new TypeError(`The second argument must be an options object or a string/boolean separator, but ${typeof options} given`);
options = options || {};
if (options.separator === "\\") throw new Error("\\ is not a valid separator because it is used for escaping. Try setting the separator to `true` instead");
const regexpPattern = transform(pattern, options.separator);
const regexp = new RegExp(`^${regexpPattern}$`, options.flags);
const fn = isMatch.bind(null, regexp);
fn.options = options;
fn.pattern = pattern;
fn.regexp = regexp;
return fn;
}
//#endregion
export { wildcardMatch };
//# sourceMappingURL=wildcard.mjs.map