heroku-debug
Version:
debugging plugin for the CLI
153 lines (128 loc) • 3.16 kB
JavaScript
var Promise = require('any-promise');
var co = require('co');
module.exports = compose;
compose.Wrap = Wrap;
function compose(middleware) {
return function (next) {
next = next || new Wrap(noop);
var i = middleware.length;
while (i--) next = new Wrap(middleware[i], this, next);
return next
}
}
/**
* Wrap a function, then lazily call it,
* always returning both a promise and a generator.
*
* @param {Function} fn
* @param {Object} ctx
* @param {Wrap} next
*/
function Wrap(fn, ctx, next) {
if (typeof fn !== 'function') throw TypeError('Not a function!');
this._fn = fn;
this._ctx = ctx;
this._next = next;
this._called = false;
this._value = undefined;
this._promise = undefined;
this._generator = undefined;
}
/**
* Lazily call the function.
* Note that if it's not an async or generator function,
*
* @returns {Mixed}
*/
Wrap.prototype._getValue = function () {
if (!this._called) {
this._called = true;
try {
this._value = this._fn.call(this._ctx, this._next);
} catch (e) {
this._value = Promise.reject(e);
}
}
return this._value
};
/**
* Lazily create a promise from the return value.
*
* @returns {Promise}
*/
Wrap.prototype._getPromise = function () {
if (this._promise === undefined) {
var value = this._getValue();
this._promise = isGenerator(value)
? co.call(this._ctx, value)
: Promise.resolve(value);
}
return this._promise
}
/**
* Lazily create a generator from the return value.
*
* @returns {Iterator}
*/
Wrap.prototype._getGenerator = function () {
if (this._generator === undefined) {
var value = this._getValue();
this._generator = isGenerator(value)
? value
: promiseToGenerator.call(this._ctx, value);
}
return this._generator
}
/**
* In later version of v8,
* `yield*` works on the `[@@iterator]` method.
*
* @returns {Iterator}
*/
if (typeof Symbol !== 'undefined') {
Wrap.prototype[Symbol.iterator] = function () {
return this._getGenerator();
}
}
/**
* This creates a generator from a promise or a value.
*
* TODO: try to avoid using a generator function for this.
*
* @returns {Iterator}
*/
var loggedDeprecationMessage = false;
function* promiseToGenerator(promise) {
if (!loggedDeprecationMessage) {
console.error('A promise was converted into a generator, which is an anti-pattern. Please avoid using `yield* next`!')
loggedDeprecationMessage = true;
}
return yield Promise.resolve(promise);
}
/**
* Proxy generator and promise methods.
*/
Wrap.prototype.then = function (resolve, reject) {
return this._getPromise().then(resolve, reject);
}
Wrap.prototype.catch = function (reject) {
return this._getPromise().catch(reject);
}
Wrap.prototype.next = function (val) {
return this._getGenerator().next(val);
}
Wrap.prototype.throw = function (err) {
return this._getGenerator().throw(err);
}
/**
* Check if `obj` is a generator.
*
* @param {Mixed} obj
* @return {Boolean}
*/
function isGenerator(obj) {
return obj
&& typeof obj.next === 'function'
&& typeof obj.throw === 'function';
}
function noop() {}