'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _regenerator;

function _load_regenerator() {
  return _regenerator = _interopRequireDefault(require('babel-runtime/regenerator'));
}

var _execa;

function _load_execa() {
  return _execa = _interopRequireDefault(require('execa'));
}

var _ramda;

function _load_ramda() {
  return _ramda = _interopRequireDefault(require('ramda'));
}

var _ramda2;

function _load_ramda2() {
  return _ramda2 = require('ramda');
}

var _chalk;

function _load_chalk() {
  return _chalk = _interopRequireDefault(require('chalk'));
}

var _fsExtra;

function _load_fsExtra() {
  return _fsExtra = _interopRequireDefault(require('fs-extra'));
}

var _path;

function _load_path() {
  return _path = _interopRequireDefault(require('path'));
}

var _logger;

function _load_logger() {
  return _logger = _interopRequireDefault(require('../logger/logger'));
}

var _constants;

function _load_constants() {
  return _constants = require('../constants');
}

var _generalError;

function _load_generalError() {
  return _generalError = _interopRequireDefault(require('../error/general-error'));
}

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }

function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }

var objectToArray = function objectToArray(obj) {
  return (0, (_ramda2 || _load_ramda2()).map)((0, (_ramda2 || _load_ramda2()).join)('@'), (0, (_ramda2 || _load_ramda2()).toPairs)(obj));
};
var rejectNils = (_ramda || _load_ramda()).default.reject((_ramda2 || _load_ramda2()).isNil);

var defaultNpmArgs = [];
var defaultYarnArgs = [];
var defaultPackageManagerArgs = {
  npm: defaultNpmArgs,
  yarn: defaultYarnArgs
};
var defaultPackageManagerProcessOptions = {
  cwd: process.cwd
};
var warningPrefix = function warningPrefix(packageManager) {
  return packageManager === 'npm' ? 'npm WARN' : 'warning';
};
var errorPrefix = function errorPrefix(packageManager) {
  return packageManager === 'npm' ? 'npm ERR!' : 'error';
};
var peerDependenciesMissing = function peerDependenciesMissing(packageManager) {
  return packageManager === 'npm' ? 'requires a peer' : 'unmet peer';
};

var stripNonNpmErrors = function stripNonNpmErrors(errors, packageManager) {
  // a workaround to remove all 'npm warn' and 'npm notice'.
  // NPM itself returns them even when --loglevel = error or when --silent/--quiet flags are set
  var prefix = errorPrefix(packageManager);
  return errors.split('\n').filter(function (error) {
    return error.startsWith(prefix);
  }).join('\n');
};

var stripNonPeerDependenciesWarnings = function stripNonPeerDependenciesWarnings(errors, packageManager) {
  var prefix = warningPrefix(packageManager);
  var peer = peerDependenciesMissing(packageManager);
  return errors.split('\n').filter(function (error) {
    return error.startsWith(prefix) && error.includes(peer);
  }).join('\n');
};

/**
 * Pick only allowed to be overridden options
 * @param {Object} userOptions
 */
var getAllowdPackageManagerProcessOptions = function getAllowdPackageManagerProcessOptions(userOptions) {
  var allowdOptions = ['shell', 'env', 'extendEnv', 'uid', 'gid', 'preferLocal', 'localDir', 'timeout'];
  return (_ramda || _load_ramda()).default.pick(allowdOptions, userOptions);
};

/**
 * Install packages in specific directory
 */
var _installInOneDirectory = function _installInOneDirectory(_ref) {
  var _ref$modules = _ref.modules,
      modules = _ref$modules === undefined ? [] : _ref$modules,
      _ref$packageManager = _ref.packageManager,
      packageManager = _ref$packageManager === undefined ? (_constants || _load_constants()).DEFAULT_PACKAGE_MANAGER : _ref$packageManager,
      _ref$packageManagerAr = _ref.packageManagerArgs,
      packageManagerArgs = _ref$packageManagerAr === undefined ? [] : _ref$packageManagerAr,
      _ref$packageManagerPr = _ref.packageManagerProcessOptions,
      packageManagerProcessOptions = _ref$packageManagerPr === undefined ? {} : _ref$packageManagerPr,
      dir = _ref.dir,
      _ref$verbose = _ref.verbose,
      verbose = _ref$verbose === undefined ? false : _ref$verbose;

  // Handle process options
  var allowedPackageManagerProcessOptions = getAllowdPackageManagerProcessOptions(packageManagerProcessOptions);
  var concretePackageManagerProcessOptions = (0, (_ramda2 || _load_ramda2()).merge)(defaultPackageManagerProcessOptions, allowedPackageManagerProcessOptions);
  concretePackageManagerProcessOptions.cwd = dir || concretePackageManagerProcessOptions.cwd;
  var cwd = concretePackageManagerProcessOptions.cwd;

  // taking care of object case
  var processedModules = (0, (_ramda2 || _load_ramda2()).is)(Object, modules) && !Array.isArray(modules) ? objectToArray(modules) : modules;

  // Handle process args
  var concretePackageManagerDefaultArgs = ['install'].concat(_toConsumableArray(processedModules), _toConsumableArray(defaultPackageManagerArgs[packageManager]));
  var concretePackageManagerArgs = rejectNils((_ramda || _load_ramda()).default.concat(concretePackageManagerDefaultArgs, packageManagerArgs));

  // Add npm verbose flag
  if (verbose && packageManager === 'npm') {
    // we may want to use it later. For now, it print too much information
    // concretePackageManagerArgs.push('--verbose');
  }

  (_fsExtra || _load_fsExtra()).default.ensureDirSync((_path || _load_path()).default.join(cwd, 'node_modules'));
  (_logger || _load_logger()).default.debug(`installing npm packages using ${packageManager} at ${cwd} with options:`, concretePackageManagerProcessOptions, `and args: ${concretePackageManagerArgs}`);

  // Set the shell to true to prevent problems with post install scripts when running as root
  var packageManagerClientName = packageManager;
  var childProcess = (0, (_execa || _load_execa()).default)(packageManagerClientName, concretePackageManagerArgs, concretePackageManagerProcessOptions);

  // Remove the install from args since it's always there
  var printArgs = concretePackageManagerArgs.filter(function (arg) {
    return arg !== 'install';
  });
  var argsString = printArgs && printArgs.length > 0 ? `with args: ${printArgs}` : '';

  return childProcess.then(function (_ref2) {
    var stdout = _ref2.stdout,
        stderr = _ref2.stderr;

    var successMessage = `\nsuccessfully ran ${packageManager} install at ${cwd} ${argsString}`;
    var peerWarnings = stripNonPeerDependenciesWarnings(stderr, packageManager);

    stdout = verbose ? stdout + successMessage : (_chalk || _load_chalk()).default.white(peerWarnings) + successMessage;
    stderr = verbose ? stderr : '';
    return { stdout, stderr };
  }).catch(function (err) {
    var stderr = `failed running ${packageManager} install at ${cwd} ${argsString}  \n`;
    stderr += verbose ? err.stderr : stripNonNpmErrors(err.stderr, packageManager);
    return Promise.reject(new (_generalError || _load_generalError()).default(`${stderr}\n\n${(_chalk || _load_chalk()).default.yellow(`see troubleshooting at https://${(_constants || _load_constants()).BASE_DOCS_DOMAIN}/docs/install-components.html`)}`));
  });
};

/**
 * Get peer dependencies for directory
 * you should run this after you run npm install
 * internally it uses npm list -j
 */
var _getPeerDeps = function () {
  var _ref3 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee(dir) {
    var packageManager, parsePeers;
    return (_regenerator || _load_regenerator()).default.wrap(function _callee$(_context) {
      while (1) {
        switch (_context.prev = _context.next) {
          case 0:
            packageManager = (_constants || _load_constants()).DEFAULT_PACKAGE_MANAGER;

            parsePeers = function parsePeers(deps) {
              var result = {};
              (_ramda || _load_ramda()).default.forEachObjIndexed(function (dep) {
                if (dep.peerMissing) {
                  var name = dep.required.name;
                  var version = dep.required.version;
                  result[name] = version;
                }
              }, deps);
              return result;
            };

            return _context.abrupt('return', (0, (_execa || _load_execa()).default)(packageManager, ['list', '-j'], { cwd: dir }).then(function (res) {
              var resObject = JSON.parse(res.stdout);
              var peers = parsePeers(resObject.dependencies);
              return peers;
            }).catch(function (err) {
              var resObject = JSON.parse(err.stdout);
              var peers = parsePeers(resObject.dependencies);
              return peers;
            }));

          case 3:
          case 'end':
            return _context.stop();
        }
      }
    }, _callee, undefined);
  }));

  return function _getPeerDeps(_x) {
    return _ref3.apply(this, arguments);
  };
}();

/**
 * A wrapper function to call the install
 * then get the peers
 * then install the peers
 */
var _installInOneDirectoryWithPeerOption = function () {
  var _ref5 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee2(_ref4) {
    var _ref4$modules = _ref4.modules,
        modules = _ref4$modules === undefined ? [] : _ref4$modules,
        _ref4$packageManager = _ref4.packageManager,
        packageManager = _ref4$packageManager === undefined ? (_constants || _load_constants()).DEFAULT_PACKAGE_MANAGER : _ref4$packageManager,
        _ref4$packageManagerA = _ref4.packageManagerArgs,
        packageManagerArgs = _ref4$packageManagerA === undefined ? [] : _ref4$packageManagerA,
        _ref4$packageManagerP = _ref4.packageManagerProcessOptions,
        packageManagerProcessOptions = _ref4$packageManagerP === undefined ? {} : _ref4$packageManagerP,
        dir = _ref4.dir,
        _ref4$installPeerDepe = _ref4.installPeerDependencies,
        installPeerDependencies = _ref4$installPeerDepe === undefined ? false : _ref4$installPeerDepe,
        _ref4$verbose = _ref4.verbose,
        verbose = _ref4$verbose === undefined ? false : _ref4$verbose;
    var rootDirResults, peers, peerResults;
    return (_regenerator || _load_regenerator()).default.wrap(function _callee2$(_context2) {
      while (1) {
        switch (_context2.prev = _context2.next) {
          case 0:
            _context2.next = 2;
            return _installInOneDirectory({
              modules,
              packageManager,
              packageManagerArgs,
              packageManagerProcessOptions,
              dir,
              installPeerDependencies,
              verbose
            });

          case 2:
            rootDirResults = _context2.sent;

            if (!installPeerDependencies) {
              _context2.next = 11;
              break;
            }

            _context2.next = 6;
            return _getPeerDeps(dir);

          case 6:
            peers = _context2.sent;
            _context2.next = 9;
            return _installInOneDirectory({
              modules: peers,
              packageManager,
              packageManagerArgs,
              packageManagerProcessOptions,
              dir,
              verbose
            });

          case 9:
            peerResults = _context2.sent;
            return _context2.abrupt('return', [rootDirResults, peerResults]);

          case 11:
            return _context2.abrupt('return', rootDirResults);

          case 12:
          case 'end':
            return _context2.stop();
        }
      }
    }, _callee2, undefined);
  }));

  return function _installInOneDirectoryWithPeerOption(_x2) {
    return _ref5.apply(this, arguments);
  };
}();

/**
 * when modules is empty, it runs 'npm install' without any package, which installs according to package.json file
 */
var installAction = function () {
  var _ref7 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee3(_ref6) {
    var modules = _ref6.modules,
        _ref6$packageManager = _ref6.packageManager,
        packageManager = _ref6$packageManager === undefined ? (_constants || _load_constants()).DEFAULT_PACKAGE_MANAGER : _ref6$packageManager,
        _ref6$packageManagerA = _ref6.packageManagerArgs,
        packageManagerArgs = _ref6$packageManagerA === undefined ? [] : _ref6$packageManagerA,
        _ref6$packageManagerP = _ref6.packageManagerProcessOptions,
        packageManagerProcessOptions = _ref6$packageManagerP === undefined ? {} : _ref6$packageManagerP,
        _ref6$useWorkspaces = _ref6.useWorkspaces,
        useWorkspaces = _ref6$useWorkspaces === undefined ? false : _ref6$useWorkspaces,
        _ref6$dirs = _ref6.dirs,
        dirs = _ref6$dirs === undefined ? [] : _ref6$dirs,
        rootDir = _ref6.rootDir,
        _ref6$installRootPack = _ref6.installRootPackageJson,
        installRootPackageJson = _ref6$installRootPack === undefined ? false : _ref6$installRootPack,
        _ref6$installPeerDepe = _ref6.installPeerDependencies,
        installPeerDependencies = _ref6$installPeerDepe === undefined ? false : _ref6$installPeerDepe,
        _ref6$verbose = _ref6.verbose,
        verbose = _ref6$verbose === undefined ? false : _ref6$verbose;
    var results, rootDirResults, promises, promisesResults;
    return (_regenerator || _load_regenerator()).default.wrap(function _callee3$(_context3) {
      while (1) {
        switch (_context3.prev = _context3.next) {
          case 0:
            if (!(useWorkspaces && packageManager === 'yarn')) {
              _context3.next = 3;
              break;
            }

            _context3.next = 3;
            return _installInOneDirectoryWithPeerOption({
              modules,
              packageManager,
              packageManagerArgs,
              packageManagerProcessOptions,
              dir: rootDir,
              installPeerDependencies,
              verbose
            });

          case 3:
            results = [];

            if (!installRootPackageJson) {
              _context3.next = 9;
              break;
            }

            _context3.next = 7;
            return _installInOneDirectoryWithPeerOption({
              modules,
              packageManager,
              packageManagerArgs,
              packageManagerProcessOptions,
              dir: rootDir,
              installPeerDependencies,
              verbose
            });

          case 7:
            rootDirResults = _context3.sent;

            if (Array.isArray(rootDirResults)) {
              results.concat(rootDirResults);
            } else {
              results.push(rootDirResults);
            }

          case 9:
            promises = dirs.map(function (dir) {
              return _installInOneDirectoryWithPeerOption({
                modules,
                packageManager,
                packageManagerArgs,
                packageManagerProcessOptions,
                dir,
                installPeerDependencies,
                verbose
              });
            });
            _context3.next = 12;
            return Promise.all(promises);

          case 12:
            promisesResults = _context3.sent;
            return _context3.abrupt('return', results.concat((_ramda || _load_ramda()).default.flatten(promisesResults)));

          case 14:
          case 'end':
            return _context3.stop();
        }
      }
    }, _callee3, undefined);
  }));

  return function installAction(_x3) {
    return _ref7.apply(this, arguments);
  };
}();

var printResults = function printResults(_ref8) {
  var stdout = _ref8.stdout,
      stderr = _ref8.stderr;

  console.log((_chalk || _load_chalk()).default.yellow(stdout)); // eslint-disable-line
  console.log((_chalk || _load_chalk()).default.yellow(stderr)); // eslint-disable-line
};

exports.default = {
  install: installAction,
  printResults
};