"use strict";

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

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

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

  _path = function () {
    return data;
  };

  return data;
}

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

  _utils = function () {
    return data;
  };

  return data;
}

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

  _constants = function () {
    return data;
  };

  return data;
}

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

  _bitId = function () {
    return data;
  };

  return data;
}

function _logger() {
  const data = _interopRequireDefault(require("../logger/logger"));

  _logger = function () {
    return data;
  };

  return data;
}

function _linkContent() {
  const data = require("./link-content");

  _linkContent = function () {
    return data;
  };

  return data;
}

function _componentIdToPackageName() {
  const data = _interopRequireDefault(require("../utils/bit/component-id-to-package-name"));

  _componentIdToPackageName = function () {
    return data;
  };

  return data;
}

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

  _path2 = function () {
    return data;
  };

  return data;
}

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

  _bitMap = function () {
    return data;
  };

  return data;
}

/**
 * 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
 */
class DependencyFileLinkGenerator {
  constructor({
    consumer,
    bitMap,
    component,
    relativePath,
    dependencyComponent,
    createNpmLinkFiles,
    targetDir
  }) {
    this.consumer = consumer;
    this.bitMap = bitMap;
    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;
  }

  generate() {
    this.linkFiles = [];

    if (this.component.dependenciesSavedAsComponents) {
      this.dependencyComponent.componentMap = this.bitMap.getComponent(this.dependencyId);
      this.dependencyComponentMap = this.dependencyComponent.componentMap;
    }

    this.relativePathInDependency = _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();
    }

    const linkFile = this.prepareLinkFile({
      linkPath: this.getLinkPath(),
      relativePathInDependency: this.relativePathInDependency,
      depRootDir: this._getDepRootDir()
    });
    this.linkFiles.push(linkFile);

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

    return this.linkFiles;
  }

  generateForCustomResolve() {
    const distRoot = this._getDistRoot();

    const relativeDistPathInDependency = this._getRelativeDistPathInDependency();

    const dependencyDistExt = (0, _utils().getExt)(relativeDistPathInDependency);
    const relativeDistExtInDependency = _linkContent().EXTENSIONS_TO_REPLACE_TO_JS_IN_PACKAGES.includes(dependencyDistExt) ? 'js' : dependencyDistExt;

    const depRootDir = this._getDepRootDir();

    const depRootDirDist = this._getDepRootDirDist();

    const isCustomResolvedWithDistInside = Boolean(this.shouldDistsBeInsideTheComponent && this.hasDist);
    const relativePathInDependency = `${(0, _utils().getWithoutExt)(this.relativePathInDependency)}.${relativeDistExtInDependency}`;
    const 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.
      const importSourcePath = this._getImportSourcePathForCustomResolve(relativeDistExtInDependency);

      const linkFileInNodeModules = this.prepareLinkFile({
        linkPath: _path().default.join(distRoot, importSourcePath),
        relativePathInDependency: relativeDistPathInDependency,
        depRootDir: depRootDirDist
      });
      this.linkFiles.push(linkFileInNodeModules);
    }

    if ((0, _utils().getExt)(this.relativePathInDependency) === 'ts') {
      // this is needed for when building Angular components inside a capsule, so we don't care
      // about the case when dist is outside the components
      const linkFileTs = this.prepareLinkFile({
        linkPath: this.getLinkPathForCustomResolve(relativeDistExtInDependency).replace('.js', '.d.ts'),
        relativePathInDependency: relativePathInDependency.replace('.js', '.ts'),
        depRootDir: isCustomResolvedWithDistInside ? depRootDirDist : depRootDir
      });

      if (this.createNpmLinkFiles && linkFile.linkContent) {
        linkFileTs.postInstallLink = true;
      }

      this.linkFiles.push(linkFileTs);
    }

    return this.linkFiles;
  }

  generateForDist() {
    const distRoot = this._getDistRoot();

    const relativeDistPathInDependency = this._getRelativeDistPathInDependency();

    const relativeDistExtInDependency = (0, _utils().getExt)(relativeDistPathInDependency);
    const sourceRelativePathWithCompiledExt = `${(0, _utils().getWithoutExt)(this.relativePath.sourceRelativePath)}.${relativeDistExtInDependency}`;
    const linkFileInDist = this.prepareLinkFile({
      linkPath: _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);
  }

  prepareLinkFile({
    linkPath,
    relativePathInDependency,
    depRootDir
  }) {
    const mainFile = this.dependencyComponent.dists.calculateMainDistFile(this.dependencyComponent.mainFile);
    let actualFilePath = depRootDir ? _path().default.join(depRootDir, relativePathInDependency) : relativePathInDependency;

    if (relativePathInDependency === mainFile) {
      actualFilePath = depRootDir ? _path().default.join(depRootDir, mainFile) : mainFile;
    }

    const relativeFilePath = _path().default.relative(_path().default.dirname(linkPath), actualFilePath);

    const importSpecifiers = this.relativePath.importSpecifiers;
    const linkContent = this.getLinkContent(relativeFilePath);

    const customResolveMapping = this._getCustomResolveMapping();

    _logger().default.debug(`prepareLinkFile, on ${linkPath}`);

    const linkPathExt = (0, _utils().getExt)(linkPath);
    const isEs6 = Boolean(importSpecifiers && linkPathExt === 'js');
    const symlinkTo = linkContent ? undefined : this._getSymlinkDest(actualFilePath);
    const postInstallSymlink = this.createNpmLinkFiles && !linkContent;
    return {
      linkPath,
      linkContent,
      isEs6,
      symlinkTo,
      customResolveMapping,
      postInstallSymlink
    };
  }

  _getSymlinkDest(filePath) {
    if (this.createNpmLinkFiles) {
      return this._getPackagePathToInternalFile();
    }

    if (!this.component.dependenciesSavedAsComponents) {
      return _path().default.join(this.getTargetDir(), 'node_modules', this._getPackagePathToInternalFile());
    } // if dependencies are saved as components, the above logic will create a symlink to a symlink


    return filePath;
  }

  getLinkContent(relativeFilePath) {
    return (0, _linkContent().getLinkToPackageContent)(relativeFilePath, this._getPackagePath(), this.relativePath.importSpecifiers);
  }

  _getPackagePath() {
    if (this.relativePath.destinationRelativePath === (0, _path2().pathNormalizeToLinux)(this.dependencyComponent.mainFile)) {
      return this._getPackageName();
    } // the link is to an internal file, not to the main file


    return this._getPackagePathToInternalFile();
  }

  _getPackageName() {
    return (0, _componentIdToPackageName().default)(this.dependencyId, this.dependencyComponent.bindingPrefix);
  }

  _getPackagePathToInternalFile() {
    const packageName = this._getPackageName();

    const internalFileInsidePackage = this._getInternalFileInsidePackage();

    const ext = (0, _utils().getExt)(internalFileInsidePackage);
    const internalFileWithoutExt = _linkContent().EXTENSIONS_TO_STRIP_FROM_PACKAGES.includes(ext) ? (0, _utils().getWithoutExt)(internalFileInsidePackage) : internalFileInsidePackage;
    return `${packageName}/${internalFileWithoutExt}`;
  }

  _getInternalFileInsidePackage() {
    const dependencySavedLocallyAndDistIsOutside = this.dependencyComponentMap && !this.shouldDistsBeInsideTheComponent;
    const distPrefix = this.dependencyComponent.dists.isEmpty() || this.relativePath.isCustomResolveUsed || dependencySavedLocallyAndDistIsOutside ? '' : `${_constants().DEFAULT_DIST_DIRNAME}/`;
    return distPrefix + this.relativePath.destinationRelativePath;
  }

  _getCustomResolveMapping() {
    if (!this.relativePath.isCustomResolveUsed) return null; // $FlowFixMe importSource is set for custom resolved

    return {
      [this.relativePath.importSource]: this._getPackagePath()
    };
  }

  getTargetDir() {
    const determineTargetDir = () => {
      if (this.targetDir) return this.targetDir;
      const 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().default.isAbsolute(writtenPath)) {
        throw new Error('getTargetDir: component.writtenPath should be relative');
      }

      return writtenPath;
    };

    const targetDir = determineTargetDir();

    if (!targetDir || !(typeof targetDir === 'string')) {
      throw new Error('targetDir must be of type string');
    }

    return targetDir;
  }

  getLinkPath() {
    const sourceRelativePath = this.relativePath.sourceRelativePath;
    const parentDir = this.getTargetDir();
    return _path().default.join(parentDir, sourceRelativePath);
  }

  getLinkPathForCustomResolve(relativeDistExtInDependency) {
    const parentDir = this.getTargetDir();

    const 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().default.join(parentDir, importSourcePath);
  }

  _getDistRoot() {
    return this.component.dists.getDistDir(this.consumer, this.componentMap.getRootDir());
  }

  _getRelativeDistPathInDependency() {
    const relativeDistPathInDependency = (0, _utils().searchFilesIgnoreExt)(this.dependencyComponent.dists.get(), this.relativePathInDependency);
    return relativeDistPathInDependency // $FlowFixMe relative is defined
    ? relativeDistPathInDependency.relative : this.relativePathInDependency;
  }

  _getImportSourcePathForCustomResolve(relativeDistExtInDependency) {
    // $FlowFixMe relativePath.importSource is set when isCustomResolveUsed
    const importSource = this.relativePath.importSource;

    const importSourceFileExt = relativeDistExtInDependency || _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


    const importSourceFile = _path().default.extname(importSource) ? importSource : _path().default.join(importSource, `${_constants().DEFAULT_INDEX_NAME}.${importSourceFileExt}`);
    return _path().default.join('node_modules', importSourceFile);
  }

  _getDepRootDir() {
    if (!this.dependencyComponentMap) return undefined;
    return this.dependencyComponentMap.getRootDir();
  }

  _getDepRootDirDist() {
    const rootDir = this._getDepRootDir();

    return rootDir ? this.dependencyComponent.dists.getDistDir(this.consumer, rootDir) : undefined;
  }

}

exports.default = DependencyFileLinkGenerator;