'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, "..", "..", "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, "..", ".."); } } return currentPath; }