@surface/path-matcher
Version:
Provides path matching capabilities.
82 lines (81 loc) • 3.27 kB
JavaScript
import os from "os";
import { parse, resolve, sep } from "path";
import { ESCAPABLE_CHARACTERS } from "./characters.js";
import Parser from "./parser.js";
const toArray = (value) => Array.isArray(value) ? value : [value];
export default class PathMatcher {
include = [];
exclude = [];
/** Path part extract from the provided patterns. */
paths = new Set();
/** Negated path part extract from the provided patterns. */
negatedPaths = new Set();
constructor(patterns, options) {
for (const pattern of toArray(patterns)) {
const resolved = options?.base
? PathMatcher.resolve(options.base, pattern, options)
: { ...PathMatcher.split(pattern, options), fullPattern: pattern };
let expressions;
if (pattern.startsWith("!")) {
expressions = this.exclude;
if (!this.paths.has(resolved.path)) {
this.negatedPaths.add(resolved.path);
}
}
else {
expressions = this.include;
this.paths.add(resolved.path);
this.negatedPaths.delete(resolved.path);
}
expressions.push(PathMatcher.makeRegex(resolved.fullPattern, options));
}
}
static internalResolve(base, pattern, options) {
const tokens = [];
const separators = [];
const splitted = PathMatcher.split(pattern, options);
const negated = splitted.pattern.startsWith("!");
const prefix = negated ? "!" : "";
const resolved = resolve(base, splitted.path);
let index = 0;
/* c8 ignore next */
for (const char of os.platform() == "win32" && options?.unix ? resolved.replace(parse(resolved).root, "/").replace(/\\/g, "/") : resolved) {
char == sep
? (tokens.push("/"), separators.push(index))
: tokens.push(ESCAPABLE_CHARACTERS.has(char) ? `\\${char}` : char);
index++;
}
const fullPattern = `${prefix + tokens.join("")}${tokens[tokens.length - 1] == "/" ? "" : "/"}${negated ? splitted.pattern.substring(1) : splitted.pattern}`;
return {
fullPattern,
path: resolved,
pattern: splitted.pattern,
};
}
/**
* Creates a regex object from given pattern
* @param pattern Pattern to be parsed.
* @param options Options object.
*/
static makeRegex(pattern, options) {
return new Parser(pattern, options).parse();
}
/**
* Splits base path from the pattern.
* @param pattern Pattern to be splitted.
*/
static split(pattern, options) {
return new Parser(pattern, options).split();
}
static resolve(base, patterns, options) {
const resolved = toArray(patterns).map(x => PathMatcher.internalResolve(base, x, options));
return typeof patterns == "string" ? resolved[0] : resolved;
}
/**
* Matches giving path.
* @param path Path to match.
*/
isMatch(path) {
return this.exclude.every(x => x.test(path)) && this.include.some(x => x.test(path));
}
}