|
import path, { dirname, win32, join } from 'node:path'; |
|
import fs, { promises } from 'node:fs'; |
|
import fsp from 'node:fs/promises'; |
|
import process from 'node:process'; |
|
import { interopDefault, resolvePathSync } from 'mlly'; |
|
import { fileURLToPath } from 'node:url'; |
|
|
|
|
|
|
|
|
|
|
|
|
|
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 () => { |
|
|
|
|
|
|
|
|
|
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; |
|
} |
|
} |
|
|
|
|
|
const testElement = async (element, tester) => tester(await element); |
|
|
|
|
|
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); |
|
|
|
|
|
const items = [...iterable].map(element => [element, limit(testElement, element, tester)]); |
|
|
|
|
|
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 (Object.hasOwnProperty.call(typeMappings, type)) { |
|
return; |
|
} |
|
|
|
throw new Error(`Invalid type specified: ${type}`); |
|
} |
|
|
|
const matchType = (type, stat) => stat[typeMappings[type]](); |
|
|
|
const toPath$1 = urlOrPath => urlOrPath instanceof 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 ? promises.stat : 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}); |
|
} |
|
|
|
const toPath = urlOrPath => urlOrPath instanceof 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 = []; |
|
|
|
while (true) { |
|
|
|
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; |
|
} |
|
|
|
async function findUp(name, options = {}) { |
|
const matches = await findUpMultiple(name, {...options, limit: 1}); |
|
return matches[0]; |
|
} |
|
|
|
function _resolve(path, options = {}) { |
|
if (options.platform === "auto" || !options.platform) |
|
options.platform = process.platform === "win32" ? "win32" : "posix"; |
|
const modulePath = resolvePathSync(path, { |
|
url: options.paths |
|
}); |
|
if (options.platform === "win32") |
|
return win32.normalize(modulePath); |
|
return modulePath; |
|
} |
|
function resolveModule(name, options = {}) { |
|
try { |
|
return _resolve(name, options); |
|
} catch (e) { |
|
return void 0; |
|
} |
|
} |
|
async function importModule(path) { |
|
const i = await import(path); |
|
if (i) |
|
return interopDefault(i); |
|
return i; |
|
} |
|
function isPackageExists(name, options = {}) { |
|
return !!resolvePackage(name, options); |
|
} |
|
function getPackageJsonPath(name, options = {}) { |
|
const entry = resolvePackage(name, options); |
|
if (!entry) |
|
return; |
|
return searchPackageJSON(entry); |
|
} |
|
async function getPackageInfo(name, options = {}) { |
|
const packageJsonPath = getPackageJsonPath(name, options); |
|
if (!packageJsonPath) |
|
return; |
|
const packageJson = JSON.parse(await fs.promises.readFile(packageJsonPath, "utf8")); |
|
return { |
|
name, |
|
version: packageJson.version, |
|
rootPath: dirname(packageJsonPath), |
|
packageJsonPath, |
|
packageJson |
|
}; |
|
} |
|
function getPackageInfoSync(name, options = {}) { |
|
const packageJsonPath = getPackageJsonPath(name, options); |
|
if (!packageJsonPath) |
|
return; |
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8")); |
|
return { |
|
name, |
|
version: packageJson.version, |
|
rootPath: dirname(packageJsonPath), |
|
packageJsonPath, |
|
packageJson |
|
}; |
|
} |
|
function resolvePackage(name, options = {}) { |
|
try { |
|
return _resolve(`${name}/package.json`, options); |
|
} catch { |
|
} |
|
try { |
|
return _resolve(name, options); |
|
} catch (e) { |
|
if (e.code !== "MODULE_NOT_FOUND" && e.code !== "ERR_MODULE_NOT_FOUND") |
|
console.error(e); |
|
return false; |
|
} |
|
} |
|
function searchPackageJSON(dir) { |
|
let packageJsonPath; |
|
while (true) { |
|
if (!dir) |
|
return; |
|
const newDir = dirname(dir); |
|
if (newDir === dir) |
|
return; |
|
dir = newDir; |
|
packageJsonPath = join(dir, "package.json"); |
|
if (fs.existsSync(packageJsonPath)) |
|
break; |
|
} |
|
return packageJsonPath; |
|
} |
|
async function loadPackageJSON(cwd = process.cwd()) { |
|
const path = await findUp("package.json", { cwd }); |
|
if (!path || !fs.existsSync(path)) |
|
return null; |
|
return JSON.parse(await fsp.readFile(path, "utf-8")); |
|
} |
|
async function isPackageListed(name, cwd) { |
|
const pkg = await loadPackageJSON(cwd) || {}; |
|
return name in (pkg.dependencies || {}) || name in (pkg.devDependencies || {}); |
|
} |
|
|
|
export { getPackageInfo, getPackageInfoSync, importModule, isPackageExists, isPackageListed, loadPackageJSON, resolveModule }; |
|
|