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 path_1 = require("path"); 29const url_1 = require("url"); 30const actualFS = __importStar(require("fs")); 31const fs_1 = require("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("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 = 16; 79// set after a successful lstat() 80const LSTAT_CALLED = 32; 81// set if an entry (or one of its parents) is definitely not a dir 82const ENOTDIR = 64; 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 = 128; 86// cannot have child entries -- also verify &IFMT is either IFDIR or IFLNK 87// set if we fail to readlink 88const ENOREADLINK = 256; 89// set if we know realpath() will fail 90const ENOREALPATH = 512; 91const ENOCHILD = ENOTDIR | ENOENT | ENOREALPATH; 92const TYPEMASK = 1023; 93const entToType = (s) => s.isFile() 94 ? IFREG 95 : s.isDirectory() 96 ? IFDIR 97 : s.isSymbolicLink() 98 ? IFLNK 99 : s.isCharacterDevice() 100 ? IFCHR 101 : s.isBlockDevice() 102 ? IFBLK 103 : s.isSocket() 104 ? IFSOCK 105 : s.isFIFO() 106 ? IFIFO 107 : UNKNOWN; 108// normalize unicode path names 109const normalizeCache = new Map(); 110const normalize = (s) => { 111 const c = normalizeCache.get(s); 112 if (c) 113 return c; 114 const n = s.normalize('NFKD'); 115 normalizeCache.set(s, n); 116 return n; 117}; 118const normalizeNocaseCache = new Map(); 119const normalizeNocase = (s) => { 120 const c = normalizeNocaseCache.get(s); 121 if (c) 122 return c; 123 const n = normalize(s.toLowerCase()); 124 normalizeNocaseCache.set(s, n); 125 return n; 126}; 127/** 128 * An LRUCache for storing resolved path strings or Path objects. 129 * @internal 130 */ 131class ResolveCache extends lru_cache_1.LRUCache { 132 constructor() { 133 super({ max: 256 }); 134 } 135} 136exports.ResolveCache = ResolveCache; 137// In order to prevent blowing out the js heap by allocating hundreds of 138// thousands of Path entries when walking extremely large trees, the "children" 139// in this tree are represented by storing an array of Path entries in an 140// LRUCache, indexed by the parent. At any time, Path.children() may return an 141// empty array, indicating that it doesn't know about any of its children, and 142// thus has to rebuild that cache. This is fine, it just means that we don't 143// benefit as much from having the cached entries, but huge directory walks 144// don't blow out the stack, and smaller ones are still as fast as possible. 145// 146//It does impose some complexity when building up the readdir data, because we 147//need to pass a reference to the children array that we started with. 148/** 149 * an LRUCache for storing child entries. 150 * @internal 151 */ 152class ChildrenCache extends lru_cache_1.LRUCache { 153 constructor(maxSize = 16 * 1024) { 154 super({ 155 maxSize, 156 // parent + children 157 sizeCalculation: a => a.length + 1, 158 }); 159 } 160} 161exports.ChildrenCache = ChildrenCache; 162const setAsCwd = Symbol('PathScurry setAsCwd'); 163/** 164 * Path objects are sort of like a super-powered 165 * {@link https://nodejs.org/docs/latest/api/fs.html#class-fsdirent fs.Dirent} 166 * 167 * Each one represents a single filesystem entry on disk, which may or may not 168 * exist. It includes methods for reading various types of information via 169 * lstat, readlink, and readdir, and caches all information to the greatest 170 * degree possible. 171 * 172 * Note that fs operations that would normally throw will instead return an 173 * "empty" value. This is in order to prevent excessive overhead from error 174 * stack traces. 175 */ 176class PathBase { 177 /** 178 * the basename of this path 179 * 180 * **Important**: *always* test the path name against any test string 181 * usingthe {@link isNamed} method, and not by directly comparing this 182 * string. Otherwise, unicode path strings that the system sees as identical 183 * will not be properly treated as the same path, leading to incorrect 184 * behavior and possible security issues. 185 */ 186 name; 187 /** 188 * the Path entry corresponding to the path root. 189 * 190 * @internal 191 */ 192 root; 193 /** 194 * All roots found within the current PathScurry family 195 * 196 * @internal 197 */ 198 roots; 199 /** 200 * a reference to the parent path, or undefined in the case of root entries 201 * 202 * @internal 203 */ 204 parent; 205 /** 206 * boolean indicating whether paths are compared case-insensitively 207 * @internal 208 */ 209 nocase; 210 // potential default fs override 211 #fs; 212 // Stats fields 213 #dev; 214 get dev() { 215 return this.#dev; 216 } 217 #mode; 218 get mode() { 219 return this.#mode; 220 } 221 #nlink; 222 get nlink() { 223 return this.#nlink; 224 } 225 #uid; 226 get uid() { 227 return this.#uid; 228 } 229 #gid; 230 get gid() { 231 return this.#gid; 232 } 233 #rdev; 234 get rdev() { 235 return this.#rdev; 236 } 237 #blksize; 238 get blksize() { 239 return this.#blksize; 240 } 241 #ino; 242 get ino() { 243 return this.#ino; 244 } 245 #size; 246 get size() { 247 return this.#size; 248 } 249 #blocks; 250 get blocks() { 251 return this.#blocks; 252 } 253 #atimeMs; 254 get atimeMs() { 255 return this.#atimeMs; 256 } 257 #mtimeMs; 258 get mtimeMs() { 259 return this.#mtimeMs; 260 } 261 #ctimeMs; 262 get ctimeMs() { 263 return this.#ctimeMs; 264 } 265 #birthtimeMs; 266 get birthtimeMs() { 267 return this.#birthtimeMs; 268 } 269 #atime; 270 get atime() { 271 return this.#atime; 272 } 273 #mtime; 274 get mtime() { 275 return this.#mtime; 276 } 277 #ctime; 278 get ctime() { 279 return this.#ctime; 280 } 281 #birthtime; 282 get birthtime() { 283 return this.#birthtime; 284 } 285 #matchName; 286 #depth; 287 #fullpath; 288 #fullpathPosix; 289 #relative; 290 #relativePosix; 291 #type; 292 #children; 293 #linkTarget; 294 #realpath; 295 /** 296 * This property is for compatibility with the Dirent class as of 297 * Node v20, where Dirent['path'] refers to the path of the directory 298 * that was passed to readdir. So, somewhat counterintuitively, this 299 * property refers to the *parent* path, not the path object itself. 300 * For root entries, it's the path to the entry itself. 301 */ 302 get path() { 303 return (this.parent || this).fullpath(); 304 } 305 /** 306 * Do not create new Path objects directly. They should always be accessed 307 * via the PathScurry class or other methods on the Path class. 308 * 309 * @internal 310 */ 311 constructor(name, type = UNKNOWN, root, roots, nocase, children, opts) { 312 this.name = name; 313 this.#matchName = nocase ? normalizeNocase(name) : normalize(name); 314 this.#type = type & TYPEMASK; 315 this.nocase = nocase; 316 this.roots = roots; 317 this.root = root || this; 318 this.#children = children; 319 this.#fullpath = opts.fullpath; 320 this.#relative = opts.relative; 321 this.#relativePosix = opts.relativePosix; 322 this.parent = opts.parent; 323 if (this.parent) { 324 this.#fs = this.parent.#fs; 325 } 326 else { 327 this.#fs = fsFromOption(opts.fs); 328 } 329 } 330 /** 331 * Returns the depth of the Path object from its root. 332 * 333 * For example, a path at `/foo/bar` would have a depth of 2. 334 */ 335 depth() { 336 if (this.#depth !== undefined) 337 return this.#depth; 338 if (!this.parent) 339 return (this.#depth = 0); 340 return (this.#depth = this.parent.depth() + 1); 341 } 342 /** 343 * @internal 344 */ 345 childrenCache() { 346 return this.#children; 347 } 348 /** 349 * Get the Path object referenced by the string path, resolved from this Path 350 */ 351 resolve(path) { 352 if (!path) { 353 return this; 354 } 355 const rootPath = this.getRootString(path); 356 const dir = path.substring(rootPath.length); 357 const dirParts = dir.split(this.splitSep); 358 const result = rootPath 359 ? this.getRoot(rootPath).#resolveParts(dirParts) 360 : this.#resolveParts(dirParts); 361 return result; 362 } 363 #resolveParts(dirParts) { 364 let p = this; 365 for (const part of dirParts) { 366 p = p.child(part); 367 } 368 return p; 369 } 370 /** 371 * Returns the cached children Path objects, if still available. If they 372 * have fallen out of the cache, then returns an empty array, and resets the 373 * READDIR_CALLED bit, so that future calls to readdir() will require an fs 374 * lookup. 375 * 376 * @internal 377 */ 378 children() { 379 const cached = this.#children.get(this); 380 if (cached) { 381 return cached; 382 } 383 const children = Object.assign([], { provisional: 0 }); 384 this.#children.set(this, children); 385 this.#type &= ~READDIR_CALLED; 386 return children; 387 } 388 /** 389 * Resolves a path portion and returns or creates the child Path. 390 * 391 * Returns `this` if pathPart is `''` or `'.'`, or `parent` if pathPart is 392 * `'..'`. 393 * 394 * This should not be called directly. If `pathPart` contains any path 395 * separators, it will lead to unsafe undefined behavior. 396 * 397 * Use `Path.resolve()` instead. 398 * 399 * @internal 400 */ 401 child(pathPart, opts) { 402 if (pathPart === '' || pathPart === '.') { 403 return this; 404 } 405 if (pathPart === '..') { 406 return this.parent || this; 407 } 408 // find the child 409 const children = this.children(); 410 const name = this.nocase 411 ? normalizeNocase(pathPart) 412 : normalize(pathPart); 413 for (const p of children) { 414 if (p.#matchName === name) { 415 return p; 416 } 417 } 418 // didn't find it, create provisional child, since it might not 419 // actually exist. If we know the parent isn't a dir, then 420 // in fact it CAN'T exist. 421 const s = this.parent ? this.sep : ''; 422 const fullpath = this.#fullpath 423 ? this.#fullpath + s + pathPart 424 : undefined; 425 const pchild = this.newChild(pathPart, UNKNOWN, { 426 ...opts, 427 parent: this, 428 fullpath, 429 }); 430 if (!this.canReaddir()) { 431 pchild.#type |= ENOENT; 432 } 433 // don't have to update provisional, because if we have real children, 434 // then provisional is set to children.length, otherwise a lower number 435 children.push(pchild); 436 return pchild; 437 } 438 /** 439 * The relative path from the cwd. If it does not share an ancestor with 440 * the cwd, then this ends up being equivalent to the fullpath() 441 */ 442 relative() { 443 if (this.#relative !== undefined) { 444 return this.#relative; 445 } 446 const name = this.name; 447 const p = this.parent; 448 if (!p) { 449 return (this.#relative = this.name); 450 } 451 const pv = p.relative(); 452 return pv + (!pv || !p.parent ? '' : this.sep) + name; 453 } 454 /** 455 * The relative path from the cwd, using / as the path separator. 456 * If it does not share an ancestor with 457 * the cwd, then this ends up being equivalent to the fullpathPosix() 458 * On posix systems, this is identical to relative(). 459 */ 460 relativePosix() { 461 if (this.sep === '/') 462 return this.relative(); 463 if (this.#relativePosix !== undefined) 464 return this.#relativePosix; 465 const name = this.name; 466 const p = this.parent; 467 if (!p) { 468 return (this.#relativePosix = this.fullpathPosix()); 469 } 470 const pv = p.relativePosix(); 471 return pv + (!pv || !p.parent ? '' : '/') + name; 472 } 473 /** 474 * The fully resolved path string for this Path entry 475 */ 476 fullpath() { 477 if (this.#fullpath !== undefined) { 478 return this.#fullpath; 479 } 480 const name = this.name; 481 const p = this.parent; 482 if (!p) { 483 return (this.#fullpath = this.name); 484 } 485 const pv = p.fullpath(); 486 const fp = pv + (!p.parent ? '' : this.sep) + name; 487 return (this.#fullpath = fp); 488 } 489 /** 490 * On platforms other than windows, this is identical to fullpath. 491 * 492 * On windows, this is overridden to return the forward-slash form of the 493 * full UNC path. 494 */ 495 fullpathPosix() { 496 if (this.#fullpathPosix !== undefined) 497 return this.#fullpathPosix; 498 if (this.sep === '/') 499 return (this.#fullpathPosix = this.fullpath()); 500 if (!this.parent) { 501 const p = this.fullpath().replace(/\\/g, '/'); 502 if (/^[a-z]:\//i.test(p)) { 503 return (this.#fullpathPosix = `//?/${p}`); 504 } 505 else { 506 return (this.#fullpathPosix = p); 507 } 508 } 509 const p = this.parent; 510 const pfpp = p.fullpathPosix(); 511 const fpp = pfpp + (!pfpp || !p.parent ? '' : '/') + this.name; 512 return (this.#fullpathPosix = fpp); 513 } 514 /** 515 * Is the Path of an unknown type? 516 * 517 * Note that we might know *something* about it if there has been a previous 518 * filesystem operation, for example that it does not exist, or is not a 519 * link, or whether it has child entries. 520 */ 521 isUnknown() { 522 return (this.#type & IFMT) === UNKNOWN; 523 } 524 isType(type) { 525 return this[`is${type}`](); 526 } 527 getType() { 528 return this.isUnknown() 529 ? 'Unknown' 530 : this.isDirectory() 531 ? 'Directory' 532 : this.isFile() 533 ? 'File' 534 : this.isSymbolicLink() 535 ? 'SymbolicLink' 536 : this.isFIFO() 537 ? 'FIFO' 538 : this.isCharacterDevice() 539 ? 'CharacterDevice' 540 : this.isBlockDevice() 541 ? 'BlockDevice' 542 : /* c8 ignore start */ this.isSocket() 543 ? '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 = this.parent.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.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 children[p].#markENOENT(); 751 } 752 } 753 #markENOENT() { 754 // mark as UNKNOWN and ENOENT 755 if (this.#type & ENOENT) 756 return; 757 this.#type = (this.#type | ENOENT) & IFMT_UNKNOWN; 758 this.#markChildrenENOENT(); 759 } 760 #markChildrenENOENT() { 761 // all children are provisional and do not exist 762 const children = this.children(); 763 children.provisional = 0; 764 for (const p of children) { 765 p.#markENOENT(); 766 } 767 } 768 #markENOREALPATH() { 769 this.#type |= ENOREALPATH; 770 this.#markENOTDIR(); 771 } 772 // save the information when we know the entry is not a dir 773 #markENOTDIR() { 774 // entry is not a directory, so any children can't exist. 775 // this *should* be impossible, since any children created 776 // after it's been marked ENOTDIR should be marked ENOENT, 777 // so it won't even get to this point. 778 /* c8 ignore start */ 779 if (this.#type & ENOTDIR) 780 return; 781 /* c8 ignore stop */ 782 let t = this.#type; 783 // this could happen if we stat a dir, then delete it, 784 // then try to read it or one of its children. 785 if ((t & IFMT) === IFDIR) 786 t &= IFMT_UNKNOWN; 787 this.#type = t | ENOTDIR; 788 this.#markChildrenENOENT(); 789 } 790 #readdirFail(code = '') { 791 // markENOTDIR and markENOENT also set provisional=0 792 if (code === 'ENOTDIR' || code === 'EPERM') { 793 this.#markENOTDIR(); 794 } 795 else if (code === 'ENOENT') { 796 this.#markENOENT(); 797 } 798 else { 799 this.children().provisional = 0; 800 } 801 } 802 #lstatFail(code = '') { 803 // Windows just raises ENOENT in this case, disable for win CI 804 /* c8 ignore start */ 805 if (code === 'ENOTDIR') { 806 // already know it has a parent by this point 807 const p = this.parent; 808 p.#markENOTDIR(); 809 } 810 else if (code === 'ENOENT') { 811 /* c8 ignore stop */ 812 this.#markENOENT(); 813 } 814 } 815 #readlinkFail(code = '') { 816 let ter = this.#type; 817 ter |= ENOREADLINK; 818 if (code === 'ENOENT') 819 ter |= ENOENT; 820 // windows gets a weird error when you try to readlink a file 821 if (code === 'EINVAL' || code === 'UNKNOWN') { 822 // exists, but not a symlink, we don't know WHAT it is, so remove 823 // all IFMT bits. 824 ter &= IFMT_UNKNOWN; 825 } 826 this.#type = ter; 827 // windows just gets ENOENT in this case. We do cover the case, 828 // just disabled because it's impossible on Windows CI 829 /* c8 ignore start */ 830 if (code === 'ENOTDIR' && this.parent) { 831 this.parent.#markENOTDIR(); 832 } 833 /* c8 ignore stop */ 834 } 835 #readdirAddChild(e, c) { 836 return (this.#readdirMaybePromoteChild(e, c) || 837 this.#readdirAddNewChild(e, c)); 838 } 839 #readdirAddNewChild(e, c) { 840 // alloc new entry at head, so it's never provisional 841 const type = entToType(e); 842 const child = this.newChild(e.name, type, { parent: this }); 843 const ifmt = child.#type & IFMT; 844 if (ifmt !== IFDIR && ifmt !== IFLNK && ifmt !== UNKNOWN) { 845 child.#type |= ENOTDIR; 846 } 847 c.unshift(child); 848 c.provisional++; 849 return child; 850 } 851 #readdirMaybePromoteChild(e, c) { 852 for (let p = c.provisional; p < c.length; p++) { 853 const pchild = c[p]; 854 const name = this.nocase 855 ? normalizeNocase(e.name) 856 : 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 const changed = new Set([]); 1156 let rp = []; 1157 let p = this; 1158 while (p && p.parent) { 1159 changed.add(p); 1160 p.#relative = rp.join(this.sep); 1161 p.#relativePosix = rp.join('/'); 1162 p = p.parent; 1163 rp.push('..'); 1164 } 1165 // now un-memoize parents of old cwd 1166 p = oldCwd; 1167 while (p && p.parent && !changed.has(p)) { 1168 p.#relative = undefined; 1169 p.#relativePosix = undefined; 1170 p = p.parent; 1171 } 1172 } 1173} 1174exports.PathBase = PathBase; 1175/** 1176 * Path class used on win32 systems 1177 * 1178 * Uses `'\\'` as the path separator for returned paths, either `'\\'` or `'/'` 1179 * as the path separator for parsing paths. 1180 */ 1181class PathWin32 extends PathBase { 1182 /** 1183 * Separator for generating path strings. 1184 */ 1185 sep = '\\'; 1186 /** 1187 * Separator for parsing path strings. 1188 */ 1189 splitSep = eitherSep; 1190 /** 1191 * Do not create new Path objects directly. They should always be accessed 1192 * via the PathScurry class or other methods on the Path class. 1193 * 1194 * @internal 1195 */ 1196 constructor(name, type = UNKNOWN, root, roots, nocase, children, opts) { 1197 super(name, type, root, roots, nocase, children, opts); 1198 } 1199 /** 1200 * @internal 1201 */ 1202 newChild(name, type = UNKNOWN, opts = {}) { 1203 return new PathWin32(name, type, this.root, this.roots, this.nocase, this.childrenCache(), opts); 1204 } 1205 /** 1206 * @internal 1207 */ 1208 getRootString(path) { 1209 return path_1.win32.parse(path).root; 1210 } 1211 /** 1212 * @internal 1213 */ 1214 getRoot(rootPath) { 1215 rootPath = uncToDrive(rootPath.toUpperCase()); 1216 if (rootPath === this.root.name) { 1217 return this.root; 1218 } 1219 // ok, not that one, check if it matches another we know about 1220 for (const [compare, root] of Object.entries(this.roots)) { 1221 if (this.sameRoot(rootPath, compare)) { 1222 return (this.roots[rootPath] = root); 1223 } 1224 } 1225 // otherwise, have to create a new one. 1226 return (this.roots[rootPath] = new PathScurryWin32(rootPath, this).root); 1227 } 1228 /** 1229 * @internal 1230 */ 1231 sameRoot(rootPath, compare = this.root.name) { 1232 // windows can (rarely) have case-sensitive filesystem, but 1233 // UNC and drive letters are always case-insensitive, and canonically 1234 // represented uppercase. 1235 rootPath = rootPath 1236 .toUpperCase() 1237 .replace(/\//g, '\\') 1238 .replace(uncDriveRegexp, '$1\\'); 1239 return rootPath === compare; 1240 } 1241} 1242exports.PathWin32 = PathWin32; 1243/** 1244 * Path class used on all posix systems. 1245 * 1246 * Uses `'/'` as the path separator. 1247 */ 1248class PathPosix extends PathBase { 1249 /** 1250 * separator for parsing path strings 1251 */ 1252 splitSep = '/'; 1253 /** 1254 * separator for generating path strings 1255 */ 1256 sep = '/'; 1257 /** 1258 * Do not create new Path objects directly. They should always be accessed 1259 * via the PathScurry class or other methods on the Path class. 1260 * 1261 * @internal 1262 */ 1263 constructor(name, type = UNKNOWN, root, roots, nocase, children, opts) { 1264 super(name, type, root, roots, nocase, children, opts); 1265 } 1266 /** 1267 * @internal 1268 */ 1269 getRootString(path) { 1270 return path.startsWith('/') ? '/' : ''; 1271 } 1272 /** 1273 * @internal 1274 */ 1275 getRoot(_rootPath) { 1276 return this.root; 1277 } 1278 /** 1279 * @internal 1280 */ 1281 newChild(name, type = UNKNOWN, opts = {}) { 1282 return new PathPosix(name, type, this.root, this.roots, this.nocase, this.childrenCache(), opts); 1283 } 1284} 1285exports.PathPosix = PathPosix; 1286/** 1287 * The base class for all PathScurry classes, providing the interface for path 1288 * resolution and filesystem operations. 1289 * 1290 * Typically, you should *not* instantiate this class directly, but rather one 1291 * of the platform-specific classes, or the exported {@link PathScurry} which 1292 * defaults to the current platform. 1293 */ 1294class PathScurryBase { 1295 /** 1296 * The root Path entry for the current working directory of this Scurry 1297 */ 1298 root; 1299 /** 1300 * The string path for the root of this Scurry's current working directory 1301 */ 1302 rootPath; 1303 /** 1304 * A collection of all roots encountered, referenced by rootPath 1305 */ 1306 roots; 1307 /** 1308 * The Path entry corresponding to this PathScurry's current working directory. 1309 */ 1310 cwd; 1311 #resolveCache; 1312 #resolvePosixCache; 1313 #children; 1314 /** 1315 * Perform path comparisons case-insensitively. 1316 * 1317 * Defaults true on Darwin and Windows systems, false elsewhere. 1318 */ 1319 nocase; 1320 #fs; 1321 /** 1322 * This class should not be instantiated directly. 1323 * 1324 * Use PathScurryWin32, PathScurryDarwin, PathScurryPosix, or PathScurry 1325 * 1326 * @internal 1327 */ 1328 constructor(cwd = process.cwd(), pathImpl, sep, { nocase, childrenCacheSize = 16 * 1024, fs = defaultFS, } = {}) { 1329 this.#fs = fsFromOption(fs); 1330 if (cwd instanceof URL || cwd.startsWith('file://')) { 1331 cwd = (0, url_1.fileURLToPath)(cwd); 1332 } 1333 // resolve and split root, and then add to the store. 1334 // this is the only time we call path.resolve() 1335 const cwdPath = pathImpl.resolve(cwd); 1336 this.roots = Object.create(null); 1337 this.rootPath = this.parseRootPath(cwdPath); 1338 this.#resolveCache = new ResolveCache(); 1339 this.#resolvePosixCache = new ResolveCache(); 1340 this.#children = new ChildrenCache(childrenCacheSize); 1341 const split = cwdPath.substring(this.rootPath.length).split(sep); 1342 // resolve('/') leaves '', splits to [''], we don't want that. 1343 if (split.length === 1 && !split[0]) { 1344 split.pop(); 1345 } 1346 /* c8 ignore start */ 1347 if (nocase === undefined) { 1348 throw new TypeError('must provide nocase setting to PathScurryBase ctor'); 1349 } 1350 /* c8 ignore stop */ 1351 this.nocase = nocase; 1352 this.root = this.newRoot(this.#fs); 1353 this.roots[this.rootPath] = this.root; 1354 let prev = this.root; 1355 let len = split.length - 1; 1356 const joinSep = pathImpl.sep; 1357 let abs = this.rootPath; 1358 let sawFirst = false; 1359 for (const part of split) { 1360 const l = len--; 1361 prev = prev.child(part, { 1362 relative: new Array(l).fill('..').join(joinSep), 1363 relativePosix: new Array(l).fill('..').join('/'), 1364 fullpath: (abs += (sawFirst ? '' : joinSep) + part), 1365 }); 1366 sawFirst = true; 1367 } 1368 this.cwd = prev; 1369 } 1370 /** 1371 * Get the depth of a provided path, string, or the cwd 1372 */ 1373 depth(path = this.cwd) { 1374 if (typeof path === 'string') { 1375 path = this.cwd.resolve(path); 1376 } 1377 return path.depth(); 1378 } 1379 /** 1380 * Return the cache of child entries. Exposed so subclasses can create 1381 * child Path objects in a platform-specific way. 1382 * 1383 * @internal 1384 */ 1385 childrenCache() { 1386 return this.#children; 1387 } 1388 /** 1389 * Resolve one or more path strings to a resolved string 1390 * 1391 * Same interface as require('path').resolve. 1392 * 1393 * Much faster than path.resolve() when called multiple times for the same 1394 * path, because the resolved Path objects are cached. Much slower 1395 * otherwise. 1396 */ 1397 resolve(...paths) { 1398 // first figure out the minimum number of paths we have to test 1399 // we always start at cwd, but any absolutes will bump the start 1400 let r = ''; 1401 for (let i = paths.length - 1; i >= 0; i--) { 1402 const p = paths[i]; 1403 if (!p || p === '.') 1404 continue; 1405 r = r ? `${p}/${r}` : p; 1406 if (this.isAbsolute(p)) { 1407 break; 1408 } 1409 } 1410 const cached = this.#resolveCache.get(r); 1411 if (cached !== undefined) { 1412 return cached; 1413 } 1414 const result = this.cwd.resolve(r).fullpath(); 1415 this.#resolveCache.set(r, result); 1416 return result; 1417 } 1418 /** 1419 * Resolve one or more path strings to a resolved string, returning 1420 * the posix path. Identical to .resolve() on posix systems, but on 1421 * windows will return a forward-slash separated UNC path. 1422 * 1423 * Same interface as require('path').resolve. 1424 * 1425 * Much faster than path.resolve() when called multiple times for the same 1426 * path, because the resolved Path objects are cached. Much slower 1427 * otherwise. 1428 */ 1429 resolvePosix(...paths) { 1430 // first figure out the minimum number of paths we have to test 1431 // we always start at cwd, but any absolutes will bump the start 1432 let r = ''; 1433 for (let i = paths.length - 1; i >= 0; i--) { 1434 const p = paths[i]; 1435 if (!p || p === '.') 1436 continue; 1437 r = r ? `${p}/${r}` : p; 1438 if (this.isAbsolute(p)) { 1439 break; 1440 } 1441 } 1442 const cached = this.#resolvePosixCache.get(r); 1443 if (cached !== undefined) { 1444 return cached; 1445 } 1446 const result = this.cwd.resolve(r).fullpathPosix(); 1447 this.#resolvePosixCache.set(r, result); 1448 return result; 1449 } 1450 /** 1451 * find the relative path from the cwd to the supplied path string or entry 1452 */ 1453 relative(entry = this.cwd) { 1454 if (typeof entry === 'string') { 1455 entry = this.cwd.resolve(entry); 1456 } 1457 return entry.relative(); 1458 } 1459 /** 1460 * find the relative path from the cwd to the supplied path string or 1461 * entry, using / as the path delimiter, even on Windows. 1462 */ 1463 relativePosix(entry = this.cwd) { 1464 if (typeof entry === 'string') { 1465 entry = this.cwd.resolve(entry); 1466 } 1467 return entry.relativePosix(); 1468 } 1469 /** 1470 * Return the basename for the provided string or Path object 1471 */ 1472 basename(entry = this.cwd) { 1473 if (typeof entry === 'string') { 1474 entry = this.cwd.resolve(entry); 1475 } 1476 return entry.name; 1477 } 1478 /** 1479 * Return the dirname for the provided string or Path object 1480 */ 1481 dirname(entry = this.cwd) { 1482 if (typeof entry === 'string') { 1483 entry = this.cwd.resolve(entry); 1484 } 1485 return (entry.parent || entry).fullpath(); 1486 } 1487 async readdir(entry = this.cwd, opts = { 1488 withFileTypes: true, 1489 }) { 1490 if (typeof entry === 'string') { 1491 entry = this.cwd.resolve(entry); 1492 } 1493 else if (!(entry instanceof PathBase)) { 1494 opts = entry; 1495 entry = this.cwd; 1496 } 1497 const { withFileTypes } = opts; 1498 if (!entry.canReaddir()) { 1499 return []; 1500 } 1501 else { 1502 const p = await entry.readdir(); 1503 return withFileTypes ? p : p.map(e => e.name); 1504 } 1505 } 1506 readdirSync(entry = this.cwd, opts = { 1507 withFileTypes: true, 1508 }) { 1509 if (typeof entry === 'string') { 1510 entry = this.cwd.resolve(entry); 1511 } 1512 else if (!(entry instanceof PathBase)) { 1513 opts = entry; 1514 entry = this.cwd; 1515 } 1516 const { withFileTypes = true } = opts; 1517 if (!entry.canReaddir()) { 1518 return []; 1519 } 1520 else if (withFileTypes) { 1521 return entry.readdirSync(); 1522 } 1523 else { 1524 return entry.readdirSync().map(e => e.name); 1525 } 1526 } 1527 /** 1528 * Call lstat() on the string or Path object, and update all known 1529 * information that can be determined. 1530 * 1531 * Note that unlike `fs.lstat()`, the returned value does not contain some 1532 * information, such as `mode`, `dev`, `nlink`, and `ino`. If that 1533 * information is required, you will need to call `fs.lstat` yourself. 1534 * 1535 * If the Path refers to a nonexistent file, or if the lstat call fails for 1536 * any reason, `undefined` is returned. Otherwise the updated Path object is 1537 * returned. 1538 * 1539 * Results are cached, and thus may be out of date if the filesystem is 1540 * mutated. 1541 */ 1542 async lstat(entry = this.cwd) { 1543 if (typeof entry === 'string') { 1544 entry = this.cwd.resolve(entry); 1545 } 1546 return entry.lstat(); 1547 } 1548 /** 1549 * synchronous {@link PathScurryBase.lstat} 1550 */ 1551 lstatSync(entry = this.cwd) { 1552 if (typeof entry === 'string') { 1553 entry = this.cwd.resolve(entry); 1554 } 1555 return entry.lstatSync(); 1556 } 1557 async readlink(entry = this.cwd, { withFileTypes } = { 1558 withFileTypes: false, 1559 }) { 1560 if (typeof entry === 'string') { 1561 entry = this.cwd.resolve(entry); 1562 } 1563 else if (!(entry instanceof PathBase)) { 1564 withFileTypes = entry.withFileTypes; 1565 entry = this.cwd; 1566 } 1567 const e = await entry.readlink(); 1568 return withFileTypes ? e : e?.fullpath(); 1569 } 1570 readlinkSync(entry = this.cwd, { withFileTypes } = { 1571 withFileTypes: false, 1572 }) { 1573 if (typeof entry === 'string') { 1574 entry = this.cwd.resolve(entry); 1575 } 1576 else if (!(entry instanceof PathBase)) { 1577 withFileTypes = entry.withFileTypes; 1578 entry = this.cwd; 1579 } 1580 const e = entry.readlinkSync(); 1581 return withFileTypes ? e : e?.fullpath(); 1582 } 1583 async realpath(entry = this.cwd, { withFileTypes } = { 1584 withFileTypes: false, 1585 }) { 1586 if (typeof entry === 'string') { 1587 entry = this.cwd.resolve(entry); 1588 } 1589 else if (!(entry instanceof PathBase)) { 1590 withFileTypes = entry.withFileTypes; 1591 entry = this.cwd; 1592 } 1593 const e = await entry.realpath(); 1594 return withFileTypes ? e : e?.fullpath(); 1595 } 1596 realpathSync(entry = this.cwd, { withFileTypes } = { 1597 withFileTypes: false, 1598 }) { 1599 if (typeof entry === 'string') { 1600 entry = this.cwd.resolve(entry); 1601 } 1602 else if (!(entry instanceof PathBase)) { 1603 withFileTypes = entry.withFileTypes; 1604 entry = this.cwd; 1605 } 1606 const e = entry.realpathSync(); 1607 return withFileTypes ? e : e?.fullpath(); 1608 } 1609 async walk(entry = this.cwd, opts = {}) { 1610 if (typeof entry === 'string') { 1611 entry = this.cwd.resolve(entry); 1612 } 1613 else if (!(entry instanceof PathBase)) { 1614 opts = entry; 1615 entry = this.cwd; 1616 } 1617 const { withFileTypes = true, follow = false, filter, walkFilter, } = opts; 1618 const results = []; 1619 if (!filter || filter(entry)) { 1620 results.push(withFileTypes ? entry : entry.fullpath()); 1621 } 1622 const dirs = new Set(); 1623 const walk = (dir, cb) => { 1624 dirs.add(dir); 1625 dir.readdirCB((er, entries) => { 1626 /* c8 ignore start */ 1627 if (er) { 1628 return cb(er); 1629 } 1630 /* c8 ignore stop */ 1631 let len = entries.length; 1632 if (!len) 1633 return cb(); 1634 const next = () => { 1635 if (--len === 0) { 1636 cb(); 1637 } 1638 }; 1639 for (const e of entries) { 1640 if (!filter || filter(e)) { 1641 results.push(withFileTypes ? e : e.fullpath()); 1642 } 1643 if (follow && e.isSymbolicLink()) { 1644 e.realpath() 1645 .then(r => (r?.isUnknown() ? r.lstat() : r)) 1646 .then(r => r?.shouldWalk(dirs, walkFilter) ? walk(r, next) : next()); 1647 } 1648 else { 1649 if (e.shouldWalk(dirs, walkFilter)) { 1650 walk(e, next); 1651 } 1652 else { 1653 next(); 1654 } 1655 } 1656 } 1657 }, true); // zalgooooooo 1658 }; 1659 const start = entry; 1660 return new Promise((res, rej) => { 1661 walk(start, er => { 1662 /* c8 ignore start */ 1663 if (er) 1664 return rej(er); 1665 /* c8 ignore stop */ 1666 res(results); 1667 }); 1668 }); 1669 } 1670 walkSync(entry = this.cwd, opts = {}) { 1671 if (typeof entry === 'string') { 1672 entry = this.cwd.resolve(entry); 1673 } 1674 else if (!(entry instanceof PathBase)) { 1675 opts = entry; 1676 entry = this.cwd; 1677 } 1678 const { withFileTypes = true, follow = false, filter, walkFilter, } = opts; 1679 const results = []; 1680 if (!filter || filter(entry)) { 1681 results.push(withFileTypes ? entry : entry.fullpath()); 1682 } 1683 const dirs = new Set([entry]); 1684 for (const dir of dirs) { 1685 const entries = dir.readdirSync(); 1686 for (const e of entries) { 1687 if (!filter || filter(e)) { 1688 results.push(withFileTypes ? e : e.fullpath()); 1689 } 1690 let r = e; 1691 if (e.isSymbolicLink()) { 1692 if (!(follow && (r = e.realpathSync()))) 1693 continue; 1694 if (r.isUnknown()) 1695 r.lstatSync(); 1696 } 1697 if (r.shouldWalk(dirs, walkFilter)) { 1698 dirs.add(r); 1699 } 1700 } 1701 } 1702 return results; 1703 } 1704 /** 1705 * Support for `for await` 1706 * 1707 * Alias for {@link PathScurryBase.iterate} 1708 * 1709 * Note: As of Node 19, this is very slow, compared to other methods of 1710 * walking. Consider using {@link PathScurryBase.stream} if memory overhead 1711 * and backpressure are concerns, or {@link PathScurryBase.walk} if not. 1712 */ 1713 [Symbol.asyncIterator]() { 1714 return this.iterate(); 1715 } 1716 iterate(entry = this.cwd, options = {}) { 1717 // iterating async over the stream is significantly more performant, 1718 // especially in the warm-cache scenario, because it buffers up directory 1719 // entries in the background instead of waiting for a yield for each one. 1720 if (typeof entry === 'string') { 1721 entry = this.cwd.resolve(entry); 1722 } 1723 else if (!(entry instanceof PathBase)) { 1724 options = entry; 1725 entry = this.cwd; 1726 } 1727 return this.stream(entry, options)[Symbol.asyncIterator](); 1728 } 1729 /** 1730 * Iterating over a PathScurry performs a synchronous walk. 1731 * 1732 * Alias for {@link PathScurryBase.iterateSync} 1733 */ 1734 [Symbol.iterator]() { 1735 return this.iterateSync(); 1736 } 1737 *iterateSync(entry = this.cwd, opts = {}) { 1738 if (typeof entry === 'string') { 1739 entry = this.cwd.resolve(entry); 1740 } 1741 else if (!(entry instanceof PathBase)) { 1742 opts = entry; 1743 entry = this.cwd; 1744 } 1745 const { withFileTypes = true, follow = false, filter, walkFilter, } = opts; 1746 if (!filter || filter(entry)) { 1747 yield withFileTypes ? entry : entry.fullpath(); 1748 } 1749 const dirs = new Set([entry]); 1750 for (const dir of dirs) { 1751 const entries = dir.readdirSync(); 1752 for (const e of entries) { 1753 if (!filter || filter(e)) { 1754 yield withFileTypes ? e : e.fullpath(); 1755 } 1756 let r = e; 1757 if (e.isSymbolicLink()) { 1758 if (!(follow && (r = e.realpathSync()))) 1759 continue; 1760 if (r.isUnknown()) 1761 r.lstatSync(); 1762 } 1763 if (r.shouldWalk(dirs, walkFilter)) { 1764 dirs.add(r); 1765 } 1766 } 1767 } 1768 } 1769 stream(entry = this.cwd, opts = {}) { 1770 if (typeof entry === 'string') { 1771 entry = this.cwd.resolve(entry); 1772 } 1773 else if (!(entry instanceof PathBase)) { 1774 opts = entry; 1775 entry = this.cwd; 1776 } 1777 const { withFileTypes = true, follow = false, filter, walkFilter, } = opts; 1778 const results = new minipass_1.Minipass({ objectMode: true }); 1779 if (!filter || filter(entry)) { 1780 results.write(withFileTypes ? entry : entry.fullpath()); 1781 } 1782 const dirs = new Set(); 1783 const queue = [entry]; 1784 let processing = 0; 1785 const process = () => { 1786 let paused = false; 1787 while (!paused) { 1788 const dir = queue.shift(); 1789 if (!dir) { 1790 if (processing === 0) 1791 results.end(); 1792 return; 1793 } 1794 processing++; 1795 dirs.add(dir); 1796 const onReaddir = (er, entries, didRealpaths = false) => { 1797 /* c8 ignore start */ 1798 if (er) 1799 return results.emit('error', er); 1800 /* c8 ignore stop */ 1801 if (follow && !didRealpaths) { 1802 const promises = []; 1803 for (const e of entries) { 1804 if (e.isSymbolicLink()) { 1805 promises.push(e 1806 .realpath() 1807 .then((r) => r?.isUnknown() ? r.lstat() : r)); 1808 } 1809 } 1810 if (promises.length) { 1811 Promise.all(promises).then(() => onReaddir(null, entries, true)); 1812 return; 1813 } 1814 } 1815 for (const e of entries) { 1816 if (e && (!filter || filter(e))) { 1817 if (!results.write(withFileTypes ? e : e.fullpath())) { 1818 paused = true; 1819 } 1820 } 1821 } 1822 processing--; 1823 for (const e of entries) { 1824 const r = e.realpathCached() || e; 1825 if (r.shouldWalk(dirs, walkFilter)) { 1826 queue.push(r); 1827 } 1828 } 1829 if (paused && !results.flowing) { 1830 results.once('drain', process); 1831 } 1832 else if (!sync) { 1833 process(); 1834 } 1835 }; 1836 // zalgo containment 1837 let sync = true; 1838 dir.readdirCB(onReaddir, true); 1839 sync = false; 1840 } 1841 }; 1842 process(); 1843 return results; 1844 } 1845 streamSync(entry = this.cwd, opts = {}) { 1846 if (typeof entry === 'string') { 1847 entry = this.cwd.resolve(entry); 1848 } 1849 else if (!(entry instanceof PathBase)) { 1850 opts = entry; 1851 entry = this.cwd; 1852 } 1853 const { withFileTypes = true, follow = false, filter, walkFilter, } = opts; 1854 const results = new minipass_1.Minipass({ objectMode: true }); 1855 const dirs = new Set(); 1856 if (!filter || filter(entry)) { 1857 results.write(withFileTypes ? entry : entry.fullpath()); 1858 } 1859 const queue = [entry]; 1860 let processing = 0; 1861 const process = () => { 1862 let paused = false; 1863 while (!paused) { 1864 const dir = queue.shift(); 1865 if (!dir) { 1866 if (processing === 0) 1867 results.end(); 1868 return; 1869 } 1870 processing++; 1871 dirs.add(dir); 1872 const entries = dir.readdirSync(); 1873 for (const e of entries) { 1874 if (!filter || filter(e)) { 1875 if (!results.write(withFileTypes ? e : e.fullpath())) { 1876 paused = true; 1877 } 1878 } 1879 } 1880 processing--; 1881 for (const e of entries) { 1882 let r = e; 1883 if (e.isSymbolicLink()) { 1884 if (!(follow && (r = e.realpathSync()))) 1885 continue; 1886 if (r.isUnknown()) 1887 r.lstatSync(); 1888 } 1889 if (r.shouldWalk(dirs, walkFilter)) { 1890 queue.push(r); 1891 } 1892 } 1893 } 1894 if (paused && !results.flowing) 1895 results.once('drain', process); 1896 }; 1897 process(); 1898 return results; 1899 } 1900 chdir(path = this.cwd) { 1901 const oldCwd = this.cwd; 1902 this.cwd = typeof path === 'string' ? this.cwd.resolve(path) : path; 1903 this.cwd[setAsCwd](oldCwd); 1904 } 1905} 1906exports.PathScurryBase = PathScurryBase; 1907/** 1908 * Windows implementation of {@link PathScurryBase} 1909 * 1910 * Defaults to case insensitve, uses `'\\'` to generate path strings. Uses 1911 * {@link PathWin32} for Path objects. 1912 */ 1913class PathScurryWin32 extends PathScurryBase { 1914 /** 1915 * separator for generating path strings 1916 */ 1917 sep = '\\'; 1918 constructor(cwd = process.cwd(), opts = {}) { 1919 const { nocase = true } = opts; 1920 super(cwd, path_1.win32, '\\', { ...opts, nocase }); 1921 this.nocase = nocase; 1922 for (let p = this.cwd; p; p = p.parent) { 1923 p.nocase = this.nocase; 1924 } 1925 } 1926 /** 1927 * @internal 1928 */ 1929 parseRootPath(dir) { 1930 // if the path starts with a single separator, it's not a UNC, and we'll 1931 // just get separator as the root, and driveFromUNC will return \ 1932 // In that case, mount \ on the root from the cwd. 1933 return path_1.win32.parse(dir).root.toUpperCase(); 1934 } 1935 /** 1936 * @internal 1937 */ 1938 newRoot(fs) { 1939 return new PathWin32(this.rootPath, IFDIR, undefined, this.roots, this.nocase, this.childrenCache(), { fs }); 1940 } 1941 /** 1942 * Return true if the provided path string is an absolute path 1943 */ 1944 isAbsolute(p) { 1945 return (p.startsWith('/') || p.startsWith('\\') || /^[a-z]:(\/|\\)/i.test(p)); 1946 } 1947} 1948exports.PathScurryWin32 = PathScurryWin32; 1949/** 1950 * {@link PathScurryBase} implementation for all posix systems other than Darwin. 1951 * 1952 * Defaults to case-sensitive matching, uses `'/'` to generate path strings. 1953 * 1954 * Uses {@link PathPosix} for Path objects. 1955 */ 1956class PathScurryPosix extends PathScurryBase { 1957 /** 1958 * separator for generating path strings 1959 */ 1960 sep = '/'; 1961 constructor(cwd = process.cwd(), opts = {}) { 1962 const { nocase = false } = opts; 1963 super(cwd, path_1.posix, '/', { ...opts, nocase }); 1964 this.nocase = nocase; 1965 } 1966 /** 1967 * @internal 1968 */ 1969 parseRootPath(_dir) { 1970 return '/'; 1971 } 1972 /** 1973 * @internal 1974 */ 1975 newRoot(fs) { 1976 return new PathPosix(this.rootPath, IFDIR, undefined, this.roots, this.nocase, this.childrenCache(), { fs }); 1977 } 1978 /** 1979 * Return true if the provided path string is an absolute path 1980 */ 1981 isAbsolute(p) { 1982 return p.startsWith('/'); 1983 } 1984} 1985exports.PathScurryPosix = PathScurryPosix; 1986/** 1987 * {@link PathScurryBase} implementation for Darwin (macOS) systems. 1988 * 1989 * Defaults to case-insensitive matching, uses `'/'` for generating path 1990 * strings. 1991 * 1992 * Uses {@link PathPosix} for Path objects. 1993 */ 1994class PathScurryDarwin extends PathScurryPosix { 1995 constructor(cwd = process.cwd(), opts = {}) { 1996 const { nocase = true } = opts; 1997 super(cwd, { ...opts, nocase }); 1998 } 1999} 2000exports.PathScurryDarwin = PathScurryDarwin; 2001/** 2002 * Default {@link PathBase} implementation for the current platform. 2003 * 2004 * {@link PathWin32} on Windows systems, {@link PathPosix} on all others. 2005 */ 2006exports.Path = process.platform === 'win32' ? PathWin32 : PathPosix; 2007/** 2008 * Default {@link PathScurryBase} implementation for the current platform. 2009 * 2010 * {@link PathScurryWin32} on Windows systems, {@link PathScurryDarwin} on 2011 * Darwin (macOS) systems, {@link PathScurryPosix} on all others. 2012 */ 2013exports.PathScurry = process.platform === 'win32' 2014 ? PathScurryWin32 2015 : process.platform === 'darwin' 2016 ? PathScurryDarwin 2017 : PathScurryPosix; 2018//# sourceMappingURL=index.js.map