'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 _;

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

var _consumer;

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

var _constants;

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

var _componentMap;

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

var _logger;

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

var _links;

function _load_links() {
  return _links = require('../../../links');
}

var _utils;

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

var _bitId;

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

var _consumerComponent;

function _load_consumerComponent() {
  return _consumerComponent = _interopRequireDefault(require('../consumer-component'));
}

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

/**
 * Dist paths are by default saved into the component's root-dir/dist. However, when dist is set in bit.json, the paths
 * are in the consumer-root/dist.target dir. If dist.entry is set, the dist.entry part is stripped from the dists paths.
 * (according to some additional conditions. See shouldDistEntryBeStripped()).
 * If there is originallySharedDir and the component is IMPORTED, it is stripped as well.
 *
 * These modifications of the paths are taken care in different stages depends on the scenario.
 * 1) using 'bit build'.
 * First, the sharedOriginallyDir is stripped (happens in consumer-component.build()). There are two scenarios here:
 *   a) the component wasn't change since the last build. It loads the dists from the model and strip the
 *      sharedOriginallyDir. (see the !needToRebuild case of build()).
 *   b) the component was changed. It re-builds it. The dists path are cloned from the files, since the files are
 *      sharedOriginallyDir stripped (because they loaded from the filesystem), so will be the dists files.
 * Next, the dist.entry is stripped. This is done when the dists are written into the file-system,  (see writeDists()).
 *
 * 2) using 'bit import'.
 * When converting the component from model to consumer-component, the sharedOriginallyDir is stripped. (see
 * stripOriginallySharedDir() ).
 * Then, Before writing the dists to the file-system, the dist-entry is taken care of. (see writeDists() ).
 *
 * 3) using 'bit link'.
 * When linking authored components, we generate an index file from node_modules/component-name to the main dist file.
 * It might happen during the import, when updateDistsPerConsumerBitJson() was running already, and it might happen
 * during the 'bit link' command. Therefore, before linking, the updateDistsPerConsumerBitJson() is running while making
 * sure it doesn't run twice.
 * (see node-modules-linker.linkToMainFile() and calculateMainDistFileForAuthored()).
 *
 * The opposite action is taken when a component is tagged. We load the component from the file-system while the dist
 * paths might be stripped from consumer dist.entry and originallySharedDir.
 * Then, before writing them to the model, we first add the originallySharedDir and then the dist.entry. We make sure
 * there were stripped before adding them. (See addSharedDirAndDistEntry function in scope.js and the comment there)
 */
var Dists = function () {
  // populated only after getDistDirForConsumer() is called
  // makes sure to not update twice
  function Dists(dists) {
    _classCallCheck(this, Dists);

    this.writeDistsFiles = true;
    this.areDistsInsideComponentDir = true;
    this.distEntryShouldBeStripped = false;
    this._distsPathsAreUpdated = false;

    this.dists = dists || []; // cover also case of null (when it comes from the model)
  } // changed only when importing a component


  _createClass(Dists, [{
    key: 'isEmpty',
    value: function isEmpty() {
      return !this.dists.length;
    }
  }, {
    key: 'get',
    value: function get() {
      return this.dists;
    }
  }, {
    key: 'getAsReadable',
    value: function getAsReadable() {
      return this.dists.map(function (file) {
        return file.toReadableString();
      });
    }

    /**
     * When dists are written by a consumer (as opposed to isolated-environment for example), the dist-entry and
     * dist-target are taken into account for calculating the path.
     * By default, the dists path is inside the component. If dist attribute is populated in bit.json, the path is
     * relative to consumer root.
     */

  }, {
    key: 'getDistDirForConsumer',
    value: function getDistDirForConsumer(consumer, componentRootDir) {
      var consumerBitJson = consumer.bitJson;
      var rootDir = componentRootDir || '.';
      if (consumer.shouldDistsBeInsideTheComponent()) {
        // should be relative to component
        this.distsRootDir = (_path || _load_path()).default.join(consumer.getPath(), rootDir, (_constants || _load_constants()).DEFAULT_DIST_DIRNAME);
      } else {
        // should be relative to consumer root
        if (consumerBitJson.distEntry) rootDir = rootDir.replace(consumerBitJson.distEntry, '');
        var distTarget = consumerBitJson.distTarget || (_constants || _load_constants()).DEFAULT_DIST_DIRNAME;
        this.areDistsInsideComponentDir = false;
        this.distsRootDir = (_path || _load_path()).default.join(consumer.getPath(), distTarget, rootDir);
      }
      return this.distsRootDir;
    }
  }, {
    key: 'updateDistsPerConsumerBitJson',
    value: function updateDistsPerConsumerBitJson(id, consumer, componentMap) {
      var _this = this;

      if (this._distsPathsAreUpdated || this.isEmpty()) return;
      // $FlowFixMe
      var newDistBase = this.getDistDirForConsumer(consumer, componentMap.rootDir);
      var distEntry = consumer.bitJson.distEntry;
      var shouldDistEntryBeStripped = function shouldDistEntryBeStripped() {
        if (!distEntry || componentMap.origin === (_constants || _load_constants()).COMPONENT_ORIGINS.NESTED) return false;
        var areAllDistsStartWithDistEntry = function areAllDistsStartWithDistEntry() {
          return _this.dists.map(function (dist) {
            return dist.relative.startsWith(distEntry);
          }).every(function (x) {
            return x;
          });
        };
        if (componentMap.origin === (_constants || _load_constants()).COMPONENT_ORIGINS.AUTHORED) {
          return areAllDistsStartWithDistEntry();
        }
        // it's IMPORTED. We first check that rootDir starts with dist.entry, it happens mostly when a user imports into
        // a specific directory (e.g. bit import --path src/). Then, we make sure all dists files start with that
        // dist.entry. In a case when originallySharedDir is the same as dist.entry, this second check returns false.
        // $FlowFixMe
        return componentMap.rootDir.startsWith(distEntry) && areAllDistsStartWithDistEntry();
      };
      var distEntryShouldBeStripped = shouldDistEntryBeStripped();
      if (distEntryShouldBeStripped) {
        // $FlowFixMe
        (_logger || _load_logger()).default.debug(`stripping dist.entry "${distEntry}" from ${id}`);
        this.distEntryShouldBeStripped = true;
      }
      var getNewRelative = function getNewRelative(dist) {
        if (distEntryShouldBeStripped) {
          return dist.relative.replace(distEntry, '');
        }
        return dist.relative;
      };
      this.dists.forEach(function (dist) {
        return dist.updatePaths({ newBase: newDistBase, newRelative: getNewRelative(dist) });
      });
      this._distsPathsAreUpdated = true;
    }
  }, {
    key: 'stripOriginallySharedDir',
    value: function stripOriginallySharedDir(originallySharedDir, pathWithoutSharedDir) {
      this.dists.forEach(function (distFile) {
        var newRelative = pathWithoutSharedDir(distFile.relative, originallySharedDir);
        distFile.updatePaths({ newBase: distFile.base, newRelative });
      });
    }

    /**
     * write dists file to the filesystem. In case there is a consumer and dist.entry should be stripped, it will be
     * done before writing the files. The originallySharedDir should be already stripped before accessing this method.
     */

  }, {
    key: 'writeDists',
    value: function () {
      var _ref = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee(component, consumer) {
        var writeLinks = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
        var componentMap, saveDist;
        return (_regenerator || _load_regenerator()).default.wrap(function _callee$(_context) {
          while (1) {
            switch (_context.prev = _context.next) {
              case 0:
                if (!(this.isEmpty() || !this.writeDistsFiles)) {
                  _context.next = 2;
                  break;
                }

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

              case 2:
                componentMap = void 0;

                if (consumer) {
                  componentMap = consumer.bitMap.getComponent(component.id, false, false, true);
                  this.updateDistsPerConsumerBitJson(component.id, consumer, componentMap);
                }
                saveDist = this.dists.map(function (distFile) {
                  return distFile.write();
                });

                if (!(writeLinks && componentMap && componentMap.origin === (_constants || _load_constants()).COMPONENT_ORIGINS.IMPORTED)) {
                  _context.next = 8;
                  break;
                }

                _context.next = 8;
                return (0, (_links || _load_links()).writeLinksInDist)(component, componentMap, consumer);

              case 8:
                return _context.abrupt('return', Promise.all(saveDist));

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

      function writeDists(_x2, _x3) {
        return _ref.apply(this, arguments);
      }

      return writeDists;
    }()

    // In case there are dist files, we want to point the index to the main dist file, not to source.
    // This important since when you require a module without specify file, it will give you the file specified under this key
    // (or index.js if key not exists)

  }, {
    key: 'calculateMainDistFile',
    value: function calculateMainDistFile(componentMainFile) {
      if (this.writeDistsFiles && this.areDistsInsideComponentDir) {
        var mainFile = (0, (_utils || _load_utils()).searchFilesIgnoreExt)(this.dists, componentMainFile, 'relative', 'relative');
        if (mainFile) return (_path || _load_path()).default.join((_constants || _load_constants()).DEFAULT_DIST_DIRNAME, mainFile);
      }
      return componentMainFile;
    }

    /**
     * authored components have the dists outside the components dir and they don't have rootDir.
     * it returns the main file or main dist file relative to consumer-root.
     */

  }, {
    key: 'calculateMainDistFileForAuthored',
    value: function calculateMainDistFileForAuthored(componentMainFile, consumer) {
      if (this.isEmpty()) return componentMainFile;
      var getMainFileToSearch = function getMainFileToSearch() {
        if (!consumer.bitJson.distEntry) return componentMainFile;
        var distEntryNormalized = (_path || _load_path()).default.normalize(consumer.bitJson.distEntry);
        return componentMainFile.replace(`${distEntryNormalized}${(_path || _load_path()).default.sep}`, '');
      };
      var mainFileToSearch = getMainFileToSearch();
      var distMainFile = (0, (_utils || _load_utils()).searchFilesIgnoreExt)(this.dists, mainFileToSearch, 'relative', 'relative');
      if (!distMainFile) return componentMainFile;
      var distTarget = consumer.bitJson.distTarget || (_constants || _load_constants()).DEFAULT_DIST_DIRNAME;
      return (_path || _load_path()).default.join(distTarget, distMainFile);
    }
  }]);

  return Dists;
}();

exports.default = Dists;