'use strict';

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

var _regenerator;

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

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

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 _commentJson;

function _load_commentJson() {
  return _commentJson = _interopRequireDefault(require('comment-json'));
}

var _logger;

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

var _constants;

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

var _exceptions;

function _load_exceptions() {
  return _exceptions = require('./exceptions');
}

var _bitId;

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

var _utils;

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

var _componentMap;

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

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 _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var BitMap = function () {
  // path => componentId

  function BitMap(projectRoot, mapPath, components, version) {
    var _this = this;

    _classCallCheck(this, BitMap);

    this._invalidateCache = function () {
      _this.paths = {};
      _this.pathsLowerCase = {};
    };

    this.projectRoot = projectRoot;
    this.mapPath = mapPath;
    this.components = components;
    this.hasChanged = false;
    this.version = version;
    this.paths = {};
    this.pathsLowerCase = {};
  } // path => componentId


  _createClass(BitMap, [{
    key: 'markAsChanged',
    value: function markAsChanged() {
      this.hasChanged = true;
      this._invalidateCache();
    }
  }, {
    key: 'setComponent',
    value: function setComponent(id, componentMap) {
      var _this2 = this;

      var bitId = (_bitId || _load_bitId()).BitId.parse(id);
      if (!bitId.hasVersion() && bitId.scope) {
        throw new (_generalError || _load_generalError()).default(`invalid bitmap id ${id}, a component must have a version when a scope-name is included`);
      }
      var idWithoutScopeAndVersion = bitId.toStringWithoutScopeAndVersion();
      if (componentMap.origin !== (_constants || _load_constants()).COMPONENT_ORIGINS.NESTED) {
        // make sure there are no duplications (same namespace+name)
        Object.keys(this.components).forEach(function (compId) {
          if ((_bitId || _load_bitId()).BitId.parse(compId).toStringWithoutScopeAndVersion() === idWithoutScopeAndVersion && _this2.components[compId].origin !== (_constants || _load_constants()).COMPONENT_ORIGINS.NESTED) {
            throw new (_generalError || _load_generalError()).default(`your id ${id} is duplicated with ${compId}`);
          }
        });
      }

      this.components[id] = componentMap;
      this.markAsChanged();
    }
  }, {
    key: 'getAllComponents',
    value: function getAllComponents(origin) {
      if (!origin) return this.components;
      var isOriginMatch = function isOriginMatch(component) {
        return component.origin === origin;
      };
      // $FlowFixMe we know origin is an array in that case
      var isOriginMatchArray = function isOriginMatchArray(component) {
        return origin.includes(component.origin);
      };
      var filter = Array.isArray(origin) ? isOriginMatchArray : isOriginMatch;
      return (_ramda || _load_ramda()).default.filter(filter, this.components);
    }
  }, {
    key: 'getAuthoredExportedComponents',
    value: function getAuthoredExportedComponents() {
      var _this3 = this;

      var componentsIds = [];
      Object.keys(this.components).forEach(function (componentId) {
        if (_this3.components[componentId].origin === (_constants || _load_constants()).COMPONENT_ORIGINS.AUTHORED) {
          var idParsed = (_bitId || _load_bitId()).BitId.parse(componentId);
          if (idParsed.scope) componentsIds.push(idParsed);
        }
      });
      return componentsIds;
    }
  }, {
    key: '_makePathRelativeToProjectRoot',
    value: function _makePathRelativeToProjectRoot(pathToChange) {
      var absolutePath = (_path || _load_path()).default.resolve(pathToChange);
      return (_path || _load_path()).default.relative(this.projectRoot, absolutePath);
    }
  }, {
    key: '_searchMainFile',
    value: function _searchMainFile(baseMainFile, files) {
      // search for an exact relative-path
      var mainFileFromFiles = files.find(function (file) {
        return file.relativePath === baseMainFile;
      });
      if (mainFileFromFiles) return baseMainFile;
      // search for a file-name
      var potentialMainFiles = files.filter(function (file) {
        return file.name === baseMainFile;
      });
      if (!potentialMainFiles.length) return null;
      // when there are several files that met the criteria, choose the closer to the root
      var sortByNumOfDirs = function sortByNumOfDirs(a, b) {
        return a.relativePath.split((_constants || _load_constants()).DEFAULT_SEPARATOR).length - b.relativePath.split((_constants || _load_constants()).DEFAULT_SEPARATOR).length;
      };
      potentialMainFiles.sort(sortByNumOfDirs);
      mainFileFromFiles = (_ramda || _load_ramda()).default.head(potentialMainFiles);
      return mainFileFromFiles.relativePath;
    }
  }, {
    key: '_getMainFile',
    value: function _getMainFile(mainFile, componentMap) {
      var _this4 = this;

      var files = componentMap.files.filter(function (file) {
        return !file.test;
      });
      // scenario 1) user entered mainFile => search the mainFile in the files array
      if (mainFile) {
        var foundMainFile = this._searchMainFile(mainFile, files);
        if (foundMainFile) return foundMainFile;
        throw new (_exceptions || _load_exceptions()).MissingMainFile(mainFile, files.map(function (file) {
          return (_path || _load_path()).default.normalize(file.relativePath);
        }));
      }
      // scenario 2) user didn't enter mainFile and the component has only one file => use that file as the main file.
      if (files.length === 1) return files[0].relativePath;
      // scenario 3) user didn't enter mainFile and the component has multiple files => search for default main files (such as index.js)
      var searchResult = void 0;
      (_constants || _load_constants()).DEFAULT_INDEX_EXTS.forEach(function (ext) {
        // TODO: can be improved - stop loop if finding main file
        if (!searchResult) {
          var mainFileNameToSearch = `${(_constants || _load_constants()).DEFAULT_INDEX_NAME}.${ext}`;
          searchResult = _this4._searchMainFile(mainFileNameToSearch, files);
        }
      });
      if (searchResult) return searchResult;
      var mainFileString = `${(_constants || _load_constants()).DEFAULT_INDEX_NAME}.[${(_constants || _load_constants()).DEFAULT_INDEX_EXTS.join(', ')}]`;
      throw new (_exceptions || _load_exceptions()).MissingMainFile(mainFileString, files.map(function (file) {
        return (_path || _load_path()).default.normalize(file.relativePath);
      }));
    }
  }, {
    key: 'addDependencyToParent',
    value: function addDependencyToParent(parent, dependency) {
      // the parent component might appear in bit.map file with full-id, e.g. when it is a dependency itself.
      // And it might be with the short id, without the scope and the version, e.g. when its origin is AUTHORED or IMPORTED
      var parentWithScope = parent.toString();
      var parentWithoutScope = parent.changeScope(null).toString();
      var parentId = void 0;
      if (this.components[parentWithScope]) {
        parentId = parentWithScope;
      } else if (this.components[parentWithoutScope]) {
        parentId = parentWithoutScope;
      } else {
        throw new (_generalError || _load_generalError()).default(`Unable to add indirect dependency ${dependency}, as its parent ${parent} does not exist`);
      }
      if (!this.components[parentId].dependencies) {
        this.components[parentId].dependencies = [dependency];
      }
      if (!this.components[parentId].dependencies.includes(dependency)) {
        // $FlowFixMe at this stage we know that dependencies is not null
        this.components[parentId].dependencies.push(dependency);
      }
      this.markAsChanged();
    }
  }, {
    key: 'deleteOlderVersionsOfComponent',
    value: function deleteOlderVersionsOfComponent(componentId) {
      var _this5 = this;

      var allVersions = Object.keys(this.components).filter(function (id) {
        return (_bitId || _load_bitId()).BitId.parse(id).toStringWithoutVersion() === componentId.toStringWithoutVersion();
      });
      allVersions.forEach(function (version) {
        if (version !== componentId.toString() && _this5.components[version].origin !== (_constants || _load_constants()).COMPONENT_ORIGINS.NESTED) {
          (_logger || _load_logger()).default.debug(`BitMap: deleting an older version ${version} of an existing component ${componentId.toString()}`);
          _this5._removeFromComponentsArray(version);
        }
      });
    }

    /**
     * When the given id doesn't include scope-name, there might be a similar component in bit.map with scope-name
     */

  }, {
    key: 'getExistingComponentId',
    value: function getExistingComponentId(componentIdStr, origin) {
      var components = this.getAllComponents(origin);
      if (components[componentIdStr]) return componentIdStr;
      var parsedId = (_bitId || _load_bitId()).BitId.parse(componentIdStr);
      if (parsedId.scope && !parsedId.hasVersion()) {
        return Object.keys(components).find(function (component) {
          return (_bitId || _load_bitId()).BitId.parse(component).toStringWithoutVersion() === componentIdStr;
        });
      }
      return Object.keys(components).find(function (component) {
        return (_bitId || _load_bitId()).BitId.parse(component).toStringWithoutScopeAndVersion() === componentIdStr;
      });
    }

    /**
     * check if both arrays are equal according to their 'relativePath', regardless the order
     */

  }, {
    key: '_areFilesArraysEqual',
    value: function _areFilesArraysEqual(filesA, filesB) {
      if (filesA.length !== filesB.length) return false;
      var cmp = function cmp(x, y) {
        return x.relativePath === y.relativePath;
      };
      var diff = (_ramda || _load_ramda()).default.differenceWith(cmp, filesA, filesB);
      if (!diff.length) return true;
      return false;
    }

    /**
     * add files from filesB that are not in filesA
     */

  }, {
    key: 'mergeFilesArray',
    value: function mergeFilesArray(filesA, filesB) {
      return (_ramda || _load_ramda()).default.unionWith((_ramda || _load_ramda()).default.eqBy((_ramda || _load_ramda()).default.prop('relativePath')), filesA, filesB);
    }

    /**
     * if an existing file is for example uppercase and the new file is lowercase it has different
     * behavior according to the OS. some OS are case sensitive, some are not.
     * it's safer to avoid saving both files and instead, replacing the old file with the new one.
     * in case a file has replaced and it is also a mainFile, replace the mainFile as well
     */

  }, {
    key: '_updateFilesWithCurrentLetterCases',
    value: function _updateFilesWithCurrentLetterCases(componentId, newFiles) {
      var currentComponentMap = this.components[componentId];
      var currentFiles = currentComponentMap.files;
      currentFiles.forEach(function (currentFile) {
        var sameFile = newFiles.find(function (newFile) {
          return newFile.relativePath.toLowerCase() === currentFile.relativePath.toLowerCase();
        });
        if (sameFile && currentFile.relativePath !== sameFile.relativePath) {
          if (currentComponentMap.mainFile === currentFile.relativePath) {
            currentComponentMap.mainFile = sameFile.relativePath;
          }
          currentFile.relativePath = sameFile.relativePath;
        }
      });
    }
  }, {
    key: 'addComponent',
    value: function addComponent(_ref) {
      var componentId = _ref.componentId,
          files = _ref.files,
          mainFile = _ref.mainFile,
          origin = _ref.origin,
          parent = _ref.parent,
          rootDir = _ref.rootDir,
          trackDir = _ref.trackDir,
          override = _ref.override,
          originallySharedDir = _ref.originallySharedDir;

      var isDependency = origin === (_constants || _load_constants()).COMPONENT_ORIGINS.NESTED;
      var componentIdStr = componentId.toString();
      (_logger || _load_logger()).default.debug(`adding to bit.map ${componentIdStr}`);
      if (isDependency) {
        if (!parent) {
          throw new (_generalError || _load_generalError()).default(`Unable to add indirect dependency ${componentIdStr}, without "parent" parameter`);
        }
        this.addDependencyToParent(parent, componentIdStr);
      }
      if (this.components[componentIdStr]) {
        (_logger || _load_logger()).default.info(`bit.map: updating an exiting component ${componentIdStr}`);
        var existingRootDir = this.components[componentIdStr].rootDir;
        if (existingRootDir) (_componentMap || _load_componentMap()).default.changeFilesPathAccordingToItsRootDir(existingRootDir, files);
        if (override) {
          this.components[componentIdStr].files = files;
        } else {
          this._updateFilesWithCurrentLetterCases(componentIdStr, files);
          // override the current componentMap.files with the given files argument
          this.components[componentIdStr].files = (_ramda || _load_ramda()).default.unionWith((_ramda || _load_ramda()).default.eqBy((_ramda || _load_ramda()).default.prop('relativePath')), files, this.components[componentIdStr].files);
        }
        if (mainFile) {
          this.components[componentIdStr].mainFile = this._getMainFile((0, (_utils || _load_utils()).pathNormalizeToLinux)(mainFile), this.components[componentIdStr]);
        }
      } else {
        if (origin === (_constants || _load_constants()).COMPONENT_ORIGINS.IMPORTED || origin === (_constants || _load_constants()).COMPONENT_ORIGINS.AUTHORED) {
          // if there are older versions, the user is updating an existing component, delete old ones from bit.map
          this.deleteOlderVersionsOfComponent(componentId);
        }
        // $FlowFixMe not easy to fix, we can't instantiate ComponentMap with mainFile because we don't have it yet
        this.setComponent(componentIdStr, new (_componentMap || _load_componentMap()).default({ files, origin }));
        this.components[componentIdStr].mainFile = this._getMainFile((0, (_utils || _load_utils()).pathNormalizeToLinux)(mainFile), this.components[componentIdStr]);
      }
      if (rootDir) {
        // when rootDir is from the cli, it is changed to be absolute first (see write-components.writeToComponentsDir)
        // and on the next line it is changed to be relative to the project-root.
        // otherwise, rootDir may be originated from previous componentMap.rootDir value, as such,
        // when running the command from an inner directory we must not run _makePathRelativeToProjectRoot.
        var rootRelative = (_path || _load_path()).default.isAbsolute(rootDir) ? this._makePathRelativeToProjectRoot(rootDir) : rootDir;
        this.components[componentIdStr].rootDir = (0, (_utils || _load_utils()).pathNormalizeToLinux)(rootRelative);
      }
      if (trackDir) {
        this.components[componentIdStr].trackDir = (0, (_utils || _load_utils()).pathNormalizeToLinux)(trackDir);
      }
      this.components[componentIdStr].removeTrackDirIfNeeded();
      if (originallySharedDir) {
        this.components[componentIdStr].originallySharedDir = originallySharedDir;
      }
      this.components[componentIdStr].sort();
      this.components[componentIdStr].validate();
      this.markAsChanged();
      return this.components[componentIdStr];
    }
  }, {
    key: 'addFilesToComponent',
    value: function addFilesToComponent(_ref2) {
      var componentId = _ref2.componentId,
          files = _ref2.files;

      var componentIdStr = componentId.toString();
      if (!this.components[componentIdStr]) {
        throw new (_generalError || _load_generalError()).default(`unable to add files to a non-exist component ${componentIdStr}`);
      }
      var existingRootDir = this.components[componentIdStr].rootDir;
      if (existingRootDir) (_componentMap || _load_componentMap()).default.changeFilesPathAccordingToItsRootDir(existingRootDir, files);
      if (this._areFilesArraysEqual(this.components[componentIdStr].files, files)) {
        return this.components[componentIdStr];
      }
      // do not override existing files, only add new files
      (_logger || _load_logger()).default.info(`bit.map: updating an exiting component ${componentIdStr}`);
      this.components[componentIdStr].files = (_ramda || _load_ramda()).default.unionWith((_ramda || _load_ramda()).default.eqBy((_ramda || _load_ramda()).default.prop('relativePath')), this.components[componentIdStr].files, files);
      this.components[componentIdStr].sort();
      this.components[componentIdStr].validate();
      this.markAsChanged();
      return this.components[componentIdStr];
    }
  }, {
    key: '_removeFromComponentsArray',
    value: function _removeFromComponentsArray(componentId) {
      delete this.components[componentId];
      this.markAsChanged();
    }
  }, {
    key: 'removeComponent',
    value: function removeComponent(id) {
      var bitId = id instanceof (_bitId || _load_bitId()).BitId ? id : (_bitId || _load_bitId()).BitId.parse(id);
      var bitmapComponent = this.getExistingComponentId(bitId.toStringWithoutScopeAndVersion());
      if (bitmapComponent) this._removeFromComponentsArray(bitmapComponent);
      return bitmapComponent;
    }
  }, {
    key: 'removeComponents',
    value: function removeComponents(ids) {
      var _this6 = this;

      return ids.map(function (id) {
        return _this6.removeComponent(id);
      });
    }
  }, {
    key: 'addMainDistFileToComponent',
    value: function addMainDistFileToComponent(id, distFilesPaths) {
      var _this7 = this;

      if (!this.components[id]) {
        (_logger || _load_logger()).default.warn(`unable to find the component ${id} in bit.map file`);
        return;
      }
      var distFilesPathsNormalized = distFilesPaths.map(function (filePath) {
        return (0, (_utils || _load_utils()).pathNormalizeToLinux)(filePath);
      });

      var mainDistFile = distFilesPathsNormalized.find(function (distFile) {
        return distFile.endsWith(_this7.components[id].mainFile);
      });
      if (!mainDistFile) {
        (_logger || _load_logger()).default.warn(`unable to find the main dist file of component ${id}. Dist files: ${distFilesPathsNormalized.join(', ')}`);
        return;
      }
      this.components[id].mainDistFile = this._makePathRelativeToProjectRoot(mainDistFile);
      this.markAsChanged();
    }
  }, {
    key: 'isExistWithSameVersion',
    value: function isExistWithSameVersion(id) {
      return id.hasVersion() && this.components[id.toString()];
    }

    /**
     * needed after exporting or tagging a component.
     * We don't support export/tag of nested components, only authored or imported. For authored/imported components, could be
     * in the file-system only one instance with the same box-name and component-name. As a result, we can strip the
     * scope-name and the version, find the older version in bit.map and update the id with the new one.
     */

  }, {
    key: 'updateComponentId',
    value: function updateComponentId(id) {
      var _this8 = this;

      var newIdString = id.toString();
      var olderComponentsIds = Object.keys(this.components).filter(function (componentId) {
        return (_bitId || _load_bitId()).BitId.parse(componentId).toStringWithoutScopeAndVersion() === id.toStringWithoutScopeAndVersion() && componentId !== newIdString && _this8.components[componentId].origin !== (_constants || _load_constants()).COMPONENT_ORIGINS.NESTED;
      });

      if (!olderComponentsIds.length) {
        (_logger || _load_logger()).default.debug(`bit-map: no need to update ${newIdString}`);
        return;
      }
      if (olderComponentsIds.length > 1) {
        throw new (_generalError || _load_generalError()).default(`Your ${(_constants || _load_constants()).BIT_MAP} file has more than one version of ${id.toStringWithoutScopeAndVersion()} and they
      are authored or imported. This scenario is not supported`);
      }
      var olderComponentId = olderComponentsIds[0];
      (_logger || _load_logger()).default.debug(`BitMap: updating an older component ${olderComponentId} with a newer component ${newIdString}`);
      var componentMap = this.components[olderComponentId];
      this._removeFromComponentsArray(olderComponentId);
      this.setComponent(newIdString, componentMap);

      // update the dependencies array if needed
      Object.keys(this.components).forEach(function (componentId) {
        var component = _this8.components[componentId];
        if (component.dependencies && component.dependencies.includes(olderComponentId)) {
          component.dependencies = component.dependencies.filter(function (dependency) {
            return dependency !== olderComponentId;
          });
          component.dependencies.push(newIdString);
        }
      });
      this.markAsChanged();
    }

    /**
     * Get component from bitmap by id if exists
     *
     * @param {string | BitId} id - component id
     * @param {Boolean} shouldThrow - should throw error in case of missing
     * @param {Boolean} includeSearchByBoxAndNameOnly - should compare with box and name of component (without scope or verison)
     * @returns {ComponentMap} componentMap
     */

  }, {
    key: 'getComponent',
    value: function getComponent(id) {
      var shouldThrow = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
      var includeSearchByBoxAndNameOnly = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
      var ignoreVersion = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;

      var bitId = (_ramda || _load_ramda()).default.is(String, id) ? (_bitId || _load_bitId()).BitId.parse(id) : id;
      if (!ignoreVersion && bitId.hasVersion()) {
        if (!this.components[bitId] && shouldThrow) throw new (_exceptions || _load_exceptions()).MissingBitMapComponent(bitId.toString());
        return this.components[bitId];
      }
      var idWithVersion = Object.keys(this.components).find(function (componentId) {
        return (_bitId || _load_bitId()).BitId.parse(componentId).toStringWithoutVersion() === bitId.toStringWithoutVersion() || includeSearchByBoxAndNameOnly && (_bitId || _load_bitId()).BitId.parse(componentId).toStringWithoutScopeAndVersion() === bitId.toStringWithoutScopeAndVersion();
      });
      if (!idWithVersion && shouldThrow) throw new (_exceptions || _load_exceptions()).MissingBitMapComponent(bitId.toString());
      // $FlowFixMe
      return this.components[idWithVersion];
    }

    /**
     * Return a potential componentMap if file is supposed to be part of it
     * by a path exist in the files object
     *
     * @param {string} componentPath relative to consumer - as stored in bit.map files object
     * @returns {ComponentMap} componentMap
     */

  }, {
    key: 'getComponentObjectOfFileByPath',
    value: function getComponentObjectOfFileByPath(componentPath) {
      var _this9 = this;

      var components = this.getAllComponents();
      return (_ramda || _load_ramda()).default.pickBy(function (component) {
        return (0, (_utils || _load_utils()).pathIsInside)(componentPath, component.rootDir || _this9.projectRoot);
      }, components);
    }

    /**
     * Return a component id as listed in bit.map file
     * by a path exist in the files object
     *
     * @param {string} componentPath relative to consumer - as stored in bit.map files object
     * @returns {string} component id
     * @memberof BitMap
     */

  }, {
    key: 'getComponentIdByPath',
    value: function getComponentIdByPath(componentPath) {
      var caseSensitive = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;

      this._populateAllPaths();
      return caseSensitive ? this.paths[componentPath] : this.pathsLowerCase[componentPath.toLowerCase()];
    }
  }, {
    key: '_populateAllPaths',
    value: function _populateAllPaths() {
      var _this10 = this;

      if ((_ramda || _load_ramda()).default.isEmpty(this.paths)) {
        Object.keys(this.components).forEach(function (componentId) {
          var component = _this10.components[componentId];
          component.files.forEach(function (file) {
            var relativeToConsumer = component.rootDir ? (0, (_utils || _load_utils()).pathJoinLinux)(component.rootDir, file.relativePath) : file.relativePath;
            _this10.paths[relativeToConsumer] = componentId;
            _this10.pathsLowerCase[relativeToConsumer.toLowerCase()] = componentId;
          });
        });
      }
    }
  }, {
    key: 'updatePathLocation',
    value: function updatePathLocation(from, to, existingPath) {
      var _this11 = this;

      var isPathDir = (0, (_utils || _load_utils()).isDir)(existingPath);
      var allChanges = [];
      Object.keys(this.components).forEach(function (componentId) {
        var componentMap = _this11.components[componentId];
        var changes = isPathDir ? componentMap.updateDirLocation(from, to) : componentMap.updateFileLocation(from, to);
        if (changes && changes.length) allChanges.push({ id: componentId, changes });
      });
      if ((_ramda || _load_ramda()).default.isEmpty(allChanges)) {
        var errorMsg = isPathDir ? `directory ${from} is not a tracked component` : `the file ${existingPath} is untracked`;
        throw new (_generalError || _load_generalError()).default(errorMsg);
      }

      this.markAsChanged();
      return allChanges;
    }

    /**
     * do not call this function directly, let consumer.onDestroy() call it.
     * consumer.onDestroy() is being called (manually) at the end of the command process.
     * the risk of calling this method in other places is a parallel writing of this file, which
     * may result in a damaged file
     */

  }, {
    key: 'write',
    value: function () {
      var _ref3 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee() {
        var bitMapContent;
        return (_regenerator || _load_regenerator()).default.wrap(function _callee$(_context) {
          while (1) {
            switch (_context.prev = _context.next) {
              case 0:
                if (this.hasChanged) {
                  _context.next = 2;
                  break;
                }

                return _context.abrupt('return', null);

              case 2:
                (_logger || _load_logger()).default.debug('writing to bit.map');
                bitMapContent = Object.assign({}, this.components, { version: this.version });
                return _context.abrupt('return', (0, (_utils || _load_utils()).outputFile)({ filePath: this.mapPath, content: JSON.stringify(bitMapContent, null, 4) }));

              case 5:
              case 'end':
                return _context.stop();
            }
          }
        }, _callee, this);
      }));

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

      return write;
    }()
  }], [{
    key: 'load',
    value: function load(dirPath) {
      var standardLocation = (_path || _load_path()).default.join(dirPath, (_constants || _load_constants()).BIT_MAP);
      var oldLocation = (_path || _load_path()).default.join(dirPath, (_constants || _load_constants()).OLD_BIT_MAP);
      var getBitMapLocation = function getBitMapLocation() {
        if ((_fsExtra || _load_fsExtra()).default.existsSync(standardLocation)) return standardLocation;
        if ((_fsExtra || _load_fsExtra()).default.existsSync(oldLocation)) return oldLocation;
        return null;
      };
      var bitMapLocation = getBitMapLocation();
      var components = {};
      if (!bitMapLocation) {
        (_logger || _load_logger()).default.info(`bit.map: unable to find an existing ${(_constants || _load_constants()).BIT_MAP} file. Will create a new one if needed`);
        return new BitMap(dirPath, standardLocation, components, (_constants || _load_constants()).BIT_VERSION);
      }
      var mapFileContent = (_fsExtra || _load_fsExtra()).default.readFileSync(bitMapLocation);
      var componentsJson = void 0;
      try {
        componentsJson = (_commentJson || _load_commentJson()).default.parse(mapFileContent.toString('utf8'), null, true);
      } catch (e) {
        (_logger || _load_logger()).default.error(e);
        throw new (_exceptions || _load_exceptions()).InvalidBitMap(bitMapLocation, e.message);
      }
      var version = componentsJson.version;
      // Don't treat version like component
      delete componentsJson.version;
      Object.keys(componentsJson).forEach(function (componentId) {
        components[componentId] = (_componentMap || _load_componentMap()).default.fromJson(componentsJson[componentId]);
      });

      return new BitMap(dirPath, bitMapLocation, components, version);
    }

    /**
     * if resetHard, delete the bitMap file.
     * Otherwise, try to load it and only if the file is corrupted then delete it.
     */

  }, {
    key: 'reset',
    value: function reset(dirPath, resetHard) {
      var bitMapPath = (_path || _load_path()).default.join(dirPath, (_constants || _load_constants()).BIT_MAP);
      var deleteBitMapFile = function deleteBitMapFile() {
        (_logger || _load_logger()).default.info(`deleting the bitMap file at ${bitMapPath}`);
        (_fsExtra || _load_fsExtra()).default.removeSync(bitMapPath);
      };
      if (resetHard) {
        deleteBitMapFile();
        return;
      }
      try {
        BitMap.load(dirPath);
      } catch (err) {
        if (err instanceof (_exceptions || _load_exceptions()).InvalidBitMap) {
          deleteBitMapFile();
          return;
        }
        throw err;
      }
    }
  }]);

  return BitMap;
}();

exports.default = BitMap;