'use strict';

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

var _regenerator;

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

var _bluebird;

function _load_bluebird() {
  return _bluebird = require('bluebird');
}

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

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

function _load_ramda() {
  return _ramda = _interopRequireDefault(require('ramda'));
}

var _fsExtra;

function _load_fsExtra() {
  return _fsExtra = _interopRequireDefault(require('fs-extra'));
}

var _pMapSeries;

function _load_pMapSeries() {
  return _pMapSeries = _interopRequireDefault(require('p-map-series'));
}

var _moveComponents;

function _load_moveComponents() {
  return _moveComponents = require('./move-components');
}

var _links;

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

var _installPackages2;

function _load_installPackages() {
  return _installPackages2 = require('../../npm-client/install-packages');
}

var _packageJsonUtils;

function _load_packageJsonUtils() {
  return _packageJsonUtils = _interopRequireWildcard(require('../component/package-json-utils'));
}

var _constants;

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

var _logger;

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

var _utils;

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

var _generalError;

function _load_generalError() {
  return _generalError = _interopRequireDefault(require('../../error/general-error'));
}

var _componentWriter;

function _load_componentWriter() {
  return _componentWriter = _interopRequireDefault(require('./component-writer'));
}

var _dataToPersist;

function _load_dataToPersist() {
  return _dataToPersist = _interopRequireDefault(require('../component/sources/data-to-persist'));
}

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

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

function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }

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

/**
 * 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.
 */
var ManyComponentsWriter = function () {
  function ManyComponentsWriter(params) {
    _classCallCheck(this, ManyComponentsWriter);

    this.consumer = params.consumer;
    this.silentPackageManagerResult = params.silentPackageManagerResult;
    this.componentsWithDependencies = params.componentsWithDependencies;
    this.writeToPath = params.writeToPath;
    this.override = this._setBooleanDefault(params.override, true);
    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 = {};
    if (this.consumer && !this.isolated) this.basePath = this.consumer.getPath();
  } // a preparation for the capsule feature


  _createClass(ManyComponentsWriter, [{
    key: '_setBooleanDefault',
    value: function _setBooleanDefault(field, defaultValue) {
      return typeof field === 'undefined' ? defaultValue : Boolean(field);
    }
  }, {
    key: 'writeAll',
    value: function () {
      var _ref = (0, (_bluebird || _load_bluebird()).coroutine)( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee() {
        return (_regenerator || _load_regenerator()).default.wrap(function _callee$(_context) {
          while (1) {
            switch (_context.prev = _context.next) {
              case 0:
                _context.next = 2;
                return this._writeComponentsAndDependencies();

              case 2:
                _context.next = 4;
                return this._installPackages();

              case 4:
                _context.next = 6;
                return this._writeLinks();

              case 6:
                (_logger || _load_logger()).default.debug('ManyComponentsWriter, Done!');

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

      function writeAll() {
        return _ref.apply(this, arguments);
      }

      return writeAll;
    }()
  }, {
    key: '_writeComponentsAndDependencies',
    value: function () {
      var _ref2 = (0, (_bluebird || _load_bluebird()).coroutine)( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee2() {
        return (_regenerator || _load_regenerator()).default.wrap(function _callee2$(_context2) {
          while (1) {
            switch (_context2.prev = _context2.next) {
              case 0:
                (_logger || _load_logger()).default.debug('ManyComponentsWriter, _writeComponentsAndDependencies');
                _context2.next = 3;
                return this._populateComponentsFilesToWrite();

              case 3:
                _context2.next = 5;
                return this._populateComponentsDependenciesToWrite();

              case 5:
                this._moveComponentsIfNeeded();
                this._addBasePathIfExistToAllFiles();
                _context2.next = 9;
                return this._persistComponentsData();

              case 9:
              case 'end':
                return _context2.stop();
            }
          }
        }, _callee2, this);
      }));

      function _writeComponentsAndDependencies() {
        return _ref2.apply(this, arguments);
      }

      return _writeComponentsAndDependencies;
    }()
  }, {
    key: '_installPackages',
    value: function () {
      var _ref3 = (0, (_bluebird || _load_bluebird()).coroutine)( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee3() {
        return (_regenerator || _load_regenerator()).default.wrap(function _callee3$(_context3) {
          while (1) {
            switch (_context3.prev = _context3.next) {
              case 0:
                (_logger || _load_logger()).default.debug('ManyComponentsWriter, _installPackages');
                _context3.next = 3;
                return (_packageJsonUtils || _load_packageJsonUtils()).addWorkspacesToPackageJson(this.consumer, this.writeToPath);

              case 3:
                if (!this.addToRootPackageJson) {
                  _context3.next = 6;
                  break;
                }

                _context3.next = 6;
                return (_packageJsonUtils || _load_packageJsonUtils()).addComponentsToRoot(this.consumer, this.writtenComponents);

              case 6:
                _context3.next = 8;
                return this._installPackagesIfNeeded();

              case 8:
              case 'end':
                return _context3.stop();
            }
          }
        }, _callee3, this);
      }));

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

      return _installPackages;
    }()
  }, {
    key: '_writeLinks',
    value: function () {
      var _ref4 = (0, (_bluebird || _load_bluebird()).coroutine)( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee4() {
        var links;
        return (_regenerator || _load_regenerator()).default.wrap(function _callee4$(_context4) {
          while (1) {
            switch (_context4.prev = _context4.next) {
              case 0:
                (_logger || _load_logger()).default.debug('ManyComponentsWriter, _writeLinks');
                _context4.next = 3;
                return this._getAllLinks();

              case 3:
                links = _context4.sent;

                if (this.basePath) {
                  links.addBasePath(this.basePath);
                }
                _context4.next = 7;
                return links.persistAllToFS();

              case 7:
              case 'end':
                return _context4.stop();
            }
          }
        }, _callee4, this);
      }));

      function _writeLinks() {
        return _ref4.apply(this, arguments);
      }

      return _writeLinks;
    }()
  }, {
    key: '_persistComponentsData',
    value: function () {
      var _ref5 = (0, (_bluebird || _load_bluebird()).coroutine)( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee5() {
        var dataToPersist, jsonFiles;
        return (_regenerator || _load_regenerator()).default.wrap(function _callee5$(_context5) {
          while (1) {
            switch (_context5.prev = _context5.next) {
              case 0:
                dataToPersist = new (_dataToPersist || _load_dataToPersist()).default();

                this.componentsWithDependencies.forEach(function (componentWithDeps) {
                  var allComponents = [componentWithDeps.component].concat(_toConsumableArray(componentWithDeps.allDependencies));
                  allComponents.forEach(function (component) {
                    return dataToPersist.merge(component.dataToPersist);
                  });
                });

                if (!this.consumer.config.overrides.hasChanged) {
                  _context5.next = 7;
                  break;
                }

                _context5.next = 5;
                return this.consumer.config.prepareToWrite({ bitDir: this.consumer.getPath() });

              case 5:
                jsonFiles = _context5.sent;

                dataToPersist.addManyFiles(jsonFiles);

              case 7:
                _context5.next = 9;
                return dataToPersist.persistAllToFS();

              case 9:
              case 'end':
                return _context5.stop();
            }
          }
        }, _callee5, this);
      }));

      function _persistComponentsData() {
        return _ref5.apply(this, arguments);
      }

      return _persistComponentsData;
    }()
  }, {
    key: '_addBasePathIfExistToAllFiles',
    value: function _addBasePathIfExistToAllFiles() {
      var _this = this;

      if (!this.basePath) return;
      this.componentsWithDependencies.forEach(function (componentWithDeps) {
        var allComponents = [componentWithDeps.component].concat(_toConsumableArray(componentWithDeps.allDependencies));
        allComponents.forEach(function (component) {
          // $FlowFixMe
          if (component.dataToPersist) component.dataToPersist.addBasePath(_this.basePath);
        });
      });
    }
  }, {
    key: '_populateComponentsFilesToWrite',
    value: function () {
      var _ref6 = (0, (_bluebird || _load_bluebird()).coroutine)( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee6() {
        var writeComponentsParams, componentWriterInstances;
        return (_regenerator || _load_regenerator()).default.wrap(function _callee6$(_context6) {
          while (1) {
            switch (_context6.prev = _context6.next) {
              case 0:
                writeComponentsParams = this._getWriteComponentsParams();
                componentWriterInstances = writeComponentsParams.map(function (writeParams) {
                  return (_componentWriter || _load_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(function (componentWriter) {
                  componentWriter.existingComponentMap = componentWriter.existingComponentMap || componentWriter.addComponentToBitMap(componentWriter.writeToPath);
                });
                _context6.next = 5;
                return (0, (_pMapSeries || _load_pMapSeries()).default)(componentWriterInstances, function (componentWriter) {
                  return componentWriter.populateComponentsFilesToWrite();
                });

              case 5:
                this.writtenComponents = _context6.sent;

              case 6:
              case 'end':
                return _context6.stop();
            }
          }
        }, _callee6, this);
      }));

      function _populateComponentsFilesToWrite() {
        return _ref6.apply(this, arguments);
      }

      return _populateComponentsFilesToWrite;
    }()
  }, {
    key: '_getWriteComponentsParams',
    value: function _getWriteComponentsParams() {
      var _this2 = this;

      return this.componentsWithDependencies.map(function (componentWithDeps) {
        return _this2._getWriteParamsOfOneComponent(componentWithDeps);
      });
    }
  }, {
    key: '_getWriteParamsOfOneComponent',
    value: function _getWriteParamsOfOneComponent(componentWithDeps) {
      var _this3 = this;

      var componentRootDir = this.writeToPath ? this.consumer.getPathRelativeToConsumer((_path || _load_path()).default.resolve(this.writeToPath)) : this.consumer.composeRelativeComponentPath(componentWithDeps.component.id);
      var getParams = function getParams() {
        if (_this3.isolated) {
          return {
            origin: (_constants || _load_constants()).COMPONENT_ORIGINS.AUTHORED
          };
        }
        // AUTHORED and IMPORTED components can't be saved with multiple versions, so we can ignore the version to
        // find the component in bit.map
        var componentMap = _this3.consumer.bitMap.getComponentPreferNonNested(componentWithDeps.component.id);
        var origin = componentMap && componentMap.origin === (_constants || _load_constants()).COMPONENT_ORIGINS.AUTHORED ? (_constants || _load_constants()).COMPONENT_ORIGINS.AUTHORED : (_constants || _load_constants()).COMPONENT_ORIGINS.IMPORTED;
        var configDirFromComponentMap = componentMap ? componentMap.configDir : undefined;
        _this3._throwErrorWhenDirectoryNotEmpty(_this3.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 = _this3.writeDists && origin === (_constants || _load_constants()).COMPONENT_ORIGINS.IMPORTED;
        return {
          configDir: _this3.configDir || configDirFromComponentMap,
          origin,
          existingComponentMap: componentMap
        };
      };
      return _extends({}, this._getDefaultWriteParams(), {
        component: componentWithDeps.component,
        writeToPath: componentRootDir,
        writeBitDependencies: this.writeBitDependencies || !componentWithDeps.component.dependenciesSavedAsComponents }, getParams());
    }
  }, {
    key: '_getDefaultWriteParams',
    value: function _getDefaultWriteParams() {
      return {
        writeConfig: this.writeConfig,
        writePackageJson: this.writePackageJson,
        consumer: this.consumer,
        excludeRegistryPrefix: this.excludeRegistryPrefix
      };
    }
  }, {
    key: '_populateComponentsDependenciesToWrite',
    value: function () {
      var _ref7 = (0, (_bluebird || _load_bluebird()).coroutine)( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee7() {
        var _this4 = this;

        var allDependenciesP, writtenDependenciesIncludesNull;
        return (_regenerator || _load_regenerator()).default.wrap(function _callee7$(_context7) {
          while (1) {
            switch (_context7.prev = _context7.next) {
              case 0:
                allDependenciesP = this.componentsWithDependencies.map(function (componentWithDeps) {
                  var writeDependenciesP = componentWithDeps.allDependencies.map(function (dep) {
                    var dependencyId = dep.id.toString();
                    var depFromBitMap = _this4.consumer.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 || _load_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 || _load_constants()).COMPONENT_ORIGINS.AUTHORED) {
                      dep.writtenPath = '.';
                      (_logger || _load_logger()).default.debugAndAddBreadCrumb('writeToComponentsDir', 'writeToComponentsDir, ignore dependency {dependencyId} as it already exists in bit map', { dependencyId });
                      return Promise.resolve(dep);
                    }
                    if (_this4.dependenciesIdsCache[dependencyId]) {
                      (_logger || _load_logger()).default.debugAndAddBreadCrumb('writeToComponentsDir', 'writeToComponentsDir, ignore dependency {dependencyId} as it already exists in cache', { dependencyId });
                      dep.writtenPath = _this4.dependenciesIdsCache[dependencyId];
                      return Promise.resolve(dep);
                    }
                    var depRootPath = _this4.consumer.composeRelativeDependencyPath(dep.id);
                    dep.writtenPath = depRootPath;
                    _this4.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.
                    var componentMap = _this4.consumer.bitMap.getComponentIfExist(dep.id);
                    var componentWriter = (_componentWriter || _load_componentWriter()).default.getInstance(_extends({}, _this4._getDefaultWriteParams(), {
                      writeConfig: false,
                      component: dep,
                      writeToPath: depRootPath,
                      origin: (_constants || _load_constants()).COMPONENT_ORIGINS.NESTED,
                      existingComponentMap: componentMap
                    }));
                    return componentWriter.populateComponentsFilesToWrite();
                  });

                  return Promise.all(writeDependenciesP).then(function (deps) {
                    return deps.filter(function (dep) {
                      return dep;
                    });
                  });
                });
                _context7.next = 3;
                return Promise.all(allDependenciesP);

              case 3:
                writtenDependenciesIncludesNull = _context7.sent;

                this.writtenDependencies = (_ramda || _load_ramda()).default.flatten(writtenDependenciesIncludesNull).filter(function (dep) {
                  return dep;
                });

              case 5:
              case 'end':
                return _context7.stop();
            }
          }
        }, _callee7, this);
      }));

      function _populateComponentsDependenciesToWrite() {
        return _ref7.apply(this, arguments);
      }

      return _populateComponentsDependenciesToWrite;
    }()
  }, {
    key: '_moveComponentsIfNeeded',
    value: function _moveComponentsIfNeeded() {
      var _this5 = this;

      if (this.writeToPath) {
        this.componentsWithDependencies.forEach(function (componentWithDeps) {
          var relativeWrittenPath = componentWithDeps.component.writtenPath;
          // $FlowFixMe relativeWrittenPath is set
          var absoluteWrittenPath = _this5.consumer.toAbsolutePath(relativeWrittenPath);
          // $FlowFixMe this.writeToPath is set
          var absoluteWriteToPath = (_path || _load_path()).default.resolve(_this5.writeToPath); // don't use consumer.toAbsolutePath, it might be an inner dir
          if (relativeWrittenPath && absoluteWrittenPath !== absoluteWriteToPath) {
            var component = componentWithDeps.component;
            (0, (_moveComponents || _load_moveComponents()).moveExistingComponent)(_this5.consumer, component, absoluteWrittenPath, absoluteWriteToPath);
          }
        });
      }
    }
  }, {
    key: '_installPackagesIfNeeded',
    value: function () {
      var _ref8 = (0, (_bluebird || _load_bluebird()).coroutine)( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee8() {
        return (_regenerator || _load_regenerator()).default.wrap(function _callee8$(_context8) {
          while (1) {
            switch (_context8.prev = _context8.next) {
              case 0:
                if (!this.installNpmPackages) {
                  _context8.next = 3;
                  break;
                }

                _context8.next = 3;
                return (0, (_installPackages2 || _load_installPackages()).installNpmPackagesForComponents)({
                  consumer: this.consumer,
                  basePath: this.basePath,
                  componentsWithDependencies: this.componentsWithDependencies,
                  verbose: this.verbose, // $FlowFixMe
                  silentPackageManagerResult: this.silentPackageManagerResult,
                  installPeerDependencies: this.installPeerDependencies
                });

              case 3:
              case 'end':
                return _context8.stop();
            }
          }
        }, _callee8, this);
      }));

      function _installPackagesIfNeeded() {
        return _ref8.apply(this, arguments);
      }

      return _installPackagesIfNeeded;
    }()
  }, {
    key: '_getAllLinks',
    value: function () {
      var _ref9 = (0, (_bluebird || _load_bluebird()).coroutine)( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee9() {
        return (_regenerator || _load_regenerator()).default.wrap(function _callee9$(_context9) {
          while (1) {
            switch (_context9.prev = _context9.next) {
              case 0:
                return _context9.abrupt('return', (0, (_links || _load_links()).getAllComponentsLinks)({
                  componentsWithDependencies: this.componentsWithDependencies,
                  writtenComponents: this.writtenComponents,
                  writtenDependencies: this.writtenDependencies,
                  consumer: this.consumer,
                  createNpmLinkFiles: this.createNpmLinkFiles,
                  writePackageJson: this.writePackageJson
                }));

              case 1:
              case 'end':
                return _context9.stop();
            }
          }
        }, _callee9, this);
      }));

      function _getAllLinks() {
        return _ref9.apply(this, arguments);
      }

      return _getAllLinks;
    }()
  }, {
    key: '_throwErrorWhenDirectoryNotEmpty',
    value: function _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 || _load_fsExtra()).default.pathExistsSync(componentDir)) {
        if (!(0, (_utils || _load_utils()).isDir)(componentDir)) {
          throw new (_generalError || _load_generalError()).default(`unable to import to ${componentDir} because it's a file`);
        }
        if (!(0, (_utils || _load_utils()).isDirEmptySync)(componentDir) && !this.override) {
          throw new (_generalError || _load_generalError()).default(`unable to import to ${componentDir}, the directory is not empty. use --override flag to delete the directory and then import`);
        }
      }
    }
  }]);

  return ManyComponentsWriter;
}();

exports.default = ManyComponentsWriter;