Spaces:
Runtime error
Runtime error
/** | |
* @fileoverview Utility for executing npm commands. | |
* @author Ian VanSchooten | |
*/ | |
; | |
//------------------------------------------------------------------------------ | |
// Requirements | |
//------------------------------------------------------------------------------ | |
const fs = require("fs"), | |
spawn = require("cross-spawn"), | |
path = require("path"), | |
log = require("../shared/logging"); | |
//------------------------------------------------------------------------------ | |
// Helpers | |
//------------------------------------------------------------------------------ | |
/** | |
* Find the closest package.json file, starting at process.cwd (by default), | |
* and working up to root. | |
* @param {string} [startDir=process.cwd()] Starting directory | |
* @returns {string} Absolute path to closest package.json file | |
*/ | |
function findPackageJson(startDir) { | |
let dir = path.resolve(startDir || process.cwd()); | |
do { | |
const pkgFile = path.join(dir, "package.json"); | |
if (!fs.existsSync(pkgFile) || !fs.statSync(pkgFile).isFile()) { | |
dir = path.join(dir, ".."); | |
continue; | |
} | |
return pkgFile; | |
} while (dir !== path.resolve(dir, "..")); | |
return null; | |
} | |
//------------------------------------------------------------------------------ | |
// Private | |
//------------------------------------------------------------------------------ | |
/** | |
* Install node modules synchronously and save to devDependencies in package.json | |
* @param {string|string[]} packages Node module or modules to install | |
* @returns {void} | |
*/ | |
function installSyncSaveDev(packages) { | |
const packageList = Array.isArray(packages) ? packages : [packages]; | |
const npmProcess = spawn.sync("npm", ["i", "--save-dev"].concat(packageList), | |
{ stdio: "inherit" }); | |
const error = npmProcess.error; | |
if (error && error.code === "ENOENT") { | |
const pluralS = packageList.length > 1 ? "s" : ""; | |
log.error(`Could not execute npm. Please install the following package${pluralS} with a package manager of your choice: ${packageList.join(", ")}`); | |
} | |
} | |
/** | |
* Fetch `peerDependencies` of the given package by `npm show` command. | |
* @param {string} packageName The package name to fetch peerDependencies. | |
* @returns {Object} Gotten peerDependencies. Returns null if npm was not found. | |
*/ | |
function fetchPeerDependencies(packageName) { | |
const npmProcess = spawn.sync( | |
"npm", | |
["show", "--json", packageName, "peerDependencies"], | |
{ encoding: "utf8" } | |
); | |
const error = npmProcess.error; | |
if (error && error.code === "ENOENT") { | |
return null; | |
} | |
const fetchedText = npmProcess.stdout.trim(); | |
return JSON.parse(fetchedText || "{}"); | |
} | |
/** | |
* Check whether node modules are include in a project's package.json. | |
* @param {string[]} packages Array of node module names | |
* @param {Object} opt Options Object | |
* @param {boolean} opt.dependencies Set to true to check for direct dependencies | |
* @param {boolean} opt.devDependencies Set to true to check for development dependencies | |
* @param {boolean} opt.startdir Directory to begin searching from | |
* @returns {Object} An object whose keys are the module names | |
* and values are booleans indicating installation. | |
*/ | |
function check(packages, opt) { | |
const deps = new Set(); | |
const pkgJson = (opt) ? findPackageJson(opt.startDir) : findPackageJson(); | |
let fileJson; | |
if (!pkgJson) { | |
throw new Error("Could not find a package.json file. Run 'npm init' to create one."); | |
} | |
try { | |
fileJson = JSON.parse(fs.readFileSync(pkgJson, "utf8")); | |
} catch (e) { | |
const error = new Error(e); | |
error.messageTemplate = "failed-to-read-json"; | |
error.messageData = { | |
path: pkgJson, | |
message: e.message | |
}; | |
throw error; | |
} | |
["dependencies", "devDependencies"].forEach(key => { | |
if (opt[key] && typeof fileJson[key] === "object") { | |
Object.keys(fileJson[key]).forEach(dep => deps.add(dep)); | |
} | |
}); | |
return packages.reduce((status, pkg) => { | |
status[pkg] = deps.has(pkg); | |
return status; | |
}, {}); | |
} | |
/** | |
* Check whether node modules are included in the dependencies of a project's | |
* package.json. | |
* | |
* Convenience wrapper around check(). | |
* @param {string[]} packages Array of node modules to check. | |
* @param {string} rootDir The directory containing a package.json | |
* @returns {Object} An object whose keys are the module names | |
* and values are booleans indicating installation. | |
*/ | |
function checkDeps(packages, rootDir) { | |
return check(packages, { dependencies: true, startDir: rootDir }); | |
} | |
/** | |
* Check whether node modules are included in the devDependencies of a project's | |
* package.json. | |
* | |
* Convenience wrapper around check(). | |
* @param {string[]} packages Array of node modules to check. | |
* @returns {Object} An object whose keys are the module names | |
* and values are booleans indicating installation. | |
*/ | |
function checkDevDeps(packages) { | |
return check(packages, { devDependencies: true }); | |
} | |
/** | |
* Check whether package.json is found in current path. | |
* @param {string} [startDir] Starting directory | |
* @returns {boolean} Whether a package.json is found in current path. | |
*/ | |
function checkPackageJson(startDir) { | |
return !!findPackageJson(startDir); | |
} | |
//------------------------------------------------------------------------------ | |
// Public Interface | |
//------------------------------------------------------------------------------ | |
module.exports = { | |
installSyncSaveDev, | |
fetchPeerDependencies, | |
checkDeps, | |
checkDevDeps, | |
checkPackageJson | |
}; | |