Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
index.js1983 linesDownload Raw Back to esm
1import { LRUCache } from 'lru-cache';
2import { posix, win32 } from 'node:path';
3import { fileURLToPath } from 'node:url';
4import { lstatSync, readdir as readdirCB, readdirSync, readlinkSync, realpathSync as rps, } from 'fs';
5import * as actualFS from 'node:fs';
6const realpathSync = rps.native;
7// TODO: test perf of fs/promises realpath vs realpathCB,
8// since the promises one uses realpath.native
9import { lstat, readdir, readlink, realpath } from 'node:fs/promises';
10import { Minipass } from 'minipass';
11const defaultFS = {
12    lstatSync,
13    readdir: readdirCB,
14    readdirSync,
15    readlinkSync,
16    realpathSync,
17    promises: {
18        lstat,
19        readdir,
20        readlink,
21        realpath,
22    },
23};
24// if they just gave us require('fs') then use our default
25const fsFromOption = (fsOption) => !fsOption || fsOption === defaultFS || fsOption === actualFS ?
26    defaultFS
27    : {
28        ...defaultFS,
29        ...fsOption,
30        promises: {
31            ...defaultFS.promises,
32            ...(fsOption.promises || {}),
33        },
34    };
35// turn something like //?/c:/ into c:\
36const uncDriveRegexp = /^\\\\\?\\([a-z]:)\\?$/i;
37const uncToDrive = (rootPath) => rootPath.replace(/\//g, '\\').replace(uncDriveRegexp, '$1\\');
38// windows paths are separated by either / or \
39const eitherSep = /[\\\/]/;
40const UNKNOWN = 0; // may not even exist, for all we know
41const IFIFO = 0b0001;
42const IFCHR = 0b0010;
43const IFDIR = 0b0100;
44const IFBLK = 0b0110;
45const IFREG = 0b1000;
46const IFLNK = 0b1010;
47const IFSOCK = 0b1100;
48const IFMT = 0b1111;
49// mask to unset low 4 bits
50const IFMT_UNKNOWN = ~IFMT;
51// set after successfully calling readdir() and getting entries.
52const READDIR_CALLED = 0b0000_0001_0000;
53// set after a successful lstat()
54const LSTAT_CALLED = 0b0000_0010_0000;
55// set if an entry (or one of its parents) is definitely not a dir
56const ENOTDIR = 0b0000_0100_0000;
57// set if an entry (or one of its parents) does not exist
58// (can also be set on lstat errors like EACCES or ENAMETOOLONG)
59const ENOENT = 0b0000_1000_0000;
60// cannot have child entries -- also verify &IFMT is either IFDIR or IFLNK
61// set if we fail to readlink
62const ENOREADLINK = 0b0001_0000_0000;
63// set if we know realpath() will fail
64const ENOREALPATH = 0b0010_0000_0000;
65const ENOCHILD = ENOTDIR | ENOENT | ENOREALPATH;
66const TYPEMASK = 0b0011_1111_1111;
67const entToType = (s) => s.isFile() ? IFREG
68    : s.isDirectory() ? IFDIR
69        : s.isSymbolicLink() ? IFLNK
70            : s.isCharacterDevice() ? IFCHR
71                : s.isBlockDevice() ? IFBLK
72                    : s.isSocket() ? IFSOCK
73                        : s.isFIFO() ? IFIFO
74                            : UNKNOWN;
75// normalize unicode path names
76const normalizeCache = new LRUCache({ max: 2 ** 12 });
77const normalize = (s) => {
78    const c = normalizeCache.get(s);
79    if (c)
80        return c;
81    const n = s.normalize('NFKD');
82    normalizeCache.set(s, n);
83    return n;
84};
85const normalizeNocaseCache = new LRUCache({ max: 2 ** 12 });
86const normalizeNocase = (s) => {
87    const c = normalizeNocaseCache.get(s);
88    if (c)
89        return c;
90    const n = normalize(s.toLowerCase());
91    normalizeNocaseCache.set(s, n);
92    return n;
93};
94/**
95 * An LRUCache for storing resolved path strings or Path objects.
96 * @internal
97 */
98export class ResolveCache extends LRUCache {
99    constructor() {
100        super({ max: 256 });
101    }
102}
103// In order to prevent blowing out the js heap by allocating hundreds of
104// thousands of Path entries when walking extremely large trees, the "children"
105// in this tree are represented by storing an array of Path entries in an
106// LRUCache, indexed by the parent.  At any time, Path.children() may return an
107// empty array, indicating that it doesn't know about any of its children, and
108// thus has to rebuild that cache.  This is fine, it just means that we don't
109// benefit as much from having the cached entries, but huge directory walks
110// don't blow out the stack, and smaller ones are still as fast as possible.
111//
112//It does impose some complexity when building up the readdir data, because we
113//need to pass a reference to the children array that we started with.
114/**
115 * an LRUCache for storing child entries.
116 * @internal
117 */
118export class ChildrenCache extends LRUCache {
119    constructor(maxSize = 16 * 1024) {
120        super({
121            maxSize,
122            // parent + children
123            sizeCalculation: a => a.length + 1,
124        });
125    }
126}
127const setAsCwd = Symbol('PathScurry setAsCwd');
128/**
129 * Path objects are sort of like a super-powered
130 * {@link https://nodejs.org/docs/latest/api/fs.html#class-fsdirent fs.Dirent}
131 *
132 * Each one represents a single filesystem entry on disk, which may or may not
133 * exist. It includes methods for reading various types of information via
134 * lstat, readlink, and readdir, and caches all information to the greatest
135 * degree possible.
136 *
137 * Note that fs operations that would normally throw will instead return an
138 * "empty" value. This is in order to prevent excessive overhead from error
139 * stack traces.
140 */
141export class PathBase {
142    /**
143     * the basename of this path
144     *
145     * **Important**: *always* test the path name against any test string
146     * usingthe {@link isNamed} method, and not by directly comparing this
147     * string. Otherwise, unicode path strings that the system sees as identical
148     * will not be properly treated as the same path, leading to incorrect
149     * behavior and possible security issues.
150     */
151    name;
152    /**
153     * the Path entry corresponding to the path root.
154     *
155     * @internal
156     */
157    root;
158    /**
159     * All roots found within the current PathScurry family
160     *
161     * @internal
162     */
163    roots;
164    /**
165     * a reference to the parent path, or undefined in the case of root entries
166     *
167     * @internal
168     */
169    parent;
170    /**
171     * boolean indicating whether paths are compared case-insensitively
172     * @internal
173     */
174    nocase;
175    /**
176     * boolean indicating that this path is the current working directory
177     * of the PathScurry collection that contains it.
178     */
179    isCWD = false;
180    // potential default fs override
181    #fs;
182    // Stats fields
183    #dev;
184    get dev() {
185        return this.#dev;
186    }
187    #mode;
188    get mode() {
189        return this.#mode;
190    }
191    #nlink;
192    get nlink() {
193        return this.#nlink;
194    }
195    #uid;
196    get uid() {
197        return this.#uid;
198    }
199    #gid;
200    get gid() {
201        return this.#gid;
202    }
203    #rdev;
204    get rdev() {
205        return this.#rdev;
206    }
207    #blksize;
208    get blksize() {
209        return this.#blksize;
210    }
211    #ino;
212    get ino() {
213        return this.#ino;
214    }
215    #size;
216    get size() {
217        return this.#size;
218    }
219    #blocks;
220    get blocks() {
221        return this.#blocks;
222    }
223    #atimeMs;
224    get atimeMs() {
225        return this.#atimeMs;
226    }
227    #mtimeMs;
228    get mtimeMs() {
229        return this.#mtimeMs;
230    }
231    #ctimeMs;
232    get ctimeMs() {
233        return this.#ctimeMs;
234    }
235    #birthtimeMs;
236    get birthtimeMs() {
237        return this.#birthtimeMs;
238    }
239    #atime;
240    get atime() {
241        return this.#atime;
242    }
243    #mtime;
244    get mtime() {
245        return this.#mtime;
246    }
247    #ctime;
248    get ctime() {
249        return this.#ctime;
250    }
251    #birthtime;
252    get birthtime() {
253        return this.#birthtime;
254    }
255    #matchName;
256    #depth;
257    #fullpath;
258    #fullpathPosix;
259    #relative;
260    #relativePosix;
261    #type;
262    #children;
263    #linkTarget;
264    #realpath;
265    /**
266     * This property is for compatibility with the Dirent class as of
267     * Node v20, where Dirent['parentPath'] refers to the path of the
268     * directory that was passed to readdir. For root entries, it's the path
269     * to the entry itself.
270     */
271    get parentPath() {
272        return (this.parent || this).fullpath();
273    }
274    /* c8 ignore start */
275    /**
276     * Deprecated alias for Dirent['parentPath'] Somewhat counterintuitively,
277     * this property refers to the *parent* path, not the path object itself.
278     *
279     * @deprecated
280     */
281    get path() {
282        return this.parentPath;
283    }
284    /* c8 ignore stop */
285    /**
286     * Do not create new Path objects directly.  They should always be accessed
287     * via the PathScurry class or other methods on the Path class.
288     *
289     * @internal
290     */
291    constructor(name, type = UNKNOWN, root, roots, nocase, children, opts) {
292        this.name = name;
293        this.#matchName = nocase ? normalizeNocase(name) : normalize(name);
294        this.#type = type & TYPEMASK;
295        this.nocase = nocase;
296        this.roots = roots;
297        this.root = root || this;
298        this.#children = children;
299        this.#fullpath = opts.fullpath;
300        this.#relative = opts.relative;
301        this.#relativePosix = opts.relativePosix;
302        this.parent = opts.parent;
303        if (this.parent) {
304            this.#fs = this.parent.#fs;
305        }
306        else {
307            this.#fs = fsFromOption(opts.fs);
308        }
309    }
310    /**
311     * Returns the depth of the Path object from its root.
312     *
313     * For example, a path at `/foo/bar` would have a depth of 2.
314     */
315    depth() {
316        if (this.#depth !== undefined)
317            return this.#depth;
318        if (!this.parent)
319            return (this.#depth = 0);
320        return (this.#depth = this.parent.depth() + 1);
321    }
322    /**
323     * @internal
324     */
325    childrenCache() {
326        return this.#children;
327    }
328    /**
329     * Get the Path object referenced by the string path, resolved from this Path
330     */
331    resolve(path) {
332        if (!path) {
333            return this;
334        }
335        const rootPath = this.getRootString(path);
336        const dir = path.substring(rootPath.length);
337        const dirParts = dir.split(this.splitSep);
338        const result = rootPath ?
339            this.getRoot(rootPath).#resolveParts(dirParts)
340            : this.#resolveParts(dirParts);
341        return result;
342    }
343    #resolveParts(dirParts) {
344        let p = this;
345        for (const part of dirParts) {
346            p = p.child(part);
347        }
348        return p;
349    }
350    /**
351     * Returns the cached children Path objects, if still available.  If they
352     * have fallen out of the cache, then returns an empty array, and resets the
353     * READDIR_CALLED bit, so that future calls to readdir() will require an fs
354     * lookup.
355     *
356     * @internal
357     */
358    children() {
359        const cached = this.#children.get(this);
360        if (cached) {
361            return cached;
362        }
363        const children = Object.assign([], { provisional: 0 });
364        this.#children.set(this, children);
365        this.#type &= ~READDIR_CALLED;
366        return children;
367    }
368    /**
369     * Resolves a path portion and returns or creates the child Path.
370     *
371     * Returns `this` if pathPart is `''` or `'.'`, or `parent` if pathPart is
372     * `'..'`.
373     *
374     * This should not be called directly.  If `pathPart` contains any path
375     * separators, it will lead to unsafe undefined behavior.
376     *
377     * Use `Path.resolve()` instead.
378     *
379     * @internal
380     */
381    child(pathPart, opts) {
382        if (pathPart === '' || pathPart === '.') {
383            return this;
384        }
385        if (pathPart === '..') {
386            return this.parent || this;
387        }
388        // find the child
389        const children = this.children();
390        const name = this.nocase ? normalizeNocase(pathPart) : normalize(pathPart);
391        for (const p of children) {
392            if (p.#matchName === name) {
393                return p;
394            }
395        }
396        // didn't find it, create provisional child, since it might not
397        // actually exist.  If we know the parent isn't a dir, then
398        // in fact it CAN'T exist.
399        const s = this.parent ? this.sep : '';
400        const fullpath = this.#fullpath ? this.#fullpath + s + pathPart : undefined;
401        const pchild = this.newChild(pathPart, UNKNOWN, {
402            ...opts,
403            parent: this,
404            fullpath,
405        });
406        if (!this.canReaddir()) {
407            pchild.#type |= ENOENT;
408        }
409        // don't have to update provisional, because if we have real children,
410        // then provisional is set to children.length, otherwise a lower number
411        children.push(pchild);
412        return pchild;
413    }
414    /**
415     * The relative path from the cwd. If it does not share an ancestor with
416     * the cwd, then this ends up being equivalent to the fullpath()
417     */
418    relative() {
419        if (this.isCWD)
420            return '';
421        if (this.#relative !== undefined) {
422            return this.#relative;
423        }
424        const name = this.name;
425        const p = this.parent;
426        if (!p) {
427            return (this.#relative = this.name);
428        }
429        const pv = p.relative();
430        return pv + (!pv || !p.parent ? '' : this.sep) + name;
431    }
432    /**
433     * The relative path from the cwd, using / as the path separator.
434     * If it does not share an ancestor with
435     * the cwd, then this ends up being equivalent to the fullpathPosix()
436     * On posix systems, this is identical to relative().
437     */
438    relativePosix() {
439        if (this.sep === '/')
440            return this.relative();
441        if (this.isCWD)
442            return '';
443        if (this.#relativePosix !== undefined)
444            return this.#relativePosix;
445        const name = this.name;
446        const p = this.parent;
447        if (!p) {
448            return (this.#relativePosix = this.fullpathPosix());
449        }
450        const pv = p.relativePosix();
451        return pv + (!pv || !p.parent ? '' : '/') + name;
452    }
453    /**
454     * The fully resolved path string for this Path entry
455     */
456    fullpath() {
457        if (this.#fullpath !== undefined) {
458            return this.#fullpath;
459        }
460        const name = this.name;
461        const p = this.parent;
462        if (!p) {
463            return (this.#fullpath = this.name);
464        }
465        const pv = p.fullpath();
466        const fp = pv + (!p.parent ? '' : this.sep) + name;
467        return (this.#fullpath = fp);
468    }
469    /**
470     * On platforms other than windows, this is identical to fullpath.
471     *
472     * On windows, this is overridden to return the forward-slash form of the
473     * full UNC path.
474     */
475    fullpathPosix() {
476        if (this.#fullpathPosix !== undefined)
477            return this.#fullpathPosix;
478        if (this.sep === '/')
479            return (this.#fullpathPosix = this.fullpath());
480        if (!this.parent) {
481            const p = this.fullpath().replace(/\\/g, '/');
482            if (/^[a-z]:\//i.test(p)) {
483                return (this.#fullpathPosix = `//?/${p}`);
484            }
485            else {
486                return (this.#fullpathPosix = p);
487            }
488        }
489        const p = this.parent;
490        const pfpp = p.fullpathPosix();
491        const fpp = pfpp + (!pfpp || !p.parent ? '' : '/') + this.name;
492        return (this.#fullpathPosix = fpp);
493    }
494    /**
495     * Is the Path of an unknown type?
496     *
497     * Note that we might know *something* about it if there has been a previous
498     * filesystem operation, for example that it does not exist, or is not a
499     * link, or whether it has child entries.
500     */
501    isUnknown() {
502        return (this.#type & IFMT) === UNKNOWN;
503    }
504    isType(type) {
505        return this[`is${type}`]();
506    }
507    getType() {
508        return (this.isUnknown() ? 'Unknown'
509            : this.isDirectory() ? 'Directory'
510                : this.isFile() ? 'File'
511                    : this.isSymbolicLink() ? 'SymbolicLink'
512                        : this.isFIFO() ? 'FIFO'
513                            : this.isCharacterDevice() ? 'CharacterDevice'
514                                : this.isBlockDevice() ? 'BlockDevice'
515                                    : /* c8 ignore start */ this.isSocket() ? 'Socket'
516                                        : 'Unknown');
517        /* c8 ignore stop */
518    }
519    /**
520     * Is the Path a regular file?
521     */
522    isFile() {
523        return (this.#type & IFMT) === IFREG;
524    }
525    /**
526     * Is the Path a directory?
527     */
528    isDirectory() {
529        return (this.#type & IFMT) === IFDIR;
530    }
531    /**
532     * Is the path a character device?
533     */
534    isCharacterDevice() {
535        return (this.#type & IFMT) === IFCHR;
536    }
537    /**
538     * Is the path a block device?
539     */
540    isBlockDevice() {
541        return (this.#type & IFMT) === IFBLK;
542    }
543    /**
544     * Is the path a FIFO pipe?
545     */
546    isFIFO() {
547        return (this.#type & IFMT) === IFIFO;
548    }
549    /**
550     * Is the path a socket?
551     */
552    isSocket() {
553        return (this.#type & IFMT) === IFSOCK;
554    }
555    /**
556     * Is the path a symbolic link?
557     */
558    isSymbolicLink() {
559        return (this.#type & IFLNK) === IFLNK;
560    }
561    /**
562     * Return the entry if it has been subject of a successful lstat, or
563     * undefined otherwise.
564     *
565     * Does not read the filesystem, so an undefined result *could* simply
566     * mean that we haven't called lstat on it.
567     */
568    lstatCached() {
569        return this.#type & LSTAT_CALLED ? this : undefined;
570    }
571    /**
572     * Return the cached link target if the entry has been the subject of a
573     * successful readlink, or undefined otherwise.
574     *
575     * Does not read the filesystem, so an undefined result *could* just mean we
576     * don't have any cached data. Only use it if you are very sure that a
577     * readlink() has been called at some point.
578     */
579    readlinkCached() {
580        return this.#linkTarget;
581    }
582    /**
583     * Returns the cached realpath target if the entry has been the subject
584     * of a successful realpath, or undefined otherwise.
585     *
586     * Does not read the filesystem, so an undefined result *could* just mean we
587     * don't have any cached data. Only use it if you are very sure that a
588     * realpath() has been called at some point.
589     */
590    realpathCached() {
591        return this.#realpath;
592    }
593    /**
594     * Returns the cached child Path entries array if the entry has been the
595     * subject of a successful readdir(), or [] otherwise.
596     *
597     * Does not read the filesystem, so an empty array *could* just mean we
598     * don't have any cached data. Only use it if you are very sure that a
599     * readdir() has been called recently enough to still be valid.
600     */
601    readdirCached() {
602        const children = this.children();
603        return children.slice(0, children.provisional);
604    }
605    /**
606     * Return true if it's worth trying to readlink.  Ie, we don't (yet) have
607     * any indication that readlink will definitely fail.
608     *
609     * Returns false if the path is known to not be a symlink, if a previous
610     * readlink failed, or if the entry does not exist.
611     */
612    canReadlink() {
613        if (this.#linkTarget)
614            return true;
615        if (!this.parent)
616            return false;
617        // cases where it cannot possibly succeed
618        const ifmt = this.#type & IFMT;
619        return !((ifmt !== UNKNOWN && ifmt !== IFLNK) ||
620            this.#type & ENOREADLINK ||
621            this.#type & ENOENT);
622    }
623    /**
624     * Return true if readdir has previously been successfully called on this
625     * path, indicating that cachedReaddir() is likely valid.
626     */
627    calledReaddir() {
628        return !!(this.#type & READDIR_CALLED);
629    }
630    /**
631     * Returns true if the path is known to not exist. That is, a previous lstat
632     * or readdir failed to verify its existence when that would have been
633     * expected, or a parent entry was marked either enoent or enotdir.
634     */
635    isENOENT() {
636        return !!(this.#type & ENOENT);
637    }
638    /**
639     * Return true if the path is a match for the given path name.  This handles
640     * case sensitivity and unicode normalization.
641     *
642     * Note: even on case-sensitive systems, it is **not** safe to test the
643     * equality of the `.name` property to determine whether a given pathname
644     * matches, due to unicode normalization mismatches.
645     *
646     * Always use this method instead of testing the `path.name` property
647     * directly.
648     */
649    isNamed(n) {
650        return !this.nocase ?
651            this.#matchName === normalize(n)
652            : this.#matchName === normalizeNocase(n);
653    }
654    /**
655     * Return the Path object corresponding to the target of a symbolic link.
656     *
657     * If the Path is not a symbolic link, or if the readlink call fails for any
658     * reason, `undefined` is returned.
659     *
660     * Result is cached, and thus may be outdated if the filesystem is mutated.
661     */
662    async readlink() {
663        const target = this.#linkTarget;
664        if (target) {
665            return target;
666        }
667        if (!this.canReadlink()) {
668            return undefined;
669        }
670        /* c8 ignore start */
671        // already covered by the canReadlink test, here for ts grumples
672        if (!this.parent) {
673            return undefined;
674        }
675        /* c8 ignore stop */
676        try {
677            const read = await this.#fs.promises.readlink(this.fullpath());
678            const linkTarget = (await this.parent.realpath())?.resolve(read);
679            if (linkTarget) {
680                return (this.#linkTarget = linkTarget);
681            }
682        }
683        catch (er) {
684            this.#readlinkFail(er.code);
685            return undefined;
686        }
687    }
688    /**
689     * Synchronous {@link PathBase.readlink}
690     */
691    readlinkSync() {
692        const target = this.#linkTarget;
693        if (target) {
694            return target;
695        }
696        if (!this.canReadlink()) {
697            return undefined;
698        }
699        /* c8 ignore start */
700        // already covered by the canReadlink test, here for ts grumples
701        if (!this.parent) {
702            return undefined;
703        }
704        /* c8 ignore stop */
705        try {
706            const read = this.#fs.readlinkSync(this.fullpath());
707            const linkTarget = this.parent.realpathSync()?.resolve(read);
708            if (linkTarget) {
709                return (this.#linkTarget = linkTarget);
710            }
711        }
712        catch (er) {
713            this.#readlinkFail(er.code);
714            return undefined;
715        }
716    }
717    #readdirSuccess(children) {
718        // succeeded, mark readdir called bit
719        this.#type |= READDIR_CALLED;
720        // mark all remaining provisional children as ENOENT
721        for (let p = children.provisional; p < children.length; p++) {
722            const c = children[p];
723            if (c)
724                c.#markENOENT();
725        }
726    }
727    #markENOENT() {
728        // mark as UNKNOWN and ENOENT
729        if (this.#type & ENOENT)
730            return;
731        this.#type = (this.#type | ENOENT) & IFMT_UNKNOWN;
732        this.#markChildrenENOENT();
733    }
734    #markChildrenENOENT() {
735        // all children are provisional and do not exist
736        const children = this.children();
737        children.provisional = 0;
738        for (const p of children) {
739            p.#markENOENT();
740        }
741    }
742    #markENOREALPATH() {
743        this.#type |= ENOREALPATH;
744        this.#markENOTDIR();
745    }
746    // save the information when we know the entry is not a dir
747    #markENOTDIR() {
748        // entry is not a directory, so any children can't exist.
749        // this *should* be impossible, since any children created
750        // after it's been marked ENOTDIR should be marked ENOENT,
751        // so it won't even get to this point.
752        /* c8 ignore start */
753        if (this.#type & ENOTDIR)
754            return;
755        /* c8 ignore stop */
756        let t = this.#type;
757        // this could happen if we stat a dir, then delete it,
758        // then try to read it or one of its children.
759        if ((t & IFMT) === IFDIR)
760            t &= IFMT_UNKNOWN;
761        this.#type = t | ENOTDIR;
762        this.#markChildrenENOENT();
763    }
764    #readdirFail(code = '') {
765        // markENOTDIR and markENOENT also set provisional=0
766        if (code === 'ENOTDIR' || code === 'EPERM') {
767            this.#markENOTDIR();
768        }
769        else if (code === 'ENOENT') {
770            this.#markENOENT();
771        }
772        else {
773            this.children().provisional = 0;
774        }
775    }
776    #lstatFail(code = '') {
777        // Windows just raises ENOENT in this case, disable for win CI
778        /* c8 ignore start */
779        if (code === 'ENOTDIR') {
780            // already know it has a parent by this point
781            const p = this.parent;
782            p.#markENOTDIR();
783        }
784        else if (code === 'ENOENT') {
785            /* c8 ignore stop */
786            this.#markENOENT();
787        }
788    }
789    #readlinkFail(code = '') {
790        let ter = this.#type;
791        ter |= ENOREADLINK;
792        if (code === 'ENOENT')
793            ter |= ENOENT;
794        // windows gets a weird error when you try to readlink a file
795        if (code === 'EINVAL' || code === 'UNKNOWN') {
796            // exists, but not a symlink, we don't know WHAT it is, so remove
797            // all IFMT bits.
798            ter &= IFMT_UNKNOWN;
799        }
800        this.#type = ter;
801        // windows just gets ENOENT in this case.  We do cover the case,
802        // just disabled because it's impossible on Windows CI
803        /* c8 ignore start */
804        if (code === 'ENOTDIR' && this.parent) {
805            this.parent.#markENOTDIR();
806        }
807        /* c8 ignore stop */
808    }
809    #readdirAddChild(e, c) {
810        return (this.#readdirMaybePromoteChild(e, c) ||
811            this.#readdirAddNewChild(e, c));
812    }
813    #readdirAddNewChild(e, c) {
814        // alloc new entry at head, so it's never provisional
815        const type = entToType(e);
816        const child = this.newChild(e.name, type, { parent: this });
817        const ifmt = child.#type & IFMT;
818        if (ifmt !== IFDIR && ifmt !== IFLNK && ifmt !== UNKNOWN) {
819            child.#type |= ENOTDIR;
820        }
821        c.unshift(child);
822        c.provisional++;
823        return child;
824    }
825    #readdirMaybePromoteChild(e, c) {
826        for (let p = c.provisional; p < c.length; p++) {
827            const pchild = c[p];
828            const name = this.nocase ? normalizeNocase(e.name) : normalize(e.name);
829            if (name !== pchild.#matchName) {
830                continue;
831            }
832            return this.#readdirPromoteChild(e, pchild, p, c);
833        }
834    }
835    #readdirPromoteChild(e, p, index, c) {
836        const v = p.name;
837        // retain any other flags, but set ifmt from dirent
838        p.#type = (p.#type & IFMT_UNKNOWN) | entToType(e);
839        // case sensitivity fixing when we learn the true name.
840        if (v !== e.name)
841            p.name = e.name;
842        // just advance provisional index (potentially off the list),
843        // otherwise we have to splice/pop it out and re-insert at head
844        if (index !== c.provisional) {
845            if (index === c.length - 1)
846                c.pop();
847            else
848                c.splice(index, 1);
849            c.unshift(p);
850        }
851        c.provisional++;
852        return p;
853    }
854    /**
855     * Call lstat() on this Path, and update all known information that can be
856     * determined.
857     *
858     * Note that unlike `fs.lstat()`, the returned value does not contain some
859     * information, such as `mode`, `dev`, `nlink`, and `ino`.  If that
860     * information is required, you will need to call `fs.lstat` yourself.
861     *
862     * If the Path refers to a nonexistent file, or if the lstat call fails for
863     * any reason, `undefined` is returned.  Otherwise the updated Path object is
864     * returned.
865     *
866     * Results are cached, and thus may be out of date if the filesystem is
867     * mutated.
868     */
869    async lstat() {
870        if ((this.#type & ENOENT) === 0) {
871            try {
872                this.#applyStat(await this.#fs.promises.lstat(this.fullpath()));
873                return this;
874            }
875            catch (er) {
876                this.#lstatFail(er.code);
877            }
878        }
879    }
880    /**
881     * synchronous {@link PathBase.lstat}
882     */
883    lstatSync() {
884        if ((this.#type & ENOENT) === 0) {
885            try {
886                this.#applyStat(this.#fs.lstatSync(this.fullpath()));
887                return this;
888            }
889            catch (er) {
890                this.#lstatFail(er.code);
891            }
892        }
893    }
894    #applyStat(st) {
895        const { atime, atimeMs, birthtime, birthtimeMs, blksize, blocks, ctime, ctimeMs, dev, gid, ino, mode, mtime, mtimeMs, nlink, rdev, size, uid, } = st;
896        this.#atime = atime;
897        this.#atimeMs = atimeMs;
898        this.#birthtime = birthtime;
899        this.#birthtimeMs = birthtimeMs;
900        this.#blksize = blksize;
901        this.#blocks = blocks;
902        this.#ctime = ctime;
903        this.#ctimeMs = ctimeMs;
904        this.#dev = dev;
905        this.#gid = gid;
906        this.#ino = ino;
907        this.#mode = mode;
908        this.#mtime = mtime;
909        this.#mtimeMs = mtimeMs;
910        this.#nlink = nlink;
911        this.#rdev = rdev;
912        this.#size = size;
913        this.#uid = uid;
914        const ifmt = entToType(st);
915        // retain any other flags, but set the ifmt
916        this.#type = (this.#type & IFMT_UNKNOWN) | ifmt | LSTAT_CALLED;
917        if (ifmt !== UNKNOWN && ifmt !== IFDIR && ifmt !== IFLNK) {
918            this.#type |= ENOTDIR;
919        }
920    }
921    #onReaddirCB = [];
922    #readdirCBInFlight = false;
923    #callOnReaddirCB(children) {
924        this.#readdirCBInFlight = false;
925        const cbs = this.#onReaddirCB.slice();
926        this.#onReaddirCB.length = 0;
927        cbs.forEach(cb => cb(null, children));
928    }
929    /**
930     * Standard node-style callback interface to get list of directory entries.
931     *
932     * If the Path cannot or does not contain any children, then an empty array
933     * is returned.
934     *
935     * Results are cached, and thus may be out of date if the filesystem is
936     * mutated.
937     *
938     * @param cb The callback called with (er, entries).  Note that the `er`
939     * param is somewhat extraneous, as all readdir() errors are handled and
940     * simply result in an empty set of entries being returned.
941     * @param allowZalgo Boolean indicating that immediately known results should
942     * *not* be deferred with `queueMicrotask`. Defaults to `false`. Release
943     * zalgo at your peril, the dark pony lord is devious and unforgiving.
944     */
945    readdirCB(cb, allowZalgo = false) {
946        if (!this.canReaddir()) {
947            if (allowZalgo)
948                cb(null, []);
949            else
950                queueMicrotask(() => cb(null, []));
951            return;
952        }
953        const children = this.children();
954        if (this.calledReaddir()) {
955            const c = children.slice(0, children.provisional);
956            if (allowZalgo)
957                cb(null, c);
958            else
959                queueMicrotask(() => cb(null, c));
960            return;
961        }
962        // don't have to worry about zalgo at this point.
963        this.#onReaddirCB.push(cb);
964        if (this.#readdirCBInFlight) {
965            return;
966        }
967        this.#readdirCBInFlight = true;
968        // else read the directory, fill up children
969        // de-provisionalize any provisional children.
970        const fullpath = this.fullpath();
971        this.#fs.readdir(fullpath, { withFileTypes: true }, (er, entries) => {
972            if (er) {
973                this.#readdirFail(er.code);
974                children.provisional = 0;
975            }
976            else {
977                // if we didn't get an error, we always get entries.
978                //@ts-ignore
979                for (const e of entries) {
980                    this.#readdirAddChild(e, children);
981                }
982                this.#readdirSuccess(children);
983            }
984            this.#callOnReaddirCB(children.slice(0, children.provisional));
985            return;
986        });
987    }
988    #asyncReaddirInFlight;
989    /**
990     * Return an array of known child entries.
991     *
992     * If the Path cannot or does not contain any children, then an empty array
993     * is returned.
994     *
995     * Results are cached, and thus may be out of date if the filesystem is
996     * mutated.
997     */
998    async readdir() {
999        if (!this.canReaddir()) {
1000            return [];
1001        }
1002        const children = this.children();
1003        if (this.calledReaddir()) {
1004            return children.slice(0, children.provisional);
1005        }
1006        // else read the directory, fill up children
1007        // de-provisionalize any provisional children.
1008        const fullpath = this.fullpath();
1009        if (this.#asyncReaddirInFlight) {
1010            await this.#asyncReaddirInFlight;
1011        }
1012        else {
1013            /* c8 ignore start */
1014            let resolve = () => { };
1015            /* c8 ignore stop */
1016            this.#asyncReaddirInFlight = new Promise(res => (resolve = res));
1017            try {
1018                for (const e of await this.#fs.promises.readdir(fullpath, {
1019                    withFileTypes: true,
1020                })) {
1021                    this.#readdirAddChild(e, children);
1022                }
1023                this.#readdirSuccess(children);
1024            }
1025            catch (er) {
1026                this.#readdirFail(er.code);
1027                children.provisional = 0;
1028            }
1029            this.#asyncReaddirInFlight = undefined;
1030            resolve();
1031        }
1032        return children.slice(0, children.provisional);
1033    }
1034    /**
1035     * synchronous {@link PathBase.readdir}
1036     */
1037    readdirSync() {
1038        if (!this.canReaddir()) {
1039            return [];
1040        }
1041        const children = this.children();
1042        if (this.calledReaddir()) {
1043            return children.slice(0, children.provisional);
1044        }
1045        // else read the directory, fill up children
1046        // de-provisionalize any provisional children.
1047        const fullpath = this.fullpath();
1048        try {
1049            for (const e of this.#fs.readdirSync(fullpath, {
1050                withFileTypes: true,
1051            })) {
1052                this.#readdirAddChild(e, children);
1053            }
1054            this.#readdirSuccess(children);
1055        }
1056        catch (er) {
1057            this.#readdirFail(er.code);
1058            children.provisional = 0;
1059        }
1060        return children.slice(0, children.provisional);
1061    }
1062    canReaddir() {
1063        if (this.#type & ENOCHILD)
1064            return false;
1065        const ifmt = IFMT & this.#type;
1066        // we always set ENOTDIR when setting IFMT, so should be impossible
1067        /* c8 ignore start */
1068        if (!(ifmt === UNKNOWN || ifmt === IFDIR || ifmt === IFLNK)) {
1069            return false;
1070        }
1071        /* c8 ignore stop */
1072        return true;
1073    }
1074    shouldWalk(dirs, walkFilter) {
1075        return ((this.#type & IFDIR) === IFDIR &&
1076            !(this.#type & ENOCHILD) &&
1077            !dirs.has(this) &&
1078            (!walkFilter || walkFilter(this)));
1079    }
1080    /**
1081     * Return the Path object corresponding to path as resolved
1082     * by realpath(3).
1083     *
1084     * If the realpath call fails for any reason, `undefined` is returned.
1085     *
1086     * Result is cached, and thus may be outdated if the filesystem is mutated.
1087     * On success, returns a Path object.
1088     */
1089    async realpath() {
1090        if (this.#realpath)
1091            return this.#realpath;
1092        if ((ENOREALPATH | ENOREADLINK | ENOENT) & this.#type)
1093            return undefined;
1094        try {
1095            const rp = await this.#fs.promises.realpath(this.fullpath());
1096            return (this.#realpath = this.resolve(rp));
1097        }
1098        catch (_) {
1099            this.#markENOREALPATH();
1100        }
1101    }
1102    /**
1103     * Synchronous {@link realpath}
1104     */
1105    realpathSync() {
1106        if (this.#realpath)
1107            return this.#realpath;
1108        if ((ENOREALPATH | ENOREADLINK | ENOENT) & this.#type)
1109            return undefined;
1110        try {
1111            const rp = this.#fs.realpathSync(this.fullpath());
1112            return (this.#realpath = this.resolve(rp));
1113        }
1114        catch (_) {
1115            this.#markENOREALPATH();
1116        }
1117    }
1118    /**
1119     * Internal method to mark this Path object as the scurry cwd,
1120     * called by {@link PathScurry#chdir}
1121     *
1122     * @internal
1123     */
1124    [setAsCwd](oldCwd) {
1125        if (oldCwd === this)
1126            return;
1127        oldCwd.isCWD = false;
1128        this.isCWD = true;
1129        const changed = new Set([]);
1130        let rp = [];
1131        let p = this;
1132        while (p && p.parent) {
1133            changed.add(p);
1134            p.#relative = rp.join(this.sep);
1135            p.#relativePosix = rp.join('/');
1136            p = p.parent;
1137            rp.push('..');
1138        }
1139        // now un-memoize parents of old cwd
1140        p = oldCwd;
1141        while (p && p.parent && !changed.has(p)) {
1142            p.#relative = undefined;
1143            p.#relativePosix = undefined;
1144            p = p.parent;
1145        }
1146    }
1147}
1148/**
1149 * Path class used on win32 systems
1150 *
1151 * Uses `'\\'` as the path separator for returned paths, either `'\\'` or `'/'`
1152 * as the path separator for parsing paths.
1153 */
1154export class PathWin32 extends PathBase {
1155    /**
1156     * Separator for generating path strings.
1157     */
1158    sep = '\\';
1159    /**
1160     * Separator for parsing path strings.
1161     */
1162    splitSep = eitherSep;
1163    /**
1164     * Do not create new Path objects directly.  They should always be accessed
1165     * via the PathScurry class or other methods on the Path class.
1166     *
1167     * @internal
1168     */
1169    constructor(name, type = UNKNOWN, root, roots, nocase, children, opts) {
1170        super(name, type, root, roots, nocase, children, opts);
1171    }
1172    /**
1173     * @internal
1174     */
1175    newChild(name, type = UNKNOWN, opts = {}) {
1176        return new PathWin32(name, type, this.root, this.roots, this.nocase, this.childrenCache(), opts);
1177    }
1178    /**
1179     * @internal
1180     */
1181    getRootString(path) {
1182        return win32.parse(path).root;
1183    }
1184    /**
1185     * @internal
1186     */
1187    getRoot(rootPath) {
1188        rootPath = uncToDrive(rootPath.toUpperCase());
1189        if (rootPath === this.root.name) {
1190            return this.root;
1191        }
1192        // ok, not that one, check if it matches another we know about
1193        for (const [compare, root] of Object.entries(this.roots)) {
1194            if (this.sameRoot(rootPath, compare)) {
1195                return (this.roots[rootPath] = root);
1196            }
1197        }
1198        // otherwise, have to create a new one.
1199        return (this.roots[rootPath] = new PathScurryWin32(rootPath, this).root);
1200    }

Showing the first 1,200 of 1983 lines. Download the file for the rest.

codekingpro/portable-devtools · Team Ai