|
import { LRUCache } from 'lru-cache'; |
|
import { posix, win32 } from 'path'; |
|
import { fileURLToPath } from 'url'; |
|
import * as actualFS from 'fs'; |
|
import { lstatSync, readdir as readdirCB, readdirSync, readlinkSync, realpathSync as rps, } from 'fs'; |
|
const realpathSync = rps.native; |
|
|
|
|
|
import { lstat, readdir, readlink, realpath } from 'fs/promises'; |
|
import { Minipass } from 'minipass'; |
|
const defaultFS = { |
|
lstatSync, |
|
readdir: readdirCB, |
|
readdirSync, |
|
readlinkSync, |
|
realpathSync, |
|
promises: { |
|
lstat, |
|
readdir, |
|
readlink, |
|
realpath, |
|
}, |
|
}; |
|
|
|
const fsFromOption = (fsOption) => !fsOption || fsOption === defaultFS || fsOption === actualFS |
|
? defaultFS |
|
: { |
|
...defaultFS, |
|
...fsOption, |
|
promises: { |
|
...defaultFS.promises, |
|
...(fsOption.promises || {}), |
|
}, |
|
}; |
|
|
|
const uncDriveRegexp = /^\\\\\?\\([a-z]:)\\?$/i; |
|
const uncToDrive = (rootPath) => rootPath.replace(/\//g, '\\').replace(uncDriveRegexp, '$1\\'); |
|
|
|
const eitherSep = /[\\\/]/; |
|
const UNKNOWN = 0; |
|
const IFIFO = 0b0001; |
|
const IFCHR = 0b0010; |
|
const IFDIR = 0b0100; |
|
const IFBLK = 0b0110; |
|
const IFREG = 0b1000; |
|
const IFLNK = 0b1010; |
|
const IFSOCK = 0b1100; |
|
const IFMT = 0b1111; |
|
|
|
const IFMT_UNKNOWN = ~IFMT; |
|
|
|
const READDIR_CALLED = 16; |
|
|
|
const LSTAT_CALLED = 32; |
|
|
|
const ENOTDIR = 64; |
|
|
|
|
|
const ENOENT = 128; |
|
|
|
|
|
const ENOREADLINK = 256; |
|
|
|
const ENOREALPATH = 512; |
|
const ENOCHILD = ENOTDIR | ENOENT | ENOREALPATH; |
|
const TYPEMASK = 1023; |
|
const entToType = (s) => s.isFile() |
|
? IFREG |
|
: s.isDirectory() |
|
? IFDIR |
|
: s.isSymbolicLink() |
|
? IFLNK |
|
: s.isCharacterDevice() |
|
? IFCHR |
|
: s.isBlockDevice() |
|
? IFBLK |
|
: s.isSocket() |
|
? IFSOCK |
|
: s.isFIFO() |
|
? IFIFO |
|
: UNKNOWN; |
|
|
|
const normalizeCache = new Map(); |
|
const normalize = (s) => { |
|
const c = normalizeCache.get(s); |
|
if (c) |
|
return c; |
|
const n = s.normalize('NFKD'); |
|
normalizeCache.set(s, n); |
|
return n; |
|
}; |
|
const normalizeNocaseCache = new Map(); |
|
const normalizeNocase = (s) => { |
|
const c = normalizeNocaseCache.get(s); |
|
if (c) |
|
return c; |
|
const n = normalize(s.toLowerCase()); |
|
normalizeNocaseCache.set(s, n); |
|
return n; |
|
}; |
|
|
|
|
|
|
|
|
|
export class ResolveCache extends LRUCache { |
|
constructor() { |
|
super({ max: 256 }); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export class ChildrenCache extends LRUCache { |
|
constructor(maxSize = 16 * 1024) { |
|
super({ |
|
maxSize, |
|
|
|
sizeCalculation: a => a.length + 1, |
|
}); |
|
} |
|
} |
|
const setAsCwd = Symbol('PathScurry setAsCwd'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export class PathBase { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
name; |
|
|
|
|
|
|
|
|
|
|
|
root; |
|
|
|
|
|
|
|
|
|
|
|
roots; |
|
|
|
|
|
|
|
|
|
|
|
parent; |
|
|
|
|
|
|
|
|
|
nocase; |
|
|
|
#fs; |
|
|
|
#dev; |
|
get dev() { |
|
return this.#dev; |
|
} |
|
#mode; |
|
get mode() { |
|
return this.#mode; |
|
} |
|
#nlink; |
|
get nlink() { |
|
return this.#nlink; |
|
} |
|
#uid; |
|
get uid() { |
|
return this.#uid; |
|
} |
|
#gid; |
|
get gid() { |
|
return this.#gid; |
|
} |
|
#rdev; |
|
get rdev() { |
|
return this.#rdev; |
|
} |
|
#blksize; |
|
get blksize() { |
|
return this.#blksize; |
|
} |
|
#ino; |
|
get ino() { |
|
return this.#ino; |
|
} |
|
#size; |
|
get size() { |
|
return this.#size; |
|
} |
|
#blocks; |
|
get blocks() { |
|
return this.#blocks; |
|
} |
|
#atimeMs; |
|
get atimeMs() { |
|
return this.#atimeMs; |
|
} |
|
#mtimeMs; |
|
get mtimeMs() { |
|
return this.#mtimeMs; |
|
} |
|
#ctimeMs; |
|
get ctimeMs() { |
|
return this.#ctimeMs; |
|
} |
|
#birthtimeMs; |
|
get birthtimeMs() { |
|
return this.#birthtimeMs; |
|
} |
|
#atime; |
|
get atime() { |
|
return this.#atime; |
|
} |
|
#mtime; |
|
get mtime() { |
|
return this.#mtime; |
|
} |
|
#ctime; |
|
get ctime() { |
|
return this.#ctime; |
|
} |
|
#birthtime; |
|
get birthtime() { |
|
return this.#birthtime; |
|
} |
|
#matchName; |
|
#depth; |
|
#fullpath; |
|
#fullpathPosix; |
|
#relative; |
|
#relativePosix; |
|
#type; |
|
#children; |
|
#linkTarget; |
|
#realpath; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
get path() { |
|
return (this.parent || this).fullpath(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
constructor(name, type = UNKNOWN, root, roots, nocase, children, opts) { |
|
this.name = name; |
|
this.#matchName = nocase ? normalizeNocase(name) : normalize(name); |
|
this.#type = type & TYPEMASK; |
|
this.nocase = nocase; |
|
this.roots = roots; |
|
this.root = root || this; |
|
this.#children = children; |
|
this.#fullpath = opts.fullpath; |
|
this.#relative = opts.relative; |
|
this.#relativePosix = opts.relativePosix; |
|
this.parent = opts.parent; |
|
if (this.parent) { |
|
this.#fs = this.parent.#fs; |
|
} |
|
else { |
|
this.#fs = fsFromOption(opts.fs); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
depth() { |
|
if (this.#depth !== undefined) |
|
return this.#depth; |
|
if (!this.parent) |
|
return (this.#depth = 0); |
|
return (this.#depth = this.parent.depth() + 1); |
|
} |
|
|
|
|
|
|
|
childrenCache() { |
|
return this.#children; |
|
} |
|
|
|
|
|
|
|
resolve(path) { |
|
if (!path) { |
|
return this; |
|
} |
|
const rootPath = this.getRootString(path); |
|
const dir = path.substring(rootPath.length); |
|
const dirParts = dir.split(this.splitSep); |
|
const result = rootPath |
|
? this.getRoot(rootPath).#resolveParts(dirParts) |
|
: this.#resolveParts(dirParts); |
|
return result; |
|
} |
|
#resolveParts(dirParts) { |
|
let p = this; |
|
for (const part of dirParts) { |
|
p = p.child(part); |
|
} |
|
return p; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
children() { |
|
const cached = this.#children.get(this); |
|
if (cached) { |
|
return cached; |
|
} |
|
const children = Object.assign([], { provisional: 0 }); |
|
this.#children.set(this, children); |
|
this.#type &= ~READDIR_CALLED; |
|
return children; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
child(pathPart, opts) { |
|
if (pathPart === '' || pathPart === '.') { |
|
return this; |
|
} |
|
if (pathPart === '..') { |
|
return this.parent || this; |
|
} |
|
|
|
const children = this.children(); |
|
const name = this.nocase |
|
? normalizeNocase(pathPart) |
|
: normalize(pathPart); |
|
for (const p of children) { |
|
if (p.#matchName === name) { |
|
return p; |
|
} |
|
} |
|
|
|
|
|
|
|
const s = this.parent ? this.sep : ''; |
|
const fullpath = this.#fullpath |
|
? this.#fullpath + s + pathPart |
|
: undefined; |
|
const pchild = this.newChild(pathPart, UNKNOWN, { |
|
...opts, |
|
parent: this, |
|
fullpath, |
|
}); |
|
if (!this.canReaddir()) { |
|
pchild.#type |= ENOENT; |
|
} |
|
|
|
|
|
children.push(pchild); |
|
return pchild; |
|
} |
|
|
|
|
|
|
|
|
|
relative() { |
|
if (this.#relative !== undefined) { |
|
return this.#relative; |
|
} |
|
const name = this.name; |
|
const p = this.parent; |
|
if (!p) { |
|
return (this.#relative = this.name); |
|
} |
|
const pv = p.relative(); |
|
return pv + (!pv || !p.parent ? '' : this.sep) + name; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
relativePosix() { |
|
if (this.sep === '/') |
|
return this.relative(); |
|
if (this.#relativePosix !== undefined) |
|
return this.#relativePosix; |
|
const name = this.name; |
|
const p = this.parent; |
|
if (!p) { |
|
return (this.#relativePosix = this.fullpathPosix()); |
|
} |
|
const pv = p.relativePosix(); |
|
return pv + (!pv || !p.parent ? '' : '/') + name; |
|
} |
|
|
|
|
|
|
|
fullpath() { |
|
if (this.#fullpath !== undefined) { |
|
return this.#fullpath; |
|
} |
|
const name = this.name; |
|
const p = this.parent; |
|
if (!p) { |
|
return (this.#fullpath = this.name); |
|
} |
|
const pv = p.fullpath(); |
|
const fp = pv + (!p.parent ? '' : this.sep) + name; |
|
return (this.#fullpath = fp); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
fullpathPosix() { |
|
if (this.#fullpathPosix !== undefined) |
|
return this.#fullpathPosix; |
|
if (this.sep === '/') |
|
return (this.#fullpathPosix = this.fullpath()); |
|
if (!this.parent) { |
|
const p = this.fullpath().replace(/\\/g, '/'); |
|
if (/^[a-z]:\//i.test(p)) { |
|
return (this.#fullpathPosix = `//?/${p}`); |
|
} |
|
else { |
|
return (this.#fullpathPosix = p); |
|
} |
|
} |
|
const p = this.parent; |
|
const pfpp = p.fullpathPosix(); |
|
const fpp = pfpp + (!pfpp || !p.parent ? '' : '/') + this.name; |
|
return (this.#fullpathPosix = fpp); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
isUnknown() { |
|
return (this.#type & IFMT) === UNKNOWN; |
|
} |
|
isType(type) { |
|
return this[`is${type}`](); |
|
} |
|
getType() { |
|
return this.isUnknown() |
|
? 'Unknown' |
|
: this.isDirectory() |
|
? 'Directory' |
|
: this.isFile() |
|
? 'File' |
|
: this.isSymbolicLink() |
|
? 'SymbolicLink' |
|
: this.isFIFO() |
|
? 'FIFO' |
|
: this.isCharacterDevice() |
|
? 'CharacterDevice' |
|
: this.isBlockDevice() |
|
? 'BlockDevice' |
|
: this.isSocket() |
|
? 'Socket' |
|
: 'Unknown'; |
|
|
|
} |
|
|
|
|
|
|
|
isFile() { |
|
return (this.#type & IFMT) === IFREG; |
|
} |
|
|
|
|
|
|
|
isDirectory() { |
|
return (this.#type & IFMT) === IFDIR; |
|
} |
|
|
|
|
|
|
|
isCharacterDevice() { |
|
return (this.#type & IFMT) === IFCHR; |
|
} |
|
|
|
|
|
|
|
isBlockDevice() { |
|
return (this.#type & IFMT) === IFBLK; |
|
} |
|
|
|
|
|
|
|
isFIFO() { |
|
return (this.#type & IFMT) === IFIFO; |
|
} |
|
|
|
|
|
|
|
isSocket() { |
|
return (this.#type & IFMT) === IFSOCK; |
|
} |
|
|
|
|
|
|
|
isSymbolicLink() { |
|
return (this.#type & IFLNK) === IFLNK; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lstatCached() { |
|
return this.#type & LSTAT_CALLED ? this : undefined; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
readlinkCached() { |
|
return this.#linkTarget; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
realpathCached() { |
|
return this.#realpath; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
readdirCached() { |
|
const children = this.children(); |
|
return children.slice(0, children.provisional); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
canReadlink() { |
|
if (this.#linkTarget) |
|
return true; |
|
if (!this.parent) |
|
return false; |
|
|
|
const ifmt = this.#type & IFMT; |
|
return !((ifmt !== UNKNOWN && ifmt !== IFLNK) || |
|
this.#type & ENOREADLINK || |
|
this.#type & ENOENT); |
|
} |
|
|
|
|
|
|
|
|
|
calledReaddir() { |
|
return !!(this.#type & READDIR_CALLED); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
isENOENT() { |
|
return !!(this.#type & ENOENT); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
isNamed(n) { |
|
return !this.nocase |
|
? this.#matchName === normalize(n) |
|
: this.#matchName === normalizeNocase(n); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async readlink() { |
|
const target = this.#linkTarget; |
|
if (target) { |
|
return target; |
|
} |
|
if (!this.canReadlink()) { |
|
return undefined; |
|
} |
|
|
|
|
|
if (!this.parent) { |
|
return undefined; |
|
} |
|
|
|
try { |
|
const read = await this.#fs.promises.readlink(this.fullpath()); |
|
const linkTarget = this.parent.resolve(read); |
|
if (linkTarget) { |
|
return (this.#linkTarget = linkTarget); |
|
} |
|
} |
|
catch (er) { |
|
this.#readlinkFail(er.code); |
|
return undefined; |
|
} |
|
} |
|
|
|
|
|
|
|
readlinkSync() { |
|
const target = this.#linkTarget; |
|
if (target) { |
|
return target; |
|
} |
|
if (!this.canReadlink()) { |
|
return undefined; |
|
} |
|
|
|
|
|
if (!this.parent) { |
|
return undefined; |
|
} |
|
|
|
try { |
|
const read = this.#fs.readlinkSync(this.fullpath()); |
|
const linkTarget = this.parent.resolve(read); |
|
if (linkTarget) { |
|
return (this.#linkTarget = linkTarget); |
|
} |
|
} |
|
catch (er) { |
|
this.#readlinkFail(er.code); |
|
return undefined; |
|
} |
|
} |
|
#readdirSuccess(children) { |
|
|
|
this.#type |= READDIR_CALLED; |
|
|
|
for (let p = children.provisional; p < children.length; p++) { |
|
children[p].#markENOENT(); |
|
} |
|
} |
|
#markENOENT() { |
|
|
|
if (this.#type & ENOENT) |
|
return; |
|
this.#type = (this.#type | ENOENT) & IFMT_UNKNOWN; |
|
this.#markChildrenENOENT(); |
|
} |
|
#markChildrenENOENT() { |
|
|
|
const children = this.children(); |
|
children.provisional = 0; |
|
for (const p of children) { |
|
p.#markENOENT(); |
|
} |
|
} |
|
#markENOREALPATH() { |
|
this.#type |= ENOREALPATH; |
|
this.#markENOTDIR(); |
|
} |
|
|
|
#markENOTDIR() { |
|
|
|
|
|
|
|
|
|
|
|
if (this.#type & ENOTDIR) |
|
return; |
|
|
|
let t = this.#type; |
|
|
|
|
|
if ((t & IFMT) === IFDIR) |
|
t &= IFMT_UNKNOWN; |
|
this.#type = t | ENOTDIR; |
|
this.#markChildrenENOENT(); |
|
} |
|
#readdirFail(code = '') { |
|
|
|
if (code === 'ENOTDIR' || code === 'EPERM') { |
|
this.#markENOTDIR(); |
|
} |
|
else if (code === 'ENOENT') { |
|
this.#markENOENT(); |
|
} |
|
else { |
|
this.children().provisional = 0; |
|
} |
|
} |
|
#lstatFail(code = '') { |
|
|
|
|
|
if (code === 'ENOTDIR') { |
|
|
|
const p = this.parent; |
|
p.#markENOTDIR(); |
|
} |
|
else if (code === 'ENOENT') { |
|
|
|
this.#markENOENT(); |
|
} |
|
} |
|
#readlinkFail(code = '') { |
|
let ter = this.#type; |
|
ter |= ENOREADLINK; |
|
if (code === 'ENOENT') |
|
ter |= ENOENT; |
|
|
|
if (code === 'EINVAL' || code === 'UNKNOWN') { |
|
|
|
|
|
ter &= IFMT_UNKNOWN; |
|
} |
|
this.#type = ter; |
|
|
|
|
|
|
|
if (code === 'ENOTDIR' && this.parent) { |
|
this.parent.#markENOTDIR(); |
|
} |
|
|
|
} |
|
#readdirAddChild(e, c) { |
|
return (this.#readdirMaybePromoteChild(e, c) || |
|
this.#readdirAddNewChild(e, c)); |
|
} |
|
#readdirAddNewChild(e, c) { |
|
|
|
const type = entToType(e); |
|
const child = this.newChild(e.name, type, { parent: this }); |
|
const ifmt = child.#type & IFMT; |
|
if (ifmt !== IFDIR && ifmt !== IFLNK && ifmt !== UNKNOWN) { |
|
child.#type |= ENOTDIR; |
|
} |
|
c.unshift(child); |
|
c.provisional++; |
|
return child; |
|
} |
|
#readdirMaybePromoteChild(e, c) { |
|
for (let p = c.provisional; p < c.length; p++) { |
|
const pchild = c[p]; |
|
const name = this.nocase |
|
? normalizeNocase(e.name) |
|
: normalize(e.name); |
|
if (name !== pchild.#matchName) { |
|
continue; |
|
} |
|
return this.#readdirPromoteChild(e, pchild, p, c); |
|
} |
|
} |
|
#readdirPromoteChild(e, p, index, c) { |
|
const v = p.name; |
|
|
|
p.#type = (p.#type & IFMT_UNKNOWN) | entToType(e); |
|
|
|
if (v !== e.name) |
|
p.name = e.name; |
|
|
|
|
|
if (index !== c.provisional) { |
|
if (index === c.length - 1) |
|
c.pop(); |
|
else |
|
c.splice(index, 1); |
|
c.unshift(p); |
|
} |
|
c.provisional++; |
|
return p; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async lstat() { |
|
if ((this.#type & ENOENT) === 0) { |
|
try { |
|
this.#applyStat(await this.#fs.promises.lstat(this.fullpath())); |
|
return this; |
|
} |
|
catch (er) { |
|
this.#lstatFail(er.code); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
lstatSync() { |
|
if ((this.#type & ENOENT) === 0) { |
|
try { |
|
this.#applyStat(this.#fs.lstatSync(this.fullpath())); |
|
return this; |
|
} |
|
catch (er) { |
|
this.#lstatFail(er.code); |
|
} |
|
} |
|
} |
|
#applyStat(st) { |
|
const { atime, atimeMs, birthtime, birthtimeMs, blksize, blocks, ctime, ctimeMs, dev, gid, ino, mode, mtime, mtimeMs, nlink, rdev, size, uid, } = st; |
|
this.#atime = atime; |
|
this.#atimeMs = atimeMs; |
|
this.#birthtime = birthtime; |
|
this.#birthtimeMs = birthtimeMs; |
|
this.#blksize = blksize; |
|
this.#blocks = blocks; |
|
this.#ctime = ctime; |
|
this.#ctimeMs = ctimeMs; |
|
this.#dev = dev; |
|
this.#gid = gid; |
|
this.#ino = ino; |
|
this.#mode = mode; |
|
this.#mtime = mtime; |
|
this.#mtimeMs = mtimeMs; |
|
this.#nlink = nlink; |
|
this.#rdev = rdev; |
|
this.#size = size; |
|
this.#uid = uid; |
|
const ifmt = entToType(st); |
|
|
|
this.#type = (this.#type & IFMT_UNKNOWN) | ifmt | LSTAT_CALLED; |
|
if (ifmt !== UNKNOWN && ifmt !== IFDIR && ifmt !== IFLNK) { |
|
this.#type |= ENOTDIR; |
|
} |
|
} |
|
#onReaddirCB = []; |
|
#readdirCBInFlight = false; |
|
#callOnReaddirCB(children) { |
|
this.#readdirCBInFlight = false; |
|
const cbs = this.#onReaddirCB.slice(); |
|
this.#onReaddirCB.length = 0; |
|
cbs.forEach(cb => cb(null, children)); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
readdirCB(cb, allowZalgo = false) { |
|
if (!this.canReaddir()) { |
|
if (allowZalgo) |
|
cb(null, []); |
|
else |
|
queueMicrotask(() => cb(null, [])); |
|
return; |
|
} |
|
const children = this.children(); |
|
if (this.calledReaddir()) { |
|
const c = children.slice(0, children.provisional); |
|
if (allowZalgo) |
|
cb(null, c); |
|
else |
|
queueMicrotask(() => cb(null, c)); |
|
return; |
|
} |
|
|
|
this.#onReaddirCB.push(cb); |
|
if (this.#readdirCBInFlight) { |
|
return; |
|
} |
|
this.#readdirCBInFlight = true; |
|
|
|
|
|
const fullpath = this.fullpath(); |
|
this.#fs.readdir(fullpath, { withFileTypes: true }, (er, entries) => { |
|
if (er) { |
|
this.#readdirFail(er.code); |
|
children.provisional = 0; |
|
} |
|
else { |
|
|
|
|
|
for (const e of entries) { |
|
this.#readdirAddChild(e, children); |
|
} |
|
this.#readdirSuccess(children); |
|
} |
|
this.#callOnReaddirCB(children.slice(0, children.provisional)); |
|
return; |
|
}); |
|
} |
|
#asyncReaddirInFlight; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async readdir() { |
|
if (!this.canReaddir()) { |
|
return []; |
|
} |
|
const children = this.children(); |
|
if (this.calledReaddir()) { |
|
return children.slice(0, children.provisional); |
|
} |
|
|
|
|
|
const fullpath = this.fullpath(); |
|
if (this.#asyncReaddirInFlight) { |
|
await this.#asyncReaddirInFlight; |
|
} |
|
else { |
|
|
|
let resolve = () => { }; |
|
|
|
this.#asyncReaddirInFlight = new Promise(res => (resolve = res)); |
|
try { |
|
for (const e of await this.#fs.promises.readdir(fullpath, { |
|
withFileTypes: true, |
|
})) { |
|
this.#readdirAddChild(e, children); |
|
} |
|
this.#readdirSuccess(children); |
|
} |
|
catch (er) { |
|
this.#readdirFail(er.code); |
|
children.provisional = 0; |
|
} |
|
this.#asyncReaddirInFlight = undefined; |
|
resolve(); |
|
} |
|
return children.slice(0, children.provisional); |
|
} |
|
|
|
|
|
|
|
readdirSync() { |
|
if (!this.canReaddir()) { |
|
return []; |
|
} |
|
const children = this.children(); |
|
if (this.calledReaddir()) { |
|
return children.slice(0, children.provisional); |
|
} |
|
|
|
|
|
const fullpath = this.fullpath(); |
|
try { |
|
for (const e of this.#fs.readdirSync(fullpath, { |
|
withFileTypes: true, |
|
})) { |
|
this.#readdirAddChild(e, children); |
|
} |
|
this.#readdirSuccess(children); |
|
} |
|
catch (er) { |
|
this.#readdirFail(er.code); |
|
children.provisional = 0; |
|
} |
|
return children.slice(0, children.provisional); |
|
} |
|
canReaddir() { |
|
if (this.#type & ENOCHILD) |
|
return false; |
|
const ifmt = IFMT & this.#type; |
|
|
|
|
|
if (!(ifmt === UNKNOWN || ifmt === IFDIR || ifmt === IFLNK)) { |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
shouldWalk(dirs, walkFilter) { |
|
return ((this.#type & IFDIR) === IFDIR && |
|
!(this.#type & ENOCHILD) && |
|
!dirs.has(this) && |
|
(!walkFilter || walkFilter(this))); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async realpath() { |
|
if (this.#realpath) |
|
return this.#realpath; |
|
if ((ENOREALPATH | ENOREADLINK | ENOENT) & this.#type) |
|
return undefined; |
|
try { |
|
const rp = await this.#fs.promises.realpath(this.fullpath()); |
|
return (this.#realpath = this.resolve(rp)); |
|
} |
|
catch (_) { |
|
this.#markENOREALPATH(); |
|
} |
|
} |
|
|
|
|
|
|
|
realpathSync() { |
|
if (this.#realpath) |
|
return this.#realpath; |
|
if ((ENOREALPATH | ENOREADLINK | ENOENT) & this.#type) |
|
return undefined; |
|
try { |
|
const rp = this.#fs.realpathSync(this.fullpath()); |
|
return (this.#realpath = this.resolve(rp)); |
|
} |
|
catch (_) { |
|
this.#markENOREALPATH(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
[setAsCwd](oldCwd) { |
|
if (oldCwd === this) |
|
return; |
|
const changed = new Set([]); |
|
let rp = []; |
|
let p = this; |
|
while (p && p.parent) { |
|
changed.add(p); |
|
p.#relative = rp.join(this.sep); |
|
p.#relativePosix = rp.join('/'); |
|
p = p.parent; |
|
rp.push('..'); |
|
} |
|
|
|
p = oldCwd; |
|
while (p && p.parent && !changed.has(p)) { |
|
p.#relative = undefined; |
|
p.#relativePosix = undefined; |
|
p = p.parent; |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
export class PathWin32 extends PathBase { |
|
|
|
|
|
|
|
sep = '\\'; |
|
|
|
|
|
|
|
splitSep = eitherSep; |
|
|
|
|
|
|
|
|
|
|
|
|
|
constructor(name, type = UNKNOWN, root, roots, nocase, children, opts) { |
|
super(name, type, root, roots, nocase, children, opts); |
|
} |
|
|
|
|
|
|
|
newChild(name, type = UNKNOWN, opts = {}) { |
|
return new PathWin32(name, type, this.root, this.roots, this.nocase, this.childrenCache(), opts); |
|
} |
|
|
|
|
|
|
|
getRootString(path) { |
|
return win32.parse(path).root; |
|
} |
|
|
|
|
|
|
|
getRoot(rootPath) { |
|
rootPath = uncToDrive(rootPath.toUpperCase()); |
|
if (rootPath === this.root.name) { |
|
return this.root; |
|
} |
|
|
|
for (const [compare, root] of Object.entries(this.roots)) { |
|
if (this.sameRoot(rootPath, compare)) { |
|
return (this.roots[rootPath] = root); |
|
} |
|
} |
|
|
|
return (this.roots[rootPath] = new PathScurryWin32(rootPath, this).root); |
|
} |
|
|
|
|
|
|
|
sameRoot(rootPath, compare = this.root.name) { |
|
|
|
|
|
|
|
rootPath = rootPath |
|
.toUpperCase() |
|
.replace(/\//g, '\\') |
|
.replace(uncDriveRegexp, '$1\\'); |
|
return rootPath === compare; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
export class PathPosix extends PathBase { |
|
|
|
|
|
|
|
splitSep = '/'; |
|
|
|
|
|
|
|
sep = '/'; |
|
|
|
|
|
|
|
|
|
|
|
|
|
constructor(name, type = UNKNOWN, root, roots, nocase, children, opts) { |
|
super(name, type, root, roots, nocase, children, opts); |
|
} |
|
|
|
|
|
|
|
getRootString(path) { |
|
return path.startsWith('/') ? '/' : ''; |
|
} |
|
|
|
|
|
|
|
getRoot(_rootPath) { |
|
return this.root; |
|
} |
|
|
|
|
|
|
|
newChild(name, type = UNKNOWN, opts = {}) { |
|
return new PathPosix(name, type, this.root, this.roots, this.nocase, this.childrenCache(), opts); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export class PathScurryBase { |
|
|
|
|
|
|
|
root; |
|
|
|
|
|
|
|
rootPath; |
|
|
|
|
|
|
|
roots; |
|
|
|
|
|
|
|
cwd; |
|
#resolveCache; |
|
#resolvePosixCache; |
|
#children; |
|
|
|
|
|
|
|
|
|
|
|
nocase; |
|
#fs; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
constructor(cwd = process.cwd(), pathImpl, sep, { nocase, childrenCacheSize = 16 * 1024, fs = defaultFS, } = {}) { |
|
this.#fs = fsFromOption(fs); |
|
if (cwd instanceof URL || cwd.startsWith('file://')) { |
|
cwd = fileURLToPath(cwd); |
|
} |
|
|
|
|
|
const cwdPath = pathImpl.resolve(cwd); |
|
this.roots = Object.create(null); |
|
this.rootPath = this.parseRootPath(cwdPath); |
|
this.#resolveCache = new ResolveCache(); |
|
this.#resolvePosixCache = new ResolveCache(); |
|
this.#children = new ChildrenCache(childrenCacheSize); |
|
const split = cwdPath.substring(this.rootPath.length).split(sep); |
|
|
|
if (split.length === 1 && !split[0]) { |
|
split.pop(); |
|
} |
|
|
|
if (nocase === undefined) { |
|
throw new TypeError('must provide nocase setting to PathScurryBase ctor'); |
|
} |
|
|
|
this.nocase = nocase; |
|
this.root = this.newRoot(this.#fs); |
|
this.roots[this.rootPath] = this.root; |
|
let prev = this.root; |
|
let len = split.length - 1; |
|
const joinSep = pathImpl.sep; |
|
let abs = this.rootPath; |
|
let sawFirst = false; |
|
for (const part of split) { |
|
const l = len--; |
|
prev = prev.child(part, { |
|
relative: new Array(l).fill('..').join(joinSep), |
|
relativePosix: new Array(l).fill('..').join('/'), |
|
fullpath: (abs += (sawFirst ? '' : joinSep) + part), |
|
}); |
|
sawFirst = true; |
|
} |
|
this.cwd = prev; |
|
} |
|
|
|
|
|
|
|
depth(path = this.cwd) { |
|
if (typeof path === 'string') { |
|
path = this.cwd.resolve(path); |
|
} |
|
return path.depth(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
childrenCache() { |
|
return this.#children; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
resolve(...paths) { |
|
|
|
|
|
let r = ''; |
|
for (let i = paths.length - 1; i >= 0; i--) { |
|
const p = paths[i]; |
|
if (!p || p === '.') |
|
continue; |
|
r = r ? `${p}/${r}` : p; |
|
if (this.isAbsolute(p)) { |
|
break; |
|
} |
|
} |
|
const cached = this.#resolveCache.get(r); |
|
if (cached !== undefined) { |
|
return cached; |
|
} |
|
const result = this.cwd.resolve(r).fullpath(); |
|
this.#resolveCache.set(r, result); |
|
return result; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
resolvePosix(...paths) { |
|
|
|
|
|
let r = ''; |
|
for (let i = paths.length - 1; i >= 0; i--) { |
|
const p = paths[i]; |
|
if (!p || p === '.') |
|
continue; |
|
r = r ? `${p}/${r}` : p; |
|
if (this.isAbsolute(p)) { |
|
break; |
|
} |
|
} |
|
const cached = this.#resolvePosixCache.get(r); |
|
if (cached !== undefined) { |
|
return cached; |
|
} |
|
const result = this.cwd.resolve(r).fullpathPosix(); |
|
this.#resolvePosixCache.set(r, result); |
|
return result; |
|
} |
|
|
|
|
|
|
|
relative(entry = this.cwd) { |
|
if (typeof entry === 'string') { |
|
entry = this.cwd.resolve(entry); |
|
} |
|
return entry.relative(); |
|
} |
|
|
|
|
|
|
|
|
|
relativePosix(entry = this.cwd) { |
|
if (typeof entry === 'string') { |
|
entry = this.cwd.resolve(entry); |
|
} |
|
return entry.relativePosix(); |
|
} |
|
|
|
|
|
|
|
basename(entry = this.cwd) { |
|
if (typeof entry === 'string') { |
|
entry = this.cwd.resolve(entry); |
|
} |
|
return entry.name; |
|
} |
|
|
|
|
|
|
|
dirname(entry = this.cwd) { |
|
if (typeof entry === 'string') { |
|
entry = this.cwd.resolve(entry); |
|
} |
|
return (entry.parent || entry).fullpath(); |
|
} |
|
async readdir(entry = this.cwd, opts = { |
|
withFileTypes: true, |
|
}) { |
|
if (typeof entry === 'string') { |
|
entry = this.cwd.resolve(entry); |
|
} |
|
else if (!(entry instanceof PathBase)) { |
|
opts = entry; |
|
entry = this.cwd; |
|
} |
|
const { withFileTypes } = opts; |
|
if (!entry.canReaddir()) { |
|
return []; |
|
} |
|
else { |
|
const p = await entry.readdir(); |
|
return withFileTypes ? p : p.map(e => e.name); |
|
} |
|
} |
|
readdirSync(entry = this.cwd, opts = { |
|
withFileTypes: true, |
|
}) { |
|
if (typeof entry === 'string') { |
|
entry = this.cwd.resolve(entry); |
|
} |
|
else if (!(entry instanceof PathBase)) { |
|
opts = entry; |
|
entry = this.cwd; |
|
} |
|
const { withFileTypes = true } = opts; |
|
if (!entry.canReaddir()) { |
|
return []; |
|
} |
|
else if (withFileTypes) { |
|
return entry.readdirSync(); |
|
} |
|
else { |
|
return entry.readdirSync().map(e => e.name); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async lstat(entry = this.cwd) { |
|
if (typeof entry === 'string') { |
|
entry = this.cwd.resolve(entry); |
|
} |
|
return entry.lstat(); |
|
} |
|
|
|
|
|
|
|
lstatSync(entry = this.cwd) { |
|
if (typeof entry === 'string') { |
|
entry = this.cwd.resolve(entry); |
|
} |
|
return entry.lstatSync(); |
|
} |
|
async readlink(entry = this.cwd, { withFileTypes } = { |
|
withFileTypes: false, |
|
}) { |
|
if (typeof entry === 'string') { |
|
entry = this.cwd.resolve(entry); |
|
} |
|
else if (!(entry instanceof PathBase)) { |
|
withFileTypes = entry.withFileTypes; |
|
entry = this.cwd; |
|
} |
|
const e = await entry.readlink(); |
|
return withFileTypes ? e : e?.fullpath(); |
|
} |
|
readlinkSync(entry = this.cwd, { withFileTypes } = { |
|
withFileTypes: false, |
|
}) { |
|
if (typeof entry === 'string') { |
|
entry = this.cwd.resolve(entry); |
|
} |
|
else if (!(entry instanceof PathBase)) { |
|
withFileTypes = entry.withFileTypes; |
|
entry = this.cwd; |
|
} |
|
const e = entry.readlinkSync(); |
|
return withFileTypes ? e : e?.fullpath(); |
|
} |
|
async realpath(entry = this.cwd, { withFileTypes } = { |
|
withFileTypes: false, |
|
}) { |
|
if (typeof entry === 'string') { |
|
entry = this.cwd.resolve(entry); |
|
} |
|
else if (!(entry instanceof PathBase)) { |
|
withFileTypes = entry.withFileTypes; |
|
entry = this.cwd; |
|
} |
|
const e = await entry.realpath(); |
|
return withFileTypes ? e : e?.fullpath(); |
|
} |
|
realpathSync(entry = this.cwd, { withFileTypes } = { |
|
withFileTypes: false, |
|
}) { |
|
if (typeof entry === 'string') { |
|
entry = this.cwd.resolve(entry); |
|
} |
|
else if (!(entry instanceof PathBase)) { |
|
withFileTypes = entry.withFileTypes; |
|
entry = this.cwd; |
|
} |
|
const e = entry.realpathSync(); |
|
return withFileTypes ? e : e?.fullpath(); |
|
} |
|
async walk(entry = this.cwd, opts = {}) { |
|
if (typeof entry === 'string') { |
|
entry = this.cwd.resolve(entry); |
|
} |
|
else if (!(entry instanceof PathBase)) { |
|
opts = entry; |
|
entry = this.cwd; |
|
} |
|
const { withFileTypes = true, follow = false, filter, walkFilter, } = opts; |
|
const results = []; |
|
if (!filter || filter(entry)) { |
|
results.push(withFileTypes ? entry : entry.fullpath()); |
|
} |
|
const dirs = new Set(); |
|
const walk = (dir, cb) => { |
|
dirs.add(dir); |
|
dir.readdirCB((er, entries) => { |
|
|
|
if (er) { |
|
return cb(er); |
|
} |
|
|
|
let len = entries.length; |
|
if (!len) |
|
return cb(); |
|
const next = () => { |
|
if (--len === 0) { |
|
cb(); |
|
} |
|
}; |
|
for (const e of entries) { |
|
if (!filter || filter(e)) { |
|
results.push(withFileTypes ? e : e.fullpath()); |
|
} |
|
if (follow && e.isSymbolicLink()) { |
|
e.realpath() |
|
.then(r => (r?.isUnknown() ? r.lstat() : r)) |
|
.then(r => r?.shouldWalk(dirs, walkFilter) ? walk(r, next) : next()); |
|
} |
|
else { |
|
if (e.shouldWalk(dirs, walkFilter)) { |
|
walk(e, next); |
|
} |
|
else { |
|
next(); |
|
} |
|
} |
|
} |
|
}, true); |
|
}; |
|
const start = entry; |
|
return new Promise((res, rej) => { |
|
walk(start, er => { |
|
|
|
if (er) |
|
return rej(er); |
|
|
|
res(results); |
|
}); |
|
}); |
|
} |
|
walkSync(entry = this.cwd, opts = {}) { |
|
if (typeof entry === 'string') { |
|
entry = this.cwd.resolve(entry); |
|
} |
|
else if (!(entry instanceof PathBase)) { |
|
opts = entry; |
|
entry = this.cwd; |
|
} |
|
const { withFileTypes = true, follow = false, filter, walkFilter, } = opts; |
|
const results = []; |
|
if (!filter || filter(entry)) { |
|
results.push(withFileTypes ? entry : entry.fullpath()); |
|
} |
|
const dirs = new Set([entry]); |
|
for (const dir of dirs) { |
|
const entries = dir.readdirSync(); |
|
for (const e of entries) { |
|
if (!filter || filter(e)) { |
|
results.push(withFileTypes ? e : e.fullpath()); |
|
} |
|
let r = e; |
|
if (e.isSymbolicLink()) { |
|
if (!(follow && (r = e.realpathSync()))) |
|
continue; |
|
if (r.isUnknown()) |
|
r.lstatSync(); |
|
} |
|
if (r.shouldWalk(dirs, walkFilter)) { |
|
dirs.add(r); |
|
} |
|
} |
|
} |
|
return results; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[Symbol.asyncIterator]() { |
|
return this.iterate(); |
|
} |
|
iterate(entry = this.cwd, options = {}) { |
|
|
|
|
|
|
|
if (typeof entry === 'string') { |
|
entry = this.cwd.resolve(entry); |
|
} |
|
else if (!(entry instanceof PathBase)) { |
|
options = entry; |
|
entry = this.cwd; |
|
} |
|
return this.stream(entry, options)[Symbol.asyncIterator](); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
[Symbol.iterator]() { |
|
return this.iterateSync(); |
|
} |
|
*iterateSync(entry = this.cwd, opts = {}) { |
|
if (typeof entry === 'string') { |
|
entry = this.cwd.resolve(entry); |
|
} |
|
else if (!(entry instanceof PathBase)) { |
|
opts = entry; |
|
entry = this.cwd; |
|
} |
|
const { withFileTypes = true, follow = false, filter, walkFilter, } = opts; |
|
if (!filter || filter(entry)) { |
|
yield withFileTypes ? entry : entry.fullpath(); |
|
} |
|
const dirs = new Set([entry]); |
|
for (const dir of dirs) { |
|
const entries = dir.readdirSync(); |
|
for (const e of entries) { |
|
if (!filter || filter(e)) { |
|
yield withFileTypes ? e : e.fullpath(); |
|
} |
|
let r = e; |
|
if (e.isSymbolicLink()) { |
|
if (!(follow && (r = e.realpathSync()))) |
|
continue; |
|
if (r.isUnknown()) |
|
r.lstatSync(); |
|
} |
|
if (r.shouldWalk(dirs, walkFilter)) { |
|
dirs.add(r); |
|
} |
|
} |
|
} |
|
} |
|
stream(entry = this.cwd, opts = {}) { |
|
if (typeof entry === 'string') { |
|
entry = this.cwd.resolve(entry); |
|
} |
|
else if (!(entry instanceof PathBase)) { |
|
opts = entry; |
|
entry = this.cwd; |
|
} |
|
const { withFileTypes = true, follow = false, filter, walkFilter, } = opts; |
|
const results = new Minipass({ objectMode: true }); |
|
if (!filter || filter(entry)) { |
|
results.write(withFileTypes ? entry : entry.fullpath()); |
|
} |
|
const dirs = new Set(); |
|
const queue = [entry]; |
|
let processing = 0; |
|
const process = () => { |
|
let paused = false; |
|
while (!paused) { |
|
const dir = queue.shift(); |
|
if (!dir) { |
|
if (processing === 0) |
|
results.end(); |
|
return; |
|
} |
|
processing++; |
|
dirs.add(dir); |
|
const onReaddir = (er, entries, didRealpaths = false) => { |
|
|
|
if (er) |
|
return results.emit('error', er); |
|
|
|
if (follow && !didRealpaths) { |
|
const promises = []; |
|
for (const e of entries) { |
|
if (e.isSymbolicLink()) { |
|
promises.push(e |
|
.realpath() |
|
.then((r) => r?.isUnknown() ? r.lstat() : r)); |
|
} |
|
} |
|
if (promises.length) { |
|
Promise.all(promises).then(() => onReaddir(null, entries, true)); |
|
return; |
|
} |
|
} |
|
for (const e of entries) { |
|
if (e && (!filter || filter(e))) { |
|
if (!results.write(withFileTypes ? e : e.fullpath())) { |
|
paused = true; |
|
} |
|
} |
|
} |
|
processing--; |
|
for (const e of entries) { |
|
const r = e.realpathCached() || e; |
|
if (r.shouldWalk(dirs, walkFilter)) { |
|
queue.push(r); |
|
} |
|
} |
|
if (paused && !results.flowing) { |
|
results.once('drain', process); |
|
} |
|
else if (!sync) { |
|
process(); |
|
} |
|
}; |
|
|
|
let sync = true; |
|
dir.readdirCB(onReaddir, true); |
|
sync = false; |
|
} |
|
}; |
|
process(); |
|
return results; |
|
} |
|
streamSync(entry = this.cwd, opts = {}) { |
|
if (typeof entry === 'string') { |
|
entry = this.cwd.resolve(entry); |
|
} |
|
else if (!(entry instanceof PathBase)) { |
|
opts = entry; |
|
entry = this.cwd; |
|
} |
|
const { withFileTypes = true, follow = false, filter, walkFilter, } = opts; |
|
const results = new Minipass({ objectMode: true }); |
|
const dirs = new Set(); |
|
if (!filter || filter(entry)) { |
|
results.write(withFileTypes ? entry : entry.fullpath()); |
|
} |
|
const queue = [entry]; |
|
let processing = 0; |
|
const process = () => { |
|
let paused = false; |
|
while (!paused) { |
|
const dir = queue.shift(); |
|
if (!dir) { |
|
if (processing === 0) |
|
results.end(); |
|
return; |
|
} |
|
processing++; |
|
dirs.add(dir); |
|
const entries = dir.readdirSync(); |
|
for (const e of entries) { |
|
if (!filter || filter(e)) { |
|
if (!results.write(withFileTypes ? e : e.fullpath())) { |
|
paused = true; |
|
} |
|
} |
|
} |
|
processing--; |
|
for (const e of entries) { |
|
let r = e; |
|
if (e.isSymbolicLink()) { |
|
if (!(follow && (r = e.realpathSync()))) |
|
continue; |
|
if (r.isUnknown()) |
|
r.lstatSync(); |
|
} |
|
if (r.shouldWalk(dirs, walkFilter)) { |
|
queue.push(r); |
|
} |
|
} |
|
} |
|
if (paused && !results.flowing) |
|
results.once('drain', process); |
|
}; |
|
process(); |
|
return results; |
|
} |
|
chdir(path = this.cwd) { |
|
const oldCwd = this.cwd; |
|
this.cwd = typeof path === 'string' ? this.cwd.resolve(path) : path; |
|
this.cwd[setAsCwd](oldCwd); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
export class PathScurryWin32 extends PathScurryBase { |
|
|
|
|
|
|
|
sep = '\\'; |
|
constructor(cwd = process.cwd(), opts = {}) { |
|
const { nocase = true } = opts; |
|
super(cwd, win32, '\\', { ...opts, nocase }); |
|
this.nocase = nocase; |
|
for (let p = this.cwd; p; p = p.parent) { |
|
p.nocase = this.nocase; |
|
} |
|
} |
|
|
|
|
|
|
|
parseRootPath(dir) { |
|
|
|
|
|
|
|
return win32.parse(dir).root.toUpperCase(); |
|
} |
|
|
|
|
|
|
|
newRoot(fs) { |
|
return new PathWin32(this.rootPath, IFDIR, undefined, this.roots, this.nocase, this.childrenCache(), { fs }); |
|
} |
|
|
|
|
|
|
|
isAbsolute(p) { |
|
return (p.startsWith('/') || p.startsWith('\\') || /^[a-z]:(\/|\\)/i.test(p)); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export class PathScurryPosix extends PathScurryBase { |
|
|
|
|
|
|
|
sep = '/'; |
|
constructor(cwd = process.cwd(), opts = {}) { |
|
const { nocase = false } = opts; |
|
super(cwd, posix, '/', { ...opts, nocase }); |
|
this.nocase = nocase; |
|
} |
|
|
|
|
|
|
|
parseRootPath(_dir) { |
|
return '/'; |
|
} |
|
|
|
|
|
|
|
newRoot(fs) { |
|
return new PathPosix(this.rootPath, IFDIR, undefined, this.roots, this.nocase, this.childrenCache(), { fs }); |
|
} |
|
|
|
|
|
|
|
isAbsolute(p) { |
|
return p.startsWith('/'); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export class PathScurryDarwin extends PathScurryPosix { |
|
constructor(cwd = process.cwd(), opts = {}) { |
|
const { nocase = true } = opts; |
|
super(cwd, { ...opts, nocase }); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
export const Path = process.platform === 'win32' ? PathWin32 : PathPosix; |
|
|
|
|
|
|
|
|
|
|
|
|
|
export const PathScurry = process.platform === 'win32' |
|
? PathScurryWin32 |
|
: process.platform === 'darwin' |
|
? PathScurryDarwin |
|
: PathScurryPosix; |
|
|