@reliverse/rematch
Version:
@reliverse/rematch is a high-performance minimal glob matcher, with micromatch-level power, zepto-level size, and reliverse-grade dx.
182 lines (181 loc) • 6.03 kB
JavaScript
import * as constants from "./constants.js";
import parse from "./parse.js";
import scan from "./scan.js";
import * as utils from "./utils.js";
const isObject = (val) => val && typeof val === "object" && !Array.isArray(val);
const rematchInternal = (glob, options, returnState = false) => {
if (Array.isArray(glob)) {
const fns = glob.map(
(input) => rematchInternal(input, options, returnState)
);
const arrayMatcher = (str) => {
for (const isMatch of fns) {
const state2 = isMatch(str);
if (state2) return state2;
}
return false;
};
return arrayMatcher;
}
const isState = isObject(glob) && glob.tokens && glob.input;
if (glob === "" || typeof glob !== "string" && !isState) {
throw new TypeError("Expected pattern to be a non-empty string");
}
const opts = options || {};
const posix = opts.windows;
const regex = isState ? rematchInternal.compileRe(glob, options) : rematchInternal.makeRe(glob, options, false, true);
let state = void 0;
if (regex instanceof RegExp && "state" in regex) {
state = regex.state;
delete regex.state;
}
let isIgnored = () => false;
if (opts.ignore) {
const ignoreOpts = {
...options,
ignore: null,
onMatch: null,
onResult: null
};
isIgnored = rematchInternal(
opts.ignore,
ignoreOpts,
returnState
);
}
const matcher = (input, returnObject = false) => {
const safeRegex = regex instanceof RegExp ? regex : rematchInternal.makeRe(String(regex), options);
const { isMatch, match, output } = rematchInternal.test(
input,
safeRegex,
options,
{
glob,
posix
}
);
const result = { glob, state, regex, posix, input, output, match, isMatch };
if (isMatch && safeRegex instanceof RegExp && !safeRegex.test(input)) {
result.isMatch = false;
return returnObject ? result : false;
}
if (typeof opts.onResult === "function") {
opts.onResult(result);
}
if (isMatch === false) {
result.isMatch = false;
return returnObject ? result : false;
}
let ignored = false;
if (typeof isIgnored === "function" && isIgnored.length > 0) {
ignored = isIgnored(input);
} else if (typeof isIgnored === "function") {
ignored = isIgnored();
}
if (ignored) {
if (typeof opts.onIgnore === "function") {
opts.onIgnore(result);
}
result.isMatch = false;
return returnObject ? result : false;
}
if (typeof opts.onMatch === "function") {
opts.onMatch(result);
}
return returnObject ? result : true;
};
if (returnState) {
matcher.state = state;
}
return matcher;
};
rematchInternal.test = (input, regex, options, extra = {}) => {
if (typeof input !== "string") {
throw new TypeError("Expected input to be a string");
}
if (input === "") {
return { isMatch: false, output: "" };
}
const opts = options || {};
const format = opts.format || (extra.posix ? utils.toPosixSlashes : null);
let match = input === extra.glob;
let output = match && typeof format === "function" ? format(input) : input;
if (match === false) {
output = typeof format === "function" ? format(input) : input;
match = output === extra.glob;
}
if (match === false || opts.capture === true) {
let safeRegex = regex;
if (!(regex instanceof RegExp)) {
safeRegex = rematchInternal.makeRe(String(regex), options);
}
if (opts.matchBase === true || opts.basename === true) {
match = rematchInternal.matchBase(input, safeRegex, options, extra.posix);
} else {
match = safeRegex instanceof RegExp ? safeRegex.exec(output) !== null : false;
}
}
return { isMatch: Boolean(match), match, output };
};
rematchInternal.matchBase = (input, glob, options, posix) => {
const regex = glob instanceof RegExp ? glob : rematchInternal.makeRe(String(glob), options);
return regex instanceof RegExp ? regex.test(utils.basename(input, { windows: posix })) : false;
};
rematchInternal.isMatch = (str, patterns, options) => {
const matcher = typeof patterns === "function" ? patterns : rematchInternal(patterns, options);
return matcher(str);
};
rematchInternal.parse = (pattern, options) => {
if (Array.isArray(pattern))
return pattern.map((p) => rematchInternal.parse(p, options));
return parse(pattern, { ...options, fastpaths: false });
};
rematchInternal.scan = (input, options) => scan(input, options);
rematchInternal.compileRe = (state, options, returnOutput = false, returnState = false) => {
if (returnOutput === true) {
return state.output;
}
const opts = options || {};
const prepend = opts.contains ? "" : "^";
const append = opts.contains ? "" : "$";
let source = `${prepend}(?:${state.output})${append}`;
if (state && state.negated === true) {
source = `^(?!${source}).*$`;
}
const regex = rematchInternal.toRegex(source, options);
if (returnState === true && regex instanceof RegExp) {
regex.state = state;
}
return regex;
};
rematchInternal.makeRe = (input, options = {}, returnOutput = false, returnState = false) => {
if (!input || typeof input !== "string") {
throw new TypeError("Expected a non-empty string");
}
let parsed = { negated: false, fastpaths: true };
if (options.fastpaths !== false && (input.startsWith(".") || input.startsWith("*"))) {
parsed.output = parse.fastpaths(input, options);
}
if (!parsed.output) {
parsed = parse(input, options);
parsed.fastpaths = false;
}
return rematchInternal.compileRe(
parsed,
options,
returnOutput,
returnState
);
};
rematchInternal.toRegex = (source, options) => {
try {
const opts = options || {};
return new RegExp(source, opts.flags || (opts.nocase ? "i" : ""));
} catch (err) {
if (options && options.debug === true) throw err;
return /$^/;
}
};
rematchInternal.constants = constants;
const rematch = rematchInternal;
export default rematch;