'use strict';

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

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

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

var _constants;

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

var _bitId;

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

var _logger;

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

var _linkContent;

function _load_linkContent() {
  return _linkContent = require('./link-content');
}

var _componentIdToPackageName;

function _load_componentIdToPackageName() {
  return _componentIdToPackageName = _interopRequireDefault(require('../utils/bit/component-id-to-package-name'));
}

var _path2;

function _load_path2() {
  return _path2 = require('../utils/path');
}

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

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

/**
 * a dependency component may have multiple files required by the main component.
 * this class generates the link content of one file of a dependency.
 * @see RelativePath docs for more info
 */
var DependencyFileLinkGenerator = function () {
  function DependencyFileLinkGenerator(_ref) {
    var consumer = _ref.consumer,
        component = _ref.component,
        relativePath = _ref.relativePath,
        dependencyComponent = _ref.dependencyComponent,
        createNpmLinkFiles = _ref.createNpmLinkFiles,
        targetDir = _ref.targetDir;

    _classCallCheck(this, DependencyFileLinkGenerator);

    this.consumer = consumer;
    this.component = component; // $FlowFixMe componentMap should be set here
    this.componentMap = this.component.componentMap;
    this.relativePath = relativePath;
    this.dependencyComponent = dependencyComponent;
    this.dependencyId = dependencyComponent.id;
    this.createNpmLinkFiles = createNpmLinkFiles;
    this.targetDir = targetDir;
    this.isLinkToPackage = this.createNpmLinkFiles || !this.component.dependenciesSavedAsComponents;
  }

  _createClass(DependencyFileLinkGenerator, [{
    key: 'generate',
    value: function generate() {
      this.linkFiles = [];
      if (this.component.dependenciesSavedAsComponents) {
        this.dependencyComponent.componentMap = this.consumer.bitMap.getComponent(this.dependencyId);
        this.dependencyComponentMap = this.dependencyComponent.componentMap;
      }
      this.relativePathInDependency = (_path || _load_path()).default.normalize(this.relativePath.destinationRelativePath);
      this.hasDist = this.component.dists.writeDistsFiles && !this.component.dists.isEmpty();
      this.shouldDistsBeInsideTheComponent = this.consumer ? this.consumer.shouldDistsBeInsideTheComponent() : true;
      if (this.relativePath.isCustomResolveUsed) {
        return this.generateForCustomResolve();
      }
      var linkFile = this.prepareLinkFile({
        linkPath: this.getLinkPath(),
        relativePathInDependency: this.relativePathInDependency,
        depRootDir: this._getDepRootDir()
      });
      this.linkFiles.push(linkFile);

      if (this.hasDist) {
        this.generateForDist();
      }

      return this.linkFiles;
    }
  }, {
    key: 'generateForCustomResolve',
    value: function generateForCustomResolve() {
      var distRoot = this._getDistRoot();
      var relativeDistPathInDependency = this._getRelativeDistPathInDependency();
      var relativeDistExtInDependency = (0, (_utils || _load_utils()).getExt)(relativeDistPathInDependency);
      var depRootDir = this._getDepRootDir();
      var depRootDirDist = this._getDepRootDirDist();

      var isCustomResolvedWithDistInside = Boolean(depRootDirDist && this.shouldDistsBeInsideTheComponent && this.hasDist);
      var isCustomResolvedWithDistAndNpmLink = Boolean(this.shouldDistsBeInsideTheComponent && this.hasDist && this.isLinkToPackage);

      var relativePathInDependency = isCustomResolvedWithDistInside || isCustomResolvedWithDistAndNpmLink ? `${(0, (_utils || _load_utils()).getWithoutExt)(this.relativePathInDependency)}.${relativeDistExtInDependency}` : this.relativePathInDependency;

      var linkFile = this.prepareLinkFile({
        linkPath: this.getLinkPathForCustomResolve(relativeDistExtInDependency),
        relativePathInDependency,
        depRootDir: isCustomResolvedWithDistInside ? depRootDirDist : depRootDir
      });
      if (this.createNpmLinkFiles && linkFile.linkContent) {
        linkFile.postInstallLink = true;
      }
      this.linkFiles.push(linkFile);

      if (this.hasDist && !this.shouldDistsBeInsideTheComponent) {
        // when isCustomResolvedUsed, the link is generated inside node_module directory, so for
        // dist inside the component, only one link is needed at the parentRootDir. for dist
        // outside the component dir, another link is needed for the dist/parentRootDir.
        var importSourcePath = this._getImportSourcePathForCustomResolve(relativeDistExtInDependency);
        var linkFileInNodeModules = this.prepareLinkFile({
          linkPath: (_path || _load_path()).default.join(distRoot, importSourcePath),
          relativePathInDependency: relativeDistPathInDependency,
          depRootDir: depRootDirDist
        });
        this.linkFiles.push(linkFileInNodeModules);
      }

      return this.linkFiles;
    }
  }, {
    key: 'generateForDist',
    value: function generateForDist() {
      var distRoot = this._getDistRoot();
      var relativeDistPathInDependency = this._getRelativeDistPathInDependency();
      var relativeDistExtInDependency = (0, (_utils || _load_utils()).getExt)(relativeDistPathInDependency);
      var sourceRelativePathWithCompiledExt = `${(0, (_utils || _load_utils()).getWithoutExt)(this.relativePath.sourceRelativePath)}.${relativeDistExtInDependency}`;
      var linkFileInDist = this.prepareLinkFile({
        linkPath: (_path || _load_path()).default.join(distRoot, sourceRelativePathWithCompiledExt), // Generate a link file inside dist folder of the dependent component
        relativePathInDependency: relativeDistPathInDependency,
        depRootDir: this._getDepRootDirDist()
      });
      this.linkFiles.push(linkFileInDist);
    }
  }, {
    key: 'prepareLinkFile',
    value: function prepareLinkFile(_ref2) {
      var linkPath = _ref2.linkPath,
          relativePathInDependency = _ref2.relativePathInDependency,
          depRootDir = _ref2.depRootDir;

      var mainFile = this.dependencyComponent.dists.calculateMainDistFile(this.dependencyComponent.mainFile);
      var actualFilePath = depRootDir ? (_path || _load_path()).default.join(depRootDir, relativePathInDependency) : relativePathInDependency;
      if (relativePathInDependency === mainFile) {
        actualFilePath = depRootDir ? (_path || _load_path()).default.join(depRootDir, mainFile) : mainFile;
      }
      var relativeFilePath = (_path || _load_path()).default.relative((_path || _load_path()).default.dirname(linkPath), actualFilePath);
      var importSpecifiers = this.relativePath.importSpecifiers;
      var linkContent = this.getLinkContent(relativeFilePath);
      var customResolveMapping = this._getCustomResolveMapping();
      (_logger || _load_logger()).default.debug(`prepareLinkFile, on ${linkPath}`);
      var linkPathExt = (0, (_utils || _load_utils()).getExt)(linkPath);
      var isEs6 = Boolean(importSpecifiers && linkPathExt === 'js');

      var symlinkTo = linkContent ? undefined : this._getSymlinkDest(actualFilePath);
      var postInstallSymlink = this.createNpmLinkFiles && !linkContent;

      return { linkPath, linkContent, isEs6, symlinkTo, customResolveMapping, postInstallSymlink };
    }
  }, {
    key: '_getSymlinkDest',
    value: function _getSymlinkDest(filePath) {
      if (this.isLinkToPackage) {
        if (this.createNpmLinkFiles) {
          return this._getPackagePathToInternalFile();
        }
        return (_path || _load_path()).default.join(this.getTargetDir(), 'node_modules', this._getPackagePathToInternalFile());
      }
      return filePath;
    }
  }, {
    key: 'getLinkContent',
    value: function getLinkContent(relativeFilePath) {
      if (this.isLinkToPackage) {
        return (0, (_linkContent || _load_linkContent()).getLinkToPackageContent)(relativeFilePath, this._getPackagePath());
      }
      return (0, (_linkContent || _load_linkContent()).getLinkToFileContent)(relativeFilePath, this.relativePath.importSpecifiers);
    }
  }, {
    key: '_getPackagePath',
    value: function _getPackagePath() {
      if (this.relativePath.destinationRelativePath === (0, (_path2 || _load_path2()).pathNormalizeToLinux)(this.dependencyComponent.mainFile)) {
        return this._getPackageName();
      }
      // the link is to an internal file, not to the main file
      return this._getPackagePathToInternalFile();
    }
  }, {
    key: '_getPackageName',
    value: function _getPackageName() {
      return (0, (_componentIdToPackageName || _load_componentIdToPackageName()).default)(this.dependencyId, this.dependencyComponent.bindingPrefix);
    }
  }, {
    key: '_getPackagePathToInternalFile',
    value: function _getPackagePathToInternalFile() {
      var packageName = this._getPackageName();
      var distPrefix = this.dependencyComponent.dists.isEmpty() || this.relativePath.isCustomResolveUsed ? '' : `${(_constants || _load_constants()).DEFAULT_DIST_DIRNAME}/`;
      return `${packageName}/${distPrefix}${this.relativePath.destinationRelativePath}`;
    }
  }, {
    key: '_getCustomResolveMapping',
    value: function _getCustomResolveMapping() {
      if (!this.relativePath.isCustomResolveUsed) return null;
      // $FlowFixMe importSource is set for custom resolved
      return { [this.relativePath.importSource]: this._getPackagePath() };
    }
  }, {
    key: 'getTargetDir',
    value: function getTargetDir() {
      var _this = this;

      var determineTargetDir = function determineTargetDir() {
        if (_this.targetDir) return _this.targetDir;
        var writtenPath = _this.component.writtenPath;
        // $FlowFixMe when running from bit build, the writtenPath is not available but it should have rootDir as it's related to the dists links
        if (!writtenPath) return _this.componentMap.rootDir;
        if ((_path || _load_path()).default.isAbsolute(writtenPath)) {
          throw new Error('getTargetDir: component.writtenPath should be relative');
        }
        return writtenPath;
      };
      var targetDir = determineTargetDir();
      if (!targetDir || !(typeof targetDir === 'string')) {
        throw new Error('targetDir must be of type string');
      }
      return targetDir;
    }
  }, {
    key: 'getLinkPath',
    value: function getLinkPath() {
      var sourceRelativePath = this.relativePath.sourceRelativePath;
      var parentDir = this.getTargetDir();
      return (_path || _load_path()).default.join(parentDir, sourceRelativePath);
    }
  }, {
    key: 'getLinkPathForCustomResolve',
    value: function getLinkPathForCustomResolve(relativeDistExtInDependency) {
      var parentDir = this.getTargetDir();
      var importSourcePath = this._getImportSourcePathForCustomResolve(relativeDistExtInDependency);
      // if createNpmLinkFiles, the path will be part of the postinstall script, so it shouldn't be absolute
      return this.createNpmLinkFiles ? importSourcePath : (_path || _load_path()).default.join(parentDir, importSourcePath);
    }
  }, {
    key: '_getDistRoot',
    value: function _getDistRoot() {
      return this.component.dists.getDistDir(this.consumer, this.componentMap.getRootDir());
    }
  }, {
    key: '_getRelativeDistPathInDependency',
    value: function _getRelativeDistPathInDependency() {
      var relativeDistPathInDependency = (0, (_utils || _load_utils()).searchFilesIgnoreExt)(this.dependencyComponent.dists.get(), this.relativePathInDependency);
      return relativeDistPathInDependency // $FlowFixMe relative is defined
      ? relativeDistPathInDependency.relative : this.relativePathInDependency;
    }
  }, {
    key: '_getImportSourcePathForCustomResolve',
    value: function _getImportSourcePathForCustomResolve(relativeDistExtInDependency) {
      // $FlowFixMe relativePath.importSource is set when isCustomResolveUsed
      var importSource = this.relativePath.importSource;
      var importSourceFileExt = relativeDistExtInDependency || (_path || _load_path()).default.extname(this.relativePath.sourceRelativePath);
      // e.g. for require('utils/is-string'), the link should be at node_modules/utils/is-string/index.js
      var importSourceFile = (_path || _load_path()).default.extname(importSource) ? importSource : (_path || _load_path()).default.join(importSource, `${(_constants || _load_constants()).DEFAULT_INDEX_NAME}.${importSourceFileExt}`);
      return (_path || _load_path()).default.join('node_modules', importSourceFile);
    }
  }, {
    key: '_getDepRootDir',
    value: function _getDepRootDir() {
      if (!this.dependencyComponentMap) return undefined;
      return this.dependencyComponentMap.getRootDir();
    }
  }, {
    key: '_getDepRootDirDist',
    value: function _getDepRootDirDist() {
      var rootDir = this._getDepRootDir();
      return rootDir ? this.dependencyComponent.dists.getDistDir(this.consumer, rootDir) : undefined;
    }
  }]);

  return DependencyFileLinkGenerator;
}();

exports.default = DependencyFileLinkGenerator;