Object.defineProperty(exports, '__esModule', { value: true }); const path = require('path'); const node_url = require('url'); const process = require('process'); const fs = require('fs'); /* How it works: `this.#head` is an instance of `Node` which keeps track of its current value and nests another instance of `Node` that keeps the value that comes after it. When a value is provided to `.enqueue()`, the code needs to iterate through `this.#head`, going deeper and deeper to find the last value. However, iterating through every single item is slow. This problem is solved by saving a reference to the last value as `this.#tail` so that it can reference it to add a new value. */ class Node { value; next; constructor(value) { this.value = value; } } class Queue { #head; #tail; #size; constructor() { this.clear(); } enqueue(value) { const node = new Node(value); if (this.#head) { this.#tail.next = node; this.#tail = node; } else { this.#head = node; this.#tail = node; } this.#size++; } dequeue() { const current = this.#head; if (!current) { return; } this.#head = this.#head.next; this.#size--; return current.value; } clear() { this.#head = undefined; this.#tail = undefined; this.#size = 0; } get size() { return this.#size; } * [Symbol.iterator]() { let current = this.#head; while (current) { yield current.value; current = current.next; } } } function pLimit(concurrency) { if (!((Number.isInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency > 0)) { throw new TypeError('Expected `concurrency` to be a number from 1 and up'); } const queue = new Queue(); let activeCount = 0; const next = () => { activeCount--; if (queue.size > 0) { queue.dequeue()(); } }; const run = async (fn, resolve, args) => { activeCount++; const result = (async () => fn(...args))(); resolve(result); try { await result; } catch {} next(); }; const enqueue = (fn, resolve, args) => { queue.enqueue(run.bind(undefined, fn, resolve, args)); (async () => { // This function needs to wait until the next microtask before comparing // `activeCount` to `concurrency`, because `activeCount` is updated asynchronously // when the run function is dequeued and called. The comparison in the if-statement // needs to happen asynchronously as well to get an up-to-date value for `activeCount`. await Promise.resolve(); if (activeCount < concurrency && queue.size > 0) { queue.dequeue()(); } })(); }; const generator = (fn, ...args) => new Promise(resolve => { enqueue(fn, resolve, args); }); Object.defineProperties(generator, { activeCount: { get: () => activeCount, }, pendingCount: { get: () => queue.size, }, clearQueue: { value: () => { queue.clear(); }, }, }); return generator; } class EndError extends Error { constructor(value) { super(); this.value = value; } } // The input can also be a promise, so we await it. const testElement = async (element, tester) => tester(await element); // The input can also be a promise, so we `Promise.all()` them both. const finder = async element => { const values = await Promise.all(element); if (values[1] === true) { throw new EndError(values[0]); } return false; }; async function pLocate( iterable, tester, { concurrency = Number.POSITIVE_INFINITY, preserveOrder = true, } = {}, ) { const limit = pLimit(concurrency); // Start all the promises concurrently with optional limit. const items = [...iterable].map(element => [element, limit(testElement, element, tester)]); // Check the promises either serially or concurrently. const checkLimit = pLimit(preserveOrder ? 1 : Number.POSITIVE_INFINITY); try { await Promise.all(items.map(element => checkLimit(finder, element))); } catch (error) { if (error instanceof EndError) { return error.value; } throw error; } } const typeMappings = { directory: 'isDirectory', file: 'isFile', }; function checkType(type) { if (type in typeMappings) { return; } throw new Error(`Invalid type specified: ${type}`); } const matchType = (type, stat) => type === undefined || stat[typeMappings[type]](); const toPath$1 = urlOrPath => urlOrPath instanceof URL ? node_url.fileURLToPath(urlOrPath) : urlOrPath; async function locatePath( paths, { cwd = process.cwd(), type = 'file', allowSymlinks = true, concurrency, preserveOrder, } = {}, ) { checkType(type); cwd = toPath$1(cwd); const statFunction = allowSymlinks ? fs.promises.stat : fs.promises.lstat; return pLocate(paths, async path_ => { try { const stat = await statFunction(path.resolve(cwd, path_)); return matchType(type, stat); } catch { return false; } }, {concurrency, preserveOrder}); } function locatePathSync( paths, { cwd = process.cwd(), type = 'file', allowSymlinks = true, } = {}, ) { checkType(type); cwd = toPath$1(cwd); const statFunction = allowSymlinks ? fs.statSync : fs.lstatSync; for (const path_ of paths) { try { const stat = statFunction(path.resolve(cwd, path_)); if (matchType(type, stat)) { return path_; } } catch {} } } async function pathExists(path) { try { await fs.promises.access(path); return true; } catch { return false; } } function pathExistsSync(path) { try { fs.accessSync(path); return true; } catch { return false; } } const toPath = urlOrPath => urlOrPath instanceof URL ? node_url.fileURLToPath(urlOrPath) : urlOrPath; const findUpStop = Symbol('findUpStop'); async function findUpMultiple(name, options = {}) { let directory = path.resolve(toPath(options.cwd) || ''); const {root} = path.parse(directory); const stopAt = path.resolve(directory, options.stopAt || root); const limit = options.limit || Number.POSITIVE_INFINITY; const paths = [name].flat(); const runMatcher = async locateOptions => { if (typeof name !== 'function') { return locatePath(paths, locateOptions); } const foundPath = await name(locateOptions.cwd); if (typeof foundPath === 'string') { return locatePath([foundPath], locateOptions); } return foundPath; }; const matches = []; // eslint-disable-next-line no-constant-condition while (true) { // eslint-disable-next-line no-await-in-loop const foundPath = await runMatcher({...options, cwd: directory}); if (foundPath === findUpStop) { break; } if (foundPath) { matches.push(path.resolve(directory, foundPath)); } if (directory === stopAt || matches.length >= limit) { break; } directory = path.dirname(directory); } return matches; } function findUpMultipleSync(name, options = {}) { let directory = path.resolve(toPath(options.cwd) || ''); const {root} = path.parse(directory); const stopAt = options.stopAt || root; const limit = options.limit || Number.POSITIVE_INFINITY; const paths = [name].flat(); const runMatcher = locateOptions => { if (typeof name !== 'function') { return locatePathSync(paths, locateOptions); } const foundPath = name(locateOptions.cwd); if (typeof foundPath === 'string') { return locatePathSync([foundPath], locateOptions); } return foundPath; }; const matches = []; // eslint-disable-next-line no-constant-condition while (true) { const foundPath = runMatcher({...options, cwd: directory}); if (foundPath === findUpStop) { break; } if (foundPath) { matches.push(path.resolve(directory, foundPath)); } if (directory === stopAt || matches.length >= limit) { break; } directory = path.dirname(directory); } return matches; } async function findUp(name, options = {}) { const matches = await findUpMultiple(name, {...options, limit: 1}); return matches[0]; } function findUpSync(name, options = {}) { const matches = findUpMultipleSync(name, {...options, limit: 1}); return matches[0]; } exports.findUp = findUp; exports.findUpMultiple = findUpMultiple; exports.findUpMultipleSync = findUpMultipleSync; exports.findUpStop = findUpStop; exports.findUpSync = findUpSync; exports.pathExists = pathExists; exports.pathExistsSync = pathExistsSync;