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