UNPKG

@surface/path-matcher

Version:

Provides path matching capabilities.

82 lines (81 loc) 3.27 kB
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)); } }