UNPKG

12.3 kBJavaScriptView Raw
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 const s = needStat ? await e.lstat() : e;
100 if (this.opts.follow && this.opts.nodir && s?.isSymbolicLink()) {
101 const target = await s.realpath();
102 /* c8 ignore start */
103 if (target && (target.isUnknown() || this.opts.stat)) {
104 await target.lstat();
105 }
106 /* c8 ignore stop */
107 }
108 return this.matchCheckTest(s, ifDir);
109 }
110 matchCheckTest(e, ifDir) {
111 return e &&
112 (this.maxDepth === Infinity || e.depth() <= this.maxDepth) &&
113 (!ifDir || e.canReaddir()) &&
114 (!this.opts.nodir || !e.isDirectory()) &&
115 (!this.opts.nodir ||
116 !this.opts.follow ||
117 !e.isSymbolicLink() ||
118 !e.realpathCached()?.isDirectory()) &&
119 !this.#ignored(e)
120 ? e
121 : undefined;
122 }
123 matchCheckSync(e, ifDir) {
124 if (ifDir && this.opts.nodir)
125 return undefined;
126 let rpc;
127 if (this.opts.realpath) {
128 rpc = e.realpathCached() || e.realpathSync();
129 if (!rpc)
130 return undefined;
131 e = rpc;
132 }
133 const needStat = e.isUnknown() || this.opts.stat;
134 const s = needStat ? e.lstatSync() : e;
135 if (this.opts.follow && this.opts.nodir && s?.isSymbolicLink()) {
136 const target = s.realpathSync();
137 if (target && (target?.isUnknown() || this.opts.stat)) {
138 target.lstatSync();
139 }
140 }
141 return this.matchCheckTest(s, ifDir);
142 }
143 matchFinish(e, absolute) {
144 if (this.#ignored(e))
145 return;
146 const abs = this.opts.absolute === undefined ? absolute : this.opts.absolute;
147 this.seen.add(e);
148 const mark = this.opts.mark && e.isDirectory() ? this.#sep : '';
149 // ok, we have what we need!
150 if (this.opts.withFileTypes) {
151 this.matchEmit(e);
152 }
153 else if (abs) {
154 const abs = this.opts.posix ? e.fullpathPosix() : e.fullpath();
155 this.matchEmit(abs + mark);
156 }
157 else {
158 const rel = this.opts.posix ? e.relativePosix() : e.relative();
159 const pre = this.opts.dotRelative && !rel.startsWith('..' + this.#sep)
160 ? '.' + this.#sep
161 : '';
162 this.matchEmit(!rel ? '.' + mark : pre + rel + mark);
163 }
164 }
165 async match(e, absolute, ifDir) {
166 const p = await this.matchCheck(e, ifDir);
167 if (p)
168 this.matchFinish(p, absolute);
169 }
170 matchSync(e, absolute, ifDir) {
171 const p = this.matchCheckSync(e, ifDir);
172 if (p)
173 this.matchFinish(p, absolute);
174 }
175 walkCB(target, patterns, cb) {
176 /* c8 ignore start */
177 if (this.signal?.aborted)
178 cb();
179 /* c8 ignore stop */
180 this.walkCB2(target, patterns, new processor_js_1.Processor(this.opts), cb);
181 }
182 walkCB2(target, patterns, processor, cb) {
183 if (this.#childrenIgnored(target))
184 return cb();
185 if (this.signal?.aborted)
186 cb();
187 if (this.paused) {
188 this.onResume(() => this.walkCB2(target, patterns, processor, cb));
189 return;
190 }
191 processor.processPatterns(target, patterns);
192 // done processing. all of the above is sync, can be abstracted out.
193 // subwalks is a map of paths to the entry filters they need
194 // matches is a map of paths to [absolute, ifDir] tuples.
195 let tasks = 1;
196 const next = () => {
197 if (--tasks === 0)
198 cb();
199 };
200 for (const [m, absolute, ifDir] of processor.matches.entries()) {
201 if (this.#ignored(m))
202 continue;
203 tasks++;
204 this.match(m, absolute, ifDir).then(() => next());
205 }
206 for (const t of processor.subwalkTargets()) {
207 if (this.maxDepth !== Infinity && t.depth() >= this.maxDepth) {
208 continue;
209 }
210 tasks++;
211 const childrenCached = t.readdirCached();
212 if (t.calledReaddir())
213 this.walkCB3(t, childrenCached, processor, next);
214 else {
215 t.readdirCB((_, entries) => this.walkCB3(t, entries, processor, next), true);
216 }
217 }
218 next();
219 }
220 walkCB3(target, entries, processor, cb) {
221 processor = processor.filterEntries(target, entries);
222 let tasks = 1;
223 const next = () => {
224 if (--tasks === 0)
225 cb();
226 };
227 for (const [m, absolute, ifDir] of processor.matches.entries()) {
228 if (this.#ignored(m))
229 continue;
230 tasks++;
231 this.match(m, absolute, ifDir).then(() => next());
232 }
233 for (const [target, patterns] of processor.subwalks.entries()) {
234 tasks++;
235 this.walkCB2(target, patterns, processor.child(), next);
236 }
237 next();
238 }
239 walkCBSync(target, patterns, cb) {
240 /* c8 ignore start */
241 if (this.signal?.aborted)
242 cb();
243 /* c8 ignore stop */
244 this.walkCB2Sync(target, patterns, new processor_js_1.Processor(this.opts), cb);
245 }
246 walkCB2Sync(target, patterns, processor, cb) {
247 if (this.#childrenIgnored(target))
248 return cb();
249 if (this.signal?.aborted)
250 cb();
251 if (this.paused) {
252 this.onResume(() => this.walkCB2Sync(target, patterns, processor, cb));
253 return;
254 }
255 processor.processPatterns(target, patterns);
256 // done processing. all of the above is sync, can be abstracted out.
257 // subwalks is a map of paths to the entry filters they need
258 // matches is a map of paths to [absolute, ifDir] tuples.
259 let tasks = 1;
260 const next = () => {
261 if (--tasks === 0)
262 cb();
263 };
264 for (const [m, absolute, ifDir] of processor.matches.entries()) {
265 if (this.#ignored(m))
266 continue;
267 this.matchSync(m, absolute, ifDir);
268 }
269 for (const t of processor.subwalkTargets()) {
270 if (this.maxDepth !== Infinity && t.depth() >= this.maxDepth) {
271 continue;
272 }
273 tasks++;
274 const children = t.readdirSync();
275 this.walkCB3Sync(t, children, processor, next);
276 }
277 next();
278 }
279 walkCB3Sync(target, entries, processor, cb) {
280 processor = processor.filterEntries(target, entries);
281 let tasks = 1;
282 const next = () => {
283 if (--tasks === 0)
284 cb();
285 };
286 for (const [m, absolute, ifDir] of processor.matches.entries()) {
287 if (this.#ignored(m))
288 continue;
289 this.matchSync(m, absolute, ifDir);
290 }
291 for (const [target, patterns] of processor.subwalks.entries()) {
292 tasks++;
293 this.walkCB2Sync(target, patterns, processor.child(), next);
294 }
295 next();
296 }
297}
298exports.GlobUtil = GlobUtil;
299class GlobWalker extends GlobUtil {
300 matches;
301 constructor(patterns, path, opts) {
302 super(patterns, path, opts);
303 this.matches = new Set();
304 }
305 matchEmit(e) {
306 this.matches.add(e);
307 }
308 async walk() {
309 if (this.signal?.aborted)
310 throw this.signal.reason;
311 if (this.path.isUnknown()) {
312 await this.path.lstat();
313 }
314 await new Promise((res, rej) => {
315 this.walkCB(this.path, this.patterns, () => {
316 if (this.signal?.aborted) {
317 rej(this.signal.reason);
318 }
319 else {
320 res(this.matches);
321 }
322 });
323 });
324 return this.matches;
325 }
326 walkSync() {
327 if (this.signal?.aborted)
328 throw this.signal.reason;
329 if (this.path.isUnknown()) {
330 this.path.lstatSync();
331 }
332 // nothing for the callback to do, because this never pauses
333 this.walkCBSync(this.path, this.patterns, () => {
334 if (this.signal?.aborted)
335 throw this.signal.reason;
336 });
337 return this.matches;
338 }
339}
340exports.GlobWalker = GlobWalker;
341class GlobStream extends GlobUtil {
342 results;
343 constructor(patterns, path, opts) {
344 super(patterns, path, opts);
345 this.results = new minipass_1.Minipass({
346 signal: this.signal,
347 objectMode: true,
348 });
349 this.results.on('drain', () => this.resume());
350 this.results.on('resume', () => this.resume());
351 }
352 matchEmit(e) {
353 this.results.write(e);
354 if (!this.results.flowing)
355 this.pause();
356 }
357 stream() {
358 const target = this.path;
359 if (target.isUnknown()) {
360 target.lstat().then(() => {
361 this.walkCB(target, this.patterns, () => this.results.end());
362 });
363 }
364 else {
365 this.walkCB(target, this.patterns, () => this.results.end());
366 }
367 return this.results;
368 }
369 streamSync() {
370 if (this.path.isUnknown()) {
371 this.path.lstatSync();
372 }
373 this.walkCBSync(this.path, this.patterns, () => this.results.end());
374 return this.results;
375 }
376}
377exports.GlobStream = GlobStream;
378//# sourceMappingURL=walker.js.map
\No newline at end of file