You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

82 lines
2.0 KiB
JavaScript

'use strict';
const fs = require("fs");
const path = require("path");
const defaultValue = require("default-value");
let defaultCheckers = {
"package.json": function(directory) {
let parentPackageFile = path.join(directory, `..${path.sep}..${path.sep}package.json`);
try {
fs.accessSync(parentPackageFile);
return false;
} catch (err) {
return true;
}
},
"vcs": function(directory) {
let files = fs.readdirSync(directory);
return [".git", ".hg"].some((target) => {
return (files.indexOf(target) !== -1);
});
}
}
function reachedRoot(candidatePath) {
/* Returns true if we hit the root of the filesystem; like / or C:\ */
return /^(?:\/|a-z:\\)$/i.test(candidatePath);
}
module.exports = function(options) {
let currentPath = defaultValue(options.basePath, __dirname);
let checker = defaultValue(options.checker, "package.json");
if (typeof checker === "string") {
if (defaultCheckers[checker] != null) {
checker = defaultCheckers[checker];
} else {
throw new Error("No such checker exists.");
}
}
/* Locate the first thing that has a package.json, in case we're in a
* subdirectory within a module. Only after we've found the nearest
* package.json, we will start using the checker.
*/
let baseFound = false;
while (baseFound === false) {
baseFound = (fs.readdirSync(currentPath).indexOf("package.json") !== -1);
if (reachedRoot(currentPath)) {
throw new Error("Reached filesystem root without encountering a module");
}
if (baseFound === false) {
currentPath = path.join(currentPath, "..");
}
}
let rootFound = false;
while (rootFound === false) {
rootFound = checker(currentPath);
if (rootFound !== true && rootFound !== false) {
throw new Error("Checker must always return either true or false");
}
if (reachedRoot(currentPath)) {
throw new Error("Reached filesystem root without encountering the project root");
}
if (rootFound === false) {
currentPath = path.join(currentPath, `..${path.sep}..`);
}
}
return currentPath;
}