'use strict';

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

var _regenerator;

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

var _path;

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

var _fsExtra;

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

var _ramda;

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

var _constants;

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

var _componentMap;

function _load_componentMap() {
  return _componentMap = _interopRequireDefault(require('../../../bit-map/component-map'));
}

var _bitId;

function _load_bitId() {
  return _bitId = require('../../../../bit-id');
}

var _component;

function _load_component() {
  return _component = _interopRequireDefault(require('../../../component'));
}

var _driver;

function _load_driver() {
  return _driver = require('../../../../driver');
}

var _utils;

function _load_utils() {
  return _utils = require('../../../../utils');
}

var _logger;

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

var _consumer;

function _load_consumer() {
  return _consumer = require('../../../../consumer');
}

var _dependencies;

function _load_dependencies() {
  return _dependencies = _interopRequireDefault(require('../dependencies'));
}

var _generalError;

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

var _;

function _load_() {
  return _ = require('..');
}

var _envExtension;

function _load_envExtension() {
  return _envExtension = _interopRequireDefault(require('../../../../extensions/env-extension'));
}

var _bitMap;

function _load_bitMap() {
  return _bitMap = _interopRequireDefault(require('../../../bit-map'));
}

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); } }

/**
 * Given the tree of file dependencies from the driver, find the components of these files.
 * Each dependency file has a path, use bit.map to search for the component name by that path.
 * If the component is found, add it to "componentsDeps". Otherwise, add it to "untrackedDeps".
 *
 * For the found components, add their sourceRelativePath and destinationRelativePath, they are being used for
 * generating links upon import:
 * sourceRelativePath - location of the link file.
 * destinationRelativePath - destination written inside the link file.
 *
 * @param {Object} tree - contains direct deps for each file
 * @param {string[]} files - component files to search
 * @param {string[]} testsFiles - component test files
 * @param {BitId} entryComponentId - component id for the entry of traversing - used to know which of the files are part of that component
 * @param {consumer} consumer
 * @param componentFromModel
 */
function findComponentsOfDepsFiles(tree, files, testsFiles, compilerFiles, testerFiles, entryComponentId, consumer, consumerComponent) {
  var driver = consumer.driver;
  var bindingPrefix = consumerComponent.bindingPrefix;
  var componentFromModel = consumerComponent.componentFromModel;
  var packagesDeps = {};
  var devPackagesDeps = {};
  var compilerPackagesDeps = {};
  var testerPackagesDeps = {};
  var componentsDeps = [];
  var devComponentsDeps = [];
  var compilerComponentsDeps = [];
  var testerComponentsDeps = [];

  var getExistingDependency = function getExistingDependency(dependencies, id) {
    return dependencies.find(function (d) {
      return d.id.isEqual(id);
    });
  };

  // issues
  var missingDependenciesOnFs = {};
  var missingPackagesDependenciesOnFs = {};
  var missingComponents = {};
  var missingLinks = {};
  var missingCustomModuleResolutionLinks = {};
  var untrackedDeps = {};
  var relativeDeps = {}; // dependencies that are required with relative path (and should be required using 'bit/').
  var missingBits = {};
  var parseErrors = {};
  var resolveErrors = {};
  var issues = {};

  var consumerPath = consumer.getPath();
  var entryComponentMap = consumer.bitMap.getComponent(entryComponentId);
  var rootDir = entryComponentMap.rootDir;
  var processedFiles = [];

  var traverseTreeForComponentId = function traverseTreeForComponentId(depFile) {
    if (!tree[depFile] || !tree[depFile].files && !tree[depFile].bits) return;
    var rootDirFullPath = (_path || _load_path()).default.join(consumerPath, rootDir);
    if (tree[depFile].files && tree[depFile].files.length) {
      var _iteratorNormalCompletion = true;
      var _didIteratorError = false;
      var _iteratorError = undefined;

      try {
        for (var _iterator = tree[depFile].files[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
          var file = _step.value;

          var fullDepFile = (_path || _load_path()).default.resolve(rootDirFullPath, file.file);
          var depRelativeToConsumer = (0, (_utils || _load_utils()).pathNormalizeToLinux)((_path || _load_path()).default.relative(consumerPath, fullDepFile));
          var componentId = consumer.bitMap.getComponentIdByPath(depRelativeToConsumer);
          if (componentId) return componentId; // eslint-disable-line consistent-return
        }
      } catch (err) {
        _didIteratorError = true;
        _iteratorError = err;
      } finally {
        try {
          if (!_iteratorNormalCompletion && _iterator.return) {
            _iterator.return();
          }
        } finally {
          if (_didIteratorError) {
            throw _iteratorError;
          }
        }
      }
    }
    if (tree[depFile].bits && tree[depFile].bits.length) {
      var _iteratorNormalCompletion2 = true;
      var _didIteratorError2 = false;
      var _iteratorError2 = undefined;

      try {
        for (var _iterator2 = tree[depFile].bits[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
          var bit = _step2.value;

          var _componentId = consumer.getComponentIdFromNodeModulesPath(bit, bindingPrefix);
          if (_componentId) return _componentId; // eslint-disable-line consistent-return
        }
      } catch (err) {
        _didIteratorError2 = true;
        _iteratorError2 = err;
      } finally {
        try {
          if (!_iteratorNormalCompletion2 && _iterator2.return) {
            _iterator2.return();
          }
        } finally {
          if (_didIteratorError2) {
            throw _iteratorError2;
          }
        }
      }
    }

    if (tree[depFile].files && tree[depFile].files.length) {
      var _iteratorNormalCompletion3 = true;
      var _didIteratorError3 = false;
      var _iteratorError3 = undefined;

      try {
        for (var _iterator3 = tree[depFile].files[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
          var _file = _step3.value;

          if (_file.file !== depFile) {
            var _componentId2 = traverseTreeForComponentId(_file.file);
            if (_componentId2) return _componentId2; // eslint-disable-line consistent-return
          } else {
            (_logger || _load_logger()).default.warn(`traverseTreeForComponentId found a cyclic dependency. ${_file.file} depends on itself`);
          }
        }
      } catch (err) {
        _didIteratorError3 = true;
        _iteratorError3 = err;
      } finally {
        try {
          if (!_iteratorNormalCompletion3 && _iterator3.return) {
            _iterator3.return();
          }
        } finally {
          if (_didIteratorError3) {
            throw _iteratorError3;
          }
        }
      }
    }
  };

  var getComponentIdByDepFile = function getComponentIdByDepFile(depFile) {
    var depFileRelative = depFile; // dependency file path relative to consumer root
    var componentId = void 0;
    var destination = void 0;

    if (rootDir) {
      // The depFileRelative is relative to rootDir, change it to be relative to current consumer.
      // We can't use path.resolve(rootDir, fileDep) because this might not work when running
      // bit commands not from root, because resolve take by default the process.cwd
      var rootDirFullPath = (_path || _load_path()).default.join(consumerPath, rootDir);
      var fullDepFile = (_path || _load_path()).default.resolve(rootDirFullPath, depFile);
      depFileRelative = (0, (_utils || _load_utils()).pathNormalizeToLinux)((_path || _load_path()).default.relative(consumerPath, fullDepFile));
    }

    componentId = consumer.bitMap.getComponentIdByPath(depFileRelative);
    // if not found here, the file is not a component file. It might be a bit-auto-generated file.
    // find the component file by the auto-generated file.
    // We make sure also that rootDir is there, otherwise, it's an AUTHORED component, which shouldn't have
    // auto-generated files.
    if (!componentId && rootDir) {
      componentId = traverseTreeForComponentId(depFile);
      if (componentId) {
        // it is verified now that this depFile is an auto-generated file, therefore the sourceRelativePath and the
        // destinationRelativePath should be a partial-path and not full-relative-to-consumer path.
        // since the dep-file is a generated file, it is safe to assume that the componentFromModel has in its
        // dependencies array this component with the relativePaths array. Find the relativePath of this dep-file
        // to get the correct destinationRelativePath. There is no other way to obtain this info.
        if (!componentFromModel) {
          throw new (_generalError || _load_generalError()).default(`Failed to resolve ${componentId.toString()} dependencies because the component is not in the model.
Try to run "bit import ${consumerComponent.id.toString()} --objects" to get the component saved in the model`);
        }
        var dependency = componentFromModel.getAllDependencies() // $FlowFixMe
        .find(function (dep) {
          return dep.id.isEqualWithoutVersion(componentId);
        });
        if (!dependency) {
          throw new (_generalError || _load_generalError()).default( // $FlowFixMe
          `the auto-generated file ${depFile} should be connected to ${componentId}, however, it's not part of the model dependencies of ${componentFromModel.id}`);
        }

        var isCompilerDependency = componentFromModel.compilerDependencies.getById(componentId);
        var isTesterDependency = componentFromModel.testerDependencies.getById(componentId);
        var isEnvDependency = isCompilerDependency || isTesterDependency;
        var isRelativeToConfigDir = isEnvDependency && entryComponentMap.configDir && entryComponentMap.rootDir;

        var getOriginallySourcePath = function getOriginallySourcePath() {
          if (isRelativeToConfigDir) {
            // find the sourceRelativePath relative to the configDir, not to the rootDir of the component
            var resolvedSource = (_path || _load_path()).default.resolve(entryComponentMap.rootDir, depFile);
            // @todo: use the new ConfigDir class that Gilad added once it is merged.

            var _BitMap$parseConfigDi = (_bitMap || _load_bitMap()).default.parseConfigDir(entryComponentMap.configDir, entryComponentMap.rootDir),
                compilerConfigDir = _BitMap$parseConfigDi.compiler,
                testerConfigDir = _BitMap$parseConfigDi.tester;

            var configDir = isCompilerDependency ? compilerConfigDir : testerConfigDir;
            var absoluteConfigDir = consumer.toAbsolutePath(configDir);
            return (0, (_utils || _load_utils()).pathRelativeLinux)(absoluteConfigDir, resolvedSource);
          }
          return entryComponentMap.originallySharedDir ? (0, (_utils || _load_utils()).pathJoinLinux)(entryComponentMap.originallySharedDir, depFile) : depFile;
        };
        var originallySource = getOriginallySourcePath();
        var relativePath = dependency.relativePaths.find(function (r) {
          return r.sourceRelativePath === originallySource;
        });
        if (!relativePath) {
          throw new (_generalError || _load_generalError()).default(`unable to find ${originallySource} path in the dependencies relativePaths of ${componentFromModel.id}`);
        }

        componentId = dependency.id;
        destination = relativePath.destinationRelativePath;

        depFileRelative = depFile; // change it back to partial-part, this will be later on the sourceRelativePath

        if (isRelativeToConfigDir) {
          // in this case, sourceRelativePath should be relative to configDir
          depFileRelative = originallySource;
        }
      }
    }

    return { componentId, depFileRelative, destination };
  };

  var pushToDependenciesArray = function pushToDependenciesArray(currentComponentsDeps, isTestFile, isCompilerFile, isTesterFile) {
    if (isTestFile) {
      devComponentsDeps.push(currentComponentsDeps);
    } else if (isCompilerFile) {
      compilerComponentsDeps.push(currentComponentsDeps);
    } else if (isTesterFile) {
      testerComponentsDeps.push(currentComponentsDeps);
    } else {
      componentsDeps.push(currentComponentsDeps);
    }
  };

  var processDepFile = function processDepFile(originFile, depFile, importSpecifiers, linkFile) {
    var isTestFile = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
    var isCompilerFile = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : false;
    var isTesterFile = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : false;
    var depFileObject = arguments[7];

    if (processedFiles.includes(depFile)) return;
    processedFiles.push(depFile);

    // if the dependency of an envFile is already included in the env files of the component, we're good
    if (isCompilerFile && compilerFiles.includes(depFile)) return;
    if (isTesterFile && testerFiles.includes(depFile)) return;

    var _getComponentIdByDepF = getComponentIdByDepFile(depFile),
        componentId = _getComponentIdByDepF.componentId,
        depFileRelative = _getComponentIdByDepF.depFileRelative,
        destination = _getComponentIdByDepF.destination;
    // the file dependency doesn't have any counterpart component. Add it to untrackedDeps


    if (!componentId) {
      if (untrackedDeps[originFile]) untrackedDeps[originFile].push(depFileRelative);else untrackedDeps[originFile] = [depFileRelative];
      return;
    }

    // happens when in the same component one file requires another one. In this case, there is
    // noting to do regarding the dependencies
    if (componentId.isEqual(entryComponentId)) {
      if (depFileObject.isCustomResolveUsed) {
        consumerComponent.customResolvedPaths.push({
          destinationPath: depFileObject.file,
          importSource: depFileObject.importSource
        });
      }
      return;
    }

    var componentMap = consumer.bitMap.getComponentIfExist(componentId);
    // found a dependency component. Add it to componentsDeps
    var depRootDir = componentMap ? componentMap.rootDir : undefined;
    var destinationRelativePath = destination || (depRootDir && depFileRelative.startsWith(depRootDir) ? (0, (_utils || _load_utils()).pathRelativeLinux)(depRootDir, depFileRelative) : depFileRelative);

    var sourceRelativePath = void 0;
    if (linkFile) {
      sourceRelativePath = linkFile;
    } else {
      // when there is no rootDir for the current dependency (it happens when it's AUTHORED), keep the original path
      sourceRelativePath = depRootDir ? depFileRelative : depFile;
    }

    var depsPaths = {
      sourceRelativePath,
      destinationRelativePath
    };
    if (importSpecifiers) {
      depsPaths.importSpecifiers = importSpecifiers;
    }
    if (depFileObject.isCustomResolveUsed) {
      depsPaths.isCustomResolveUsed = depFileObject.isCustomResolveUsed;
      depsPaths.importSource = depFileObject.importSource;
    }
    var currentComponentsDeps = { id: componentId, relativePaths: [depsPaths] };

    if (componentMap && (componentMap.origin === (_constants || _load_constants()).COMPONENT_ORIGINS.IMPORTED && entryComponentMap.origin === (_constants || _load_constants()).COMPONENT_ORIGINS.AUTHORED || componentMap.origin === (_constants || _load_constants()).COMPONENT_ORIGINS.AUTHORED && entryComponentMap.origin === (_constants || _load_constants()).COMPONENT_ORIGINS.IMPORTED)) {
      // prevent author using relative paths for IMPORTED component
      // also prevent adding AUTHORED component to an IMPORTED component using relative syntax. The reason is that when
      // this component is imported somewhere else, a link-file between the IMPORTED and the AUTHORED must be written
      // outside the component directory, which might override user files.
      if (relativeDeps[originFile]) relativeDeps[originFile].push(componentId);else relativeDeps[originFile] = [componentId];
      return;
    }

    var allDependencies = [].concat(componentsDeps, _toConsumableArray(devComponentsDeps), _toConsumableArray(compilerComponentsDeps), _toConsumableArray(testerComponentsDeps));
    var existingDependency = getExistingDependency(allDependencies, componentId);
    if (existingDependency) {
      // it is another file of an already existing component. Just add the new path
      existingDependency.relativePaths.push(depsPaths);
    } else {
      pushToDependenciesArray(currentComponentsDeps, isTestFile, isCompilerFile, isTesterFile);
    }
  };

  var processLinkFile = function processLinkFile(originFile, linkFile) {
    var isTestFile = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
    var isCompilerFile = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
    var isTesterFile = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;

    if (!linkFile.linkDependencies || (_ramda || _load_ramda()).default.isEmpty(linkFile.linkDependencies)) return;
    var nonLinkImportSpecifiers = [];
    linkFile.linkDependencies.forEach(function (dependency) {
      var component = getComponentIdByDepFile(linkFile.file);
      if (component.componentId) {
        // the linkFile is already a component, no need to treat it differently than other depFile
        // aggregate all dependencies using the same linkFile and ultimately run processDepFile
        // with all importSpecifiers of that linkFile.
        // also, delete the linkFile attribute of importSpecifiers so then once the component is
        // imported and the link is generated, it won't be treated as a linkFile.
        dependency.importSpecifiers.map(function (a) {
          return delete a.linkFile;
        });
        nonLinkImportSpecifiers.push(dependency.importSpecifiers);
      } else {
        processDepFile(originFile, dependency.file, dependency.importSpecifiers, linkFile.file, isTestFile, isCompilerFile, isTesterFile, linkFile);
      }
    });
    if (nonLinkImportSpecifiers.length) {
      processDepFile(originFile, linkFile.file, (_ramda || _load_ramda()).default.flatten(nonLinkImportSpecifiers), undefined, isTestFile, isCompilerFile, isTesterFile, linkFile);
    }
  };

  var processDepFiles = function processDepFiles(originFile, allDepsFiles) {
    var isTestFile = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
    var isCompilerFile = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
    var isTesterFile = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;

    if (!allDepsFiles || (_ramda || _load_ramda()).default.isEmpty(allDepsFiles)) return;
    allDepsFiles.forEach(function (depFile) {
      if (depFile.isLink) processLinkFile(originFile, depFile, isTestFile, isCompilerFile, isTesterFile);else {
        processDepFile(originFile, depFile.file, depFile.importSpecifiers, undefined, isTestFile, isCompilerFile, isTesterFile, depFile);
      }
    });
  };

  var processBits = function processBits(originFile, bits, isTestFile, isCompilerFile, isTesterFile) {
    if (!bits || (_ramda || _load_ramda()).default.isEmpty(bits)) return;
    bits.forEach(function (bitDep) {
      var componentId = consumer.getComponentIdFromNodeModulesPath(bitDep, bindingPrefix);
      var getExistingId = function getExistingId() {
        var existingId = consumer.bitmapIds.searchWithoutVersion(componentId);
        if (existingId) return existingId;

        // maybe the dependencies were imported as npm packages
        if (bitDep.startsWith('node_modules')) {
          // Add the root dir in case it exists (to make sure we search for the dependency package json in the correct place)
          var basePath = rootDir ? (_path || _load_path()).default.join(consumerPath, rootDir) : consumerPath;
          var depPath = (_path || _load_path()).default.join(basePath, bitDep);
          var packageJson = driver.driver.PackageJson.findPackage(depPath);
          if (packageJson) {
            var depVersion = packageJson.version;
            existingId = componentId.changeVersion(depVersion);
            return existingId;
          }
        }
        if (componentFromModel) {
          var modelDep = componentFromModel.getAllDependenciesIds().searchWithoutVersion(componentId);
          if (modelDep) return modelDep;
        }
        return null;
      };
      var existingId = getExistingId();
      if (existingId) {
        var currentComponentsDeps = { id: existingId, relativePaths: [] };
        var existingDependency = getExistingDependency(componentsDeps, existingId);
        if (!existingDependency) {
          pushToDependenciesArray(currentComponentsDeps, isTestFile, isCompilerFile, isTesterFile);
        }
      } else {
        missingBits[originFile] ? missingBits[originFile].push(componentId) : missingBits[originFile] = [componentId];
      }
    });
  };

  var processPackages = function processPackages(originFile, packages, isTestFile, isCompilerFile, isTesterFile) {
    if (packages && !(_ramda || _load_ramda()).default.isEmpty(packages)) {
      if (isTestFile) {
        Object.assign(devPackagesDeps, packages);
      } else if (isCompilerFile) {
        Object.assign(compilerPackagesDeps, packages);
      } else if (isTesterFile) {
        Object.assign(testerPackagesDeps, packages);
      } else {
        Object.assign(packagesDeps, packages);
      }
    }
  };

  /**
   * currently the only unidentified packages being process are the ones coming from custom-modules-resolution.
   * assuming the author used custom-resolution, which enable using non-relative import syntax,
   * for example, requiring the file 'src/utils/is-string' from anywhere as require('utils/is-string');
   * now, when the component is imported, the driver recognizes 'utils/is-string' as a package,
   * because it's not relative.
   * the goal here is to use the 'package' the driver found and match it with one of the
   * dependencies from the model. In the example above, we might find in the model, a dependency
   * is-string with importSource of 'utils/is-string'.
   * Once a match is found, copy the relativePaths from the model. Before coping, we must strip the
   * originallySharedDir, because in this process, the component is loaded from the filesystem and
   * as such the sharedDir is expected to be stripped.
   */
  var processUnidentifiedPackages = function processUnidentifiedPackages(originFile, unidentifiedPackages) {
    var isTestFile = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
    var isCompilerFile = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
    var isTesterFile = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;

    if (!unidentifiedPackages) return;
    if (!componentFromModel) return; // not relevant, the component is not imported
    var getDependencies = function getDependencies() {
      if (isTestFile) return componentFromModel.devDependencies;
      if (isCompilerFile) return componentFromModel.compilerDependencies;
      if (isTesterFile) return componentFromModel.testerDependencies;
      return componentFromModel.dependencies;
    };
    var dependencies = getDependencies();
    if (dependencies.isEmpty()) return;
    var importSourceMap = dependencies.getCustomResolvedData();
    if ((_ramda || _load_ramda()).default.isEmpty(importSourceMap)) return;
    // clone before stripping the sharedDir to not change the model by mistake
    var clonedDependencies = new (_dependencies || _load_dependencies()).default(dependencies.getClone());
    if (entryComponentMap.originallySharedDir) {
      // @todo: disabled for now. When the files are saved into the model, we don't add the sharedDir
      // in this case, so it should be fine. (see sources.consumerComponentToVersion())
      // it needs some more thinking whether there are cases when it is needed
      // clonedDependencies.stripOriginallySharedDir(consumer.bitMap, entryComponentMap.originallySharedDir);
    }
    unidentifiedPackages.forEach(function (unidentifiedPackage) {
      var packageLinuxFormat = (0, (_utils || _load_utils()).pathNormalizeToLinux)(unidentifiedPackage);
      var packageWithNoNodeModules = packageLinuxFormat.replace('node_modules/', '');
      var foundImportSource = Object.keys(importSourceMap).find(function (importSource) {
        return packageWithNoNodeModules.startsWith(importSource);
      });
      if (foundImportSource) {
        var dependencyId = importSourceMap[foundImportSource];
        var existingDependency = getExistingDependency(componentsDeps, dependencyId);
        var existingDevDependency = getExistingDependency(devComponentsDeps, dependencyId);
        var existingCompilerDependency = getExistingDependency(compilerComponentsDeps, dependencyId);
        var existingTesterDependency = getExistingDependency(testerComponentsDeps, dependencyId);
        var currentComponentDeps = {
          id: dependencyId,
          relativePaths: clonedDependencies.getById(dependencyId).relativePaths
        };
        if (isTestFile && !existingDevDependency) {
          devComponentsDeps.push(currentComponentDeps);
        } else if (isCompilerFile && !existingCompilerDependency) {
          compilerComponentsDeps.push(currentComponentDeps);
        } else if (isTesterFile && !existingTesterDependency) {
          testerComponentsDeps.push(currentComponentDeps);
        } else if (!isTestFile && !isCompilerFile && !isTesterFile && !existingDependency) {
          componentsDeps.push(currentComponentDeps);
        }
      }
    });
  };

  /**
   * Remove the dependencies which appear both in dev and regular deps from the dev
   * Because if a dependency is both dev dependency and regular dependency it should be treated as regular one
   * Apply for both packages and components dependencies
   */
  var removeDevAndEnvDepsIfTheyAlsoRegulars = function removeDevAndEnvDepsIfTheyAlsoRegulars() {
    // remove dev and env packages that are also regular packages
    var getNotRegularPackages = function getNotRegularPackages(packages) {
      return (_ramda || _load_ramda()).default.difference((_ramda || _load_ramda()).default.keys(packages), (_ramda || _load_ramda()).default.keys(packagesDeps));
    };
    devPackagesDeps = (_ramda || _load_ramda()).default.pick(getNotRegularPackages(devPackagesDeps), devPackagesDeps);
    compilerPackagesDeps = (_ramda || _load_ramda()).default.pick(getNotRegularPackages(compilerPackagesDeps), compilerPackagesDeps);
    testerPackagesDeps = (_ramda || _load_ramda()).default.pick(getNotRegularPackages(testerPackagesDeps), testerPackagesDeps);

    // remove dev and env dependencies that are also regular dependencies
    var componentDepsIds = new (Function.prototype.bind.apply((_bitId || _load_bitId()).BitIds, [null].concat(_toConsumableArray(componentsDeps.map(function (c) {
      return c.id;
    })))))();
    devComponentsDeps = devComponentsDeps.filter(function (d) {
      return !componentDepsIds.has(d.id);
    });
    compilerComponentsDeps = compilerComponentsDeps.filter(function (d) {
      return !componentDepsIds.has(d.id);
    });
    testerComponentsDeps = testerComponentsDeps.filter(function (d) {
      return !componentDepsIds.has(d.id);
    });
  };

  /**
   * given missing packages name, find whether they were dependencies with custom-resolve before.
   */
  var findOriginallyCustomModuleResolvedDependencies = function findOriginallyCustomModuleResolvedDependencies(packages) {
    if (!packages) return undefined;
    if (!componentFromModel) return undefined; // not relevant, the component is not imported
    var dependencies = new (_dependencies || _load_dependencies()).default(componentFromModel.getAllDependencies());
    if (dependencies.isEmpty()) return undefined;
    var importSourceMap = dependencies.getCustomResolvedData();
    if ((_ramda || _load_ramda()).default.isEmpty(importSourceMap)) return undefined;
    var foundPackages = packages.reduce(function (acc, value) {
      var packageLinuxFormat = (0, (_utils || _load_utils()).pathNormalizeToLinux)(value);
      var foundImportSource = Object.keys(importSourceMap).find(function (importSource) {
        return importSource.startsWith(packageLinuxFormat);
      });
      if (foundImportSource) {
        var dependencyId = importSourceMap[foundImportSource];
        acc[value] = dependencyId;
      }
      return acc;
    }, {});

    return (_ramda || _load_ramda()).default.isEmpty(foundPackages) ? undefined : foundPackages;
  };

  var processMissing = function processMissing(originFile, missing) {
    if (!missing) return;
    if (missing.files && !(_ramda || _load_ramda()).default.isEmpty(missing.files)) {
      if (missingDependenciesOnFs[originFile]) {
        missingDependenciesOnFs[originFile].concat(missing.files);
      } else missingDependenciesOnFs[originFile] = missing.files;
    }
    if (missing.packages && !(_ramda || _load_ramda()).default.isEmpty(missing.packages)) {
      var customResolvedDependencies = findOriginallyCustomModuleResolvedDependencies(missing.packages);
      if (customResolvedDependencies) {
        Object.keys(customResolvedDependencies).forEach(function (missingPackage) {
          var componentId = customResolvedDependencies[missingPackage].toString();
          if (missingCustomModuleResolutionLinks[originFile]) {
            missingCustomModuleResolutionLinks[originFile].push(componentId);
          } else missingCustomModuleResolutionLinks[originFile] = [componentId];
        });
      }
      var missingPackages = customResolvedDependencies ? (_ramda || _load_ramda()).default.difference(missing.packages, Object.keys(customResolvedDependencies)) : missing.packages;

      if (!(_ramda || _load_ramda()).default.isEmpty(missingPackages)) {
        if (missingPackagesDependenciesOnFs[originFile]) {
          missingPackagesDependenciesOnFs[originFile].concat(missing.packages);
        } else missingPackagesDependenciesOnFs[originFile] = missing.packages;
      }
    }

    if (missing.bits && !(_ramda || _load_ramda()).default.isEmpty(missing.bits)) {
      missing.bits.forEach(function (missingBit) {
        var componentId = consumer.getComponentIdFromNodeModulesPath(missingBit, consumerComponent.bindingPrefix);
        // todo: a component might be on bit.map but not on the FS, yet, it's not about missing links.
        if (consumer.bitMap.getBitIdIfExist(componentId, { ignoreVersion: true })) {
          if (missingLinks[originFile]) missingLinks[originFile].push(componentId);else missingLinks[originFile] = [componentId];
        } else if (missingComponents[originFile]) missingComponents[originFile].push(componentId);else missingComponents[originFile] = [componentId];
      });
    }
  };

  var processErrors = function processErrors(originFile, error) {
    if (!error) return;
    (_logger || _load_logger()).default.error('got an error from the driver while resolving dependencies');
    (_logger || _load_logger()).default.error(error);
    // $FlowFixMe error.code is set when it comes from bit-javascript, otherwise, it's undefined and treated as resolve-error
    if (error.code === 'PARSING_ERROR') parseErrors[originFile] = error.message;else resolveErrors[originFile] = error.message;
  };

  // when env dependencies are not sent to the dependency-resolver, they should be copied from the model
  var copyEnvDependenciesFromModelIfNeeded = function copyEnvDependenciesFromModelIfNeeded() {
    if (!componentFromModel) return;
    if (!shouldProcessEnvDependencies(consumerComponent.compiler)) {
      compilerComponentsDeps = componentFromModel.compilerDependencies.get();
      compilerPackagesDeps = componentFromModel.compilerPackageDependencies;
    }
    if (!shouldProcessEnvDependencies(consumerComponent.tester)) {
      testerComponentsDeps = componentFromModel.testerDependencies.get();
      testerPackagesDeps = componentFromModel.testerPackageDependencies;
    }
  };

  var combineIssues = function combineIssues() {
    if (!(_ramda || _load_ramda()).default.isEmpty(missingDependenciesOnFs)) issues.missingDependenciesOnFs = missingDependenciesOnFs;
    if (!(_ramda || _load_ramda()).default.isEmpty(missingPackagesDependenciesOnFs)) {
      issues.missingPackagesDependenciesOnFs = missingPackagesDependenciesOnFs;
    }
    if (!(_ramda || _load_ramda()).default.isEmpty(missingLinks)) issues.missingLinks = missingLinks;
    if (!(_ramda || _load_ramda()).default.isEmpty(missingCustomModuleResolutionLinks)) {
      issues.missingCustomModuleResolutionLinks = missingCustomModuleResolutionLinks;
    }
    if (!(_ramda || _load_ramda()).default.isEmpty(missingComponents)) issues.missingComponents = missingComponents;

    if (!(_ramda || _load_ramda()).default.isEmpty(untrackedDeps)) issues.untrackedDependencies = untrackedDeps;
    if (!(_ramda || _load_ramda()).default.isEmpty(relativeDeps)) issues.relativeComponents = relativeDeps;
    if (!(_ramda || _load_ramda()).default.isEmpty(missingBits)) {
      if (!issues.missingComponents) issues.missingComponents = {};
      Object.keys(missingBits).forEach(function (missingBit) {
        if (issues.missingComponents[missingBit]) {
          issues.missingComponents[missingBit] = issues.missingComponents[missingBit].concat(missingBits[missingBit]);
        } else {
          issues.missingComponents[missingBit] = missingBits[missingBit];
        }
      });
    }
    if (!(_ramda || _load_ramda()).default.isEmpty(parseErrors)) issues.parseErrors = parseErrors;
    if (!(_ramda || _load_ramda()).default.isEmpty(resolveErrors)) issues.resolveErrors = resolveErrors;
  };

  files.forEach(function (file) {
    var isTestFile = (_ramda || _load_ramda()).default.contains(file, testsFiles);
    var isCompilerFile = (_ramda || _load_ramda()).default.contains(file, compilerFiles);
    var isTesterFile = (_ramda || _load_ramda()).default.contains(file, testerFiles);
    if (!tree[file]) {
      throw new Error(`DependencyResolver: a file "${file}" was not returned from the driver, its dependencies are unknown`);
    }
    processMissing(file, tree[file].missing);
    processErrors(file, tree[file].error);
    processPackages(file, tree[file].packages, isTestFile, isCompilerFile, isTesterFile);
    processBits(file, tree[file].bits, isTestFile, isCompilerFile, isTesterFile);
    processDepFiles(file, tree[file].files, isTestFile, isCompilerFile, isTesterFile);
    processUnidentifiedPackages(file, tree[file].unidentifiedPackages, isTestFile, isCompilerFile, isTesterFile);
  });
  removeDevAndEnvDepsIfTheyAlsoRegulars();
  copyEnvDependenciesFromModelIfNeeded();
  combineIssues();

  return {
    componentsDeps,
    devComponentsDeps,
    compilerComponentsDeps,
    testerComponentsDeps,
    packagesDeps,
    devPackagesDeps,
    compilerPackagesDeps,
    testerPackagesDeps,
    issues
  };
}

// @todo: move to bit-javascript
/**
 * For author, the peer-dependencies are set in the root package.json
 * For imported components, we don't want to change the peerDependencies of the author, unless
 * we're certain the user intent to do so. Therefore, we ignore the root package.json and look for
 * the package.json in the component's directory.
 */
function findPeerDependencies(consumerPath, component) {
  var componentMap = component.componentMap;
  var getPeerDependencies = function getPeerDependencies() {
    var componentRoot = componentMap.origin === (_constants || _load_constants()).COMPONENT_ORIGINS.AUTHORED ? consumerPath : componentMap.rootDir;
    var packageJsonLocation = (_path || _load_path()).default.join(componentRoot, 'package.json');
    if ((_fsExtra || _load_fsExtra()).default.existsSync(packageJsonLocation)) {
      try {
        var packageJson = (_fsExtra || _load_fsExtra()).default.readJsonSync(packageJsonLocation);
        if (packageJson.peerDependencies) return packageJson.peerDependencies;
      } catch (err) {
        (_logger || _load_logger()).default.error(`Failed reading the project package.json at ${packageJsonLocation}. Error Message: ${err.message}`);
      }
    }
    if (component.componentFromModel && componentMap.origin !== (_constants || _load_constants()).COMPONENT_ORIGINS.AUTHORED) {
      return component.componentFromModel.peerPackageDependencies;
    }
    return {};
  };
  var projectPeerDependencies = getPeerDependencies();
  var peerPackages = {};
  if ((_ramda || _load_ramda()).default.isEmpty(projectPeerDependencies)) return {};
  // check whether the peer-dependencies was actually require in the code. if so, remove it from
  // the packages/dev-packages and add it as a peer-package.
  Object.keys(projectPeerDependencies).forEach(function (pkg) {
    ['packageDependencies', 'devPackageDependencies'].forEach(function (field) {
      if (Object.keys(component[field]).includes(pkg)) {
        delete component[field][pkg];
        peerPackages[pkg] = projectPeerDependencies[pkg];
      }
    });
  });
  return peerPackages;
}

/**
 * if the component doesn't have the env files written on the filesystem there is nothing to pass
 * to the dependencyResolver
 */
function shouldProcessEnvDependencies(env) {
  return Boolean(env && env.files && env.files.every(function (file) {
    return !file.fromModel;
  }));
}

function getRelativeEnvFiles(files, consumer, componentMap) {
  var getPathsRelativeToComponentRoot = function getPathsRelativeToComponentRoot() {
    var rootDirAbsolute = consumer.toAbsolutePath(componentMap.rootDir || '.');
    return files.map(function (file) {
      var envAbsolute = file.path;
      return (_path || _load_path()).default.relative(rootDirAbsolute, envAbsolute);
    });
  };
  return getPathsRelativeToComponentRoot().map(function (file) {
    return (0, (_utils || _load_utils()).pathNormalizeToLinux)(file);
  });
}

function getCompilerFiles(componentMap, component, consumer) {
  var compilerFiles = shouldProcessEnvDependencies(component.compiler) ? component.compiler.files : [];
  return getRelativeEnvFiles(compilerFiles, consumer, componentMap);
}

function getTesterFiles(componentMap, component, consumer) {
  var testerFiles = shouldProcessEnvDependencies(component.tester) ? component.tester.files : [];
  return getRelativeEnvFiles(testerFiles, consumer, componentMap);
}

/**
 * Load components and packages dependencies for a component. The process is as follows:
 * 1) Use the language driver to parse the component files and find for each file its dependencies.
 * 2) The results we get from the driver per file tells us what are the files and packages that depend on our file.
 * and also whether there are missing packages and files.
 * 3) Using the information from the driver, we go over each one of the dependencies files and find its counterpart
 * component. The way how we find it, is by using the bit.map file which has a mapping between the component name and
 * the file paths.
 * 4) If we find a component to the file dependency, we add it to component.dependencies. Otherwise, it's added to
 * component.issues.untrackedDependencies
 * 5) Similarly, when we find the packages dependencies, they are added to component.packageDependencies. Otherwise,
 * they're added to component.issues.missingPackagesDependenciesOnFs
 * 6) In case the driver found a file dependency that is not on the file-system, we add that file to
 * component.issues.missingDependenciesOnFs
 */

exports.default = function () {
  var _ref = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee2(component, bitDir, consumer, idWithConcreteVersion) {
    var _this = this;

    var driver, consumerPath, componentMap, _componentMap$getFile, nonTestsFiles, testsFiles, compilerFiles, testerFiles, allFiles, getDependenciesTree, dependenciesTree, traversedDeps, componentsDeps, devComponentsDeps, compilerComponentsDeps, testerComponentsDeps, issues;

    return (_regenerator || _load_regenerator()).default.wrap(function _callee2$(_context2) {
      while (1) {
        switch (_context2.prev = _context2.next) {
          case 0:
            driver = consumer.driver;
            consumerPath = consumer.getPath();
            componentMap = component.componentMap;
            _componentMap$getFile = componentMap.getFilesGroupedByBeingTests(), nonTestsFiles = _componentMap$getFile.nonTestsFiles, testsFiles = _componentMap$getFile.testsFiles;
            compilerFiles = getCompilerFiles(componentMap, component, consumer);
            testerFiles = getTesterFiles(componentMap, component, consumer);
            allFiles = [].concat(_toConsumableArray(nonTestsFiles), _toConsumableArray(testsFiles), _toConsumableArray(compilerFiles), _toConsumableArray(testerFiles));

            getDependenciesTree = function () {
              var _ref2 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee() {
                return (_regenerator || _load_regenerator()).default.wrap(function _callee$(_context) {
                  while (1) {
                    switch (_context.prev = _context.next) {
                      case 0:
                        return _context.abrupt('return', driver.getDependencyTree(bitDir, consumerPath, allFiles, component.bindingPrefix, consumer.bitJson.resolveModules));

                      case 1:
                      case 'end':
                        return _context.stop();
                    }
                  }
                }, _callee, _this);
              }));

              return function getDependenciesTree() {
                return _ref2.apply(this, arguments);
              };
            }();
            // find the dependencies (internal files and packages) through automatic dependency resolution


            _context2.next = 10;
            return getDependenciesTree();

          case 10:
            dependenciesTree = _context2.sent;

            // we have the files dependencies, these files should be components that are registered in bit.map. Otherwise,
            // they are referred as "untracked components" and the user should add them later on in order to commit
            traversedDeps = findComponentsOfDepsFiles(dependenciesTree.tree, allFiles, testsFiles, compilerFiles, testerFiles, idWithConcreteVersion, consumer, component);
            componentsDeps = traversedDeps.componentsDeps, devComponentsDeps = traversedDeps.devComponentsDeps, compilerComponentsDeps = traversedDeps.compilerComponentsDeps, testerComponentsDeps = traversedDeps.testerComponentsDeps, issues = traversedDeps.issues;

            component.setDependencies(componentsDeps);
            component.setDevDependencies(devComponentsDeps);
            component.setCompilerDependencies(compilerComponentsDeps);
            component.setTesterDependencies(testerComponentsDeps);
            component.packageDependencies = traversedDeps.packagesDeps;
            component.devPackageDependencies = traversedDeps.devPackagesDeps;
            component.compilerPackageDependencies = (_ramda || _load_ramda()).default.merge(traversedDeps.compilerPackagesDeps, component.compilerPackageDependencies);
            component.testerPackageDependencies = (_ramda || _load_ramda()).default.merge(traversedDeps.testerPackagesDeps, component.testerPackageDependencies);
            component.peerPackageDependencies = findPeerDependencies(consumerPath, component);
            // assign issues to component only when it has data.
            // Otherwise, when it's empty, component.issues will be an empty object ({}), and for some weird reason,
            // Ramda.isEmpty returns false when the component is received after async/await of Array.map.
            if (!(_ramda || _load_ramda()).default.isEmpty(issues)) component.issues = issues;

            return _context2.abrupt('return', component);

          case 24:
          case 'end':
            return _context2.stop();
        }
      }
    }, _callee2, this);
  }));

  function loadDependenciesForComponent(_x13, _x14, _x15, _x16) {
    return _ref.apply(this, arguments);
  }

  return loadDependenciesForComponent;
}();