mirror of
				https://scm.univ-tours.fr/22107988t/rappaurio-sae501_502.git
				synced 2025-11-04 00:55:22 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			352 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			352 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/**
 | 
						|
 * Single-use utility classes to provide functionality to the {@link Glob}
 | 
						|
 * methods.
 | 
						|
 *
 | 
						|
 * @module
 | 
						|
 */
 | 
						|
import { Minipass } from 'minipass';
 | 
						|
import { Ignore } from './ignore.js';
 | 
						|
import { Processor } from './processor.js';
 | 
						|
const makeIgnore = (ignore, opts) => typeof ignore === 'string'
 | 
						|
    ? new Ignore([ignore], opts)
 | 
						|
    : Array.isArray(ignore)
 | 
						|
        ? new Ignore(ignore, opts)
 | 
						|
        : ignore;
 | 
						|
/**
 | 
						|
 * basic walking utilities that all the glob walker types use
 | 
						|
 */
 | 
						|
export class GlobUtil {
 | 
						|
    path;
 | 
						|
    patterns;
 | 
						|
    opts;
 | 
						|
    seen = new Set();
 | 
						|
    paused = false;
 | 
						|
    aborted = false;
 | 
						|
    #onResume = [];
 | 
						|
    #ignore;
 | 
						|
    #sep;
 | 
						|
    signal;
 | 
						|
    maxDepth;
 | 
						|
    constructor(patterns, path, opts) {
 | 
						|
        this.patterns = patterns;
 | 
						|
        this.path = path;
 | 
						|
        this.opts = opts;
 | 
						|
        this.#sep = !opts.posix && opts.platform === 'win32' ? '\\' : '/';
 | 
						|
        if (opts.ignore) {
 | 
						|
            this.#ignore = makeIgnore(opts.ignore, opts);
 | 
						|
        }
 | 
						|
        // 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;
 | 
						|
        return this.matchCheckTest(needStat ? await e.lstat() : e, ifDir);
 | 
						|
    }
 | 
						|
    matchCheckTest(e, ifDir) {
 | 
						|
        return e &&
 | 
						|
            (this.maxDepth === Infinity || e.depth() <= this.maxDepth) &&
 | 
						|
            (!ifDir || e.canReaddir()) &&
 | 
						|
            (!this.opts.nodir || !e.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;
 | 
						|
        return this.matchCheckTest(needStat ? e.lstatSync() : e, ifDir);
 | 
						|
    }
 | 
						|
    matchFinish(e, absolute) {
 | 
						|
        if (this.#ignored(e))
 | 
						|
            return;
 | 
						|
        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(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(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();
 | 
						|
    }
 | 
						|
}
 | 
						|
export class GlobWalker extends GlobUtil {
 | 
						|
    matches;
 | 
						|
    constructor(patterns, path, opts) {
 | 
						|
        super(patterns, path, opts);
 | 
						|
        this.matches = new Set();
 | 
						|
    }
 | 
						|
    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;
 | 
						|
    }
 | 
						|
}
 | 
						|
export class GlobStream extends GlobUtil {
 | 
						|
    results;
 | 
						|
    constructor(patterns, path, opts) {
 | 
						|
        super(patterns, path, opts);
 | 
						|
        this.results = new 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;
 | 
						|
    }
 | 
						|
}
 | 
						|
//# sourceMappingURL=walker.js.map
 |