text: /**
   * @fileoverview Utility for executing npm commands.
   * @author Ian VanSchooten
   */
  
  "use strict";
  
  //------------------------------------------------------------------------------
  // 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) {
      let deps = [];
      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;
      }
  
      if (opt.devDependencies && typeof fileJson.devDependencies === "object") {
          deps = deps.concat(Object.keys(fileJson.devDependencies));
      }
      if (opt.dependencies && typeof fileJson.dependencies === "object") {
          deps = deps.concat(Object.keys(fileJson.dependencies));
      }
      return packages.reduce((status, pkg) => {
          status[pkg] = deps.indexOf(pkg) !== -1;
          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 contianing 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
  };
  