"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 _bluebird() {
  const data = require("bluebird");

  _bluebird = function () {
    return data;
  };

  return data;
}

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

  _defineProperty2 = function () {
    return data;
  };

  return data;
}

function pathLib() {
  const data = _interopRequireWildcard(require("path"));

  pathLib = function () {
    return data;
  };

  return data;
}

function _semver() {
  const data = _interopRequireDefault(require("semver"));

  _semver = function () {
    return data;
  };

  return data;
}

function _fsExtra() {
  const data = _interopRequireDefault(require("fs-extra"));

  _fsExtra = function () {
    return data;
  };

  return data;
}

function _ramda() {
  const data = _interopRequireDefault(require("ramda"));

  _ramda = function () {
    return data;
  };

  return data;
}

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

  _pMapSeries = function () {
    return data;
  };

  return data;
}

function _componentObjects() {
  const data = _interopRequireDefault(require("./component-objects"));

  _componentObjects = function () {
    return data;
  };

  return data;
}

function _models() {
  const data = require("./models");

  _models = 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 _scopeJson() {
  const data = require("./scope-json");

  _scopeJson = function () {
    return data;
  };

  return data;
}

function _exceptions() {
  const data = require("./exceptions");

  _exceptions = function () {
    return data;
  };

  return data;
}

function _repositories() {
  const data = require("./repositories");

  _repositories = function () {
    return data;
  };

  return data;
}

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

  _bitId = function () {
    return data;
  };

  return data;
}

function _componentVersion() {
  const data = _interopRequireDefault(require("./component-version"));

  _componentVersion = function () {
    return data;
  };

  return data;
}

function _objects() {
  const data = require("./objects");

  _objects = function () {
    return data;
  };

  return data;
}

function _sources() {
  const data = _interopRequireDefault(require("./repositories/sources"));

  _sources = function () {
    return data;
  };

  return data;
}

function _loader() {
  const data = _interopRequireDefault(require("../cli/loader"));

  _loader = function () {
    return data;
  };

  return data;
}

function _scopeMigratorManifest() {
  const data = _interopRequireDefault(require("./migrations/scope-migrator-manifest"));

  _scopeMigratorManifest = function () {
    return data;
  };

  return data;
}

function _scopeMigrator() {
  const data = _interopRequireDefault(require("./migrations/scope-migrator"));

  _scopeMigrator = function () {
    return data;
  };

  return data;
}

function _loaderMessages() {
  const data = require("../cli/loader/loader-messages");

  _loaderMessages = function () {
    return data;
  };

  return data;
}

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

  _logger = function () {
    return data;
  };

  return data;
}

function _removedComponents() {
  const data = require("./removed-components");

  _removedComponents = function () {
    return data;
  };

  return data;
}

function _scopeGraph() {
  const data = _interopRequireDefault(require("./graph/scope-graph"));

  _scopeGraph = function () {
    return data;
  };

  return data;
}

function _removeModelComponents() {
  const data = _interopRequireDefault(require("./component-ops/remove-model-components"));

  _removeModelComponents = function () {
    return data;
  };

  return data;
}

function _dists() {
  const data = _interopRequireDefault(require("../consumer/component/sources/dists"));

  _dists = function () {
    return data;
  };

  return data;
}

function _specsResults() {
  const data = _interopRequireDefault(require("../consumer/specs-results"));

  _specsResults = function () {
    return data;
  };

  return data;
}

function _analytics() {
  const data = require("../analytics/analytics");

  _analytics = function () {
    return data;
  };

  return data;
}

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

  _generalError = function () {
    return data;
  };

  return data;
}

const removeNils = _ramda().default.reject(_ramda().default.isNil);

const pathHasScope = (0, _utils().pathHasAll)([_constants().OBJECTS_DIR, _constants().SCOPE_JSON]);

class Scope {
  // cache DependencyGraph instance
  constructor(scopeProps) {
    (0, _defineProperty2().default)(this, "created", false);
    this.path = scopeProps.path;
    this.scopeJson = scopeProps.scopeJson;
    this.created = scopeProps.created || false;
    this.tmp = scopeProps.tmp || new (_repositories().Tmp)(this);
    this.sources = scopeProps.sources || new (_sources().default)(this);
    this.objects = scopeProps.objects;
  }

  getDependencyGraph() {
    var _this = this;

    return (0, _bluebird().coroutine)(function* () {
      if (!_this._dependencyGraph) {
        _this._dependencyGraph = yield _scopeGraph().default.load(_this);
      }

      return _this._dependencyGraph;
    })();
  }

  get groupName() {
    if (!this.scopeJson.groupName) return null;
    return this.scopeJson.groupName;
  }

  get name() {
    return this.scopeJson.name;
  }

  getPath() {
    return this.path;
  }

  getComponentsPath() {
    return pathLib().join(this.path, Scope.getComponentsRelativePath());
  }
  /**
   * Get the relative components path inside the scope
   * (components such as compilers / testers / extensions)
   * currently components
   */


  static getComponentsRelativePath() {
    return _constants().BITS_DIRNAME;
  }
  /**
   * Get a relative (to scope) path to a specific component such as compiler / tester / extension
   * Support getting the latest installed version
   * @param {BitId} id
   */


  static getComponentRelativePath(id, scopePath) {
    if (!id.scope) {
      throw new Error('could not find id.scope');
    }

    const relativePath = pathLib().join(id.name, id.scope);

    if (!id.getVersion().latest) {
      if (!id.version) {
        // brought closer because flow can't deduce if it's done in the beginning.
        throw new Error('could not find id.version');
      }

      return pathLib().join(relativePath, id.version);
    }

    if (!scopePath) {
      throw new Error(`could not find the latest version of ${id.toString()} without the scope path`);
    }

    const componentFullPath = pathLib().join(scopePath, Scope.getComponentsRelativePath(), relativePath);
    if (!_fsExtra().default.existsSync(componentFullPath)) return '';

    const versions = _fsExtra().default.readdirSync(componentFullPath);

    const latestVersion = _semver().default.maxSatisfying(versions, '*');

    return pathLib().join(relativePath, latestVersion);
  }

  getBitPathInComponentsDir(id) {
    return pathLib().join(this.getComponentsPath(), id.toFullPath());
  }
  /**
   * Running migration process for scope to update the stores (bit objects) to the current version
   *
   * @param {any} verbose - print debug logs
   * @returns {Object} - wether the process run and wether it successeded
   * @memberof Consumer
   */


  migrate(verbose) {
    var _this2 = this;

    return (0, _bluebird().coroutine)(function* () {
      _logger().default.debug('scope.migrate, running migration process for scope');

      if (verbose) console.log('running migration process for scope'); // eslint-disable-line
      // We start to use this process after version 0.10.9, so we assume the scope is in the last production version

      const scopeVersion = _this2.scopeJson.get('version') || '0.10.9';

      if (_semver().default.gte(scopeVersion, _constants().BIT_VERSION)) {
        const upToDateMsg = 'scope version is up to date';
        if (verbose) console.log(upToDateMsg); // eslint-disable-line

        _logger().default.debug(`scope.migrate, ${upToDateMsg}`);

        return {
          run: false
        };
      }

      _loader().default.start(_loaderMessages().BEFORE_MIGRATION);

      _logger().default.debugAndAddBreadCrumb('scope.migrate', `start scope migration. scope version ${scopeVersion}, bit version ${_constants().BIT_VERSION}`);

      const rawObjects = yield _this2.objects.listRawObjects();
      const resultObjects = yield (0, _scopeMigrator().default)(scopeVersion, _scopeMigratorManifest().default, rawObjects, verbose); // Add the new / updated objects

      _this2.objects.addMany(resultObjects.newObjects); // Remove old objects


      _this2.objects.removeManyObjects(resultObjects.refsToRemove); // Persists new / remove objects


      const validateBeforePersist = false;
      yield _this2.objects.persist(validateBeforePersist); // Update the scope version

      _this2.scopeJson.set('version', _constants().BIT_VERSION);

      _logger().default.debugAndAddBreadCrumb('scope.migrate', `updating scope version to version ${_constants().BIT_VERSION}`);

      yield _this2.scopeJson.write(_this2.getPath());

      _loader().default.stop();

      return {
        run: true,
        success: true
      };
    })();
  }

  describe() {
    return {
      name: this.name
    };
  }

  toConsumerComponents(components) {
    return Promise.all(components.filter(comp => !(comp instanceof _models().Symlink)).map(c => c.toConsumerComponent(c.latestExisting(this.objects).toString(), this.name, this.objects)));
  }

  list() {
    var _this3 = this;

    return (0, _bluebird().coroutine)(function* () {
      return _this3.objects.listComponents();
    })();
  }

  listIncludesSymlinks() {
    var _this4 = this;

    return (0, _bluebird().coroutine)(function* () {
      return _this4.objects.listComponentsIncludeSymlinks();
    })();
  }

  listLocal() {
    var _this5 = this;

    return (0, _bluebird().coroutine)(function* () {
      const listResults = yield _this5.list();
      return listResults.filter(result => !result.scope || result.scope === _this5.name);
    })();
  }

  latestVersions(componentIds, throwOnFailure = true) {
    var _this6 = this;

    return (0, _bluebird().coroutine)(function* () {
      componentIds = componentIds.map(componentId => componentId.changeVersion(null));
      const components = yield _this6.sources.getMany(componentIds);
      const ids = components.map(component => {
        const getVersion = () => {
          if (component.component) {
            return component.component.latest();
          }

          if (throwOnFailure) throw new (_exceptions().ComponentNotFound)(component.id.toString());
          return _constants().DEFAULT_BIT_VERSION;
        };

        const version = getVersion();
        return component.id.changeVersion(version);
      });
      return _bitId().BitIds.fromArray(ids);
    })();
  }
  /**
   * Build multiple components sequentially, not in parallel.
   *
   * Two reasons why not running them in parallel:
   * 1) when several components have the same environment, it'll try to install them multiple times.
   * 2) npm throws errors when running 'npm install' from several directories
   *
   * Also, make sure to first build and write dists files of all components, and only then, write
   * the links inside the dists. otherwise, you it could fail when writing links of one component
   * needs another component dists files. (see 'importing all components and then deleting the dist
   * directory' test case)
   */


  buildMultiple(components, consumer, noCache, verbose, dontPrintEnvMsg = false) {
    var _this7 = this;

    return (0, _bluebird().coroutine)(function* () {
      _logger().default.debugAndAddBreadCrumb('scope.buildMultiple', 'scope.buildMultiple: sequentially build multiple components'); // Make sure to not start the loader if there are no components to build


      if (components && components.length) {
        _loader().default.start(_loaderMessages().BEFORE_RUNNING_BUILD);
      }

      const build =
      /*#__PURE__*/
      function () {
        var _ref = (0, _bluebird().coroutine)(function* (component) {
          yield component.build({
            scope: _this7,
            consumer,
            noCache,
            verbose,
            dontPrintEnvMsg
          });
          const buildResults = yield component.dists.writeDists(component, consumer, false);
          return {
            component: component.id.toString(),
            buildResults
          };
        });

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

      const writeLinks =
      /*#__PURE__*/
      function () {
        var _ref2 = (0, _bluebird().coroutine)(function* (component) {
          return component.dists.writeDistsLinks(component, consumer);
        });

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

      const buildResults = yield (0, _pMapSeries().default)(components, build);
      yield (0, _pMapSeries().default)(components, writeLinks);
      return buildResults;
    })();
  }
  /**
   * when custom-module-resolution is used, the test process needs to set the custom module
   * directory to the dist directory
   */


  injectNodePathIfNeeded(consumer, components) {
    const nodePathDirDist = _dists().default.getNodePathDir(consumer); // only author components need this injection. for imported the links are built on node_modules


    const isNodePathNeeded = nodePathDirDist && components.some(component => (component.dependencies.isCustomResolvedUsed() || component.devDependencies.isCustomResolvedUsed() || component.compilerDependencies.isCustomResolvedUsed() || component.testerDependencies.isCustomResolvedUsed()) && component.componentMap && component.componentMap.origin === _constants().COMPONENT_ORIGINS.AUTHORED && !component.dists.isEmpty());

    if (isNodePathNeeded) {
      const getCurrentNodePathWithDirDist = () => {
        if (!process.env.NODE_PATH) return nodePathDirDist;
        const separator = process.env.NODE_PATH.endsWith(_constants().NODE_PATH_SEPARATOR) ? '' : _constants().NODE_PATH_SEPARATOR; // $FlowFixMe

        return process.env.NODE_PATH + separator + nodePathDirDist;
      };

      process.env.NODE_PATH = getCurrentNodePathWithDirDist(); // $FlowFixMe

      require('module').Module._initPaths(); // eslint-disable-line

    }
  }
  /**
   * Test multiple components sequentially, not in parallel.
   *
   * See the reason not to run them in parallel at @buildMultiple()
   */


  testMultiple({
    components,
    consumer,
    verbose,
    dontPrintEnvMsg = false,
    rejectOnFailure = false
  }) {
    var _this8 = this;

    return (0, _bluebird().coroutine)(function* () {
      _logger().default.debugAndAddBreadCrumb('scope.testMultiple', 'scope.testMultiple: sequentially test multiple components'); // Make sure not starting the loader when there is nothing to test


      if (components && components.length) {
        _loader().default.start(_loaderMessages().BEFORE_RUNNING_SPECS);
      }

      _this8.injectNodePathIfNeeded(consumer, components);

      const test =
      /*#__PURE__*/
      function () {
        var _ref3 = (0, _bluebird().coroutine)(function* (component) {
          if (!component.tester) {
            return {
              componentId: component.id,
              missingTester: true,
              pass: true
            };
          }

          const specs = yield component.runSpecs({
            scope: _this8,
            rejectOnFailure,
            consumer,
            verbose,
            dontPrintEnvMsg
          });
          const pass = specs ? specs.every(spec => spec.pass) : true;
          return {
            componentId: component.id,
            specs,
            pass
          };
        });

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

      return (0, _pMapSeries().default)(components, test);
    })();
  }
  /**
   * Writes a component as an object into the 'objects' directory
   */


  writeComponentToModel(componentObjects) {
    const objects = componentObjects.toObjects(this.objects);

    _logger().default.debugAndAddBreadCrumb('writeComponentToModel', 'writing into the model, Main id: {id}. It might have dependencies which are going to be written too', {
      id: objects.component.id().toString()
    });

    return this.sources.merge(objects).then(() => this.objects.persist());
  }
  /**
   * Writes components as objects into the 'objects' directory
   */


  writeManyComponentsToModel(componentsObjects, persist = true) {
    var _this9 = this;

    return (0, _bluebird().coroutine)(function* () {
      _logger().default.debugAndAddBreadCrumb('scope.writeManyComponentsToModel', `total componentsObjects ${componentsObjects.length}`);

      yield (0, _pMapSeries().default)(componentsObjects, componentObjects => componentObjects.toObjectsAsync(_this9.objects).then(objects => _this9.sources.merge(objects)));
      return persist ? _this9.objects.persist() : Promise.resolve();
    })();
  }

  getObject(hash) {
    return new (_objects().Ref)(hash).load(this.objects);
  }

  getRawObject(hash) {
    return this.objects.loadRawObject(new (_objects().Ref)(hash));
  }

  getModelComponentIfExist(id) {
    var _this10 = this;

    return (0, _bluebird().coroutine)(function* () {
      return _this10.sources.get(id);
    })();
  }
  /**
   * Remove components from scope
   * @force Boolean - remove component from scope even if other components use it
   */


  removeMany(bitIds, force, removeSameOrigin = false, consumer) {
    var _this11 = this;

    return (0, _bluebird().coroutine)(function* () {
      _logger().default.debug(`scope.removeMany ${bitIds.toString()} with force flag: ${force.toString()}`);

      _analytics().Analytics.addBreadCrumb('removeMany', `scope.removeMany ${_analytics().Analytics.hashData(bitIds)} with force flag: ${force.toString()}`);

      const removeComponents = new (_removeModelComponents().default)(_this11, bitIds, force, removeSameOrigin, consumer);
      return removeComponents.remove();
    })();
  }
  /**
   * findDependentBits
   * foreach component in array find the componnet that uses that component
   */


  findDependentBits(bitIds, returnResultsWithVersion = false) {
    var _this12 = this;

    return (0, _bluebird().coroutine)(function* () {
      const allComponents = yield _this12.list();
      const allComponentVersions = yield Promise.all(allComponents.map(
      /*#__PURE__*/
      function () {
        var _ref4 = (0, _bluebird().coroutine)(function* (component) {
          const loadedVersions = yield Promise.all(Object.keys(component.versions).map(
          /*#__PURE__*/
          function () {
            var _ref5 = (0, _bluebird().coroutine)(function* (version) {
              const componentVersion = yield component.loadVersion(version, _this12.objects);
              if (!componentVersion) return;
              componentVersion.id = component.toBitId();
              return componentVersion;
            });

            return function (_x5) {
              return _ref5.apply(this, arguments);
            };
          }()));
          return loadedVersions.filter(x => x);
        });

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

      const allScopeComponents = _ramda().default.flatten(allComponentVersions);

      const dependentBits = {};
      bitIds.forEach(bitId => {
        const dependencies = [];
        allScopeComponents.forEach(scopeComponents => {
          scopeComponents.flattenedDependencies.forEach(flattenedDependency => {
            if (flattenedDependency.isEqualWithoutVersion(bitId)) {
              returnResultsWithVersion ? dependencies.push(scopeComponents.id) : dependencies.push(scopeComponents.id.changeVersion(null));
            }
          });
        });

        if (!_ramda().default.isEmpty(dependencies)) {
          dependentBits[bitId.toStringWithoutVersion()] = _bitId().BitIds.uniqFromArray(dependencies);
        }
      });
      return Promise.resolve(dependentBits);
    })();
  }
  /**
   * split bit array to found and missing components (incase user misspelled id)
   */


  filterFoundAndMissingComponents(bitIds) {
    var _this13 = this;

    return (0, _bluebird().coroutine)(function* () {
      const missingComponents = new (_bitId().BitIds)();
      const foundComponents = new (_bitId().BitIds)();
      const resultP = bitIds.map(
      /*#__PURE__*/
      function () {
        var _ref6 = (0, _bluebird().coroutine)(function* (id) {
          const component = yield _this13.getModelComponentIfExist(id);
          if (!component) missingComponents.push(id);else foundComponents.push(id);
        });

        return function (_x6) {
          return _ref6.apply(this, arguments);
        };
      }());
      yield Promise.all(resultP);
      return {
        missingComponents,
        foundComponents
      };
    })();
  }
  /**
   * load components from the model and return them as ComponentVersion array.
   * if a component is not available locally, it'll just ignore it without throwing any error.
   */


  loadLocalComponents(ids) {
    var _this14 = this;

    return (0, _bluebird().coroutine)(function* () {
      const componentsObjects = yield _this14.sources.getMany(ids);
      const components = componentsObjects.map(componentObject => {
        const component = componentObject.component;
        if (!component) return null;
        const version = componentObject.id.hasVersion() ? componentObject.id.version : component.latest();
        return component.toComponentVersion(version);
      });
      return removeNils(components);
    })();
  }

  loadComponentLogs(id) {
    return this.getModelComponentIfExist(id).then(componentModel => {
      if (!componentModel) throw new (_exceptions().ComponentNotFound)(id.toString());
      return componentModel.collectLogs(this.objects);
    });
  }

  loadAllVersions(id) {
    return this.getModelComponentIfExist(id).then(componentModel => {
      if (!componentModel) throw new (_exceptions().ComponentNotFound)(id.toString());
      return componentModel.collectVersions(this.objects);
    });
  }
  /**
   * get ModelComponent instance per bit-id.
   * it throws an error if the component wasn't found.
   * @see getModelComponentIfExist to not throw an error
   * @see getModelComponentIgnoreScope to ignore the scope name
   */


  getModelComponent(id) {
    var _this15 = this;

    return (0, _bluebird().coroutine)(function* () {
      const component = yield _this15.getModelComponentIfExist(id);
      if (component) return component;
      throw new (_exceptions().ComponentNotFound)(id.toString());
    })();
  }
  /**
   * the id can be either with or without a scope-name.
   * in case the component is saved in the model only with the scope (imported), it loads all
   * components and search for it.
   * it throws an error if the component wasn't found.
   */


  getModelComponentIgnoreScope(id) {
    var _this16 = this;

    return (0, _bluebird().coroutine)(function* () {
      const component = yield _this16.getModelComponentIfExist(id);
      if (component) return component;

      if (!id.scope) {
        // search for the complete ID
        const components = yield _this16.list();
        const foundComponent = components.filter(c => c.toBitId().isEqualWithoutScopeAndVersion(id)); // $FlowFixMe

        if (foundComponent.length) return (0, _utils().first)(foundComponent);
      }

      throw new (_exceptions().ComponentNotFound)(id.toString());
    })();
  }
  /**
   * throws if component was not found
   */


  getConsumerComponent(id) {
    var _this17 = this;

    return (0, _bluebird().coroutine)(function* () {
      const modelComponent = yield _this17.getModelComponent(id); // $FlowFixMe version must be set

      const componentVersion = modelComponent.toComponentVersion(id.version);
      return componentVersion.toConsumer(_this17.objects);
    })();
  }

  getVersionInstance(id) {
    var _this18 = this;

    return (0, _bluebird().coroutine)(function* () {
      if (!id.hasVersion()) throw new TypeError(`scope.getVersionInstance - id ${id.toString()} is missing the version`);
      const component = yield _this18.getModelComponent(id); // $FlowFixMe id.version is not null, was checked above

      return component.loadVersion(id.version, _this18.objects);
    })();
  }

  getComponentsAndVersions(ids) {
    var _this19 = this;

    return (0, _bluebird().coroutine)(function* () {
      const componentsObjects = yield _this19.sources.getMany(ids);
      const componentsAndVersionsP = componentsObjects.map(
      /*#__PURE__*/
      function () {
        var _ref7 = (0, _bluebird().coroutine)(function* (componentObjects) {
          if (!componentObjects.component) return null;
          const component = componentObjects.component;
          const versionStr = componentObjects.id.getVersion().toString();
          const version = yield component.loadVersion(versionStr, _this19.objects);
          return {
            component,
            version,
            versionStr
          };
        });

        return function (_x7) {
          return _ref7.apply(this, arguments);
        };
      }());
      const componentsAndVersions = yield Promise.all(componentsAndVersionsP);
      return removeNils(componentsAndVersions);
    })();
  }
  /**
   * Creates a symlink object with the local-scope which links to the real-object of the remote-scope
   * This way, local components that have dependencies to the exported component won't break.
   */


  createSymlink(id, remote) {
    const symlink = new (_models().Symlink)({
      scope: id.scope,
      name: id.name,
      realScope: remote
    });
    return this.objects.add(symlink);
  }

  ensureDir() {
    _fsExtra().default.ensureDirSync(this.getComponentsPath());

    return this.tmp.ensureDir().then(() => this.scopeJson.write(this.getPath())).then(() => this.objects.ensureDir()).then(() => this);
  }
  /**
   * find the components in componentsPool which one of their dependencies include in potentialDependencies
   */


  findDirectDependentComponents(componentsPool, potentialDependencies) {
    var _this20 = this;

    return (0, _bluebird().coroutine)(function* () {
      const componentsVersions = yield _this20.loadLocalComponents(componentsPool);
      const dependentsP = componentsVersions.map(
      /*#__PURE__*/
      function () {
        var _ref8 = (0, _bluebird().coroutine)(function* (componentVersion) {
          const component = yield componentVersion.getVersion(_this20.objects);
          const found = component.getAllDependencies().find(dependency => potentialDependencies.searchWithoutVersion(dependency.id));
          return found ? componentVersion : null;
        });

        return function (_x8) {
          return _ref8.apply(this, arguments);
        };
      }());
      const dependents = yield Promise.all(dependentsP);
      const dependentsWithoutNull = removeNils(dependents);
      return _bitId().BitIds.fromArray(dependentsWithoutNull.map(c => c.id));
    })();
  }

  runComponentSpecs({
    bitId,
    consumer,
    save,
    verbose,
    isolated,
    directory,
    keep
  }) {
    var _this21 = this;

    return (0, _bluebird().coroutine)(function* () {
      if (!bitId.isLocal(_this21.name)) {
        throw new (_generalError().default)('cannot run specs on remote component');
      }

      const component = yield _this21.getConsumerComponent(bitId);
      return component.runSpecs({
        scope: _this21,
        consumer,
        save,
        verbose,
        isolated,
        directory,
        keep
      });
    })();
  }

  build({
    bitId,
    save,
    consumer,
    verbose,
    directory,
    keep,
    noCache
  }) {
    var _this22 = this;

    return (0, _bluebird().coroutine)(function* () {
      if (!bitId.isLocal(_this22.name)) {
        throw new (_generalError().default)('cannot run build on remote component');
      }

      const component = yield _this22.getConsumerComponent(bitId);
      return component.build({
        scope: _this22,
        save,
        consumer,
        verbose,
        directory,
        keep,
        noCache
      });
    })();
  }

  loadModelComponentByIdStr(id) {
    var _this23 = this;

    return (0, _bluebird().coroutine)(function* () {
      // Remove the version before hashing since hashing with the version number will result a wrong hash
      const idWithoutVersion = _bitId().BitId.getStringWithoutVersion(id);

      const ref = _objects().Ref.from(_objects().BitObject.makeHash(idWithoutVersion)); // $FlowFixMe


      return _this23.objects.load(ref);
    })();
  }
  /**
   * if it's not in the scope, it's probably new, we assume it doesn't have scope.
   */


  isIdHasScope(id) {
    var _this24 = this;

    return (0, _bluebird().coroutine)(function* () {
      const component = yield _this24.loadModelComponentByIdStr(id);
      return Boolean(component && component.scope);
    })();
  }

  getParsedId(id) {
    var _this25 = this;

    return (0, _bluebird().coroutine)(function* () {
      const idHasScope = yield _this25.isIdHasScope(id);
      return _bitId().BitId.parse(id, idHasScope);
    })();
  }

  static ensure(path, name, groupName) {
    if (pathHasScope(path)) return this.load(path);
    if (!name) name = (0, _utils().currentDirName)();
    const scopeJson = new (_scopeJson().ScopeJson)({
      name,
      groupName,
      version: _constants().BIT_VERSION
    });

    const repository = _objects().Repository.create({
      scopePath: path,
      scopeJson
    });

    return Promise.resolve(new Scope({
      path,
      created: true,
      scopeJson,
      objects: repository
    }));
  }

  static reset(path, resetHard) {
    return (0, _bluebird().coroutine)(function* () {
      yield _objects().Repository.reset(path);

      if (resetHard) {
        _logger().default.info(`deleting the whole scope at ${path}`);

        yield _fsExtra().default.emptyDir(path);
      }
    })();
  }

  static load(absPath) {
    return (0, _bluebird().coroutine)(function* () {
      let scopePath = (0, _utils().propogateUntil)(absPath);
      if (!scopePath) throw new (_exceptions().ScopeNotFound)(absPath);

      if (_fsExtra().default.existsSync(pathLib().join(scopePath, _constants().BIT_HIDDEN_DIR))) {
        scopePath = pathLib().join(scopePath, _constants().BIT_HIDDEN_DIR);
      }

      const scopeJson = yield _scopeJson().ScopeJson.loadFromFile((0, _scopeJson().getPath)(scopePath));
      const objects = yield _objects().Repository.load({
        scopePath,
        scopeJson
      });
      return new Scope({
        path: scopePath,
        scopeJson,
        objects
      });
    })();
  }

}

exports.default = Scope;