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