index.js 63 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979
  1. import { LRUCache } from 'lru-cache';
  2. import { posix, win32 } from 'node:path';
  3. import { fileURLToPath } from 'node:url';
  4. import { lstatSync, readdir as readdirCB, readdirSync, readlinkSync, realpathSync as rps, } from 'fs';
  5. import * as actualFS from 'node:fs';
  6. const realpathSync = rps.native;
  7. // TODO: test perf of fs/promises realpath vs realpathCB,
  8. // since the promises one uses realpath.native
  9. import { lstat, readdir, readlink, realpath } from 'node:fs/promises';
  10. import { Minipass } from 'minipass';
  11. const defaultFS = {
  12. lstatSync,
  13. readdir: readdirCB,
  14. readdirSync,
  15. readlinkSync,
  16. realpathSync,
  17. promises: {
  18. lstat,
  19. readdir,
  20. readlink,
  21. realpath,
  22. },
  23. };
  24. // if they just gave us require('fs') then use our default
  25. const fsFromOption = (fsOption) => !fsOption || fsOption === defaultFS || fsOption === actualFS ?
  26. defaultFS
  27. : {
  28. ...defaultFS,
  29. ...fsOption,
  30. promises: {
  31. ...defaultFS.promises,
  32. ...(fsOption.promises || {}),
  33. },
  34. };
  35. // turn something like //?/c:/ into c:\
  36. const uncDriveRegexp = /^\\\\\?\\([a-z]:)\\?$/i;
  37. const uncToDrive = (rootPath) => rootPath.replace(/\//g, '\\').replace(uncDriveRegexp, '$1\\');
  38. // windows paths are separated by either / or \
  39. const eitherSep = /[\\\/]/;
  40. const UNKNOWN = 0; // may not even exist, for all we know
  41. const IFIFO = 0b0001;
  42. const IFCHR = 0b0010;
  43. const IFDIR = 0b0100;
  44. const IFBLK = 0b0110;
  45. const IFREG = 0b1000;
  46. const IFLNK = 0b1010;
  47. const IFSOCK = 0b1100;
  48. const IFMT = 0b1111;
  49. // mask to unset low 4 bits
  50. const IFMT_UNKNOWN = ~IFMT;
  51. // set after successfully calling readdir() and getting entries.
  52. const READDIR_CALLED = 0b0000_0001_0000;
  53. // set after a successful lstat()
  54. const LSTAT_CALLED = 0b0000_0010_0000;
  55. // set if an entry (or one of its parents) is definitely not a dir
  56. const ENOTDIR = 0b0000_0100_0000;
  57. // set if an entry (or one of its parents) does not exist
  58. // (can also be set on lstat errors like EACCES or ENAMETOOLONG)
  59. const ENOENT = 0b0000_1000_0000;
  60. // cannot have child entries -- also verify &IFMT is either IFDIR or IFLNK
  61. // set if we fail to readlink
  62. const ENOREADLINK = 0b0001_0000_0000;
  63. // set if we know realpath() will fail
  64. const ENOREALPATH = 0b0010_0000_0000;
  65. const ENOCHILD = ENOTDIR | ENOENT | ENOREALPATH;
  66. const TYPEMASK = 0b0011_1111_1111;
  67. const entToType = (s) => s.isFile() ? IFREG
  68. : s.isDirectory() ? IFDIR
  69. : s.isSymbolicLink() ? IFLNK
  70. : s.isCharacterDevice() ? IFCHR
  71. : s.isBlockDevice() ? IFBLK
  72. : s.isSocket() ? IFSOCK
  73. : s.isFIFO() ? IFIFO
  74. : UNKNOWN;
  75. // normalize unicode path names
  76. const normalizeCache = new Map();
  77. const normalize = (s) => {
  78. const c = normalizeCache.get(s);
  79. if (c)
  80. return c;
  81. const n = s.normalize('NFKD');
  82. normalizeCache.set(s, n);
  83. return n;
  84. };
  85. const normalizeNocaseCache = new Map();
  86. const normalizeNocase = (s) => {
  87. const c = normalizeNocaseCache.get(s);
  88. if (c)
  89. return c;
  90. const n = normalize(s.toLowerCase());
  91. normalizeNocaseCache.set(s, n);
  92. return n;
  93. };
  94. /**
  95. * An LRUCache for storing resolved path strings or Path objects.
  96. * @internal
  97. */
  98. export class ResolveCache extends LRUCache {
  99. constructor() {
  100. super({ max: 256 });
  101. }
  102. }
  103. // In order to prevent blowing out the js heap by allocating hundreds of
  104. // thousands of Path entries when walking extremely large trees, the "children"
  105. // in this tree are represented by storing an array of Path entries in an
  106. // LRUCache, indexed by the parent. At any time, Path.children() may return an
  107. // empty array, indicating that it doesn't know about any of its children, and
  108. // thus has to rebuild that cache. This is fine, it just means that we don't
  109. // benefit as much from having the cached entries, but huge directory walks
  110. // don't blow out the stack, and smaller ones are still as fast as possible.
  111. //
  112. //It does impose some complexity when building up the readdir data, because we
  113. //need to pass a reference to the children array that we started with.
  114. /**
  115. * an LRUCache for storing child entries.
  116. * @internal
  117. */
  118. export class ChildrenCache extends LRUCache {
  119. constructor(maxSize = 16 * 1024) {
  120. super({
  121. maxSize,
  122. // parent + children
  123. sizeCalculation: a => a.length + 1,
  124. });
  125. }
  126. }
  127. const setAsCwd = Symbol('PathScurry setAsCwd');
  128. /**
  129. * Path objects are sort of like a super-powered
  130. * {@link https://nodejs.org/docs/latest/api/fs.html#class-fsdirent fs.Dirent}
  131. *
  132. * Each one represents a single filesystem entry on disk, which may or may not
  133. * exist. It includes methods for reading various types of information via
  134. * lstat, readlink, and readdir, and caches all information to the greatest
  135. * degree possible.
  136. *
  137. * Note that fs operations that would normally throw will instead return an
  138. * "empty" value. This is in order to prevent excessive overhead from error
  139. * stack traces.
  140. */
  141. export class PathBase {
  142. /**
  143. * the basename of this path
  144. *
  145. * **Important**: *always* test the path name against any test string
  146. * usingthe {@link isNamed} method, and not by directly comparing this
  147. * string. Otherwise, unicode path strings that the system sees as identical
  148. * will not be properly treated as the same path, leading to incorrect
  149. * behavior and possible security issues.
  150. */
  151. name;
  152. /**
  153. * the Path entry corresponding to the path root.
  154. *
  155. * @internal
  156. */
  157. root;
  158. /**
  159. * All roots found within the current PathScurry family
  160. *
  161. * @internal
  162. */
  163. roots;
  164. /**
  165. * a reference to the parent path, or undefined in the case of root entries
  166. *
  167. * @internal
  168. */
  169. parent;
  170. /**
  171. * boolean indicating whether paths are compared case-insensitively
  172. * @internal
  173. */
  174. nocase;
  175. /**
  176. * boolean indicating that this path is the current working directory
  177. * of the PathScurry collection that contains it.
  178. */
  179. isCWD = false;
  180. // potential default fs override
  181. #fs;
  182. // Stats fields
  183. #dev;
  184. get dev() {
  185. return this.#dev;
  186. }
  187. #mode;
  188. get mode() {
  189. return this.#mode;
  190. }
  191. #nlink;
  192. get nlink() {
  193. return this.#nlink;
  194. }
  195. #uid;
  196. get uid() {
  197. return this.#uid;
  198. }
  199. #gid;
  200. get gid() {
  201. return this.#gid;
  202. }
  203. #rdev;
  204. get rdev() {
  205. return this.#rdev;
  206. }
  207. #blksize;
  208. get blksize() {
  209. return this.#blksize;
  210. }
  211. #ino;
  212. get ino() {
  213. return this.#ino;
  214. }
  215. #size;
  216. get size() {
  217. return this.#size;
  218. }
  219. #blocks;
  220. get blocks() {
  221. return this.#blocks;
  222. }
  223. #atimeMs;
  224. get atimeMs() {
  225. return this.#atimeMs;
  226. }
  227. #mtimeMs;
  228. get mtimeMs() {
  229. return this.#mtimeMs;
  230. }
  231. #ctimeMs;
  232. get ctimeMs() {
  233. return this.#ctimeMs;
  234. }
  235. #birthtimeMs;
  236. get birthtimeMs() {
  237. return this.#birthtimeMs;
  238. }
  239. #atime;
  240. get atime() {
  241. return this.#atime;
  242. }
  243. #mtime;
  244. get mtime() {
  245. return this.#mtime;
  246. }
  247. #ctime;
  248. get ctime() {
  249. return this.#ctime;
  250. }
  251. #birthtime;
  252. get birthtime() {
  253. return this.#birthtime;
  254. }
  255. #matchName;
  256. #depth;
  257. #fullpath;
  258. #fullpathPosix;
  259. #relative;
  260. #relativePosix;
  261. #type;
  262. #children;
  263. #linkTarget;
  264. #realpath;
  265. /**
  266. * This property is for compatibility with the Dirent class as of
  267. * Node v20, where Dirent['parentPath'] refers to the path of the
  268. * directory that was passed to readdir. For root entries, it's the path
  269. * to the entry itself.
  270. */
  271. get parentPath() {
  272. return (this.parent || this).fullpath();
  273. }
  274. /**
  275. * Deprecated alias for Dirent['parentPath'] Somewhat counterintuitively,
  276. * this property refers to the *parent* path, not the path object itself.
  277. */
  278. get path() {
  279. return this.parentPath;
  280. }
  281. /**
  282. * Do not create new Path objects directly. They should always be accessed
  283. * via the PathScurry class or other methods on the Path class.
  284. *
  285. * @internal
  286. */
  287. constructor(name, type = UNKNOWN, root, roots, nocase, children, opts) {
  288. this.name = name;
  289. this.#matchName = nocase ? normalizeNocase(name) : normalize(name);
  290. this.#type = type & TYPEMASK;
  291. this.nocase = nocase;
  292. this.roots = roots;
  293. this.root = root || this;
  294. this.#children = children;
  295. this.#fullpath = opts.fullpath;
  296. this.#relative = opts.relative;
  297. this.#relativePosix = opts.relativePosix;
  298. this.parent = opts.parent;
  299. if (this.parent) {
  300. this.#fs = this.parent.#fs;
  301. }
  302. else {
  303. this.#fs = fsFromOption(opts.fs);
  304. }
  305. }
  306. /**
  307. * Returns the depth of the Path object from its root.
  308. *
  309. * For example, a path at `/foo/bar` would have a depth of 2.
  310. */
  311. depth() {
  312. if (this.#depth !== undefined)
  313. return this.#depth;
  314. if (!this.parent)
  315. return (this.#depth = 0);
  316. return (this.#depth = this.parent.depth() + 1);
  317. }
  318. /**
  319. * @internal
  320. */
  321. childrenCache() {
  322. return this.#children;
  323. }
  324. /**
  325. * Get the Path object referenced by the string path, resolved from this Path
  326. */
  327. resolve(path) {
  328. if (!path) {
  329. return this;
  330. }
  331. const rootPath = this.getRootString(path);
  332. const dir = path.substring(rootPath.length);
  333. const dirParts = dir.split(this.splitSep);
  334. const result = rootPath ?
  335. this.getRoot(rootPath).#resolveParts(dirParts)
  336. : this.#resolveParts(dirParts);
  337. return result;
  338. }
  339. #resolveParts(dirParts) {
  340. let p = this;
  341. for (const part of dirParts) {
  342. p = p.child(part);
  343. }
  344. return p;
  345. }
  346. /**
  347. * Returns the cached children Path objects, if still available. If they
  348. * have fallen out of the cache, then returns an empty array, and resets the
  349. * READDIR_CALLED bit, so that future calls to readdir() will require an fs
  350. * lookup.
  351. *
  352. * @internal
  353. */
  354. children() {
  355. const cached = this.#children.get(this);
  356. if (cached) {
  357. return cached;
  358. }
  359. const children = Object.assign([], { provisional: 0 });
  360. this.#children.set(this, children);
  361. this.#type &= ~READDIR_CALLED;
  362. return children;
  363. }
  364. /**
  365. * Resolves a path portion and returns or creates the child Path.
  366. *
  367. * Returns `this` if pathPart is `''` or `'.'`, or `parent` if pathPart is
  368. * `'..'`.
  369. *
  370. * This should not be called directly. If `pathPart` contains any path
  371. * separators, it will lead to unsafe undefined behavior.
  372. *
  373. * Use `Path.resolve()` instead.
  374. *
  375. * @internal
  376. */
  377. child(pathPart, opts) {
  378. if (pathPart === '' || pathPart === '.') {
  379. return this;
  380. }
  381. if (pathPart === '..') {
  382. return this.parent || this;
  383. }
  384. // find the child
  385. const children = this.children();
  386. const name = this.nocase ? normalizeNocase(pathPart) : normalize(pathPart);
  387. for (const p of children) {
  388. if (p.#matchName === name) {
  389. return p;
  390. }
  391. }
  392. // didn't find it, create provisional child, since it might not
  393. // actually exist. If we know the parent isn't a dir, then
  394. // in fact it CAN'T exist.
  395. const s = this.parent ? this.sep : '';
  396. const fullpath = this.#fullpath ? this.#fullpath + s + pathPart : undefined;
  397. const pchild = this.newChild(pathPart, UNKNOWN, {
  398. ...opts,
  399. parent: this,
  400. fullpath,
  401. });
  402. if (!this.canReaddir()) {
  403. pchild.#type |= ENOENT;
  404. }
  405. // don't have to update provisional, because if we have real children,
  406. // then provisional is set to children.length, otherwise a lower number
  407. children.push(pchild);
  408. return pchild;
  409. }
  410. /**
  411. * The relative path from the cwd. If it does not share an ancestor with
  412. * the cwd, then this ends up being equivalent to the fullpath()
  413. */
  414. relative() {
  415. if (this.isCWD)
  416. return '';
  417. if (this.#relative !== undefined) {
  418. return this.#relative;
  419. }
  420. const name = this.name;
  421. const p = this.parent;
  422. if (!p) {
  423. return (this.#relative = this.name);
  424. }
  425. const pv = p.relative();
  426. return pv + (!pv || !p.parent ? '' : this.sep) + name;
  427. }
  428. /**
  429. * The relative path from the cwd, using / as the path separator.
  430. * If it does not share an ancestor with
  431. * the cwd, then this ends up being equivalent to the fullpathPosix()
  432. * On posix systems, this is identical to relative().
  433. */
  434. relativePosix() {
  435. if (this.sep === '/')
  436. return this.relative();
  437. if (this.isCWD)
  438. return '';
  439. if (this.#relativePosix !== undefined)
  440. return this.#relativePosix;
  441. const name = this.name;
  442. const p = this.parent;
  443. if (!p) {
  444. return (this.#relativePosix = this.fullpathPosix());
  445. }
  446. const pv = p.relativePosix();
  447. return pv + (!pv || !p.parent ? '' : '/') + name;
  448. }
  449. /**
  450. * The fully resolved path string for this Path entry
  451. */
  452. fullpath() {
  453. if (this.#fullpath !== undefined) {
  454. return this.#fullpath;
  455. }
  456. const name = this.name;
  457. const p = this.parent;
  458. if (!p) {
  459. return (this.#fullpath = this.name);
  460. }
  461. const pv = p.fullpath();
  462. const fp = pv + (!p.parent ? '' : this.sep) + name;
  463. return (this.#fullpath = fp);
  464. }
  465. /**
  466. * On platforms other than windows, this is identical to fullpath.
  467. *
  468. * On windows, this is overridden to return the forward-slash form of the
  469. * full UNC path.
  470. */
  471. fullpathPosix() {
  472. if (this.#fullpathPosix !== undefined)
  473. return this.#fullpathPosix;
  474. if (this.sep === '/')
  475. return (this.#fullpathPosix = this.fullpath());
  476. if (!this.parent) {
  477. const p = this.fullpath().replace(/\\/g, '/');
  478. if (/^[a-z]:\//i.test(p)) {
  479. return (this.#fullpathPosix = `//?/${p}`);
  480. }
  481. else {
  482. return (this.#fullpathPosix = p);
  483. }
  484. }
  485. const p = this.parent;
  486. const pfpp = p.fullpathPosix();
  487. const fpp = pfpp + (!pfpp || !p.parent ? '' : '/') + this.name;
  488. return (this.#fullpathPosix = fpp);
  489. }
  490. /**
  491. * Is the Path of an unknown type?
  492. *
  493. * Note that we might know *something* about it if there has been a previous
  494. * filesystem operation, for example that it does not exist, or is not a
  495. * link, or whether it has child entries.
  496. */
  497. isUnknown() {
  498. return (this.#type & IFMT) === UNKNOWN;
  499. }
  500. isType(type) {
  501. return this[`is${type}`]();
  502. }
  503. getType() {
  504. return (this.isUnknown() ? 'Unknown'
  505. : this.isDirectory() ? 'Directory'
  506. : this.isFile() ? 'File'
  507. : this.isSymbolicLink() ? 'SymbolicLink'
  508. : this.isFIFO() ? 'FIFO'
  509. : this.isCharacterDevice() ? 'CharacterDevice'
  510. : this.isBlockDevice() ? 'BlockDevice'
  511. : /* c8 ignore start */ this.isSocket() ? 'Socket'
  512. : 'Unknown');
  513. /* c8 ignore stop */
  514. }
  515. /**
  516. * Is the Path a regular file?
  517. */
  518. isFile() {
  519. return (this.#type & IFMT) === IFREG;
  520. }
  521. /**
  522. * Is the Path a directory?
  523. */
  524. isDirectory() {
  525. return (this.#type & IFMT) === IFDIR;
  526. }
  527. /**
  528. * Is the path a character device?
  529. */
  530. isCharacterDevice() {
  531. return (this.#type & IFMT) === IFCHR;
  532. }
  533. /**
  534. * Is the path a block device?
  535. */
  536. isBlockDevice() {
  537. return (this.#type & IFMT) === IFBLK;
  538. }
  539. /**
  540. * Is the path a FIFO pipe?
  541. */
  542. isFIFO() {
  543. return (this.#type & IFMT) === IFIFO;
  544. }
  545. /**
  546. * Is the path a socket?
  547. */
  548. isSocket() {
  549. return (this.#type & IFMT) === IFSOCK;
  550. }
  551. /**
  552. * Is the path a symbolic link?
  553. */
  554. isSymbolicLink() {
  555. return (this.#type & IFLNK) === IFLNK;
  556. }
  557. /**
  558. * Return the entry if it has been subject of a successful lstat, or
  559. * undefined otherwise.
  560. *
  561. * Does not read the filesystem, so an undefined result *could* simply
  562. * mean that we haven't called lstat on it.
  563. */
  564. lstatCached() {
  565. return this.#type & LSTAT_CALLED ? this : undefined;
  566. }
  567. /**
  568. * Return the cached link target if the entry has been the subject of a
  569. * successful readlink, or undefined otherwise.
  570. *
  571. * Does not read the filesystem, so an undefined result *could* just mean we
  572. * don't have any cached data. Only use it if you are very sure that a
  573. * readlink() has been called at some point.
  574. */
  575. readlinkCached() {
  576. return this.#linkTarget;
  577. }
  578. /**
  579. * Returns the cached realpath target if the entry has been the subject
  580. * of a successful realpath, or undefined otherwise.
  581. *
  582. * Does not read the filesystem, so an undefined result *could* just mean we
  583. * don't have any cached data. Only use it if you are very sure that a
  584. * realpath() has been called at some point.
  585. */
  586. realpathCached() {
  587. return this.#realpath;
  588. }
  589. /**
  590. * Returns the cached child Path entries array if the entry has been the
  591. * subject of a successful readdir(), or [] otherwise.
  592. *
  593. * Does not read the filesystem, so an empty array *could* just mean we
  594. * don't have any cached data. Only use it if you are very sure that a
  595. * readdir() has been called recently enough to still be valid.
  596. */
  597. readdirCached() {
  598. const children = this.children();
  599. return children.slice(0, children.provisional);
  600. }
  601. /**
  602. * Return true if it's worth trying to readlink. Ie, we don't (yet) have
  603. * any indication that readlink will definitely fail.
  604. *
  605. * Returns false if the path is known to not be a symlink, if a previous
  606. * readlink failed, or if the entry does not exist.
  607. */
  608. canReadlink() {
  609. if (this.#linkTarget)
  610. return true;
  611. if (!this.parent)
  612. return false;
  613. // cases where it cannot possibly succeed
  614. const ifmt = this.#type & IFMT;
  615. return !((ifmt !== UNKNOWN && ifmt !== IFLNK) ||
  616. this.#type & ENOREADLINK ||
  617. this.#type & ENOENT);
  618. }
  619. /**
  620. * Return true if readdir has previously been successfully called on this
  621. * path, indicating that cachedReaddir() is likely valid.
  622. */
  623. calledReaddir() {
  624. return !!(this.#type & READDIR_CALLED);
  625. }
  626. /**
  627. * Returns true if the path is known to not exist. That is, a previous lstat
  628. * or readdir failed to verify its existence when that would have been
  629. * expected, or a parent entry was marked either enoent or enotdir.
  630. */
  631. isENOENT() {
  632. return !!(this.#type & ENOENT);
  633. }
  634. /**
  635. * Return true if the path is a match for the given path name. This handles
  636. * case sensitivity and unicode normalization.
  637. *
  638. * Note: even on case-sensitive systems, it is **not** safe to test the
  639. * equality of the `.name` property to determine whether a given pathname
  640. * matches, due to unicode normalization mismatches.
  641. *
  642. * Always use this method instead of testing the `path.name` property
  643. * directly.
  644. */
  645. isNamed(n) {
  646. return !this.nocase ?
  647. this.#matchName === normalize(n)
  648. : this.#matchName === normalizeNocase(n);
  649. }
  650. /**
  651. * Return the Path object corresponding to the target of a symbolic link.
  652. *
  653. * If the Path is not a symbolic link, or if the readlink call fails for any
  654. * reason, `undefined` is returned.
  655. *
  656. * Result is cached, and thus may be outdated if the filesystem is mutated.
  657. */
  658. async readlink() {
  659. const target = this.#linkTarget;
  660. if (target) {
  661. return target;
  662. }
  663. if (!this.canReadlink()) {
  664. return undefined;
  665. }
  666. /* c8 ignore start */
  667. // already covered by the canReadlink test, here for ts grumples
  668. if (!this.parent) {
  669. return undefined;
  670. }
  671. /* c8 ignore stop */
  672. try {
  673. const read = await this.#fs.promises.readlink(this.fullpath());
  674. const linkTarget = (await this.parent.realpath())?.resolve(read);
  675. if (linkTarget) {
  676. return (this.#linkTarget = linkTarget);
  677. }
  678. }
  679. catch (er) {
  680. this.#readlinkFail(er.code);
  681. return undefined;
  682. }
  683. }
  684. /**
  685. * Synchronous {@link PathBase.readlink}
  686. */
  687. readlinkSync() {
  688. const target = this.#linkTarget;
  689. if (target) {
  690. return target;
  691. }
  692. if (!this.canReadlink()) {
  693. return undefined;
  694. }
  695. /* c8 ignore start */
  696. // already covered by the canReadlink test, here for ts grumples
  697. if (!this.parent) {
  698. return undefined;
  699. }
  700. /* c8 ignore stop */
  701. try {
  702. const read = this.#fs.readlinkSync(this.fullpath());
  703. const linkTarget = this.parent.realpathSync()?.resolve(read);
  704. if (linkTarget) {
  705. return (this.#linkTarget = linkTarget);
  706. }
  707. }
  708. catch (er) {
  709. this.#readlinkFail(er.code);
  710. return undefined;
  711. }
  712. }
  713. #readdirSuccess(children) {
  714. // succeeded, mark readdir called bit
  715. this.#type |= READDIR_CALLED;
  716. // mark all remaining provisional children as ENOENT
  717. for (let p = children.provisional; p < children.length; p++) {
  718. const c = children[p];
  719. if (c)
  720. c.#markENOENT();
  721. }
  722. }
  723. #markENOENT() {
  724. // mark as UNKNOWN and ENOENT
  725. if (this.#type & ENOENT)
  726. return;
  727. this.#type = (this.#type | ENOENT) & IFMT_UNKNOWN;
  728. this.#markChildrenENOENT();
  729. }
  730. #markChildrenENOENT() {
  731. // all children are provisional and do not exist
  732. const children = this.children();
  733. children.provisional = 0;
  734. for (const p of children) {
  735. p.#markENOENT();
  736. }
  737. }
  738. #markENOREALPATH() {
  739. this.#type |= ENOREALPATH;
  740. this.#markENOTDIR();
  741. }
  742. // save the information when we know the entry is not a dir
  743. #markENOTDIR() {
  744. // entry is not a directory, so any children can't exist.
  745. // this *should* be impossible, since any children created
  746. // after it's been marked ENOTDIR should be marked ENOENT,
  747. // so it won't even get to this point.
  748. /* c8 ignore start */
  749. if (this.#type & ENOTDIR)
  750. return;
  751. /* c8 ignore stop */
  752. let t = this.#type;
  753. // this could happen if we stat a dir, then delete it,
  754. // then try to read it or one of its children.
  755. if ((t & IFMT) === IFDIR)
  756. t &= IFMT_UNKNOWN;
  757. this.#type = t | ENOTDIR;
  758. this.#markChildrenENOENT();
  759. }
  760. #readdirFail(code = '') {
  761. // markENOTDIR and markENOENT also set provisional=0
  762. if (code === 'ENOTDIR' || code === 'EPERM') {
  763. this.#markENOTDIR();
  764. }
  765. else if (code === 'ENOENT') {
  766. this.#markENOENT();
  767. }
  768. else {
  769. this.children().provisional = 0;
  770. }
  771. }
  772. #lstatFail(code = '') {
  773. // Windows just raises ENOENT in this case, disable for win CI
  774. /* c8 ignore start */
  775. if (code === 'ENOTDIR') {
  776. // already know it has a parent by this point
  777. const p = this.parent;
  778. p.#markENOTDIR();
  779. }
  780. else if (code === 'ENOENT') {
  781. /* c8 ignore stop */
  782. this.#markENOENT();
  783. }
  784. }
  785. #readlinkFail(code = '') {
  786. let ter = this.#type;
  787. ter |= ENOREADLINK;
  788. if (code === 'ENOENT')
  789. ter |= ENOENT;
  790. // windows gets a weird error when you try to readlink a file
  791. if (code === 'EINVAL' || code === 'UNKNOWN') {
  792. // exists, but not a symlink, we don't know WHAT it is, so remove
  793. // all IFMT bits.
  794. ter &= IFMT_UNKNOWN;
  795. }
  796. this.#type = ter;
  797. // windows just gets ENOENT in this case. We do cover the case,
  798. // just disabled because it's impossible on Windows CI
  799. /* c8 ignore start */
  800. if (code === 'ENOTDIR' && this.parent) {
  801. this.parent.#markENOTDIR();
  802. }
  803. /* c8 ignore stop */
  804. }
  805. #readdirAddChild(e, c) {
  806. return (this.#readdirMaybePromoteChild(e, c) ||
  807. this.#readdirAddNewChild(e, c));
  808. }
  809. #readdirAddNewChild(e, c) {
  810. // alloc new entry at head, so it's never provisional
  811. const type = entToType(e);
  812. const child = this.newChild(e.name, type, { parent: this });
  813. const ifmt = child.#type & IFMT;
  814. if (ifmt !== IFDIR && ifmt !== IFLNK && ifmt !== UNKNOWN) {
  815. child.#type |= ENOTDIR;
  816. }
  817. c.unshift(child);
  818. c.provisional++;
  819. return child;
  820. }
  821. #readdirMaybePromoteChild(e, c) {
  822. for (let p = c.provisional; p < c.length; p++) {
  823. const pchild = c[p];
  824. const name = this.nocase ? normalizeNocase(e.name) : normalize(e.name);
  825. if (name !== pchild.#matchName) {
  826. continue;
  827. }
  828. return this.#readdirPromoteChild(e, pchild, p, c);
  829. }
  830. }
  831. #readdirPromoteChild(e, p, index, c) {
  832. const v = p.name;
  833. // retain any other flags, but set ifmt from dirent
  834. p.#type = (p.#type & IFMT_UNKNOWN) | entToType(e);
  835. // case sensitivity fixing when we learn the true name.
  836. if (v !== e.name)
  837. p.name = e.name;
  838. // just advance provisional index (potentially off the list),
  839. // otherwise we have to splice/pop it out and re-insert at head
  840. if (index !== c.provisional) {
  841. if (index === c.length - 1)
  842. c.pop();
  843. else
  844. c.splice(index, 1);
  845. c.unshift(p);
  846. }
  847. c.provisional++;
  848. return p;
  849. }
  850. /**
  851. * Call lstat() on this Path, and update all known information that can be
  852. * determined.
  853. *
  854. * Note that unlike `fs.lstat()`, the returned value does not contain some
  855. * information, such as `mode`, `dev`, `nlink`, and `ino`. If that
  856. * information is required, you will need to call `fs.lstat` yourself.
  857. *
  858. * If the Path refers to a nonexistent file, or if the lstat call fails for
  859. * any reason, `undefined` is returned. Otherwise the updated Path object is
  860. * returned.
  861. *
  862. * Results are cached, and thus may be out of date if the filesystem is
  863. * mutated.
  864. */
  865. async lstat() {
  866. if ((this.#type & ENOENT) === 0) {
  867. try {
  868. this.#applyStat(await this.#fs.promises.lstat(this.fullpath()));
  869. return this;
  870. }
  871. catch (er) {
  872. this.#lstatFail(er.code);
  873. }
  874. }
  875. }
  876. /**
  877. * synchronous {@link PathBase.lstat}
  878. */
  879. lstatSync() {
  880. if ((this.#type & ENOENT) === 0) {
  881. try {
  882. this.#applyStat(this.#fs.lstatSync(this.fullpath()));
  883. return this;
  884. }
  885. catch (er) {
  886. this.#lstatFail(er.code);
  887. }
  888. }
  889. }
  890. #applyStat(st) {
  891. const { atime, atimeMs, birthtime, birthtimeMs, blksize, blocks, ctime, ctimeMs, dev, gid, ino, mode, mtime, mtimeMs, nlink, rdev, size, uid, } = st;
  892. this.#atime = atime;
  893. this.#atimeMs = atimeMs;
  894. this.#birthtime = birthtime;
  895. this.#birthtimeMs = birthtimeMs;
  896. this.#blksize = blksize;
  897. this.#blocks = blocks;
  898. this.#ctime = ctime;
  899. this.#ctimeMs = ctimeMs;
  900. this.#dev = dev;
  901. this.#gid = gid;
  902. this.#ino = ino;
  903. this.#mode = mode;
  904. this.#mtime = mtime;
  905. this.#mtimeMs = mtimeMs;
  906. this.#nlink = nlink;
  907. this.#rdev = rdev;
  908. this.#size = size;
  909. this.#uid = uid;
  910. const ifmt = entToType(st);
  911. // retain any other flags, but set the ifmt
  912. this.#type = (this.#type & IFMT_UNKNOWN) | ifmt | LSTAT_CALLED;
  913. if (ifmt !== UNKNOWN && ifmt !== IFDIR && ifmt !== IFLNK) {
  914. this.#type |= ENOTDIR;
  915. }
  916. }
  917. #onReaddirCB = [];
  918. #readdirCBInFlight = false;
  919. #callOnReaddirCB(children) {
  920. this.#readdirCBInFlight = false;
  921. const cbs = this.#onReaddirCB.slice();
  922. this.#onReaddirCB.length = 0;
  923. cbs.forEach(cb => cb(null, children));
  924. }
  925. /**
  926. * Standard node-style callback interface to get list of directory entries.
  927. *
  928. * If the Path cannot or does not contain any children, then an empty array
  929. * is returned.
  930. *
  931. * Results are cached, and thus may be out of date if the filesystem is
  932. * mutated.
  933. *
  934. * @param cb The callback called with (er, entries). Note that the `er`
  935. * param is somewhat extraneous, as all readdir() errors are handled and
  936. * simply result in an empty set of entries being returned.
  937. * @param allowZalgo Boolean indicating that immediately known results should
  938. * *not* be deferred with `queueMicrotask`. Defaults to `false`. Release
  939. * zalgo at your peril, the dark pony lord is devious and unforgiving.
  940. */
  941. readdirCB(cb, allowZalgo = false) {
  942. if (!this.canReaddir()) {
  943. if (allowZalgo)
  944. cb(null, []);
  945. else
  946. queueMicrotask(() => cb(null, []));
  947. return;
  948. }
  949. const children = this.children();
  950. if (this.calledReaddir()) {
  951. const c = children.slice(0, children.provisional);
  952. if (allowZalgo)
  953. cb(null, c);
  954. else
  955. queueMicrotask(() => cb(null, c));
  956. return;
  957. }
  958. // don't have to worry about zalgo at this point.
  959. this.#onReaddirCB.push(cb);
  960. if (this.#readdirCBInFlight) {
  961. return;
  962. }
  963. this.#readdirCBInFlight = true;
  964. // else read the directory, fill up children
  965. // de-provisionalize any provisional children.
  966. const fullpath = this.fullpath();
  967. this.#fs.readdir(fullpath, { withFileTypes: true }, (er, entries) => {
  968. if (er) {
  969. this.#readdirFail(er.code);
  970. children.provisional = 0;
  971. }
  972. else {
  973. // if we didn't get an error, we always get entries.
  974. //@ts-ignore
  975. for (const e of entries) {
  976. this.#readdirAddChild(e, children);
  977. }
  978. this.#readdirSuccess(children);
  979. }
  980. this.#callOnReaddirCB(children.slice(0, children.provisional));
  981. return;
  982. });
  983. }
  984. #asyncReaddirInFlight;
  985. /**
  986. * Return an array of known child entries.
  987. *
  988. * If the Path cannot or does not contain any children, then an empty array
  989. * is returned.
  990. *
  991. * Results are cached, and thus may be out of date if the filesystem is
  992. * mutated.
  993. */
  994. async readdir() {
  995. if (!this.canReaddir()) {
  996. return [];
  997. }
  998. const children = this.children();
  999. if (this.calledReaddir()) {
  1000. return children.slice(0, children.provisional);
  1001. }
  1002. // else read the directory, fill up children
  1003. // de-provisionalize any provisional children.
  1004. const fullpath = this.fullpath();
  1005. if (this.#asyncReaddirInFlight) {
  1006. await this.#asyncReaddirInFlight;
  1007. }
  1008. else {
  1009. /* c8 ignore start */
  1010. let resolve = () => { };
  1011. /* c8 ignore stop */
  1012. this.#asyncReaddirInFlight = new Promise(res => (resolve = res));
  1013. try {
  1014. for (const e of await this.#fs.promises.readdir(fullpath, {
  1015. withFileTypes: true,
  1016. })) {
  1017. this.#readdirAddChild(e, children);
  1018. }
  1019. this.#readdirSuccess(children);
  1020. }
  1021. catch (er) {
  1022. this.#readdirFail(er.code);
  1023. children.provisional = 0;
  1024. }
  1025. this.#asyncReaddirInFlight = undefined;
  1026. resolve();
  1027. }
  1028. return children.slice(0, children.provisional);
  1029. }
  1030. /**
  1031. * synchronous {@link PathBase.readdir}
  1032. */
  1033. readdirSync() {
  1034. if (!this.canReaddir()) {
  1035. return [];
  1036. }
  1037. const children = this.children();
  1038. if (this.calledReaddir()) {
  1039. return children.slice(0, children.provisional);
  1040. }
  1041. // else read the directory, fill up children
  1042. // de-provisionalize any provisional children.
  1043. const fullpath = this.fullpath();
  1044. try {
  1045. for (const e of this.#fs.readdirSync(fullpath, {
  1046. withFileTypes: true,
  1047. })) {
  1048. this.#readdirAddChild(e, children);
  1049. }
  1050. this.#readdirSuccess(children);
  1051. }
  1052. catch (er) {
  1053. this.#readdirFail(er.code);
  1054. children.provisional = 0;
  1055. }
  1056. return children.slice(0, children.provisional);
  1057. }
  1058. canReaddir() {
  1059. if (this.#type & ENOCHILD)
  1060. return false;
  1061. const ifmt = IFMT & this.#type;
  1062. // we always set ENOTDIR when setting IFMT, so should be impossible
  1063. /* c8 ignore start */
  1064. if (!(ifmt === UNKNOWN || ifmt === IFDIR || ifmt === IFLNK)) {
  1065. return false;
  1066. }
  1067. /* c8 ignore stop */
  1068. return true;
  1069. }
  1070. shouldWalk(dirs, walkFilter) {
  1071. return ((this.#type & IFDIR) === IFDIR &&
  1072. !(this.#type & ENOCHILD) &&
  1073. !dirs.has(this) &&
  1074. (!walkFilter || walkFilter(this)));
  1075. }
  1076. /**
  1077. * Return the Path object corresponding to path as resolved
  1078. * by realpath(3).
  1079. *
  1080. * If the realpath call fails for any reason, `undefined` is returned.
  1081. *
  1082. * Result is cached, and thus may be outdated if the filesystem is mutated.
  1083. * On success, returns a Path object.
  1084. */
  1085. async realpath() {
  1086. if (this.#realpath)
  1087. return this.#realpath;
  1088. if ((ENOREALPATH | ENOREADLINK | ENOENT) & this.#type)
  1089. return undefined;
  1090. try {
  1091. const rp = await this.#fs.promises.realpath(this.fullpath());
  1092. return (this.#realpath = this.resolve(rp));
  1093. }
  1094. catch (_) {
  1095. this.#markENOREALPATH();
  1096. }
  1097. }
  1098. /**
  1099. * Synchronous {@link realpath}
  1100. */
  1101. realpathSync() {
  1102. if (this.#realpath)
  1103. return this.#realpath;
  1104. if ((ENOREALPATH | ENOREADLINK | ENOENT) & this.#type)
  1105. return undefined;
  1106. try {
  1107. const rp = this.#fs.realpathSync(this.fullpath());
  1108. return (this.#realpath = this.resolve(rp));
  1109. }
  1110. catch (_) {
  1111. this.#markENOREALPATH();
  1112. }
  1113. }
  1114. /**
  1115. * Internal method to mark this Path object as the scurry cwd,
  1116. * called by {@link PathScurry#chdir}
  1117. *
  1118. * @internal
  1119. */
  1120. [setAsCwd](oldCwd) {
  1121. if (oldCwd === this)
  1122. return;
  1123. oldCwd.isCWD = false;
  1124. this.isCWD = true;
  1125. const changed = new Set([]);
  1126. let rp = [];
  1127. let p = this;
  1128. while (p && p.parent) {
  1129. changed.add(p);
  1130. p.#relative = rp.join(this.sep);
  1131. p.#relativePosix = rp.join('/');
  1132. p = p.parent;
  1133. rp.push('..');
  1134. }
  1135. // now un-memoize parents of old cwd
  1136. p = oldCwd;
  1137. while (p && p.parent && !changed.has(p)) {
  1138. p.#relative = undefined;
  1139. p.#relativePosix = undefined;
  1140. p = p.parent;
  1141. }
  1142. }
  1143. }
  1144. /**
  1145. * Path class used on win32 systems
  1146. *
  1147. * Uses `'\\'` as the path separator for returned paths, either `'\\'` or `'/'`
  1148. * as the path separator for parsing paths.
  1149. */
  1150. export class PathWin32 extends PathBase {
  1151. /**
  1152. * Separator for generating path strings.
  1153. */
  1154. sep = '\\';
  1155. /**
  1156. * Separator for parsing path strings.
  1157. */
  1158. splitSep = eitherSep;
  1159. /**
  1160. * Do not create new Path objects directly. They should always be accessed
  1161. * via the PathScurry class or other methods on the Path class.
  1162. *
  1163. * @internal
  1164. */
  1165. constructor(name, type = UNKNOWN, root, roots, nocase, children, opts) {
  1166. super(name, type, root, roots, nocase, children, opts);
  1167. }
  1168. /**
  1169. * @internal
  1170. */
  1171. newChild(name, type = UNKNOWN, opts = {}) {
  1172. return new PathWin32(name, type, this.root, this.roots, this.nocase, this.childrenCache(), opts);
  1173. }
  1174. /**
  1175. * @internal
  1176. */
  1177. getRootString(path) {
  1178. return win32.parse(path).root;
  1179. }
  1180. /**
  1181. * @internal
  1182. */
  1183. getRoot(rootPath) {
  1184. rootPath = uncToDrive(rootPath.toUpperCase());
  1185. if (rootPath === this.root.name) {
  1186. return this.root;
  1187. }
  1188. // ok, not that one, check if it matches another we know about
  1189. for (const [compare, root] of Object.entries(this.roots)) {
  1190. if (this.sameRoot(rootPath, compare)) {
  1191. return (this.roots[rootPath] = root);
  1192. }
  1193. }
  1194. // otherwise, have to create a new one.
  1195. return (this.roots[rootPath] = new PathScurryWin32(rootPath, this).root);
  1196. }
  1197. /**
  1198. * @internal
  1199. */
  1200. sameRoot(rootPath, compare = this.root.name) {
  1201. // windows can (rarely) have case-sensitive filesystem, but
  1202. // UNC and drive letters are always case-insensitive, and canonically
  1203. // represented uppercase.
  1204. rootPath = rootPath
  1205. .toUpperCase()
  1206. .replace(/\//g, '\\')
  1207. .replace(uncDriveRegexp, '$1\\');
  1208. return rootPath === compare;
  1209. }
  1210. }
  1211. /**
  1212. * Path class used on all posix systems.
  1213. *
  1214. * Uses `'/'` as the path separator.
  1215. */
  1216. export class PathPosix extends PathBase {
  1217. /**
  1218. * separator for parsing path strings
  1219. */
  1220. splitSep = '/';
  1221. /**
  1222. * separator for generating path strings
  1223. */
  1224. sep = '/';
  1225. /**
  1226. * Do not create new Path objects directly. They should always be accessed
  1227. * via the PathScurry class or other methods on the Path class.
  1228. *
  1229. * @internal
  1230. */
  1231. constructor(name, type = UNKNOWN, root, roots, nocase, children, opts) {
  1232. super(name, type, root, roots, nocase, children, opts);
  1233. }
  1234. /**
  1235. * @internal
  1236. */
  1237. getRootString(path) {
  1238. return path.startsWith('/') ? '/' : '';
  1239. }
  1240. /**
  1241. * @internal
  1242. */
  1243. getRoot(_rootPath) {
  1244. return this.root;
  1245. }
  1246. /**
  1247. * @internal
  1248. */
  1249. newChild(name, type = UNKNOWN, opts = {}) {
  1250. return new PathPosix(name, type, this.root, this.roots, this.nocase, this.childrenCache(), opts);
  1251. }
  1252. }
  1253. /**
  1254. * The base class for all PathScurry classes, providing the interface for path
  1255. * resolution and filesystem operations.
  1256. *
  1257. * Typically, you should *not* instantiate this class directly, but rather one
  1258. * of the platform-specific classes, or the exported {@link PathScurry} which
  1259. * defaults to the current platform.
  1260. */
  1261. export class PathScurryBase {
  1262. /**
  1263. * The root Path entry for the current working directory of this Scurry
  1264. */
  1265. root;
  1266. /**
  1267. * The string path for the root of this Scurry's current working directory
  1268. */
  1269. rootPath;
  1270. /**
  1271. * A collection of all roots encountered, referenced by rootPath
  1272. */
  1273. roots;
  1274. /**
  1275. * The Path entry corresponding to this PathScurry's current working directory.
  1276. */
  1277. cwd;
  1278. #resolveCache;
  1279. #resolvePosixCache;
  1280. #children;
  1281. /**
  1282. * Perform path comparisons case-insensitively.
  1283. *
  1284. * Defaults true on Darwin and Windows systems, false elsewhere.
  1285. */
  1286. nocase;
  1287. #fs;
  1288. /**
  1289. * This class should not be instantiated directly.
  1290. *
  1291. * Use PathScurryWin32, PathScurryDarwin, PathScurryPosix, or PathScurry
  1292. *
  1293. * @internal
  1294. */
  1295. constructor(cwd = process.cwd(), pathImpl, sep, { nocase, childrenCacheSize = 16 * 1024, fs = defaultFS, } = {}) {
  1296. this.#fs = fsFromOption(fs);
  1297. if (cwd instanceof URL || cwd.startsWith('file://')) {
  1298. cwd = fileURLToPath(cwd);
  1299. }
  1300. // resolve and split root, and then add to the store.
  1301. // this is the only time we call path.resolve()
  1302. const cwdPath = pathImpl.resolve(cwd);
  1303. this.roots = Object.create(null);
  1304. this.rootPath = this.parseRootPath(cwdPath);
  1305. this.#resolveCache = new ResolveCache();
  1306. this.#resolvePosixCache = new ResolveCache();
  1307. this.#children = new ChildrenCache(childrenCacheSize);
  1308. const split = cwdPath.substring(this.rootPath.length).split(sep);
  1309. // resolve('/') leaves '', splits to [''], we don't want that.
  1310. if (split.length === 1 && !split[0]) {
  1311. split.pop();
  1312. }
  1313. /* c8 ignore start */
  1314. if (nocase === undefined) {
  1315. throw new TypeError('must provide nocase setting to PathScurryBase ctor');
  1316. }
  1317. /* c8 ignore stop */
  1318. this.nocase = nocase;
  1319. this.root = this.newRoot(this.#fs);
  1320. this.roots[this.rootPath] = this.root;
  1321. let prev = this.root;
  1322. let len = split.length - 1;
  1323. const joinSep = pathImpl.sep;
  1324. let abs = this.rootPath;
  1325. let sawFirst = false;
  1326. for (const part of split) {
  1327. const l = len--;
  1328. prev = prev.child(part, {
  1329. relative: new Array(l).fill('..').join(joinSep),
  1330. relativePosix: new Array(l).fill('..').join('/'),
  1331. fullpath: (abs += (sawFirst ? '' : joinSep) + part),
  1332. });
  1333. sawFirst = true;
  1334. }
  1335. this.cwd = prev;
  1336. }
  1337. /**
  1338. * Get the depth of a provided path, string, or the cwd
  1339. */
  1340. depth(path = this.cwd) {
  1341. if (typeof path === 'string') {
  1342. path = this.cwd.resolve(path);
  1343. }
  1344. return path.depth();
  1345. }
  1346. /**
  1347. * Return the cache of child entries. Exposed so subclasses can create
  1348. * child Path objects in a platform-specific way.
  1349. *
  1350. * @internal
  1351. */
  1352. childrenCache() {
  1353. return this.#children;
  1354. }
  1355. /**
  1356. * Resolve one or more path strings to a resolved string
  1357. *
  1358. * Same interface as require('path').resolve.
  1359. *
  1360. * Much faster than path.resolve() when called multiple times for the same
  1361. * path, because the resolved Path objects are cached. Much slower
  1362. * otherwise.
  1363. */
  1364. resolve(...paths) {
  1365. // first figure out the minimum number of paths we have to test
  1366. // we always start at cwd, but any absolutes will bump the start
  1367. let r = '';
  1368. for (let i = paths.length - 1; i >= 0; i--) {
  1369. const p = paths[i];
  1370. if (!p || p === '.')
  1371. continue;
  1372. r = r ? `${p}/${r}` : p;
  1373. if (this.isAbsolute(p)) {
  1374. break;
  1375. }
  1376. }
  1377. const cached = this.#resolveCache.get(r);
  1378. if (cached !== undefined) {
  1379. return cached;
  1380. }
  1381. const result = this.cwd.resolve(r).fullpath();
  1382. this.#resolveCache.set(r, result);
  1383. return result;
  1384. }
  1385. /**
  1386. * Resolve one or more path strings to a resolved string, returning
  1387. * the posix path. Identical to .resolve() on posix systems, but on
  1388. * windows will return a forward-slash separated UNC path.
  1389. *
  1390. * Same interface as require('path').resolve.
  1391. *
  1392. * Much faster than path.resolve() when called multiple times for the same
  1393. * path, because the resolved Path objects are cached. Much slower
  1394. * otherwise.
  1395. */
  1396. resolvePosix(...paths) {
  1397. // first figure out the minimum number of paths we have to test
  1398. // we always start at cwd, but any absolutes will bump the start
  1399. let r = '';
  1400. for (let i = paths.length - 1; i >= 0; i--) {
  1401. const p = paths[i];
  1402. if (!p || p === '.')
  1403. continue;
  1404. r = r ? `${p}/${r}` : p;
  1405. if (this.isAbsolute(p)) {
  1406. break;
  1407. }
  1408. }
  1409. const cached = this.#resolvePosixCache.get(r);
  1410. if (cached !== undefined) {
  1411. return cached;
  1412. }
  1413. const result = this.cwd.resolve(r).fullpathPosix();
  1414. this.#resolvePosixCache.set(r, result);
  1415. return result;
  1416. }
  1417. /**
  1418. * find the relative path from the cwd to the supplied path string or entry
  1419. */
  1420. relative(entry = this.cwd) {
  1421. if (typeof entry === 'string') {
  1422. entry = this.cwd.resolve(entry);
  1423. }
  1424. return entry.relative();
  1425. }
  1426. /**
  1427. * find the relative path from the cwd to the supplied path string or
  1428. * entry, using / as the path delimiter, even on Windows.
  1429. */
  1430. relativePosix(entry = this.cwd) {
  1431. if (typeof entry === 'string') {
  1432. entry = this.cwd.resolve(entry);
  1433. }
  1434. return entry.relativePosix();
  1435. }
  1436. /**
  1437. * Return the basename for the provided string or Path object
  1438. */
  1439. basename(entry = this.cwd) {
  1440. if (typeof entry === 'string') {
  1441. entry = this.cwd.resolve(entry);
  1442. }
  1443. return entry.name;
  1444. }
  1445. /**
  1446. * Return the dirname for the provided string or Path object
  1447. */
  1448. dirname(entry = this.cwd) {
  1449. if (typeof entry === 'string') {
  1450. entry = this.cwd.resolve(entry);
  1451. }
  1452. return (entry.parent || entry).fullpath();
  1453. }
  1454. async readdir(entry = this.cwd, opts = {
  1455. withFileTypes: true,
  1456. }) {
  1457. if (typeof entry === 'string') {
  1458. entry = this.cwd.resolve(entry);
  1459. }
  1460. else if (!(entry instanceof PathBase)) {
  1461. opts = entry;
  1462. entry = this.cwd;
  1463. }
  1464. const { withFileTypes } = opts;
  1465. if (!entry.canReaddir()) {
  1466. return [];
  1467. }
  1468. else {
  1469. const p = await entry.readdir();
  1470. return withFileTypes ? p : p.map(e => e.name);
  1471. }
  1472. }
  1473. readdirSync(entry = this.cwd, opts = {
  1474. withFileTypes: true,
  1475. }) {
  1476. if (typeof entry === 'string') {
  1477. entry = this.cwd.resolve(entry);
  1478. }
  1479. else if (!(entry instanceof PathBase)) {
  1480. opts = entry;
  1481. entry = this.cwd;
  1482. }
  1483. const { withFileTypes = true } = opts;
  1484. if (!entry.canReaddir()) {
  1485. return [];
  1486. }
  1487. else if (withFileTypes) {
  1488. return entry.readdirSync();
  1489. }
  1490. else {
  1491. return entry.readdirSync().map(e => e.name);
  1492. }
  1493. }
  1494. /**
  1495. * Call lstat() on the string or Path object, and update all known
  1496. * information that can be determined.
  1497. *
  1498. * Note that unlike `fs.lstat()`, the returned value does not contain some
  1499. * information, such as `mode`, `dev`, `nlink`, and `ino`. If that
  1500. * information is required, you will need to call `fs.lstat` yourself.
  1501. *
  1502. * If the Path refers to a nonexistent file, or if the lstat call fails for
  1503. * any reason, `undefined` is returned. Otherwise the updated Path object is
  1504. * returned.
  1505. *
  1506. * Results are cached, and thus may be out of date if the filesystem is
  1507. * mutated.
  1508. */
  1509. async lstat(entry = this.cwd) {
  1510. if (typeof entry === 'string') {
  1511. entry = this.cwd.resolve(entry);
  1512. }
  1513. return entry.lstat();
  1514. }
  1515. /**
  1516. * synchronous {@link PathScurryBase.lstat}
  1517. */
  1518. lstatSync(entry = this.cwd) {
  1519. if (typeof entry === 'string') {
  1520. entry = this.cwd.resolve(entry);
  1521. }
  1522. return entry.lstatSync();
  1523. }
  1524. async readlink(entry = this.cwd, { withFileTypes } = {
  1525. withFileTypes: false,
  1526. }) {
  1527. if (typeof entry === 'string') {
  1528. entry = this.cwd.resolve(entry);
  1529. }
  1530. else if (!(entry instanceof PathBase)) {
  1531. withFileTypes = entry.withFileTypes;
  1532. entry = this.cwd;
  1533. }
  1534. const e = await entry.readlink();
  1535. return withFileTypes ? e : e?.fullpath();
  1536. }
  1537. readlinkSync(entry = this.cwd, { withFileTypes } = {
  1538. withFileTypes: false,
  1539. }) {
  1540. if (typeof entry === 'string') {
  1541. entry = this.cwd.resolve(entry);
  1542. }
  1543. else if (!(entry instanceof PathBase)) {
  1544. withFileTypes = entry.withFileTypes;
  1545. entry = this.cwd;
  1546. }
  1547. const e = entry.readlinkSync();
  1548. return withFileTypes ? e : e?.fullpath();
  1549. }
  1550. async realpath(entry = this.cwd, { withFileTypes } = {
  1551. withFileTypes: false,
  1552. }) {
  1553. if (typeof entry === 'string') {
  1554. entry = this.cwd.resolve(entry);
  1555. }
  1556. else if (!(entry instanceof PathBase)) {
  1557. withFileTypes = entry.withFileTypes;
  1558. entry = this.cwd;
  1559. }
  1560. const e = await entry.realpath();
  1561. return withFileTypes ? e : e?.fullpath();
  1562. }
  1563. realpathSync(entry = this.cwd, { withFileTypes } = {
  1564. withFileTypes: false,
  1565. }) {
  1566. if (typeof entry === 'string') {
  1567. entry = this.cwd.resolve(entry);
  1568. }
  1569. else if (!(entry instanceof PathBase)) {
  1570. withFileTypes = entry.withFileTypes;
  1571. entry = this.cwd;
  1572. }
  1573. const e = entry.realpathSync();
  1574. return withFileTypes ? e : e?.fullpath();
  1575. }
  1576. async walk(entry = this.cwd, opts = {}) {
  1577. if (typeof entry === 'string') {
  1578. entry = this.cwd.resolve(entry);
  1579. }
  1580. else if (!(entry instanceof PathBase)) {
  1581. opts = entry;
  1582. entry = this.cwd;
  1583. }
  1584. const { withFileTypes = true, follow = false, filter, walkFilter, } = opts;
  1585. const results = [];
  1586. if (!filter || filter(entry)) {
  1587. results.push(withFileTypes ? entry : entry.fullpath());
  1588. }
  1589. const dirs = new Set();
  1590. const walk = (dir, cb) => {
  1591. dirs.add(dir);
  1592. dir.readdirCB((er, entries) => {
  1593. /* c8 ignore start */
  1594. if (er) {
  1595. return cb(er);
  1596. }
  1597. /* c8 ignore stop */
  1598. let len = entries.length;
  1599. if (!len)
  1600. return cb();
  1601. const next = () => {
  1602. if (--len === 0) {
  1603. cb();
  1604. }
  1605. };
  1606. for (const e of entries) {
  1607. if (!filter || filter(e)) {
  1608. results.push(withFileTypes ? e : e.fullpath());
  1609. }
  1610. if (follow && e.isSymbolicLink()) {
  1611. e.realpath()
  1612. .then(r => (r?.isUnknown() ? r.lstat() : r))
  1613. .then(r => r?.shouldWalk(dirs, walkFilter) ? walk(r, next) : next());
  1614. }
  1615. else {
  1616. if (e.shouldWalk(dirs, walkFilter)) {
  1617. walk(e, next);
  1618. }
  1619. else {
  1620. next();
  1621. }
  1622. }
  1623. }
  1624. }, true); // zalgooooooo
  1625. };
  1626. const start = entry;
  1627. return new Promise((res, rej) => {
  1628. walk(start, er => {
  1629. /* c8 ignore start */
  1630. if (er)
  1631. return rej(er);
  1632. /* c8 ignore stop */
  1633. res(results);
  1634. });
  1635. });
  1636. }
  1637. walkSync(entry = this.cwd, opts = {}) {
  1638. if (typeof entry === 'string') {
  1639. entry = this.cwd.resolve(entry);
  1640. }
  1641. else if (!(entry instanceof PathBase)) {
  1642. opts = entry;
  1643. entry = this.cwd;
  1644. }
  1645. const { withFileTypes = true, follow = false, filter, walkFilter, } = opts;
  1646. const results = [];
  1647. if (!filter || filter(entry)) {
  1648. results.push(withFileTypes ? entry : entry.fullpath());
  1649. }
  1650. const dirs = new Set([entry]);
  1651. for (const dir of dirs) {
  1652. const entries = dir.readdirSync();
  1653. for (const e of entries) {
  1654. if (!filter || filter(e)) {
  1655. results.push(withFileTypes ? e : e.fullpath());
  1656. }
  1657. let r = e;
  1658. if (e.isSymbolicLink()) {
  1659. if (!(follow && (r = e.realpathSync())))
  1660. continue;
  1661. if (r.isUnknown())
  1662. r.lstatSync();
  1663. }
  1664. if (r.shouldWalk(dirs, walkFilter)) {
  1665. dirs.add(r);
  1666. }
  1667. }
  1668. }
  1669. return results;
  1670. }
  1671. /**
  1672. * Support for `for await`
  1673. *
  1674. * Alias for {@link PathScurryBase.iterate}
  1675. *
  1676. * Note: As of Node 19, this is very slow, compared to other methods of
  1677. * walking. Consider using {@link PathScurryBase.stream} if memory overhead
  1678. * and backpressure are concerns, or {@link PathScurryBase.walk} if not.
  1679. */
  1680. [Symbol.asyncIterator]() {
  1681. return this.iterate();
  1682. }
  1683. iterate(entry = this.cwd, options = {}) {
  1684. // iterating async over the stream is significantly more performant,
  1685. // especially in the warm-cache scenario, because it buffers up directory
  1686. // entries in the background instead of waiting for a yield for each one.
  1687. if (typeof entry === 'string') {
  1688. entry = this.cwd.resolve(entry);
  1689. }
  1690. else if (!(entry instanceof PathBase)) {
  1691. options = entry;
  1692. entry = this.cwd;
  1693. }
  1694. return this.stream(entry, options)[Symbol.asyncIterator]();
  1695. }
  1696. /**
  1697. * Iterating over a PathScurry performs a synchronous walk.
  1698. *
  1699. * Alias for {@link PathScurryBase.iterateSync}
  1700. */
  1701. [Symbol.iterator]() {
  1702. return this.iterateSync();
  1703. }
  1704. *iterateSync(entry = this.cwd, opts = {}) {
  1705. if (typeof entry === 'string') {
  1706. entry = this.cwd.resolve(entry);
  1707. }
  1708. else if (!(entry instanceof PathBase)) {
  1709. opts = entry;
  1710. entry = this.cwd;
  1711. }
  1712. const { withFileTypes = true, follow = false, filter, walkFilter, } = opts;
  1713. if (!filter || filter(entry)) {
  1714. yield withFileTypes ? entry : entry.fullpath();
  1715. }
  1716. const dirs = new Set([entry]);
  1717. for (const dir of dirs) {
  1718. const entries = dir.readdirSync();
  1719. for (const e of entries) {
  1720. if (!filter || filter(e)) {
  1721. yield withFileTypes ? e : e.fullpath();
  1722. }
  1723. let r = e;
  1724. if (e.isSymbolicLink()) {
  1725. if (!(follow && (r = e.realpathSync())))
  1726. continue;
  1727. if (r.isUnknown())
  1728. r.lstatSync();
  1729. }
  1730. if (r.shouldWalk(dirs, walkFilter)) {
  1731. dirs.add(r);
  1732. }
  1733. }
  1734. }
  1735. }
  1736. stream(entry = this.cwd, opts = {}) {
  1737. if (typeof entry === 'string') {
  1738. entry = this.cwd.resolve(entry);
  1739. }
  1740. else if (!(entry instanceof PathBase)) {
  1741. opts = entry;
  1742. entry = this.cwd;
  1743. }
  1744. const { withFileTypes = true, follow = false, filter, walkFilter, } = opts;
  1745. const results = new Minipass({ objectMode: true });
  1746. if (!filter || filter(entry)) {
  1747. results.write(withFileTypes ? entry : entry.fullpath());
  1748. }
  1749. const dirs = new Set();
  1750. const queue = [entry];
  1751. let processing = 0;
  1752. const process = () => {
  1753. let paused = false;
  1754. while (!paused) {
  1755. const dir = queue.shift();
  1756. if (!dir) {
  1757. if (processing === 0)
  1758. results.end();
  1759. return;
  1760. }
  1761. processing++;
  1762. dirs.add(dir);
  1763. const onReaddir = (er, entries, didRealpaths = false) => {
  1764. /* c8 ignore start */
  1765. if (er)
  1766. return results.emit('error', er);
  1767. /* c8 ignore stop */
  1768. if (follow && !didRealpaths) {
  1769. const promises = [];
  1770. for (const e of entries) {
  1771. if (e.isSymbolicLink()) {
  1772. promises.push(e
  1773. .realpath()
  1774. .then((r) => r?.isUnknown() ? r.lstat() : r));
  1775. }
  1776. }
  1777. if (promises.length) {
  1778. Promise.all(promises).then(() => onReaddir(null, entries, true));
  1779. return;
  1780. }
  1781. }
  1782. for (const e of entries) {
  1783. if (e && (!filter || filter(e))) {
  1784. if (!results.write(withFileTypes ? e : e.fullpath())) {
  1785. paused = true;
  1786. }
  1787. }
  1788. }
  1789. processing--;
  1790. for (const e of entries) {
  1791. const r = e.realpathCached() || e;
  1792. if (r.shouldWalk(dirs, walkFilter)) {
  1793. queue.push(r);
  1794. }
  1795. }
  1796. if (paused && !results.flowing) {
  1797. results.once('drain', process);
  1798. }
  1799. else if (!sync) {
  1800. process();
  1801. }
  1802. };
  1803. // zalgo containment
  1804. let sync = true;
  1805. dir.readdirCB(onReaddir, true);
  1806. sync = false;
  1807. }
  1808. };
  1809. process();
  1810. return results;
  1811. }
  1812. streamSync(entry = this.cwd, opts = {}) {
  1813. if (typeof entry === 'string') {
  1814. entry = this.cwd.resolve(entry);
  1815. }
  1816. else if (!(entry instanceof PathBase)) {
  1817. opts = entry;
  1818. entry = this.cwd;
  1819. }
  1820. const { withFileTypes = true, follow = false, filter, walkFilter, } = opts;
  1821. const results = new Minipass({ objectMode: true });
  1822. const dirs = new Set();
  1823. if (!filter || filter(entry)) {
  1824. results.write(withFileTypes ? entry : entry.fullpath());
  1825. }
  1826. const queue = [entry];
  1827. let processing = 0;
  1828. const process = () => {
  1829. let paused = false;
  1830. while (!paused) {
  1831. const dir = queue.shift();
  1832. if (!dir) {
  1833. if (processing === 0)
  1834. results.end();
  1835. return;
  1836. }
  1837. processing++;
  1838. dirs.add(dir);
  1839. const entries = dir.readdirSync();
  1840. for (const e of entries) {
  1841. if (!filter || filter(e)) {
  1842. if (!results.write(withFileTypes ? e : e.fullpath())) {
  1843. paused = true;
  1844. }
  1845. }
  1846. }
  1847. processing--;
  1848. for (const e of entries) {
  1849. let r = e;
  1850. if (e.isSymbolicLink()) {
  1851. if (!(follow && (r = e.realpathSync())))
  1852. continue;
  1853. if (r.isUnknown())
  1854. r.lstatSync();
  1855. }
  1856. if (r.shouldWalk(dirs, walkFilter)) {
  1857. queue.push(r);
  1858. }
  1859. }
  1860. }
  1861. if (paused && !results.flowing)
  1862. results.once('drain', process);
  1863. };
  1864. process();
  1865. return results;
  1866. }
  1867. chdir(path = this.cwd) {
  1868. const oldCwd = this.cwd;
  1869. this.cwd = typeof path === 'string' ? this.cwd.resolve(path) : path;
  1870. this.cwd[setAsCwd](oldCwd);
  1871. }
  1872. }
  1873. /**
  1874. * Windows implementation of {@link PathScurryBase}
  1875. *
  1876. * Defaults to case insensitve, uses `'\\'` to generate path strings. Uses
  1877. * {@link PathWin32} for Path objects.
  1878. */
  1879. export class PathScurryWin32 extends PathScurryBase {
  1880. /**
  1881. * separator for generating path strings
  1882. */
  1883. sep = '\\';
  1884. constructor(cwd = process.cwd(), opts = {}) {
  1885. const { nocase = true } = opts;
  1886. super(cwd, win32, '\\', { ...opts, nocase });
  1887. this.nocase = nocase;
  1888. for (let p = this.cwd; p; p = p.parent) {
  1889. p.nocase = this.nocase;
  1890. }
  1891. }
  1892. /**
  1893. * @internal
  1894. */
  1895. parseRootPath(dir) {
  1896. // if the path starts with a single separator, it's not a UNC, and we'll
  1897. // just get separator as the root, and driveFromUNC will return \
  1898. // In that case, mount \ on the root from the cwd.
  1899. return win32.parse(dir).root.toUpperCase();
  1900. }
  1901. /**
  1902. * @internal
  1903. */
  1904. newRoot(fs) {
  1905. return new PathWin32(this.rootPath, IFDIR, undefined, this.roots, this.nocase, this.childrenCache(), { fs });
  1906. }
  1907. /**
  1908. * Return true if the provided path string is an absolute path
  1909. */
  1910. isAbsolute(p) {
  1911. return (p.startsWith('/') || p.startsWith('\\') || /^[a-z]:(\/|\\)/i.test(p));
  1912. }
  1913. }
  1914. /**
  1915. * {@link PathScurryBase} implementation for all posix systems other than Darwin.
  1916. *
  1917. * Defaults to case-sensitive matching, uses `'/'` to generate path strings.
  1918. *
  1919. * Uses {@link PathPosix} for Path objects.
  1920. */
  1921. export class PathScurryPosix extends PathScurryBase {
  1922. /**
  1923. * separator for generating path strings
  1924. */
  1925. sep = '/';
  1926. constructor(cwd = process.cwd(), opts = {}) {
  1927. const { nocase = false } = opts;
  1928. super(cwd, posix, '/', { ...opts, nocase });
  1929. this.nocase = nocase;
  1930. }
  1931. /**
  1932. * @internal
  1933. */
  1934. parseRootPath(_dir) {
  1935. return '/';
  1936. }
  1937. /**
  1938. * @internal
  1939. */
  1940. newRoot(fs) {
  1941. return new PathPosix(this.rootPath, IFDIR, undefined, this.roots, this.nocase, this.childrenCache(), { fs });
  1942. }
  1943. /**
  1944. * Return true if the provided path string is an absolute path
  1945. */
  1946. isAbsolute(p) {
  1947. return p.startsWith('/');
  1948. }
  1949. }
  1950. /**
  1951. * {@link PathScurryBase} implementation for Darwin (macOS) systems.
  1952. *
  1953. * Defaults to case-insensitive matching, uses `'/'` for generating path
  1954. * strings.
  1955. *
  1956. * Uses {@link PathPosix} for Path objects.
  1957. */
  1958. export class PathScurryDarwin extends PathScurryPosix {
  1959. constructor(cwd = process.cwd(), opts = {}) {
  1960. const { nocase = true } = opts;
  1961. super(cwd, { ...opts, nocase });
  1962. }
  1963. }
  1964. /**
  1965. * Default {@link PathBase} implementation for the current platform.
  1966. *
  1967. * {@link PathWin32} on Windows systems, {@link PathPosix} on all others.
  1968. */
  1969. export const Path = process.platform === 'win32' ? PathWin32 : PathPosix;
  1970. /**
  1971. * Default {@link PathScurryBase} implementation for the current platform.
  1972. *
  1973. * {@link PathScurryWin32} on Windows systems, {@link PathScurryDarwin} on
  1974. * Darwin (macOS) systems, {@link PathScurryPosix} on all others.
  1975. */
  1976. export const PathScurry = process.platform === 'win32' ? PathScurryWin32
  1977. : process.platform === 'darwin' ? PathScurryDarwin
  1978. : PathScurryPosix;
  1979. //# sourceMappingURL=index.js.map