1"use strict"; 2Object.defineProperty(exports, "__esModule", { value: true }); 3exports.GlobStream = exports.GlobWalker = exports.GlobUtil = void 0; 4/** 5 * Single-use utility classes to provide functionality to the {@link Glob} 6 * methods. 7 * 8 * @module 9 */ 10const minipass_1 = require("minipass"); 11const ignore_js_1 = require("./ignore.js"); 12const processor_js_1 = require("./processor.js"); 13const makeIgnore = (ignore, opts) => typeof ignore === 'string' 14 ? new ignore_js_1.Ignore([ignore], opts) 15 : Array.isArray(ignore) 16 ? new ignore_js_1.Ignore(ignore, opts) 17 : ignore; 18/** 19 * basic walking utilities that all the glob walker types use 20 */ 21class GlobUtil { 22 path; 23 patterns; 24 opts; 25 seen = new Set(); 26 paused = false; 27 aborted = false; 28 #onResume = []; 29 #ignore; 30 #sep; 31 signal; 32 maxDepth; 33 constructor(patterns, path, opts) { 34 this.patterns = patterns; 35 this.path = path; 36 this.opts = opts; 37 this.#sep = !opts.posix && opts.platform === 'win32' ? '\\' : '/'; 38 if (opts.ignore) { 39 this.#ignore = makeIgnore(opts.ignore, opts); 40 } 41 // ignore, always set with maxDepth, but it's optional on the 42 // GlobOptions type 43 /* c8 ignore start */ 44 this.maxDepth = opts.maxDepth || Infinity; 45 /* c8 ignore stop */ 46 if (opts.signal) { 47 this.signal = opts.signal; 48 this.signal.addEventListener('abort', () => { 49 this.#onResume.length = 0; 50 }); 51 } 52 } 53 #ignored(path) { 54 return this.seen.has(path) || !!this.#ignore?.ignored?.(path); 55 } 56 #childrenIgnored(path) { 57 return !!this.#ignore?.childrenIgnored?.(path); 58 } 59 // backpressure mechanism 60 pause() { 61 this.paused = true; 62 } 63 resume() { 64 /* c8 ignore start */ 65 if (this.signal?.aborted) 66 return; 67 /* c8 ignore stop */ 68 this.paused = false; 69 let fn = undefined; 70 while (!this.paused && (fn = this.#onResume.shift())) { 71 fn(); 72 } 73 } 74 onResume(fn) { 75 if (this.signal?.aborted) 76 return; 77 /* c8 ignore start */ 78 if (!this.paused) { 79 fn(); 80 } 81 else { 82 /* c8 ignore stop */ 83 this.#onResume.push(fn); 84 } 85 } 86 // do the requisite realpath/stat checking, and return the path 87 // to add or undefined to filter it out. 88 async matchCheck(e, ifDir) { 89 if (ifDir && this.opts.nodir) 90 return undefined; 91 let rpc; 92 if (this.opts.realpath) { 93 rpc = e.realpathCached() || (await e.realpath()); 94 if (!rpc) 95 return undefined; 96 e = rpc; 97 } 98 const needStat = e.isUnknown() || this.opts.stat; 99 return this.matchCheckTest(needStat ? await e.lstat() : e, ifDir); 100 } 101 matchCheckTest(e, ifDir) { 102 return e && 103 (this.maxDepth === Infinity || e.depth() <= this.maxDepth) && 104 (!ifDir || e.canReaddir()) && 105 (!this.opts.nodir || !e.isDirectory()) && 106 !this.#ignored(e) 107 ? e 108 : undefined; 109 } 110 matchCheckSync(e, ifDir) { 111 if (ifDir && this.opts.nodir) 112 return undefined; 113 let rpc; 114 if (this.opts.realpath) { 115 rpc = e.realpathCached() || e.realpathSync(); 116 if (!rpc) 117 return undefined; 118 e = rpc; 119 } 120 const needStat = e.isUnknown() || this.opts.stat; 121 return this.matchCheckTest(needStat ? e.lstatSync() : e, ifDir); 122 } 123 matchFinish(e, absolute) { 124 if (this.#ignored(e)) 125 return; 126 const abs = this.opts.absolute === undefined ? absolute : this.opts.absolute; 127 this.seen.add(e); 128 const mark = this.opts.mark && e.isDirectory() ? this.#sep : ''; 129 // ok, we have what we need! 130 if (this.opts.withFileTypes) { 131 this.matchEmit(e); 132 } 133 else if (abs) { 134 const abs = this.opts.posix ? e.fullpathPosix() : e.fullpath(); 135 this.matchEmit(abs + mark); 136 } 137 else { 138 const rel = this.opts.posix ? e.relativePosix() : e.relative(); 139 const pre = this.opts.dotRelative && !rel.startsWith('..' + this.#sep) 140 ? '.' + this.#sep 141 : ''; 142 this.matchEmit(!rel ? '.' + mark : pre + rel + mark); 143 } 144 } 145 async match(e, absolute, ifDir) { 146 const p = await this.matchCheck(e, ifDir); 147 if (p) 148 this.matchFinish(p, absolute); 149 } 150 matchSync(e, absolute, ifDir) { 151 const p = this.matchCheckSync(e, ifDir); 152 if (p) 153 this.matchFinish(p, absolute); 154 } 155 walkCB(target, patterns, cb) { 156 /* c8 ignore start */ 157 if (this.signal?.aborted) 158 cb(); 159 /* c8 ignore stop */ 160 this.walkCB2(target, patterns, new processor_js_1.Processor(this.opts), cb); 161 } 162 walkCB2(target, patterns, processor, cb) { 163 if (this.#childrenIgnored(target)) 164 return cb(); 165 if (this.signal?.aborted) 166 cb(); 167 if (this.paused) { 168 this.onResume(() => this.walkCB2(target, patterns, processor, cb)); 169 return; 170 } 171 processor.processPatterns(target, patterns); 172 // done processing. all of the above is sync, can be abstracted out. 173 // subwalks is a map of paths to the entry filters they need 174 // matches is a map of paths to [absolute, ifDir] tuples. 175 let tasks = 1; 176 const next = () => { 177 if (--tasks === 0) 178 cb(); 179 }; 180 for (const [m, absolute, ifDir] of processor.matches.entries()) { 181 if (this.#ignored(m)) 182 continue; 183 tasks++; 184 this.match(m, absolute, ifDir).then(() => next()); 185 } 186 for (const t of processor.subwalkTargets()) { 187 if (this.maxDepth !== Infinity && t.depth() >= this.maxDepth) { 188 continue; 189 } 190 tasks++; 191 const childrenCached = t.readdirCached(); 192 if (t.calledReaddir()) 193 this.walkCB3(t, childrenCached, processor, next); 194 else { 195 t.readdirCB((_, entries) => this.walkCB3(t, entries, processor, next), true); 196 } 197 } 198 next(); 199 } 200 walkCB3(target, entries, processor, cb) { 201 processor = processor.filterEntries(target, entries); 202 let tasks = 1; 203 const next = () => { 204 if (--tasks === 0) 205 cb(); 206 }; 207 for (const [m, absolute, ifDir] of processor.matches.entries()) { 208 if (this.#ignored(m)) 209 continue; 210 tasks++; 211 this.match(m, absolute, ifDir).then(() => next()); 212 } 213 for (const [target, patterns] of processor.subwalks.entries()) { 214 tasks++; 215 this.walkCB2(target, patterns, processor.child(), next); 216 } 217 next(); 218 } 219 walkCBSync(target, patterns, cb) { 220 /* c8 ignore start */ 221 if (this.signal?.aborted) 222 cb(); 223 /* c8 ignore stop */ 224 this.walkCB2Sync(target, patterns, new processor_js_1.Processor(this.opts), cb); 225 } 226 walkCB2Sync(target, patterns, processor, cb) { 227 if (this.#childrenIgnored(target)) 228 return cb(); 229 if (this.signal?.aborted) 230 cb(); 231 if (this.paused) { 232 this.onResume(() => this.walkCB2Sync(target, patterns, processor, cb)); 233 return; 234 } 235 processor.processPatterns(target, patterns); 236 // done processing. all of the above is sync, can be abstracted out. 237 // subwalks is a map of paths to the entry filters they need 238 // matches is a map of paths to [absolute, ifDir] tuples. 239 let tasks = 1; 240 const next = () => { 241 if (--tasks === 0) 242 cb(); 243 }; 244 for (const [m, absolute, ifDir] of processor.matches.entries()) { 245 if (this.#ignored(m)) 246 continue; 247 this.matchSync(m, absolute, ifDir); 248 } 249 for (const t of processor.subwalkTargets()) { 250 if (this.maxDepth !== Infinity && t.depth() >= this.maxDepth) { 251 continue; 252 } 253 tasks++; 254 const children = t.readdirSync(); 255 this.walkCB3Sync(t, children, processor, next); 256 } 257 next(); 258 } 259 walkCB3Sync(target, entries, processor, cb) { 260 processor = processor.filterEntries(target, entries); 261 let tasks = 1; 262 const next = () => { 263 if (--tasks === 0) 264 cb(); 265 }; 266 for (const [m, absolute, ifDir] of processor.matches.entries()) { 267 if (this.#ignored(m)) 268 continue; 269 this.matchSync(m, absolute, ifDir); 270 } 271 for (const [target, patterns] of processor.subwalks.entries()) { 272 tasks++; 273 this.walkCB2Sync(target, patterns, processor.child(), next); 274 } 275 next(); 276 } 277} 278exports.GlobUtil = GlobUtil; 279class GlobWalker extends GlobUtil { 280 matches; 281 constructor(patterns, path, opts) { 282 super(patterns, path, opts); 283 this.matches = new Set(); 284 } 285 matchEmit(e) { 286 this.matches.add(e); 287 } 288 async walk() { 289 if (this.signal?.aborted) 290 throw this.signal.reason; 291 if (this.path.isUnknown()) { 292 await this.path.lstat(); 293 } 294 await new Promise((res, rej) => { 295 this.walkCB(this.path, this.patterns, () => { 296 if (this.signal?.aborted) { 297 rej(this.signal.reason); 298 } 299 else { 300 res(this.matches); 301 } 302 }); 303 }); 304 return this.matches; 305 } 306 walkSync() { 307 if (this.signal?.aborted) 308 throw this.signal.reason; 309 if (this.path.isUnknown()) { 310 this.path.lstatSync(); 311 } 312 // nothing for the callback to do, because this never pauses 313 this.walkCBSync(this.path, this.patterns, () => { 314 if (this.signal?.aborted) 315 throw this.signal.reason; 316 }); 317 return this.matches; 318 } 319} 320exports.GlobWalker = GlobWalker; 321class GlobStream extends GlobUtil { 322 results; 323 constructor(patterns, path, opts) { 324 super(patterns, path, opts); 325 this.results = new minipass_1.Minipass({ 326 signal: this.signal, 327 objectMode: true, 328 }); 329 this.results.on('drain', () => this.resume()); 330 this.results.on('resume', () => this.resume()); 331 } 332 matchEmit(e) { 333 this.results.write(e); 334 if (!this.results.flowing) 335 this.pause(); 336 } 337 stream() { 338 const target = this.path; 339 if (target.isUnknown()) { 340 target.lstat().then(() => { 341 this.walkCB(target, this.patterns, () => this.results.end()); 342 }); 343 } 344 else { 345 this.walkCB(target, this.patterns, () => this.results.end()); 346 } 347 return this.results; 348 } 349 streamSync() { 350 if (this.path.isUnknown()) { 351 this.path.lstatSync(); 352 } 353 this.walkCBSync(this.path, this.patterns, () => this.results.end()); 354 return this.results; 355 } 356} 357exports.GlobStream = GlobStream; 358//# sourceMappingURL=walker.js.map