387 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			387 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
"use strict";
 | 
						|
Object.defineProperty(exports, "__esModule", { value: true });
 | 
						|
exports.GlobStream = exports.GlobWalker = exports.GlobUtil = void 0;
 | 
						|
/**
 | 
						|
 * Single-use utility classes to provide functionality to the {@link Glob}
 | 
						|
 * methods.
 | 
						|
 *
 | 
						|
 * @module
 | 
						|
 */
 | 
						|
const minipass_1 = require("minipass");
 | 
						|
const ignore_js_1 = require("./ignore.js");
 | 
						|
const processor_js_1 = require("./processor.js");
 | 
						|
const makeIgnore = (ignore, opts) => typeof ignore === 'string' ? new ignore_js_1.Ignore([ignore], opts)
 | 
						|
    : Array.isArray(ignore) ? new ignore_js_1.Ignore(ignore, opts)
 | 
						|
        : ignore;
 | 
						|
/**
 | 
						|
 * basic walking utilities that all the glob walker types use
 | 
						|
 */
 | 
						|
class GlobUtil {
 | 
						|
    path;
 | 
						|
    patterns;
 | 
						|
    opts;
 | 
						|
    seen = new Set();
 | 
						|
    paused = false;
 | 
						|
    aborted = false;
 | 
						|
    #onResume = [];
 | 
						|
    #ignore;
 | 
						|
    #sep;
 | 
						|
    signal;
 | 
						|
    maxDepth;
 | 
						|
    includeChildMatches;
 | 
						|
    constructor(patterns, path, opts) {
 | 
						|
        this.patterns = patterns;
 | 
						|
        this.path = path;
 | 
						|
        this.opts = opts;
 | 
						|
        this.#sep = !opts.posix && opts.platform === 'win32' ? '\\' : '/';
 | 
						|
        this.includeChildMatches = opts.includeChildMatches !== false;
 | 
						|
        if (opts.ignore || !this.includeChildMatches) {
 | 
						|
            this.#ignore = makeIgnore(opts.ignore ?? [], opts);
 | 
						|
            if (!this.includeChildMatches &&
 | 
						|
                typeof this.#ignore.add !== 'function') {
 | 
						|
                const m = 'cannot ignore child matches, ignore lacks add() method.';
 | 
						|
                throw new Error(m);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // ignore, always set with maxDepth, but it's optional on the
 | 
						|
        // GlobOptions type
 | 
						|
        /* c8 ignore start */
 | 
						|
        this.maxDepth = opts.maxDepth || Infinity;
 | 
						|
        /* c8 ignore stop */
 | 
						|
        if (opts.signal) {
 | 
						|
            this.signal = opts.signal;
 | 
						|
            this.signal.addEventListener('abort', () => {
 | 
						|
                this.#onResume.length = 0;
 | 
						|
            });
 | 
						|
        }
 | 
						|
    }
 | 
						|
    #ignored(path) {
 | 
						|
        return this.seen.has(path) || !!this.#ignore?.ignored?.(path);
 | 
						|
    }
 | 
						|
    #childrenIgnored(path) {
 | 
						|
        return !!this.#ignore?.childrenIgnored?.(path);
 | 
						|
    }
 | 
						|
    // backpressure mechanism
 | 
						|
    pause() {
 | 
						|
        this.paused = true;
 | 
						|
    }
 | 
						|
    resume() {
 | 
						|
        /* c8 ignore start */
 | 
						|
        if (this.signal?.aborted)
 | 
						|
            return;
 | 
						|
        /* c8 ignore stop */
 | 
						|
        this.paused = false;
 | 
						|
        let fn = undefined;
 | 
						|
        while (!this.paused && (fn = this.#onResume.shift())) {
 | 
						|
            fn();
 | 
						|
        }
 | 
						|
    }
 | 
						|
    onResume(fn) {
 | 
						|
        if (this.signal?.aborted)
 | 
						|
            return;
 | 
						|
        /* c8 ignore start */
 | 
						|
        if (!this.paused) {
 | 
						|
            fn();
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            /* c8 ignore stop */
 | 
						|
            this.#onResume.push(fn);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    // do the requisite realpath/stat checking, and return the path
 | 
						|
    // to add or undefined to filter it out.
 | 
						|
    async matchCheck(e, ifDir) {
 | 
						|
        if (ifDir && this.opts.nodir)
 | 
						|
            return undefined;
 | 
						|
        let rpc;
 | 
						|
        if (this.opts.realpath) {
 | 
						|
            rpc = e.realpathCached() || (await e.realpath());
 | 
						|
            if (!rpc)
 | 
						|
                return undefined;
 | 
						|
            e = rpc;
 | 
						|
        }
 | 
						|
        const needStat = e.isUnknown() || this.opts.stat;
 | 
						|
        const s = needStat ? await e.lstat() : e;
 | 
						|
        if (this.opts.follow && this.opts.nodir && s?.isSymbolicLink()) {
 | 
						|
            const target = await s.realpath();
 | 
						|
            /* c8 ignore start */
 | 
						|
            if (target && (target.isUnknown() || this.opts.stat)) {
 | 
						|
                await target.lstat();
 | 
						|
            }
 | 
						|
            /* c8 ignore stop */
 | 
						|
        }
 | 
						|
        return this.matchCheckTest(s, ifDir);
 | 
						|
    }
 | 
						|
    matchCheckTest(e, ifDir) {
 | 
						|
        return (e &&
 | 
						|
            (this.maxDepth === Infinity || e.depth() <= this.maxDepth) &&
 | 
						|
            (!ifDir || e.canReaddir()) &&
 | 
						|
            (!this.opts.nodir || !e.isDirectory()) &&
 | 
						|
            (!this.opts.nodir ||
 | 
						|
                !this.opts.follow ||
 | 
						|
                !e.isSymbolicLink() ||
 | 
						|
                !e.realpathCached()?.isDirectory()) &&
 | 
						|
            !this.#ignored(e)) ?
 | 
						|
            e
 | 
						|
            : undefined;
 | 
						|
    }
 | 
						|
    matchCheckSync(e, ifDir) {
 | 
						|
        if (ifDir && this.opts.nodir)
 | 
						|
            return undefined;
 | 
						|
        let rpc;
 | 
						|
        if (this.opts.realpath) {
 | 
						|
            rpc = e.realpathCached() || e.realpathSync();
 | 
						|
            if (!rpc)
 | 
						|
                return undefined;
 | 
						|
            e = rpc;
 | 
						|
        }
 | 
						|
        const needStat = e.isUnknown() || this.opts.stat;
 | 
						|
        const s = needStat ? e.lstatSync() : e;
 | 
						|
        if (this.opts.follow && this.opts.nodir && s?.isSymbolicLink()) {
 | 
						|
            const target = s.realpathSync();
 | 
						|
            if (target && (target?.isUnknown() || this.opts.stat)) {
 | 
						|
                target.lstatSync();
 | 
						|
            }
 | 
						|
        }
 | 
						|
        return this.matchCheckTest(s, ifDir);
 | 
						|
    }
 | 
						|
    matchFinish(e, absolute) {
 | 
						|
        if (this.#ignored(e))
 | 
						|
            return;
 | 
						|
        // we know we have an ignore if this is false, but TS doesn't
 | 
						|
        if (!this.includeChildMatches && this.#ignore?.add) {
 | 
						|
            const ign = `${e.relativePosix()}/**`;
 | 
						|
            this.#ignore.add(ign);
 | 
						|
        }
 | 
						|
        const abs = this.opts.absolute === undefined ? absolute : this.opts.absolute;
 | 
						|
        this.seen.add(e);
 | 
						|
        const mark = this.opts.mark && e.isDirectory() ? this.#sep : '';
 | 
						|
        // ok, we have what we need!
 | 
						|
        if (this.opts.withFileTypes) {
 | 
						|
            this.matchEmit(e);
 | 
						|
        }
 | 
						|
        else if (abs) {
 | 
						|
            const abs = this.opts.posix ? e.fullpathPosix() : e.fullpath();
 | 
						|
            this.matchEmit(abs + mark);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            const rel = this.opts.posix ? e.relativePosix() : e.relative();
 | 
						|
            const pre = this.opts.dotRelative && !rel.startsWith('..' + this.#sep) ?
 | 
						|
                '.' + this.#sep
 | 
						|
                : '';
 | 
						|
            this.matchEmit(!rel ? '.' + mark : pre + rel + mark);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    async match(e, absolute, ifDir) {
 | 
						|
        const p = await this.matchCheck(e, ifDir);
 | 
						|
        if (p)
 | 
						|
            this.matchFinish(p, absolute);
 | 
						|
    }
 | 
						|
    matchSync(e, absolute, ifDir) {
 | 
						|
        const p = this.matchCheckSync(e, ifDir);
 | 
						|
        if (p)
 | 
						|
            this.matchFinish(p, absolute);
 | 
						|
    }
 | 
						|
    walkCB(target, patterns, cb) {
 | 
						|
        /* c8 ignore start */
 | 
						|
        if (this.signal?.aborted)
 | 
						|
            cb();
 | 
						|
        /* c8 ignore stop */
 | 
						|
        this.walkCB2(target, patterns, new processor_js_1.Processor(this.opts), cb);
 | 
						|
    }
 | 
						|
    walkCB2(target, patterns, processor, cb) {
 | 
						|
        if (this.#childrenIgnored(target))
 | 
						|
            return cb();
 | 
						|
        if (this.signal?.aborted)
 | 
						|
            cb();
 | 
						|
        if (this.paused) {
 | 
						|
            this.onResume(() => this.walkCB2(target, patterns, processor, cb));
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        processor.processPatterns(target, patterns);
 | 
						|
        // done processing.  all of the above is sync, can be abstracted out.
 | 
						|
        // subwalks is a map of paths to the entry filters they need
 | 
						|
        // matches is a map of paths to [absolute, ifDir] tuples.
 | 
						|
        let tasks = 1;
 | 
						|
        const next = () => {
 | 
						|
            if (--tasks === 0)
 | 
						|
                cb();
 | 
						|
        };
 | 
						|
        for (const [m, absolute, ifDir] of processor.matches.entries()) {
 | 
						|
            if (this.#ignored(m))
 | 
						|
                continue;
 | 
						|
            tasks++;
 | 
						|
            this.match(m, absolute, ifDir).then(() => next());
 | 
						|
        }
 | 
						|
        for (const t of processor.subwalkTargets()) {
 | 
						|
            if (this.maxDepth !== Infinity && t.depth() >= this.maxDepth) {
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
            tasks++;
 | 
						|
            const childrenCached = t.readdirCached();
 | 
						|
            if (t.calledReaddir())
 | 
						|
                this.walkCB3(t, childrenCached, processor, next);
 | 
						|
            else {
 | 
						|
                t.readdirCB((_, entries) => this.walkCB3(t, entries, processor, next), true);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        next();
 | 
						|
    }
 | 
						|
    walkCB3(target, entries, processor, cb) {
 | 
						|
        processor = processor.filterEntries(target, entries);
 | 
						|
        let tasks = 1;
 | 
						|
        const next = () => {
 | 
						|
            if (--tasks === 0)
 | 
						|
                cb();
 | 
						|
        };
 | 
						|
        for (const [m, absolute, ifDir] of processor.matches.entries()) {
 | 
						|
            if (this.#ignored(m))
 | 
						|
                continue;
 | 
						|
            tasks++;
 | 
						|
            this.match(m, absolute, ifDir).then(() => next());
 | 
						|
        }
 | 
						|
        for (const [target, patterns] of processor.subwalks.entries()) {
 | 
						|
            tasks++;
 | 
						|
            this.walkCB2(target, patterns, processor.child(), next);
 | 
						|
        }
 | 
						|
        next();
 | 
						|
    }
 | 
						|
    walkCBSync(target, patterns, cb) {
 | 
						|
        /* c8 ignore start */
 | 
						|
        if (this.signal?.aborted)
 | 
						|
            cb();
 | 
						|
        /* c8 ignore stop */
 | 
						|
        this.walkCB2Sync(target, patterns, new processor_js_1.Processor(this.opts), cb);
 | 
						|
    }
 | 
						|
    walkCB2Sync(target, patterns, processor, cb) {
 | 
						|
        if (this.#childrenIgnored(target))
 | 
						|
            return cb();
 | 
						|
        if (this.signal?.aborted)
 | 
						|
            cb();
 | 
						|
        if (this.paused) {
 | 
						|
            this.onResume(() => this.walkCB2Sync(target, patterns, processor, cb));
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        processor.processPatterns(target, patterns);
 | 
						|
        // done processing.  all of the above is sync, can be abstracted out.
 | 
						|
        // subwalks is a map of paths to the entry filters they need
 | 
						|
        // matches is a map of paths to [absolute, ifDir] tuples.
 | 
						|
        let tasks = 1;
 | 
						|
        const next = () => {
 | 
						|
            if (--tasks === 0)
 | 
						|
                cb();
 | 
						|
        };
 | 
						|
        for (const [m, absolute, ifDir] of processor.matches.entries()) {
 | 
						|
            if (this.#ignored(m))
 | 
						|
                continue;
 | 
						|
            this.matchSync(m, absolute, ifDir);
 | 
						|
        }
 | 
						|
        for (const t of processor.subwalkTargets()) {
 | 
						|
            if (this.maxDepth !== Infinity && t.depth() >= this.maxDepth) {
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
            tasks++;
 | 
						|
            const children = t.readdirSync();
 | 
						|
            this.walkCB3Sync(t, children, processor, next);
 | 
						|
        }
 | 
						|
        next();
 | 
						|
    }
 | 
						|
    walkCB3Sync(target, entries, processor, cb) {
 | 
						|
        processor = processor.filterEntries(target, entries);
 | 
						|
        let tasks = 1;
 | 
						|
        const next = () => {
 | 
						|
            if (--tasks === 0)
 | 
						|
                cb();
 | 
						|
        };
 | 
						|
        for (const [m, absolute, ifDir] of processor.matches.entries()) {
 | 
						|
            if (this.#ignored(m))
 | 
						|
                continue;
 | 
						|
            this.matchSync(m, absolute, ifDir);
 | 
						|
        }
 | 
						|
        for (const [target, patterns] of processor.subwalks.entries()) {
 | 
						|
            tasks++;
 | 
						|
            this.walkCB2Sync(target, patterns, processor.child(), next);
 | 
						|
        }
 | 
						|
        next();
 | 
						|
    }
 | 
						|
}
 | 
						|
exports.GlobUtil = GlobUtil;
 | 
						|
class GlobWalker extends GlobUtil {
 | 
						|
    matches = new Set();
 | 
						|
    constructor(patterns, path, opts) {
 | 
						|
        super(patterns, path, opts);
 | 
						|
    }
 | 
						|
    matchEmit(e) {
 | 
						|
        this.matches.add(e);
 | 
						|
    }
 | 
						|
    async walk() {
 | 
						|
        if (this.signal?.aborted)
 | 
						|
            throw this.signal.reason;
 | 
						|
        if (this.path.isUnknown()) {
 | 
						|
            await this.path.lstat();
 | 
						|
        }
 | 
						|
        await new Promise((res, rej) => {
 | 
						|
            this.walkCB(this.path, this.patterns, () => {
 | 
						|
                if (this.signal?.aborted) {
 | 
						|
                    rej(this.signal.reason);
 | 
						|
                }
 | 
						|
                else {
 | 
						|
                    res(this.matches);
 | 
						|
                }
 | 
						|
            });
 | 
						|
        });
 | 
						|
        return this.matches;
 | 
						|
    }
 | 
						|
    walkSync() {
 | 
						|
        if (this.signal?.aborted)
 | 
						|
            throw this.signal.reason;
 | 
						|
        if (this.path.isUnknown()) {
 | 
						|
            this.path.lstatSync();
 | 
						|
        }
 | 
						|
        // nothing for the callback to do, because this never pauses
 | 
						|
        this.walkCBSync(this.path, this.patterns, () => {
 | 
						|
            if (this.signal?.aborted)
 | 
						|
                throw this.signal.reason;
 | 
						|
        });
 | 
						|
        return this.matches;
 | 
						|
    }
 | 
						|
}
 | 
						|
exports.GlobWalker = GlobWalker;
 | 
						|
class GlobStream extends GlobUtil {
 | 
						|
    results;
 | 
						|
    constructor(patterns, path, opts) {
 | 
						|
        super(patterns, path, opts);
 | 
						|
        this.results = new minipass_1.Minipass({
 | 
						|
            signal: this.signal,
 | 
						|
            objectMode: true,
 | 
						|
        });
 | 
						|
        this.results.on('drain', () => this.resume());
 | 
						|
        this.results.on('resume', () => this.resume());
 | 
						|
    }
 | 
						|
    matchEmit(e) {
 | 
						|
        this.results.write(e);
 | 
						|
        if (!this.results.flowing)
 | 
						|
            this.pause();
 | 
						|
    }
 | 
						|
    stream() {
 | 
						|
        const target = this.path;
 | 
						|
        if (target.isUnknown()) {
 | 
						|
            target.lstat().then(() => {
 | 
						|
                this.walkCB(target, this.patterns, () => this.results.end());
 | 
						|
            });
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            this.walkCB(target, this.patterns, () => this.results.end());
 | 
						|
        }
 | 
						|
        return this.results;
 | 
						|
    }
 | 
						|
    streamSync() {
 | 
						|
        if (this.path.isUnknown()) {
 | 
						|
            this.path.lstatSync();
 | 
						|
        }
 | 
						|
        this.walkCBSync(this.path, this.patterns, () => this.results.end());
 | 
						|
        return this.results;
 | 
						|
    }
 | 
						|
}
 | 
						|
exports.GlobStream = GlobStream;
 | 
						|
//# sourceMappingURL=walker.js.map
 |