"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getManipulateDirForExistingComponents = getManipulateDirForExistingComponents;
exports.getManipulateDirWhenImportingComponents = getManipulateDirWhenImportingComponents;
exports.getManipulateDirForConsumerComponent = getManipulateDirForConsumerComponent;
exports.getManipulateDirForComponentWithDependencies = getManipulateDirForComponentWithDependencies;
exports.revertDirManipulationForPath = revertDirManipulationForPath;
exports.stripSharedDirFromPath = stripSharedDirFromPath;

function _bluebird() {
  const data = require("bluebird");

  _bluebird = function () {
    return data;
  };

  return data;
}

function _path() {
  const data = _interopRequireDefault(require("path"));

  _path = function () {
    return data;
  };

  return data;
}

function _ramda() {
  const data = _interopRequireDefault(require("ramda"));

  _ramda = function () {
    return data;
  };

  return data;
}

function _bitId() {
  const data = require("../../bit-id");

  _bitId = function () {
    return data;
  };

  return data;
}

function _versionDependencies() {
  const data = _interopRequireDefault(require("../../scope/version-dependencies"));

  _versionDependencies = function () {
    return data;
  };

  return data;
}

function _utils() {
  const data = require("../../utils");

  _utils = function () {
    return data;
  };

  return data;
}

function _dependencies() {
  const data = require("../component/dependencies");

  _dependencies = function () {
    return data;
  };

  return data;
}

function _constants() {
  const data = require("../../constants");

  _constants = function () {
    return data;
  };

  return data;
}

function _componentMap() {
  const data = _interopRequireDefault(require("../bit-map/component-map"));

  _componentMap = function () {
    return data;
  };

  return data;
}

function _componentVersion() {
  const data = _interopRequireDefault(require("../../scope/component-version"));

  _componentVersion = function () {
    return data;
  };

  return data;
}

function _bitIds() {
  const data = _interopRequireDefault(require("../../bit-id/bit-ids"));

  _bitIds = function () {
    return data;
  };

  return data;
}

function _repository() {
  const data = _interopRequireDefault(require("../../scope/objects/repository"));

  _repository = function () {
    return data;
  };

  return data;
}

function _componentOverrides() {
  const data = _interopRequireDefault(require("../config/component-overrides"));

  _componentOverrides = function () {
    return data;
  };

  return data;
}

function _corruptedComponent() {
  const data = _interopRequireDefault(require("../../scope/exceptions/corrupted-component"));

  _corruptedComponent = function () {
    return data;
  };

  return data;
}

function _consumerComponent() {
  const data = _interopRequireDefault(require("../component/consumer-component"));

  _consumerComponent = function () {
    return data;
  };

  return data;
}

function _scope() {
  const data = require("../../scope");

  _scope = function () {
    return data;
  };

  return data;
}

/**
 * use this method when loading an existing component. don't use it during the import process
 */
function getManipulateDirForExistingComponents(_x, _x2) {
  return _getManipulateDirForExistingComponents.apply(this, arguments);
}
/**
 * use this method while importing a component.
 * the data from bitMap is not enough because a component might be NESTED on bitmap but is now
 * imported.
 */


function _getManipulateDirForExistingComponents() {
  _getManipulateDirForExistingComponents = (0, _bluebird().coroutine)(function* (consumer, componentVersion) {
    const id = componentVersion.id;
    const manipulateDirData = [];
    const componentMap = consumer.bitMap.getComponentIfExist(id, {
      ignoreVersion: true
    });
    const version = yield componentVersion.getVersion(consumer.scope.objects);

    if (!version) {
      throw new (_corruptedComponent().default)(id.toString(), componentVersion.version);
    }

    const originallySharedDir = componentMap ? getOriginallySharedDirIfNeeded(componentMap.origin, version) : null;
    const wrapDir = componentMap ? getWrapDirIfNeeded(componentMap.origin, version) : null;
    manipulateDirData.push({
      id,
      originallySharedDir,
      wrapDir
    });
    const dependencies = version.getAllDependencies();
    dependencies.forEach(dependency => {
      const depComponentMap = getDependencyComponentMap(consumer.bitMap, dependency.id);
      const manipulateDirDep = {
        id: dependency.id,
        originallySharedDir: depComponentMap ? depComponentMap.originallySharedDir : null,
        wrapDir: depComponentMap ? depComponentMap.wrapDir : null
      };
      manipulateDirData.push(manipulateDirDep);
    });
    return manipulateDirData;
  });
  return _getManipulateDirForExistingComponents.apply(this, arguments);
}

function getManipulateDirWhenImportingComponents(_x3, _x4, _x5) {
  return _getManipulateDirWhenImportingComponents.apply(this, arguments);
}
/**
 * this doesn't return the manipulate-dir for the dependencies, only for the given component.
 * it is useful for stripping the shared-dir for author when generating symlinks from node_modules
 */


function _getManipulateDirWhenImportingComponents() {
  _getManipulateDirWhenImportingComponents = (0, _bluebird().coroutine)(function* (bitMap, versionsDependencies, repository) {
    const nonDependencies = _bitIds().default.fromArray(versionsDependencies.map(versionDependency => versionDependency.component.id));

    const manipulateDirDataP = versionsDependencies.map(
    /*#__PURE__*/
    function () {
      var _ref = (0, _bluebird().coroutine)(function* (versionDependency) {
        const manipulateDirComponent = yield getManipulateDirItemFromComponentVersion(versionDependency.component, bitMap, repository, false);
        const manipulateDirDependenciesP = versionDependency.allDependencies.map(dependency => {
          return getManipulateDirItemFromComponentVersion(dependency, bitMap, repository, true);
        });
        const manipulateDirDependencies = yield Promise.all(manipulateDirDependenciesP); // a component might be a dependency and directly imported at the same time, in which case,
        // it should be considered as imported, not nested

        const manipulateDirDependenciesOnly = manipulateDirDependencies.filter(m => !nonDependencies.has(m.id));
        return [manipulateDirComponent, ...manipulateDirDependenciesOnly];
      });

      return function (_x10) {
        return _ref.apply(this, arguments);
      };
    }());
    const manipulateDirData = yield Promise.all(manipulateDirDataP);
    return _ramda().default.flatten(manipulateDirData);
  });
  return _getManipulateDirWhenImportingComponents.apply(this, arguments);
}

function getManipulateDirForConsumerComponent(component) {
  const id = component.id;
  const originallySharedDir = component.originallySharedDir || calculateOriginallySharedDirForConsumerComponent(component);
  const wrapDir = isWrapperDirNeededForConsumerComponent(component) ? _constants().WRAPPER_DIR : null;
  return {
    id,
    originallySharedDir,
    wrapDir
  };
}

function getManipulateDirForComponentWithDependencies(componentWithDependencies) {
  const allComponents = [componentWithDependencies.component, ...componentWithDependencies.allDependencies];
  return allComponents.map(component => getManipulateDirForConsumerComponent(component));
}

function revertDirManipulationForPath(pathStr, originallySharedDir, wrapDir) {
  const withSharedDir = addSharedDirForPath(pathStr, originallySharedDir);
  return removeWrapperDirFromPath(withSharedDir, wrapDir);
}

function stripSharedDirFromPath(pathStr, sharedDir) {
  if (!sharedDir) return pathStr;

  const partToRemove = _path().default.normalize(sharedDir) + _path().default.sep;

  return pathStr.replace(partToRemove, '');
}
/**
 * find a shared directory among the files of the main component and its dependencies
 */


function calculateOriginallySharedDirForVersion(version) {
  const filePaths = version.files.map(file => (0, _utils().pathNormalizeToLinux)(file.relativePath));
  const allDependencies = new (_dependencies().Dependencies)(version.getAllDependencies());

  const overridesDependenciesFiles = _componentOverrides().default.getAllFilesPaths(version.overrides);

  return _calculateSharedDir(filePaths, allDependencies, overridesDependenciesFiles);
}

function calculateOriginallySharedDirForConsumerComponent(component) {
  const filePaths = component.files.map(file => (0, _utils().pathNormalizeToLinux)(file.relative));
  const allDependencies = new (_dependencies().Dependencies)(component.getAllDependencies());

  const overridesDependenciesFiles = _componentOverrides().default.getAllFilesPaths(component.overrides);

  return _calculateSharedDir(filePaths, allDependencies, overridesDependenciesFiles);
}

function _calculateSharedDir(filePaths, allDependencies, overridesDependenciesFiles) {
  const pathSep = '/'; // it works for Windows as well as all paths are normalized to Linux

  const dependenciesPaths = allDependencies.getSourcesPaths();
  const allPaths = [...filePaths, ...dependenciesPaths, ...overridesDependenciesFiles];
  const sharedStart = (0, _utils().sharedStartOfArray)(allPaths);
  if (!sharedStart || !sharedStart.includes(pathSep)) return null;
  const sharedStartDirectories = sharedStart.split(pathSep);
  sharedStartDirectories.pop(); // the sharedStart ended with a slash, remove it.

  if (allPaths.some(p => p.replace(sharedStart, '') === _constants().PACKAGE_JSON)) {
    // if package.json is located in an inside dir, don't consider that dir as a sharedDir, we
    // must keep this directory in order to not collide with the generated package.json.
    sharedStartDirectories.pop();
  }

  return sharedStartDirectories.join(pathSep);
}

function getOriginallySharedDirIfNeeded(origin, version) {
  if (origin === _constants().COMPONENT_ORIGINS.AUTHORED) return null;
  return calculateOriginallySharedDirForVersion(version);
}
/**
 * if one of the files is 'package.json' and it's on the root, we need a wrapper dir to avoid
 * collision with Bit generated package.json file.
 * also, if one of the files requires the root package.json, because we need to generate the
 * "package.json" file as a link once imported, we have to wrap it as well.
 */


function isWrapperDirNeeded(version) {
  const allDependencies = new (_dependencies().Dependencies)(version.getAllDependencies());
  const dependenciesSourcePaths = allDependencies.getSourcesPaths();
  return version.files.some(file => file.relativePath === _constants().PACKAGE_JSON) || dependenciesSourcePaths.some(dependencyPath => dependencyPath === _constants().PACKAGE_JSON);
}

function isWrapperDirNeededForConsumerComponent(component) {
  const allDependencies = new (_dependencies().Dependencies)(component.getAllDependencies());
  const dependenciesSourcePaths = allDependencies.getSourcesPaths();
  return component.files.some(file => file.relative === _constants().PACKAGE_JSON) || dependenciesSourcePaths.some(dependencyPath => dependencyPath === _constants().PACKAGE_JSON);
}

function getWrapDirIfNeeded(origin, version) {
  if (origin === _constants().COMPONENT_ORIGINS.AUTHORED) return null;
  return isWrapperDirNeeded(version) ? _constants().WRAPPER_DIR : null;
}
/**
 * a dependency might be imported with a different version.
 * e.g. is-string@0.0.1 has a dependency is-type@0.0.1, however is-type@0.0.2 has been imported directly
 * in this case, we should ignore the version when looking for it in .bitmap
 * on the other hand, a dependency might be nested, and as a nested it's ok to have multiple
 * components with different versions, in this case, we look for the exact version.
 * so we do prefer an exact version, but if it doesn't find one try without a version.
 */


function getDependencyComponentMap(bitMap, dependencyId) {
  return bitMap.getComponentIfExist(dependencyId) || bitMap.getComponentIfExist(dependencyId, {
    ignoreVersion: true
  });
}
/**
 * an authored component that is now imported, is still authored.
 * however, nested component that is now imported directly, is actually imported.
 * if there is no entry for this component in bitmap, it is imported.
 */


function getComponentOrigin(bitmapOrigin, isDependency) {
  if (!bitmapOrigin) return isDependency ? _constants().COMPONENT_ORIGINS.NESTED : _constants().COMPONENT_ORIGINS.IMPORTED;

  if (bitmapOrigin === _constants().COMPONENT_ORIGINS.NESTED && !isDependency) {
    return _constants().COMPONENT_ORIGINS.IMPORTED;
  }

  return bitmapOrigin;
}

function getManipulateDirItemFromComponentVersion(_x6, _x7, _x8, _x9) {
  return _getManipulateDirItemFromComponentVersion.apply(this, arguments);
}

function _getManipulateDirItemFromComponentVersion() {
  _getManipulateDirItemFromComponentVersion = (0, _bluebird().coroutine)(function* (componentVersion, bitMap, repository, isDependency) {
    const id = componentVersion.id; // when a component is now imported, ignore the version because if it was nested before, we just
    // replace it with the imported one.
    // however, the opposite is not true, if it is now nested and was imported before, we can have them both.
    // (see 'when imported component has lower dependencies versions than local' in import.e2e for such a case).
    // we might change this behavior as it is confusing.

    const componentMap = isDependency ? bitMap.getComponentIfExist(id) : bitMap.getComponentPreferNonNested(id);
    const bitmapOrigin = componentMap ? componentMap.origin : null;
    const origin = getComponentOrigin(bitmapOrigin, isDependency);
    const version = yield componentVersion.getVersion(repository);
    const originallySharedDir = getOriginallySharedDirIfNeeded(origin, version);
    const wrapDir = getWrapDirIfNeeded(origin, version);
    return {
      id,
      originallySharedDir,
      wrapDir
    };
  });
  return _getManipulateDirItemFromComponentVersion.apply(this, arguments);
}

function addSharedDirForPath(pathStr, originallySharedDir) {
  const withSharedDir = originallySharedDir ? _path().default.join(originallySharedDir, pathStr) : pathStr;
  return (0, _utils().pathNormalizeToLinux)(withSharedDir);
}

function removeWrapperDirFromPath(pathStr, wrapDir) {
  return wrapDir ? pathStr.replace(`${wrapDir}/`, '') : pathStr;
}