Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
walker.js381 linesDownload Raw Back to esm
1/**
2 * Single-use utility classes to provide functionality to the {@link Glob}
3 * methods.
4 *
5 * @module
6 */
7import { Minipass } from 'minipass';
8import { Ignore } from './ignore.js';
9import { Processor } from './processor.js';
10const makeIgnore = (ignore, opts) => typeof ignore === 'string' ? new Ignore([ignore], opts)
11    : Array.isArray(ignore) ? new Ignore(ignore, opts)
12        : ignore;
13/**
14 * basic walking utilities that all the glob walker types use
15 */
16export class GlobUtil {
17    path;
18    patterns;
19    opts;
20    seen = new Set();
21    paused = false;
22    aborted = false;
23    #onResume = [];
24    #ignore;
25    #sep;
26    signal;
27    maxDepth;
28    includeChildMatches;
29    constructor(patterns, path, opts) {
30        this.patterns = patterns;
31        this.path = path;
32        this.opts = opts;
33        this.#sep = !opts.posix && opts.platform === 'win32' ? '\\' : '/';
34        this.includeChildMatches = opts.includeChildMatches !== false;
35        if (opts.ignore || !this.includeChildMatches) {
36            this.#ignore = makeIgnore(opts.ignore ?? [], opts);
37            if (!this.includeChildMatches &&
38                typeof this.#ignore.add !== 'function') {
39                const m = 'cannot ignore child matches, ignore lacks add() method.';
40                throw new Error(m);
41            }
42        }
43        // ignore, always set with maxDepth, but it's optional on the
44        // GlobOptions type
45        /* c8 ignore start */
46        this.maxDepth = opts.maxDepth || Infinity;
47        /* c8 ignore stop */
48        if (opts.signal) {
49            this.signal = opts.signal;
50            this.signal.addEventListener('abort', () => {
51                this.#onResume.length = 0;
52            });
53        }
54    }
55    #ignored(path) {
56        return this.seen.has(path) || !!this.#ignore?.ignored?.(path);
57    }
58    #childrenIgnored(path) {
59        return !!this.#ignore?.childrenIgnored?.(path);
60    }
61    // backpressure mechanism
62    pause() {
63        this.paused = true;
64    }
65    resume() {
66        /* c8 ignore start */
67        if (this.signal?.aborted)
68            return;
69        /* c8 ignore stop */
70        this.paused = false;
71        let fn = undefined;
72        while (!this.paused && (fn = this.#onResume.shift())) {
73            fn();
74        }
75    }
76    onResume(fn) {
77        if (this.signal?.aborted)
78            return;
79        /* c8 ignore start */
80        if (!this.paused) {
81            fn();
82        }
83        else {
84            /* c8 ignore stop */
85            this.#onResume.push(fn);
86        }
87    }
88    // do the requisite realpath/stat checking, and return the path
89    // to add or undefined to filter it out.
90    async matchCheck(e, ifDir) {
91        if (ifDir && this.opts.nodir)
92            return undefined;
93        let rpc;
94        if (this.opts.realpath) {
95            rpc = e.realpathCached() || (await e.realpath());
96            if (!rpc)
97                return undefined;
98            e = rpc;
99        }
100        const needStat = e.isUnknown() || this.opts.stat;
101        const s = needStat ? await e.lstat() : e;
102        if (this.opts.follow && this.opts.nodir && s?.isSymbolicLink()) {
103            const target = await s.realpath();
104            /* c8 ignore start */
105            if (target && (target.isUnknown() || this.opts.stat)) {
106                await target.lstat();
107            }
108            /* c8 ignore stop */
109        }
110        return this.matchCheckTest(s, ifDir);
111    }
112    matchCheckTest(e, ifDir) {
113        return (e &&
114            (this.maxDepth === Infinity || e.depth() <= this.maxDepth) &&
115            (!ifDir || e.canReaddir()) &&
116            (!this.opts.nodir || !e.isDirectory()) &&
117            (!this.opts.nodir ||
118                !this.opts.follow ||
119                !e.isSymbolicLink() ||
120                !e.realpathCached()?.isDirectory()) &&
121            !this.#ignored(e)) ?
122            e
123            : undefined;
124    }
125    matchCheckSync(e, ifDir) {
126        if (ifDir && this.opts.nodir)
127            return undefined;
128        let rpc;
129        if (this.opts.realpath) {
130            rpc = e.realpathCached() || e.realpathSync();
131            if (!rpc)
132                return undefined;
133            e = rpc;
134        }
135        const needStat = e.isUnknown() || this.opts.stat;
136        const s = needStat ? e.lstatSync() : e;
137        if (this.opts.follow && this.opts.nodir && s?.isSymbolicLink()) {
138            const target = s.realpathSync();
139            if (target && (target?.isUnknown() || this.opts.stat)) {
140                target.lstatSync();
141            }
142        }
143        return this.matchCheckTest(s, ifDir);
144    }
145    matchFinish(e, absolute) {
146        if (this.#ignored(e))
147            return;
148        // we know we have an ignore if this is false, but TS doesn't
149        if (!this.includeChildMatches && this.#ignore?.add) {
150            const ign = `${e.relativePosix()}/**`;
151            this.#ignore.add(ign);
152        }
153        const abs = this.opts.absolute === undefined ? absolute : this.opts.absolute;
154        this.seen.add(e);
155        const mark = this.opts.mark && e.isDirectory() ? this.#sep : '';
156        // ok, we have what we need!
157        if (this.opts.withFileTypes) {
158            this.matchEmit(e);
159        }
160        else if (abs) {
161            const abs = this.opts.posix ? e.fullpathPosix() : e.fullpath();
162            this.matchEmit(abs + mark);
163        }
164        else {
165            const rel = this.opts.posix ? e.relativePosix() : e.relative();
166            const pre = this.opts.dotRelative && !rel.startsWith('..' + this.#sep) ?
167                '.' + this.#sep
168                : '';
169            this.matchEmit(!rel ? '.' + mark : pre + rel + mark);
170        }
171    }
172    async match(e, absolute, ifDir) {
173        const p = await this.matchCheck(e, ifDir);
174        if (p)
175            this.matchFinish(p, absolute);
176    }
177    matchSync(e, absolute, ifDir) {
178        const p = this.matchCheckSync(e, ifDir);
179        if (p)
180            this.matchFinish(p, absolute);
181    }
182    walkCB(target, patterns, cb) {
183        /* c8 ignore start */
184        if (this.signal?.aborted)
185            cb();
186        /* c8 ignore stop */
187        this.walkCB2(target, patterns, new Processor(this.opts), cb);
188    }
189    walkCB2(target, patterns, processor, cb) {
190        if (this.#childrenIgnored(target))
191            return cb();
192        if (this.signal?.aborted)
193            cb();
194        if (this.paused) {
195            this.onResume(() => this.walkCB2(target, patterns, processor, cb));
196            return;
197        }
198        processor.processPatterns(target, patterns);
199        // done processing.  all of the above is sync, can be abstracted out.
200        // subwalks is a map of paths to the entry filters they need
201        // matches is a map of paths to [absolute, ifDir] tuples.
202        let tasks = 1;
203        const next = () => {
204            if (--tasks === 0)
205                cb();
206        };
207        for (const [m, absolute, ifDir] of processor.matches.entries()) {
208            if (this.#ignored(m))
209                continue;
210            tasks++;
211            this.match(m, absolute, ifDir).then(() => next());
212        }
213        for (const t of processor.subwalkTargets()) {
214            if (this.maxDepth !== Infinity && t.depth() >= this.maxDepth) {
215                continue;
216            }
217            tasks++;
218            const childrenCached = t.readdirCached();
219            if (t.calledReaddir())
220                this.walkCB3(t, childrenCached, processor, next);
221            else {
222                t.readdirCB((_, entries) => this.walkCB3(t, entries, processor, next), true);
223            }
224        }
225        next();
226    }
227    walkCB3(target, entries, processor, cb) {
228        processor = processor.filterEntries(target, entries);
229        let tasks = 1;
230        const next = () => {
231            if (--tasks === 0)
232                cb();
233        };
234        for (const [m, absolute, ifDir] of processor.matches.entries()) {
235            if (this.#ignored(m))
236                continue;
237            tasks++;
238            this.match(m, absolute, ifDir).then(() => next());
239        }
240        for (const [target, patterns] of processor.subwalks.entries()) {
241            tasks++;
242            this.walkCB2(target, patterns, processor.child(), next);
243        }
244        next();
245    }
246    walkCBSync(target, patterns, cb) {
247        /* c8 ignore start */
248        if (this.signal?.aborted)
249            cb();
250        /* c8 ignore stop */
251        this.walkCB2Sync(target, patterns, new Processor(this.opts), cb);
252    }
253    walkCB2Sync(target, patterns, processor, cb) {
254        if (this.#childrenIgnored(target))
255            return cb();
256        if (this.signal?.aborted)
257            cb();
258        if (this.paused) {
259            this.onResume(() => this.walkCB2Sync(target, patterns, processor, cb));
260            return;
261        }
262        processor.processPatterns(target, patterns);
263        // done processing.  all of the above is sync, can be abstracted out.
264        // subwalks is a map of paths to the entry filters they need
265        // matches is a map of paths to [absolute, ifDir] tuples.
266        let tasks = 1;
267        const next = () => {
268            if (--tasks === 0)
269                cb();
270        };
271        for (const [m, absolute, ifDir] of processor.matches.entries()) {
272            if (this.#ignored(m))
273                continue;
274            this.matchSync(m, absolute, ifDir);
275        }
276        for (const t of processor.subwalkTargets()) {
277            if (this.maxDepth !== Infinity && t.depth() >= this.maxDepth) {
278                continue;
279            }
280            tasks++;
281            const children = t.readdirSync();
282            this.walkCB3Sync(t, children, processor, next);
283        }
284        next();
285    }
286    walkCB3Sync(target, entries, processor, cb) {
287        processor = processor.filterEntries(target, entries);
288        let tasks = 1;
289        const next = () => {
290            if (--tasks === 0)
291                cb();
292        };
293        for (const [m, absolute, ifDir] of processor.matches.entries()) {
294            if (this.#ignored(m))
295                continue;
296            this.matchSync(m, absolute, ifDir);
297        }
298        for (const [target, patterns] of processor.subwalks.entries()) {
299            tasks++;
300            this.walkCB2Sync(target, patterns, processor.child(), next);
301        }
302        next();
303    }
304}
305export class GlobWalker extends GlobUtil {
306    matches = new Set();
307    constructor(patterns, path, opts) {
308        super(patterns, path, opts);
309    }
310    matchEmit(e) {
311        this.matches.add(e);
312    }
313    async walk() {
314        if (this.signal?.aborted)
315            throw this.signal.reason;
316        if (this.path.isUnknown()) {
317            await this.path.lstat();
318        }
319        await new Promise((res, rej) => {
320            this.walkCB(this.path, this.patterns, () => {
321                if (this.signal?.aborted) {
322                    rej(this.signal.reason);
323                }
324                else {
325                    res(this.matches);
326                }
327            });
328        });
329        return this.matches;
330    }
331    walkSync() {
332        if (this.signal?.aborted)
333            throw this.signal.reason;
334        if (this.path.isUnknown()) {
335            this.path.lstatSync();
336        }
337        // nothing for the callback to do, because this never pauses
338        this.walkCBSync(this.path, this.patterns, () => {
339            if (this.signal?.aborted)
340                throw this.signal.reason;
341        });
342        return this.matches;
343    }
344}
345export class GlobStream extends GlobUtil {
346    results;
347    constructor(patterns, path, opts) {
348        super(patterns, path, opts);
349        this.results = new Minipass({
350            signal: this.signal,
351            objectMode: true,
352        });
353        this.results.on('drain', () => this.resume());
354        this.results.on('resume', () => this.resume());
355    }
356    matchEmit(e) {
357        this.results.write(e);
358        if (!this.results.flowing)
359            this.pause();
360    }
361    stream() {
362        const target = this.path;
363        if (target.isUnknown()) {
364            target.lstat().then(() => {
365                this.walkCB(target, this.patterns, () => this.results.end());
366            });
367        }
368        else {
369            this.walkCB(target, this.patterns, () => this.results.end());
370        }
371        return this.results;
372    }
373    streamSync() {
374        if (this.path.isUnknown()) {
375            this.path.lstatSync();
376        }
377        this.walkCBSync(this.path, this.patterns, () => this.results.end());
378        return this.results;
379    }
380}
381//# sourceMappingURL=walker.js.map
codekingpro/portable-devtools · Team Ai