walker.js 11 KB

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