diff --git a/lib/core/Manager.js b/lib/core/Manager.js index de40a78f..48844af0 100644 --- a/lib/core/Manager.js +++ b/lib/core/Manager.js @@ -64,6 +64,7 @@ Manager.prototype.resolve = function () { this._fetching = {}; this._nrFetching = 0; this._failed = {}; + this._failFast = false; this._deferred = Q.defer(); // If there's nothing to resolve, simply dissect @@ -195,6 +196,11 @@ Manager.prototype._fetch = function (decEndpoint) { var deferred = this._deferred; var that = this; + // Check if the whole process is marked to fail fast + if (this._failFast) { + return; + } + // Mark as being fetched this._fetching[name] = this._fetching[name] || []; this._fetching[name].push(decEndpoint); @@ -204,15 +210,13 @@ Manager.prototype._fetch = function (decEndpoint) { // Note that the promise is stored in the decomposed endpoint // because it might be reused if a similar endpoint needs to be resolved decEndpoint.promise = this._repository.fetch(decEndpoint) - // When done, call onFetch - .spread(this._onFetch.bind(this, deferred, decEndpoint)) - // If it fails, we make the whole process to error out - .fail(function (err) { - that._extendNotification(err, decEndpoint); - deferred.reject(err); - }) + // When done, call onFetchSuccess + .spread(this._onFetchSuccess.bind(this, deferred, decEndpoint)) + // If it fails, call onFetchFailure + .fail(this._onFetchError.bind(this, deferred, decEndpoint)) // Listen to progress to proxy them to the resolve deferred - // Note that we also mark where the notification is coming from + // Note that we also add additional information to it, such as + // where the notification is coming from .progress(function (notification) { that._extendNotification(notification, decEndpoint); deferred.notify(notification); @@ -221,18 +225,38 @@ Manager.prototype._fetch = function (decEndpoint) { return decEndpoint.promise; }; -Manager.prototype._onFetch = function (deferred, decEndpoint, canonicalPkg, pkgMeta) { +Manager.prototype._onFetchError = function (deferred, decEndpoint, err) { + var name = decEndpoint.name; + + this._extendNotification(err, decEndpoint); + + // Remove from being fetched list + mout.array.remove(this._fetching[name], decEndpoint); + this._nrFetching--; + + + // Add to the failed list + this._failed[name] = this._failed[name] || []; + this._failed[name].push(err); + delete decEndpoint.promise; + + // Make the whole process to fail fast + this._failFast = true; + + // If the resolve process ended, parse the resolved packages + // to find the most suitable version for each package + if (this._nrFetching <= 0) { + process.nextTick(this._dissect.bind(this)); + } +}; + + +Manager.prototype._onFetchSuccess = function (deferred, decEndpoint, canonicalPkg, pkgMeta) { var name; var resolved; var index; var initialName = decEndpoint.name; - // If the deferred associated with the process is already rejected, - // do not proceed. - if (deferred.promise.isRejected()) { - return; - } - // Remove from being fetched list mout.array.remove(this._fetching[initialName], decEndpoint); this._nrFetching--; @@ -320,8 +344,18 @@ Manager.prototype._parseDependencies = function (decEndpoint, pkgMeta) { }; Manager.prototype._dissect = function () { - var suitables = {}; + var err; + var suitables; + // If something failed, reject the whole resolve promise + // with the first error + if (this._failFast) { + err = mout.object.values(this._failed)[0][0]; + this._deferred.reject(err); + return; + } + + suitables = {}; mout.object.forOwn(this._resolved, function (decEndpoints, name) { var nonSemver; var validSemver;