Spaces:
Sleeping
Sleeping
; | |
Object.defineProperty(exports, "__esModule", { value: true }); | |
exports.GlobStream = exports.GlobWalker = exports.GlobUtil = void 0; | |
/** | |
* Single-use utility classes to provide functionality to the {@link Glob} | |
* methods. | |
* | |
* @module | |
*/ | |
const minipass_1 = require("minipass"); | |
const ignore_js_1 = require("./ignore.js"); | |
const processor_js_1 = require("./processor.js"); | |
const makeIgnore = (ignore, opts) => typeof ignore === 'string' ? new ignore_js_1.Ignore([ignore], opts) | |
: Array.isArray(ignore) ? new ignore_js_1.Ignore(ignore, opts) | |
: ignore; | |
/** | |
* basic walking utilities that all the glob walker types use | |
*/ | |
class GlobUtil { | |
path; | |
patterns; | |
opts; | |
seen = new Set(); | |
paused = false; | |
aborted = false; | |
#onResume = []; | |
#ignore; | |
#sep; | |
signal; | |
maxDepth; | |
includeChildMatches; | |
constructor(patterns, path, opts) { | |
this.patterns = patterns; | |
this.path = path; | |
this.opts = opts; | |
this.#sep = !opts.posix && opts.platform === 'win32' ? '\\' : '/'; | |
this.includeChildMatches = opts.includeChildMatches !== false; | |
if (opts.ignore || !this.includeChildMatches) { | |
this.#ignore = makeIgnore(opts.ignore ?? [], opts); | |
if (!this.includeChildMatches && | |
typeof this.#ignore.add !== 'function') { | |
const m = 'cannot ignore child matches, ignore lacks add() method.'; | |
throw new Error(m); | |
} | |
} | |
// ignore, always set with maxDepth, but it's optional on the | |
// GlobOptions type | |
/* c8 ignore start */ | |
this.maxDepth = opts.maxDepth || Infinity; | |
/* c8 ignore stop */ | |
if (opts.signal) { | |
this.signal = opts.signal; | |
this.signal.addEventListener('abort', () => { | |
this.#onResume.length = 0; | |
}); | |
} | |
} | |
#ignored(path) { | |
return this.seen.has(path) || !!this.#ignore?.ignored?.(path); | |
} | |
#childrenIgnored(path) { | |
return !!this.#ignore?.childrenIgnored?.(path); | |
} | |
// backpressure mechanism | |
pause() { | |
this.paused = true; | |
} | |
resume() { | |
/* c8 ignore start */ | |
if (this.signal?.aborted) | |
return; | |
/* c8 ignore stop */ | |
this.paused = false; | |
let fn = undefined; | |
while (!this.paused && (fn = this.#onResume.shift())) { | |
fn(); | |
} | |
} | |
onResume(fn) { | |
if (this.signal?.aborted) | |
return; | |
/* c8 ignore start */ | |
if (!this.paused) { | |
fn(); | |
} | |
else { | |
/* c8 ignore stop */ | |
this.#onResume.push(fn); | |
} | |
} | |
// do the requisite realpath/stat checking, and return the path | |
// to add or undefined to filter it out. | |
async matchCheck(e, ifDir) { | |
if (ifDir && this.opts.nodir) | |
return undefined; | |
let rpc; | |
if (this.opts.realpath) { | |
rpc = e.realpathCached() || (await e.realpath()); | |
if (!rpc) | |
return undefined; | |
e = rpc; | |
} | |
const needStat = e.isUnknown() || this.opts.stat; | |
const s = needStat ? await e.lstat() : e; | |
if (this.opts.follow && this.opts.nodir && s?.isSymbolicLink()) { | |
const target = await s.realpath(); | |
/* c8 ignore start */ | |
if (target && (target.isUnknown() || this.opts.stat)) { | |
await target.lstat(); | |
} | |
/* c8 ignore stop */ | |
} | |
return this.matchCheckTest(s, ifDir); | |
} | |
matchCheckTest(e, ifDir) { | |
return (e && | |
(this.maxDepth === Infinity || e.depth() <= this.maxDepth) && | |
(!ifDir || e.canReaddir()) && | |
(!this.opts.nodir || !e.isDirectory()) && | |
(!this.opts.nodir || | |
!this.opts.follow || | |
!e.isSymbolicLink() || | |
!e.realpathCached()?.isDirectory()) && | |
!this.#ignored(e)) ? | |
e | |
: undefined; | |
} | |
matchCheckSync(e, ifDir) { | |
if (ifDir && this.opts.nodir) | |
return undefined; | |
let rpc; | |
if (this.opts.realpath) { | |
rpc = e.realpathCached() || e.realpathSync(); | |
if (!rpc) | |
return undefined; | |
e = rpc; | |
} | |
const needStat = e.isUnknown() || this.opts.stat; | |
const s = needStat ? e.lstatSync() : e; | |
if (this.opts.follow && this.opts.nodir && s?.isSymbolicLink()) { | |
const target = s.realpathSync(); | |
if (target && (target?.isUnknown() || this.opts.stat)) { | |
target.lstatSync(); | |
} | |
} | |
return this.matchCheckTest(s, ifDir); | |
} | |
matchFinish(e, absolute) { | |
if (this.#ignored(e)) | |
return; | |
// we know we have an ignore if this is false, but TS doesn't | |
if (!this.includeChildMatches && this.#ignore?.add) { | |
const ign = `${e.relativePosix()}/**`; | |
this.#ignore.add(ign); | |
} | |
const abs = this.opts.absolute === undefined ? absolute : this.opts.absolute; | |
this.seen.add(e); | |
const mark = this.opts.mark && e.isDirectory() ? this.#sep : ''; | |
// ok, we have what we need! | |
if (this.opts.withFileTypes) { | |
this.matchEmit(e); | |
} | |
else if (abs) { | |
const abs = this.opts.posix ? e.fullpathPosix() : e.fullpath(); | |
this.matchEmit(abs + mark); | |
} | |
else { | |
const rel = this.opts.posix ? e.relativePosix() : e.relative(); | |
const pre = this.opts.dotRelative && !rel.startsWith('..' + this.#sep) ? | |
'.' + this.#sep | |
: ''; | |
this.matchEmit(!rel ? '.' + mark : pre + rel + mark); | |
} | |
} | |
async match(e, absolute, ifDir) { | |
const p = await this.matchCheck(e, ifDir); | |
if (p) | |
this.matchFinish(p, absolute); | |
} | |
matchSync(e, absolute, ifDir) { | |
const p = this.matchCheckSync(e, ifDir); | |
if (p) | |
this.matchFinish(p, absolute); | |
} | |
walkCB(target, patterns, cb) { | |
/* c8 ignore start */ | |
if (this.signal?.aborted) | |
cb(); | |
/* c8 ignore stop */ | |
this.walkCB2(target, patterns, new processor_js_1.Processor(this.opts), cb); | |
} | |
walkCB2(target, patterns, processor, cb) { | |
if (this.#childrenIgnored(target)) | |
return cb(); | |
if (this.signal?.aborted) | |
cb(); | |
if (this.paused) { | |
this.onResume(() => this.walkCB2(target, patterns, processor, cb)); | |
return; | |
} | |
processor.processPatterns(target, patterns); | |
// done processing. all of the above is sync, can be abstracted out. | |
// subwalks is a map of paths to the entry filters they need | |
// matches is a map of paths to [absolute, ifDir] tuples. | |
let tasks = 1; | |
const next = () => { | |
if (--tasks === 0) | |
cb(); | |
}; | |
for (const [m, absolute, ifDir] of processor.matches.entries()) { | |
if (this.#ignored(m)) | |
continue; | |
tasks++; | |
this.match(m, absolute, ifDir).then(() => next()); | |
} | |
for (const t of processor.subwalkTargets()) { | |
if (this.maxDepth !== Infinity && t.depth() >= this.maxDepth) { | |
continue; | |
} | |
tasks++; | |
const childrenCached = t.readdirCached(); | |
if (t.calledReaddir()) | |
this.walkCB3(t, childrenCached, processor, next); | |
else { | |
t.readdirCB((_, entries) => this.walkCB3(t, entries, processor, next), true); | |
} | |
} | |
next(); | |
} | |
walkCB3(target, entries, processor, cb) { | |
processor = processor.filterEntries(target, entries); | |
let tasks = 1; | |
const next = () => { | |
if (--tasks === 0) | |
cb(); | |
}; | |
for (const [m, absolute, ifDir] of processor.matches.entries()) { | |
if (this.#ignored(m)) | |
continue; | |
tasks++; | |
this.match(m, absolute, ifDir).then(() => next()); | |
} | |
for (const [target, patterns] of processor.subwalks.entries()) { | |
tasks++; | |
this.walkCB2(target, patterns, processor.child(), next); | |
} | |
next(); | |
} | |
walkCBSync(target, patterns, cb) { | |
/* c8 ignore start */ | |
if (this.signal?.aborted) | |
cb(); | |
/* c8 ignore stop */ | |
this.walkCB2Sync(target, patterns, new processor_js_1.Processor(this.opts), cb); | |
} | |
walkCB2Sync(target, patterns, processor, cb) { | |
if (this.#childrenIgnored(target)) | |
return cb(); | |
if (this.signal?.aborted) | |
cb(); | |
if (this.paused) { | |
this.onResume(() => this.walkCB2Sync(target, patterns, processor, cb)); | |
return; | |
} | |
processor.processPatterns(target, patterns); | |
// done processing. all of the above is sync, can be abstracted out. | |
// subwalks is a map of paths to the entry filters they need | |
// matches is a map of paths to [absolute, ifDir] tuples. | |
let tasks = 1; | |
const next = () => { | |
if (--tasks === 0) | |
cb(); | |
}; | |
for (const [m, absolute, ifDir] of processor.matches.entries()) { | |
if (this.#ignored(m)) | |
continue; | |
this.matchSync(m, absolute, ifDir); | |
} | |
for (const t of processor.subwalkTargets()) { | |
if (this.maxDepth !== Infinity && t.depth() >= this.maxDepth) { | |
continue; | |
} | |
tasks++; | |
const children = t.readdirSync(); | |
this.walkCB3Sync(t, children, processor, next); | |
} | |
next(); | |
} | |
walkCB3Sync(target, entries, processor, cb) { | |
processor = processor.filterEntries(target, entries); | |
let tasks = 1; | |
const next = () => { | |
if (--tasks === 0) | |
cb(); | |
}; | |
for (const [m, absolute, ifDir] of processor.matches.entries()) { | |
if (this.#ignored(m)) | |
continue; | |
this.matchSync(m, absolute, ifDir); | |
} | |
for (const [target, patterns] of processor.subwalks.entries()) { | |
tasks++; | |
this.walkCB2Sync(target, patterns, processor.child(), next); | |
} | |
next(); | |
} | |
} | |
exports.GlobUtil = GlobUtil; | |
class GlobWalker extends GlobUtil { | |
matches = new Set(); | |
constructor(patterns, path, opts) { | |
super(patterns, path, opts); | |
} | |
matchEmit(e) { | |
this.matches.add(e); | |
} | |
async walk() { | |
if (this.signal?.aborted) | |
throw this.signal.reason; | |
if (this.path.isUnknown()) { | |
await this.path.lstat(); | |
} | |
await new Promise((res, rej) => { | |
this.walkCB(this.path, this.patterns, () => { | |
if (this.signal?.aborted) { | |
rej(this.signal.reason); | |
} | |
else { | |
res(this.matches); | |
} | |
}); | |
}); | |
return this.matches; | |
} | |
walkSync() { | |
if (this.signal?.aborted) | |
throw this.signal.reason; | |
if (this.path.isUnknown()) { | |
this.path.lstatSync(); | |
} | |
// nothing for the callback to do, because this never pauses | |
this.walkCBSync(this.path, this.patterns, () => { | |
if (this.signal?.aborted) | |
throw this.signal.reason; | |
}); | |
return this.matches; | |
} | |
} | |
exports.GlobWalker = GlobWalker; | |
class GlobStream extends GlobUtil { | |
results; | |
constructor(patterns, path, opts) { | |
super(patterns, path, opts); | |
this.results = new minipass_1.Minipass({ | |
signal: this.signal, | |
objectMode: true, | |
}); | |
this.results.on('drain', () => this.resume()); | |
this.results.on('resume', () => this.resume()); | |
} | |
matchEmit(e) { | |
this.results.write(e); | |
if (!this.results.flowing) | |
this.pause(); | |
} | |
stream() { | |
const target = this.path; | |
if (target.isUnknown()) { | |
target.lstat().then(() => { | |
this.walkCB(target, this.patterns, () => this.results.end()); | |
}); | |
} | |
else { | |
this.walkCB(target, this.patterns, () => this.results.end()); | |
} | |
return this.results; | |
} | |
streamSync() { | |
if (this.path.isUnknown()) { | |
this.path.lstatSync(); | |
} | |
this.walkCBSync(this.path, this.patterns, () => this.results.end()); | |
return this.results; | |
} | |
} | |
exports.GlobStream = GlobStream; | |
//# sourceMappingURL=walker.js.map |