mirror of https://github.com/lukechilds/node.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
133 lines
2.8 KiB
133 lines
2.8 KiB
'use strict';
|
|
|
|
/* Expose. */
|
|
module.exports = trough;
|
|
|
|
/* Methods. */
|
|
var slice = [].slice;
|
|
|
|
/* Create new middleware. */
|
|
function trough() {
|
|
var fns = [];
|
|
var middleware = {};
|
|
|
|
middleware.run = run;
|
|
middleware.use = use;
|
|
|
|
return middleware;
|
|
|
|
/* Run `fns`. Last argument must be
|
|
* a completion handler. */
|
|
function run() {
|
|
var index = -1;
|
|
var input = slice.call(arguments, 0, -1);
|
|
var done = arguments[arguments.length - 1];
|
|
|
|
if (typeof done !== 'function') {
|
|
throw new Error('Expected function as last argument, not ' + done);
|
|
}
|
|
|
|
next.apply(null, [null].concat(input));
|
|
|
|
/* Run the next `fn`, if any. */
|
|
function next(err) {
|
|
var fn = fns[++index];
|
|
var params = slice.call(arguments, 0);
|
|
var values = params.slice(1);
|
|
var length = input.length;
|
|
var pos = -1;
|
|
|
|
if (err) {
|
|
done(err);
|
|
return;
|
|
}
|
|
|
|
/* Copy non-nully input into values. */
|
|
while (++pos < length) {
|
|
if (values[pos] === null || values[pos] === undefined) {
|
|
values[pos] = input[pos];
|
|
}
|
|
}
|
|
|
|
input = values;
|
|
|
|
/* Next or done. */
|
|
if (fn) {
|
|
wrap(fn, next).apply(null, input);
|
|
} else {
|
|
done.apply(null, [null].concat(input));
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Add `fn` to the list. */
|
|
function use(fn) {
|
|
if (typeof fn !== 'function') {
|
|
throw new Error('Expected `fn` to be a function, not ' + fn);
|
|
}
|
|
|
|
fns.push(fn);
|
|
|
|
return middleware;
|
|
}
|
|
}
|
|
|
|
/* Wrap `fn`. Can be sync or async; return a promise,
|
|
* receive a completion handler, return new values and
|
|
* errors. */
|
|
function wrap(fn, next) {
|
|
var invoked;
|
|
|
|
return wrapped;
|
|
|
|
function wrapped() {
|
|
var params = slice.call(arguments, 0);
|
|
var callback = fn.length > params.length;
|
|
var result;
|
|
|
|
if (callback) {
|
|
params.push(done);
|
|
}
|
|
|
|
try {
|
|
result = fn.apply(null, params);
|
|
} catch (err) {
|
|
/* Well, this is quite the pickle. `fn` received
|
|
* a callback and invoked it (thus continuing the
|
|
* pipeline), but later also threw an error.
|
|
* We’re not about to restart the pipeline again,
|
|
* so the only thing left to do is to throw the
|
|
* thing instea. */
|
|
if (callback && invoked) {
|
|
throw err;
|
|
}
|
|
|
|
return done(err);
|
|
}
|
|
|
|
if (!callback) {
|
|
if (result && typeof result.then === 'function') {
|
|
result.then(then, done);
|
|
} else if (result instanceof Error) {
|
|
done(result);
|
|
} else {
|
|
then(result);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Invoke `next`, only once. */
|
|
function done() {
|
|
if (!invoked) {
|
|
invoked = true;
|
|
|
|
next.apply(null, arguments);
|
|
}
|
|
}
|
|
|
|
/* Invoke `done` with one value.
|
|
* Tracks if an error is passed, too. */
|
|
function then(value) {
|
|
done(null, value);
|
|
}
|
|
}
|
|
|