"use strict";

var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");

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

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

function _objectSpread2() {
  const data = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread"));

  _objectSpread2 = function () {
    return data;
  };

  return data;
}

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 _fsExtra() {
  const data = _interopRequireDefault(require("fs-extra"));

  _fsExtra = function () {
    return data;
  };

  return data;
}

function _pMapSeries() {
  const data = _interopRequireDefault(require("p-map-series"));

  _pMapSeries = function () {
    return data;
  };

  return data;
}

function _moveComponents() {
  const data = require("./move-components");

  _moveComponents = function () {
    return data;
  };

  return data;
}

function _links() {
  const data = require("../../links");

  _links = function () {
    return data;
  };

  return data;
}

function _installPackages() {
  const data = require("../../npm-client/install-packages");

  _installPackages = function () {
    return data;
  };

  return data;
}

function packageJsonUtils() {
  const data = _interopRequireWildcard(require("../component/package-json-utils"));

  packageJsonUtils = function () {
    return data;
  };

  return data;
}

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

  _constants = function () {
    return data;
  };

  return data;
}

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

  _logger = function () {
    return data;
  };

  return data;
}

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

  _utils = function () {
    return data;
  };

  return data;
}

function _generalError() {
  const data = _interopRequireDefault(require("../../error/general-error"));

  _generalError = function () {
    return data;
  };

  return data;
}

function _componentWriter() {
  const data = _interopRequireDefault(require("./component-writer"));

  _componentWriter = function () {
    return data;
  };

  return data;
}

function _dataToPersist() {
  const data = _interopRequireDefault(require("../component/sources/data-to-persist"));

  _dataToPersist = function () {
    return data;
  };

  return data;
}

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

  _bitMap = function () {
    return data;
  };

  return data;
}

function _composeComponentPath() {
  const data = require("../../utils/bit/compose-component-path");

  _composeComponentPath = function () {
    return data;
  };

  return data;
}

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

  _bitId = function () {
    return data;
  };

  return data;
}

/**
 * write the components into '/components' dir (or according to the bit.map) and its dependencies in the
 * '/components/.dependencies' dir. Both directories are configurable in bit.json
 * For example: global/a has a dependency my-scope/global/b@1. The directories will be:
 * project/root/components/global/a/impl.js
 * project/root/components/.dependencies/global/b/my-scope/1/impl.js
 *
 * In case there are some same dependencies shared between the components, it makes sure to
 * write them only once.
 */
class ManyComponentsWriter {
  // a preparation for the capsule feature
  constructor(params) {
    this.consumer = params.consumer;
    this.silentPackageManagerResult = params.silentPackageManagerResult;
    this.componentsWithDependencies = params.componentsWithDependencies;
    this.writeToPath = params.writeToPath;
    this.override = this._setBooleanDefault(params.override, true);
    this.isolated = this._setBooleanDefault(params.isolated, false);
    this.writePackageJson = this._setBooleanDefault(params.writePackageJson, true);
    this.writeConfig = this._setBooleanDefault(params.writeConfig, false);
    this.configDir = params.configDir;
    this.writeBitDependencies = this._setBooleanDefault(params.writeBitDependencies, false);
    this.createNpmLinkFiles = this._setBooleanDefault(params.createNpmLinkFiles, false);
    this.writeDists = this._setBooleanDefault(params.writeDists, true);
    this.installPeerDependencies = this._setBooleanDefault(params.installPeerDependencies, false);
    this.installNpmPackages = this._setBooleanDefault(params.installNpmPackages, true);
    this.addToRootPackageJson = this._setBooleanDefault(params.addToRootPackageJson, true);
    this.verbose = this._setBooleanDefault(params.verbose, false);
    this.excludeRegistryPrefix = this._setBooleanDefault(params.excludeRegistryPrefix, false);
    this.dependenciesIdsCache = {};
    this.bitMap = this.consumer ? this.consumer.bitMap : new (_bitMap().default)();
    if (this.consumer && !this.isolated) this.basePath = this.consumer.getPath();
  }

  _setBooleanDefault(field, defaultValue) {
    return typeof field === 'undefined' ? defaultValue : Boolean(field);
  }

  writeAll() {
    var _this = this;

    return (0, _bluebird().coroutine)(function* () {
      yield _this._writeComponentsAndDependencies();
      yield _this._installPackages();
      yield _this._writeLinks();

      _logger().default.debug('ManyComponentsWriter, Done!');
    })();
  }

  _writeComponentsAndDependencies() {
    var _this2 = this;

    return (0, _bluebird().coroutine)(function* () {
      _logger().default.debug('ManyComponentsWriter, _writeComponentsAndDependencies');

      yield _this2._populateComponentsFilesToWrite();
      yield _this2._populateComponentsDependenciesToWrite();

      _this2._moveComponentsIfNeeded();

      yield _this2._persistComponentsData();
    })();
  }

  _installPackages() {
    var _this3 = this;

    return (0, _bluebird().coroutine)(function* () {
      _logger().default.debug('ManyComponentsWriter, _installPackages');

      if (_this3.consumer) {
        yield packageJsonUtils().addWorkspacesToPackageJson(_this3.consumer, _this3.writeToPath);

        if (_this3.addToRootPackageJson && _this3.consumer) {
          yield packageJsonUtils().addComponentsToRoot(_this3.consumer, _this3.writtenComponents);
        }
      }

      yield _this3._installPackagesIfNeeded();
    })();
  }

  _writeLinks() {
    var _this4 = this;

    return (0, _bluebird().coroutine)(function* () {
      _logger().default.debug('ManyComponentsWriter, _writeLinks');

      const links = yield _this4._getAllLinks();

      if (_this4.basePath) {
        links.addBasePath(_this4.basePath);
      }

      yield links.persistAllToFS();
    })();
  }

  _persistComponentsData() {
    var _this5 = this;

    return (0, _bluebird().coroutine)(function* () {
      const dataToPersist = new (_dataToPersist().default)();

      _this5.componentsWithDependencies.forEach(componentWithDeps => {
        const allComponents = [componentWithDeps.component, ...componentWithDeps.allDependencies];
        allComponents.forEach(component => dataToPersist.merge(component.dataToPersist));
      });

      if (_this5.consumer && _this5.consumer.config.overrides.hasChanged) {
        const jsonFiles = yield _this5.consumer.config.prepareToWrite({
          workspaceDir: _this5.consumer.getPath()
        });
        dataToPersist.addManyFiles(jsonFiles);
      }

      dataToPersist.addBasePath(_this5.basePath);
      yield dataToPersist.persistAllToFS();
    })();
  }

  _populateComponentsFilesToWrite() {
    var _this6 = this;

    return (0, _bluebird().coroutine)(function* () {
      const writeComponentsParams = _this6._getWriteComponentsParams();

      const componentWriterInstances = writeComponentsParams.map(writeParams => _componentWriter().default.getInstance(writeParams)); // add componentMap entries into .bitmap before starting the process because steps like writing package-json
      // rely on .bitmap to determine whether a dependency exists and what's its origin

      componentWriterInstances.forEach(componentWriter => {
        componentWriter.existingComponentMap = componentWriter.existingComponentMap || componentWriter.addComponentToBitMap(componentWriter.writeToPath);
      });
      _this6.writtenComponents = yield (0, _pMapSeries().default)(componentWriterInstances, componentWriter => componentWriter.populateComponentsFilesToWrite());
    })();
  }

  _getWriteComponentsParams() {
    return this.componentsWithDependencies.map(componentWithDeps => this._getWriteParamsOfOneComponent(componentWithDeps));
  }

  _getWriteParamsOfOneComponent(componentWithDeps) {
    // for isolated components, the component files should be on the root. see #1758
    const componentRootDir = this.isolated ? '.' : this._getComponentRootDir(componentWithDeps.component.id);

    const getParams = () => {
      if (!this.consumer) {
        return {
          origin: _constants().COMPONENT_ORIGINS.IMPORTED
        };
      } // AUTHORED and IMPORTED components can't be saved with multiple versions, so we can ignore the version to
      // find the component in bit.map


      const componentMap = this.bitMap.getComponentPreferNonNested(componentWithDeps.component.id);
      const origin = componentMap && componentMap.origin === _constants().COMPONENT_ORIGINS.AUTHORED ? _constants().COMPONENT_ORIGINS.AUTHORED : _constants().COMPONENT_ORIGINS.IMPORTED;
      const configDirFromComponentMap = componentMap ? componentMap.configDir : undefined; // $FlowFixMe consumer is set here

      this._throwErrorWhenDirectoryNotEmpty(this.consumer.toAbsolutePath(componentRootDir), componentMap); // don't write dists files for authored components as the author has its own mechanism to generate them
      // also, don't write dists file for imported component, unless the user used '--dist' flag


      componentWithDeps.component.dists.writeDistsFiles = this.writeDists && origin === _constants().COMPONENT_ORIGINS.IMPORTED;
      return {
        configDir: this.configDir || configDirFromComponentMap,
        origin,
        existingComponentMap: componentMap
      };
    };

    return (0, _objectSpread2().default)({}, this._getDefaultWriteParams(), {
      component: componentWithDeps.component,
      writeToPath: componentRootDir,
      writeBitDependencies: this.writeBitDependencies || !componentWithDeps.component.dependenciesSavedAsComponents
    }, getParams());
  }

  _getDefaultWriteParams() {
    return {
      writeConfig: this.writeConfig,
      writePackageJson: this.writePackageJson,
      consumer: this.consumer,
      bitMap: this.bitMap,
      isolated: this.isolated,
      excludeRegistryPrefix: this.excludeRegistryPrefix
    };
  }

  _populateComponentsDependenciesToWrite() {
    var _this7 = this;

    return (0, _bluebird().coroutine)(function* () {
      const allDependenciesP = _this7.componentsWithDependencies.map(componentWithDeps => {
        const writeDependenciesP = componentWithDeps.allDependencies.map(dep => {
          const dependencyId = dep.id.toString();

          const depFromBitMap = _this7.bitMap.getComponentIfExist(dep.id);

          if (!dep.componentMap) dep.componentMap = depFromBitMap;

          if (!componentWithDeps.component.dependenciesSavedAsComponents && !depFromBitMap) {
            // when depFromBitMap is true, it means that this component was imported as a component already before
            // don't change it now from a component to a package. (a user can do it at any time by using export --eject).
            _logger().default.debugAndAddBreadCrumb('writeToComponentsDir', "ignore dependency {dependencyId}. It'll be installed later using npm-client", {
              dependencyId
            });

            return Promise.resolve(null);
          }

          if (depFromBitMap && depFromBitMap.origin === _constants().COMPONENT_ORIGINS.AUTHORED) {
            dep.writtenPath = '.';

            _logger().default.debugAndAddBreadCrumb('writeToComponentsDir', 'writeToComponentsDir, ignore dependency {dependencyId} as it already exists in bit map', {
              dependencyId
            });

            return Promise.resolve(dep);
          }

          if (_this7.dependenciesIdsCache[dependencyId]) {
            _logger().default.debugAndAddBreadCrumb('writeToComponentsDir', 'writeToComponentsDir, ignore dependency {dependencyId} as it already exists in cache', {
              dependencyId
            });

            dep.writtenPath = _this7.dependenciesIdsCache[dependencyId];
            return Promise.resolve(dep);
          }

          const depRootPath = _this7._getDependencyRootDir(dep.id);

          dep.writtenPath = depRootPath;
          _this7.dependenciesIdsCache[dependencyId] = depRootPath; // When a component is NESTED we do interested in the exact version, because multiple components with the same scope
          // and namespace can co-exist with different versions.

          const componentMap = _this7.bitMap.getComponentIfExist(dep.id);

          const componentWriter = _componentWriter().default.getInstance((0, _objectSpread2().default)({}, _this7._getDefaultWriteParams(), {
            writeConfig: false,
            component: dep,
            writeToPath: depRootPath,
            origin: _constants().COMPONENT_ORIGINS.NESTED,
            existingComponentMap: componentMap
          }));

          return componentWriter.populateComponentsFilesToWrite();
        });
        return Promise.all(writeDependenciesP).then(deps => deps.filter(dep => dep));
      });

      const writtenDependenciesIncludesNull = yield Promise.all(allDependenciesP);
      _this7.writtenDependencies = _ramda().default.flatten(writtenDependenciesIncludesNull).filter(dep => dep);
    })();
  }

  _moveComponentsIfNeeded() {
    if (this.writeToPath && this.consumer) {
      this.componentsWithDependencies.forEach(componentWithDeps => {
        // $FlowFixMe componentWithDeps.component.componentMap is set
        const componentMap = componentWithDeps.component.componentMap;

        if (componentMap.origin === _constants().COMPONENT_ORIGINS.AUTHORED && !componentMap.trackDir) {
          throw new (_generalError().default)(`unable to use "--path" flag.
to move individual files, use bit move.
to move all component files to a different directory, run bit remove and then bit import --path`);
        }

        const relativeWrittenPath = componentMap.trackDir ? componentMap.trackDir : componentWithDeps.component.writtenPath; // $FlowFixMe relativeWrittenPath is set

        const absoluteWrittenPath = this.consumer.toAbsolutePath(relativeWrittenPath); // $FlowFixMe this.writeToPath is set

        const absoluteWriteToPath = _path().default.resolve(this.writeToPath); // don't use consumer.toAbsolutePath, it might be an inner dir


        if (relativeWrittenPath && absoluteWrittenPath !== absoluteWriteToPath) {
          const component = componentWithDeps.component; // $FlowFixMe consumer is set here

          (0, _moveComponents().moveExistingComponent)(this.consumer, component, absoluteWrittenPath, absoluteWriteToPath);
        }
      });
    }
  }

  _installPackagesIfNeeded() {
    var _this8 = this;

    return (0, _bluebird().coroutine)(function* () {
      if (!_this8.installNpmPackages) return;
      yield (0, _installPackages().installNpmPackagesForComponents)({
        // $FlowFixMe consumer is set here
        consumer: _this8.consumer,
        basePath: _this8.basePath,
        componentsWithDependencies: _this8.componentsWithDependencies,
        verbose: _this8.verbose,
        // $FlowFixMe
        silentPackageManagerResult: _this8.silentPackageManagerResult,
        installPeerDependencies: _this8.installPeerDependencies
      });
    })();
  }

  _getAllLinks() {
    var _this9 = this;

    return (0, _bluebird().coroutine)(function* () {
      return (0, _links().getAllComponentsLinks)({
        componentsWithDependencies: _this9.componentsWithDependencies,
        writtenComponents: _this9.writtenComponents,
        writtenDependencies: _this9.writtenDependencies,
        consumer: _this9.consumer,
        bitMap: _this9.bitMap,
        createNpmLinkFiles: _this9.createNpmLinkFiles,
        writePackageJson: _this9.writePackageJson
      });
    })();
  }

  _getComponentRootDir(bitId) {
    if (this.consumer) {
      return this.writeToPath ? this.consumer.getPathRelativeToConsumer(_path().default.resolve(this.writeToPath)) : this.consumer.composeRelativeComponentPath(bitId);
    }

    return (0, _composeComponentPath().composeComponentPath)(bitId);
  }

  _getDependencyRootDir(bitId) {
    if (this.isolated) {
      return (0, _composeComponentPath().composeDependencyPathForIsolated)(bitId, _constants().DEFAULT_DIR_DEPENDENCIES);
    } // $FlowFixMe consumer is set here


    return this.consumer.composeRelativeDependencyPath(bitId);
  }

  _throwErrorWhenDirectoryNotEmpty(componentDir, componentMap) {
    // if not writeToPath specified, it goes to the default directory. When componentMap exists, the
    // component is not new, and it's ok to override the existing directory.
    if (!this.writeToPath && componentMap) return; // if writeToPath specified and that directory is already used for that component, it's ok to override

    if (this.writeToPath && componentMap && componentMap.rootDir && componentMap.rootDir === this.writeToPath) return;

    if (_fsExtra().default.pathExistsSync(componentDir)) {
      if (!(0, _utils().isDir)(componentDir)) {
        throw new (_generalError().default)(`unable to import to ${componentDir} because it's a file`);
      }

      if (!(0, _utils().isDirEmptySync)(componentDir) && !this.override) {
        throw new (_generalError().default)(`unable to import to ${componentDir}, the directory is not empty. use --override flag to delete the directory and then import`);
      }
    }
  }

}

exports.default = ManyComponentsWriter;