Spaces:
Sleeping
Sleeping
| ; | |
| // synchronous utility for filtering entries and calculating subwalks | |
| Object.defineProperty(exports, "__esModule", { value: true }); | |
| exports.Processor = exports.SubWalks = exports.MatchRecord = exports.HasWalkedCache = void 0; | |
| const minimatch_1 = require("minimatch"); | |
| /** | |
| * A cache of which patterns have been processed for a given Path | |
| */ | |
| class HasWalkedCache { | |
| store; | |
| constructor(store = new Map()) { | |
| this.store = store; | |
| } | |
| copy() { | |
| return new HasWalkedCache(new Map(this.store)); | |
| } | |
| hasWalked(target, pattern) { | |
| return this.store.get(target.fullpath())?.has(pattern.globString()); | |
| } | |
| storeWalked(target, pattern) { | |
| const fullpath = target.fullpath(); | |
| const cached = this.store.get(fullpath); | |
| if (cached) | |
| cached.add(pattern.globString()); | |
| else | |
| this.store.set(fullpath, new Set([pattern.globString()])); | |
| } | |
| } | |
| exports.HasWalkedCache = HasWalkedCache; | |
| /** | |
| * A record of which paths have been matched in a given walk step, | |
| * and whether they only are considered a match if they are a directory, | |
| * and whether their absolute or relative path should be returned. | |
| */ | |
| class MatchRecord { | |
| store = new Map(); | |
| add(target, absolute, ifDir) { | |
| const n = (absolute ? 2 : 0) | (ifDir ? 1 : 0); | |
| const current = this.store.get(target); | |
| this.store.set(target, current === undefined ? n : n & current); | |
| } | |
| // match, absolute, ifdir | |
| entries() { | |
| return [...this.store.entries()].map(([path, n]) => [ | |
| path, | |
| !!(n & 2), | |
| !!(n & 1), | |
| ]); | |
| } | |
| } | |
| exports.MatchRecord = MatchRecord; | |
| /** | |
| * A collection of patterns that must be processed in a subsequent step | |
| * for a given path. | |
| */ | |
| class SubWalks { | |
| store = new Map(); | |
| add(target, pattern) { | |
| if (!target.canReaddir()) { | |
| return; | |
| } | |
| const subs = this.store.get(target); | |
| if (subs) { | |
| if (!subs.find(p => p.globString() === pattern.globString())) { | |
| subs.push(pattern); | |
| } | |
| } | |
| else | |
| this.store.set(target, [pattern]); | |
| } | |
| get(target) { | |
| const subs = this.store.get(target); | |
| /* c8 ignore start */ | |
| if (!subs) { | |
| throw new Error('attempting to walk unknown path'); | |
| } | |
| /* c8 ignore stop */ | |
| return subs; | |
| } | |
| entries() { | |
| return this.keys().map(k => [k, this.store.get(k)]); | |
| } | |
| keys() { | |
| return [...this.store.keys()].filter(t => t.canReaddir()); | |
| } | |
| } | |
| exports.SubWalks = SubWalks; | |
| /** | |
| * The class that processes patterns for a given path. | |
| * | |
| * Handles child entry filtering, and determining whether a path's | |
| * directory contents must be read. | |
| */ | |
| class Processor { | |
| hasWalkedCache; | |
| matches = new MatchRecord(); | |
| subwalks = new SubWalks(); | |
| patterns; | |
| follow; | |
| dot; | |
| opts; | |
| constructor(opts, hasWalkedCache) { | |
| this.opts = opts; | |
| this.follow = !!opts.follow; | |
| this.dot = !!opts.dot; | |
| this.hasWalkedCache = | |
| hasWalkedCache ? hasWalkedCache.copy() : new HasWalkedCache(); | |
| } | |
| processPatterns(target, patterns) { | |
| this.patterns = patterns; | |
| const processingSet = patterns.map(p => [target, p]); | |
| // map of paths to the magic-starting subwalks they need to walk | |
| // first item in patterns is the filter | |
| for (let [t, pattern] of processingSet) { | |
| this.hasWalkedCache.storeWalked(t, pattern); | |
| const root = pattern.root(); | |
| const absolute = pattern.isAbsolute() && this.opts.absolute !== false; | |
| // start absolute patterns at root | |
| if (root) { | |
| t = t.resolve(root === '/' && this.opts.root !== undefined ? | |
| this.opts.root | |
| : root); | |
| const rest = pattern.rest(); | |
| if (!rest) { | |
| this.matches.add(t, true, false); | |
| continue; | |
| } | |
| else { | |
| pattern = rest; | |
| } | |
| } | |
| if (t.isENOENT()) | |
| continue; | |
| let p; | |
| let rest; | |
| let changed = false; | |
| while (typeof (p = pattern.pattern()) === 'string' && | |
| (rest = pattern.rest())) { | |
| const c = t.resolve(p); | |
| t = c; | |
| pattern = rest; | |
| changed = true; | |
| } | |
| p = pattern.pattern(); | |
| rest = pattern.rest(); | |
| if (changed) { | |
| if (this.hasWalkedCache.hasWalked(t, pattern)) | |
| continue; | |
| this.hasWalkedCache.storeWalked(t, pattern); | |
| } | |
| // now we have either a final string for a known entry, | |
| // more strings for an unknown entry, | |
| // or a pattern starting with magic, mounted on t. | |
| if (typeof p === 'string') { | |
| // must not be final entry, otherwise we would have | |
| // concatenated it earlier. | |
| const ifDir = p === '..' || p === '' || p === '.'; | |
| this.matches.add(t.resolve(p), absolute, ifDir); | |
| continue; | |
| } | |
| else if (p === minimatch_1.GLOBSTAR) { | |
| // if no rest, match and subwalk pattern | |
| // if rest, process rest and subwalk pattern | |
| // if it's a symlink, but we didn't get here by way of a | |
| // globstar match (meaning it's the first time THIS globstar | |
| // has traversed a symlink), then we follow it. Otherwise, stop. | |
| if (!t.isSymbolicLink() || | |
| this.follow || | |
| pattern.checkFollowGlobstar()) { | |
| this.subwalks.add(t, pattern); | |
| } | |
| const rp = rest?.pattern(); | |
| const rrest = rest?.rest(); | |
| if (!rest || ((rp === '' || rp === '.') && !rrest)) { | |
| // only HAS to be a dir if it ends in **/ or **/. | |
| // but ending in ** will match files as well. | |
| this.matches.add(t, absolute, rp === '' || rp === '.'); | |
| } | |
| else { | |
| if (rp === '..') { | |
| // this would mean you're matching **/.. at the fs root, | |
| // and no thanks, I'm not gonna test that specific case. | |
| /* c8 ignore start */ | |
| const tp = t.parent || t; | |
| /* c8 ignore stop */ | |
| if (!rrest) | |
| this.matches.add(tp, absolute, true); | |
| else if (!this.hasWalkedCache.hasWalked(tp, rrest)) { | |
| this.subwalks.add(tp, rrest); | |
| } | |
| } | |
| } | |
| } | |
| else if (p instanceof RegExp) { | |
| this.subwalks.add(t, pattern); | |
| } | |
| } | |
| return this; | |
| } | |
| subwalkTargets() { | |
| return this.subwalks.keys(); | |
| } | |
| child() { | |
| return new Processor(this.opts, this.hasWalkedCache); | |
| } | |
| // return a new Processor containing the subwalks for each | |
| // child entry, and a set of matches, and | |
| // a hasWalkedCache that's a copy of this one | |
| // then we're going to call | |
| filterEntries(parent, entries) { | |
| const patterns = this.subwalks.get(parent); | |
| // put matches and entry walks into the results processor | |
| const results = this.child(); | |
| for (const e of entries) { | |
| for (const pattern of patterns) { | |
| const absolute = pattern.isAbsolute(); | |
| const p = pattern.pattern(); | |
| const rest = pattern.rest(); | |
| if (p === minimatch_1.GLOBSTAR) { | |
| results.testGlobstar(e, pattern, rest, absolute); | |
| } | |
| else if (p instanceof RegExp) { | |
| results.testRegExp(e, p, rest, absolute); | |
| } | |
| else { | |
| results.testString(e, p, rest, absolute); | |
| } | |
| } | |
| } | |
| return results; | |
| } | |
| testGlobstar(e, pattern, rest, absolute) { | |
| if (this.dot || !e.name.startsWith('.')) { | |
| if (!pattern.hasMore()) { | |
| this.matches.add(e, absolute, false); | |
| } | |
| if (e.canReaddir()) { | |
| // if we're in follow mode or it's not a symlink, just keep | |
| // testing the same pattern. If there's more after the globstar, | |
| // then this symlink consumes the globstar. If not, then we can | |
| // follow at most ONE symlink along the way, so we mark it, which | |
| // also checks to ensure that it wasn't already marked. | |
| if (this.follow || !e.isSymbolicLink()) { | |
| this.subwalks.add(e, pattern); | |
| } | |
| else if (e.isSymbolicLink()) { | |
| if (rest && pattern.checkFollowGlobstar()) { | |
| this.subwalks.add(e, rest); | |
| } | |
| else if (pattern.markFollowGlobstar()) { | |
| this.subwalks.add(e, pattern); | |
| } | |
| } | |
| } | |
| } | |
| // if the NEXT thing matches this entry, then also add | |
| // the rest. | |
| if (rest) { | |
| const rp = rest.pattern(); | |
| if (typeof rp === 'string' && | |
| // dots and empty were handled already | |
| rp !== '..' && | |
| rp !== '' && | |
| rp !== '.') { | |
| this.testString(e, rp, rest.rest(), absolute); | |
| } | |
| else if (rp === '..') { | |
| /* c8 ignore start */ | |
| const ep = e.parent || e; | |
| /* c8 ignore stop */ | |
| this.subwalks.add(ep, rest); | |
| } | |
| else if (rp instanceof RegExp) { | |
| this.testRegExp(e, rp, rest.rest(), absolute); | |
| } | |
| } | |
| } | |
| testRegExp(e, p, rest, absolute) { | |
| if (!p.test(e.name)) | |
| return; | |
| if (!rest) { | |
| this.matches.add(e, absolute, false); | |
| } | |
| else { | |
| this.subwalks.add(e, rest); | |
| } | |
| } | |
| testString(e, p, rest, absolute) { | |
| // should never happen? | |
| if (!e.isNamed(p)) | |
| return; | |
| if (!rest) { | |
| this.matches.add(e, absolute, false); | |
| } | |
| else { | |
| this.subwalks.add(e, rest); | |
| } | |
| } | |
| } | |
| exports.Processor = Processor; | |
| //# sourceMappingURL=processor.js.map |