68 lines
2.7 KiB
JavaScript
68 lines
2.7 KiB
JavaScript
|
|
import fs from 'graceful-fs';
|
||
|
|
import path from 'node:path';
|
||
|
|
async function shouldContinueSearch(traversedPath, rootPath, stopAtPackageJSON) {
|
||
|
|
if (rootPath) {
|
||
|
|
return Promise.resolve(traversedPath !== path.dirname(rootPath));
|
||
|
|
}
|
||
|
|
else if (stopAtPackageJSON) {
|
||
|
|
return fs.existsSync(path.join(traversedPath, 'package.json'));
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
async function traverseAncestorDirectories(cwd, pathGenerator, rootPath, maxItems, stopAtPackageJSON) {
|
||
|
|
const paths = [];
|
||
|
|
let traversedPath = path.resolve(cwd);
|
||
|
|
while (await shouldContinueSearch(traversedPath, rootPath, stopAtPackageJSON)) {
|
||
|
|
const generatedPath = pathGenerator(traversedPath);
|
||
|
|
if (fs.existsSync(generatedPath)) {
|
||
|
|
paths.push(generatedPath);
|
||
|
|
}
|
||
|
|
const parentPath = path.dirname(traversedPath);
|
||
|
|
if (parentPath === traversedPath || (maxItems && paths.length >= maxItems)) {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
traversedPath = parentPath;
|
||
|
|
}
|
||
|
|
return paths;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Find all instances of a given module in node_modules subdirectories while traversing up
|
||
|
|
* ancestor directories.
|
||
|
|
*
|
||
|
|
* @param cwd the initial directory to traverse
|
||
|
|
* @param moduleName the Node module name (should work for scoped modules as well)
|
||
|
|
* @param rootPath the project's root path. If provided, the traversal will stop at this path.
|
||
|
|
*/
|
||
|
|
export async function searchForModule(cwd, moduleName, rootPath) {
|
||
|
|
const pathGenerator = (traversedPath) => path.join(traversedPath, 'node_modules', moduleName);
|
||
|
|
return traverseAncestorDirectories(cwd, pathGenerator, rootPath, undefined, true);
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Find all instances of node_modules subdirectories while traversing up ancestor directories.
|
||
|
|
*
|
||
|
|
* @param cwd the initial directory to traverse
|
||
|
|
* @param rootPath the project's root path. If provided, the traversal will stop at this path.
|
||
|
|
*/
|
||
|
|
export async function searchForNodeModules(cwd, rootPath) {
|
||
|
|
const pathGenerator = (traversedPath) => path.join(traversedPath, 'node_modules');
|
||
|
|
return traverseAncestorDirectories(cwd, pathGenerator, rootPath, undefined, true);
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Determine the root directory of a given project, by looking for a directory with an
|
||
|
|
* NPM or yarn lockfile or pnpm lockfile.
|
||
|
|
*
|
||
|
|
* @param cwd the initial directory to traverse
|
||
|
|
*/
|
||
|
|
export async function getProjectRootPath(cwd) {
|
||
|
|
for (const lockFilename of ['yarn.lock', 'package-lock.json', 'pnpm-lock.yaml']) {
|
||
|
|
const pathGenerator = (traversedPath) => path.join(traversedPath, lockFilename);
|
||
|
|
const lockPaths = await traverseAncestorDirectories(cwd, pathGenerator, undefined, 1);
|
||
|
|
if (lockPaths.length > 0) {
|
||
|
|
return path.dirname(lockPaths[0]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return cwd;
|
||
|
|
}
|
||
|
|
//# sourceMappingURL=search-module.js.map
|