gear
Version:
Gear.js - Build System for Node.js and the Browser
1,649 lines (1,469 loc) • 533 kB
JavaScript
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.Gear=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
(function (process){
/*
* Copyright (c) 2011-2012, Yahoo! Inc. All rights reserved.
* Copyrights licensed under the New BSD License.
* See the accompanying LICENSE file for terms.
*/
/*
* Blob - Immutable data object. Contains a property bag as well as a private result property.
* Any properties in the property bag are merged, while result is concatenated. Result can be
* Buffer or String. Immutability is necessary due to Blobs being able to be parallel
* processed.
*
* Loosely based on W3C Blob:
* http://www.w3.org/TR/FileAPI/#dfn-Blob
* https://developer.mozilla.org/en/DOM/Blob
*
* @param parts {Buffer|String|Blob|Array} Create new Blob from Buffer/String/Blob or Array of Buffer/String/Blob.
*/
var Blob = exports.Blob = function Blob(parts, properties) {
parts = typeof parts === 'undefined' ? [] : Array.prototype.concat(parts);
properties = properties || {};
function mergeProps(object, props) {
Object.keys(props).forEach(function(prop) {
object[prop] = props[prop];
});
}
var result = parts.length ? parts.shift() : '',
props = {},
self = this;
if (result instanceof Blob) {
mergeProps(props, result);
result = result.result;
}
parts.forEach(function(part) {
if (part instanceof Blob) {
mergeProps(props, part);
result += part.result;
} else {
result += part;
}
});
mergeProps(props, properties);
// Define immutable properties
Object.keys(props).forEach(function(prop) {
if (prop !== 'result') {
Object.defineProperty(self, prop, {enumerable: true, value: props[prop]});
}
});
Object.defineProperty(this, 'result', {value: result});
// v8 performance of seal is not good :(
// http://jsperf.com/freeze-vs-seal-vs-normal/3
// Object.seal(this);
};
Blob.prototype.toString = function() {
return this.result;
};
var readFile = {
server: function(name, encoding, callback, sync) {
var fs = require('fs');
if (sync) {
readFile.serverSync(name, encoding, callback);
} else {
fs.readFile(name, encoding === 'bin' ? undefined : encoding, function(err, data) {
if (err) {
callback(err);
} else {
callback(null, new Blob(data, {name: name}));
}
});
}
},
// readFileSync added due to some strange async behavior with readFile
serverSync: function(name, encoding, callback) {
var fs = require('fs'),
data;
try {
data = fs.readFileSync(name, encoding === 'bin' ? undefined : encoding);
callback(null, new Blob(data, {name: name}));
} catch(e) {
callback(e);
}
},
client: function(name, encoding, callback) {
if (name in localStorage) {
callback(null, new Blob(localStorage[name], {name: name}));
} else {
callback('localStorage has no item ' + name);
}
}
};
Blob.readFile = Blob.prototype.readFile = process.browser ? readFile.client : readFile.server;
var writeFile = {
server: function(name, blob, encoding, callback) {
var fs = require('fs'),
path = require('path'),
nodeExists = fs.exists || path.exists,
mkdirp = require('mkdirp').mkdirp,
Crypto = require('crypto');
function writeFile(filename, b) {
fs.writeFile(filename, b.result, encoding === 'bin' ? undefined : encoding, function(err) {
callback(err, new Blob(b, {name: filename}));
});
}
var dirname = path.resolve(path.dirname(name)),
checksum;
if (name.indexOf('{checksum}') > -1) { // Replace {checksum} with md5 string
checksum = Crypto.createHash('md5');
checksum.update(blob.result);
name = name.replace('{checksum}', checksum.digest('hex'));
}
nodeExists(dirname, function(exists) {
if (!exists) {
mkdirp(dirname, '0755', function(err) {
if (err) {
callback(err);
} else {
writeFile(name, blob);
}
});
}
else {
writeFile(name, blob);
}
});
},
client: function(name, blob, encoding, callback) {
localStorage[name] = blob.result;
callback(null, new blob.constructor(blob, {name: name}));
}
};
Blob.writeFile = Blob.prototype.writeFile = process.browser ? writeFile.client : writeFile.server;
}).call(this,require('_process'))
},{"_process":163,"crypto":21,"fs":15,"mkdirp":179,"path":162}],2:[function(require,module,exports){
module.exports = {
Registry: require('./registry').Registry,
Blob: require('./blob').Blob,
Queue: require('./queue').Queue,
Util: require('./util'),
tasks: require('./tasks')
};
},{"./blob":1,"./queue":3,"./registry":4,"./tasks":7,"./util":13}],3:[function(require,module,exports){
/*
* Copyright (c) 2011-2012, Yahoo! Inc. All rights reserved.
* Copyrights licensed under the New BSD License.
* See the accompanying LICENSE file for terms.
*/
var async = require('async'),
Registry = require('./registry').Registry,
Blob = require('./blob').Blob;
// Zip two arrays together a la Python
function zip(arr1, arr2) {
var zipped = [];
for (var i = 0; i < Math.min(arr1.length, arr2.length); i++) {
zipped.push([arr1[i], arr2[i]]);
}
return zipped;
}
function arrayize(arr) {
return typeof arr === 'undefined' ? [] : Array.prototype.concat(arr);
}
/*
* Queue - Perform async operations on array of immutable Blobs.
*/
var Queue = exports.Queue = function Queue(options) {
var self = this;
options = options || {};
this._logger = options.logger || console;
if (typeof options.registry === 'string') {
this._registry = new Registry({module: options.registry});
} else {
this._registry = options.registry || new Registry();
}
this._clear();
// Add registry tasks
this._registry.tasks.forEach(function(name) {
self[name] = self.task.bind(self, name);
});
};
Queue.prototype._clear = function() {
this._queue = [
function(callback) {
callback(null, []);
}
];
};
Queue.prototype._log = function(message) {
this._logger.log(message);
};
function asTask(queue) {
return function(result, callback) {
queue._queue[0]= function(done) {
done(null, result);
};
queue.run(callback);
};
}
Queue.prototype._dispatch = function(name, options, blobs, done) {
if (name instanceof Queue) {
return asTask(name)(blobs, done);
}
var task = this._registry.task(name),
types = { // Allow task type to be inferred based on task params
2: 'append',
3: 'map',
4: 'reduce'
},
type = task.type ? task.type : types[task.length];
// Wrap error with task name and possibly other diagnostics
function doneWrap(err, res) {
if (err) {
err = {task: name, error: err};
}
done(err, res);
}
switch (type) {
case 'append': // Concats new blobs to existing queue
async.map(arrayize(options), task.bind(this), function(err, results) {
doneWrap(err, blobs.concat(results));
});
break;
case 'prepend': // Prepends new blobs to existing queue
async.map(arrayize(options), task.bind(this), function(err, results) {
doneWrap(err, results.concat(blobs));
});
break;
case 'collect': // Task can inspect entire queue
task.call(this, options, blobs, doneWrap);
break;
case 'slice': // Slice options.length blobs from queue
async.map(zip(arrayize(options), blobs), (function(arr, cb) {
task.call(this, arr[0], arr[1], cb);
}).bind(this), doneWrap);
break;
case 'each': // Allow task to abort immediately
async.forEach(blobs, task.bind(this, options), function(err) {
doneWrap(err, blobs);
});
break;
case 'map': // Task transforms one blob at a time
async.map(blobs, task.bind(this, options), doneWrap);
break;
case 'syncmap': // Task transforms one blob at a time until done is called.
async.mapSeries(blobs, task.bind(this, options), doneWrap);
break;
case 'reduce': // Merges blobs from left to right
async.reduce(blobs, new Blob(), task.bind(this, options), function(err, results) {
doneWrap(err, [results]);
});
break;
default:
throw new Error('Task "' + name + '" has unknown type. Add a type property to the task function.');
}
};
Queue.prototype.task = function(name, options) {
this._queue.push(this._dispatch.bind(this, name, options));
return this;
};
Queue.prototype.series = function(sequence) {
if (sequence instanceof Queue) {
sequence = [].slice.call(arguments);
}
this._queue = this._queue.concat(sequence.map(asTask));
return this;
};
Queue.prototype.run = function(callback) {
var self = this;
async.waterfall(this._queue, callback || function(err, res) {if (err) {self._log(err);}});
};
},{"./blob":1,"./registry":4,"async":14}],4:[function(require,module,exports){
(function (process,__dirname){
/*
* Copyright (c) 2011-2012, Yahoo! Inc. All rights reserved.
* Copyrights licensed under the New BSD License.
* See the accompanying LICENSE file for terms.
*/
var fs = require('fs'),
path = require('path');
/*
* Registry - Container for available tasks.
*/
var Registry = exports.Registry = function Registry(options) {
var self = this;
this._tasks = {};
// Load default tasks
this.load(process.browser ? {tasks: require('./tasks')} : {dirname: __dirname + '/tasks'});
if (options) {
this.load(options);
}
Object.defineProperty(this, 'tasks', {get: function() {
return Object.keys(self._tasks);
}});
};
Registry.prototype = {
/*
* Load tasks from NPM, directory, file, or object.
*/
load: function(options) {
options = options || {};
var module = options.module,
dirname = options.dirname,
filename = options.filename,
tasks = options.tasks;
if (module) {
if (!this._loadModule(module)) {
throw new Error('Module ' + module + ' doesn\'t exist');
}
}
if (dirname) {
if (!this._loadDir(dirname)) {
throw new Error('Directory ' + dirname + ' doesn\'t exist');
}
}
if (filename) {
if (!this._loadFile(filename)) {
throw new Error('File ' + filename + ' doesn\'t exist');
}
}
if (tasks) {
if (!this._loadTasks(tasks)) {
throw new Error('Failed to load tasks');
}
}
return this;
},
_loadModule: function(name) {
if (require) {
try {
return this._loadTasks(require(name));
} catch (err) {}
}
return this._loadDir(path.resolve('node_modules', name, 'lib'));
},
_loadDir: function(dirname) {
if (!fs.existsSync(dirname)) {return false;}
var files = fs.readdirSync(dirname),
self = this;
files.forEach(function(filename) {
var name = path.join(dirname, filename);
if (path.extname(name) !== '.js') {return;}
self._loadFile(name);
});
return true;
},
_loadFile: function(filename) {
if (!fs.existsSync(filename)) {return false;}
return this._loadTasks(require(filename));
},
_loadTasks: function(tasks) {
var name;
for (name in tasks) {
this._tasks[name] = tasks[name];
}
return true;
},
task: function(name) {
if (!(name in this._tasks)) {
throw new Error('Task ' + name + ' doesn\'t exist');
}
return this._tasks[name];
}
};
}).call(this,require('_process'),"/lib")
},{"./tasks":7,"_process":163,"fs":15,"path":162}],5:[function(require,module,exports){
/*
* Copyright (c) 2011-2012, Yahoo! Inc. All rights reserved.
* Copyrights licensed under the New BSD License.
* See the accompanying LICENSE file for terms.
*/
/**
* Concatenates blobs.
*
* @param options {Object} Concat options.
* @param options.callback {Function} Callback on each blob.
* @param blobs {Array} Incoming blobs.
* @param done {Function} Callback on task completion.
*/
exports.concat = function(options, prev, blob, done) {
options = options || {};
done(null, new blob.constructor([prev, options.callback ? options.callback(blob) : blob]));
};
},{}],6:[function(require,module,exports){
/*
* Copyright (c) 2011-2012, Yahoo! Inc. All rights reserved.
* Copyrights licensed under the New BSD License.
* See the accompanying LICENSE file for terms.
*/
var Blob = require('../blob').Blob;
function loadfn() {
return function(string, done) {
done(null, new Blob(string));
};
}
/**
* Append a blob string.
*
* @param index {Integer} Index of blobs.
* @param blobs {Array} Incoming blobs.
* @param done {Function} Callback on task completion.
*/
exports.append = exports.load = loadfn();
/**
* Prepend a blob string before all other blobs.
*
* @param string {String} the string to prepend.
* @param done {Function} Callback on task completion.
*/
var prepend = exports.prepend = loadfn();
prepend.type = 'prepend';
/**
* Test Blobs, abort if callback returns non-null.
*
* @param options {Object} Options for task.
* @param options.callback {Function} Callback for testing blob
* @param blob {Object} Current blob.
* @param done {Function} Callback on task completion.
*/
var test = exports.test = function(options, blob, done) {
options = options || {};
done(options.callback ? options.callback(blob) : null);
};
test.type = 'each';
/**
* Gets a blob.
*
* @param index {Integer} Index of blobs.
* @param blobs {Array} Incoming blobs.
* @param done {Function} Callback on task completion.
*/
var get = exports.get = function get(index, blobs, done) {
done(null, blobs.slice(index, index + 1));
};
get.type = 'collect';
/**
* Log a string.
*
* @param string {String} String to log.
* @param blob {Array} Incoming blobs.
* @param done {Function} Callback on task completion.
*/
var log = exports.log = function log(string, blobs, done) {
this._log(string);
done(null, blobs);
};
log.type = 'collect';
/**
* Inspects blobs.
*
* @param options {Object} Ignored.
* @param blob {Object} Incoming blobs.
* @param done {Function} Callback on task completion.
*/
var inspect = exports.inspect = function inspect(options, blobs, done) {
var self = this;
blobs.forEach(function(blob, index) {
var obj = {result: blob.result};
Object.keys(blob).forEach(function(attr) {obj[attr] = blob[attr];});
self._log('blob ' + (index + 1) + ': ' + JSON.stringify(obj, null, ' '));
});
done(null, blobs);
};
inspect.type = 'collect';
/**
* Do nothing.
*
* @param dummy {N/A} N/A.
* @param blob {Array} Incoming blob.
* @param done {Function} Callback on task completion.
*/
exports.noop = function(dummy, blob, done) {
done(null, blob);
};
},{"../blob":1}],7:[function(require,module,exports){
var Tasks = {
write: require('./write').write,
tasks: require('./tasks').tasks,
stamp: require('./stamp').stamp,
replace: require('./replace').replace,
concat: require('./concat').concat
};
function addTasks(o) {
Object.keys(o).map(function(k) {
Tasks[k] = o[k];
});
}
addTasks(require('./core'));
addTasks(require('./read'));
module.exports = Tasks;
},{"./concat":5,"./core":6,"./read":8,"./replace":9,"./stamp":10,"./tasks":11,"./write":12}],8:[function(require,module,exports){
/*
* Copyright (c) 2011-2012, Yahoo! Inc. All rights reserved.
* Copyrights licensed under the New BSD License.
* See the accompanying LICENSE file for terms.
*/
var Blob = require('../blob').Blob;
function readfn() {
return function(options, done) {
options = (typeof options === 'string') ? {name: options} : options;
var encoding = options.encoding || 'utf8';
Blob.readFile(options.name, encoding, done, options.sync);
};
}
/**
* Appends file contents onto queue.
*
* @param options {Object} File options or filename.
* @param options.name {String} Filename to read.
* @param options.encoding {String} File encoding.
* @param done {Function} Callback on task completion.
*/
exports.read = readfn();
/**
* Prepends file contents to the queue.
*
* @param options {Object} File options or filename.
* @param options.name {String} Filename to read.
* @param options.encoding {String} File encoding.
* @param done {Function} Callback on task completion.
*/
var readBefore = exports.readBefore = readfn();
readBefore.type = 'prepend';
},{"../blob":1}],9:[function(require,module,exports){
/*
* Copyright (c) 2011-2012, Yahoo! Inc. All rights reserved.
* Copyrights licensed under the New BSD License.
* See the accompanying LICENSE file for terms.
*/
/**
* Replace with regular expresion.
*
* @param options {Object} RegEx options.
* @param blob {Object} Incoming blob.
* @param done {Function} Callback on task completion.
*/
exports.replace = function(items, blob, done) {
var output = blob.result;
if (!Array.isArray(items)) {
items = [items];
}
items.forEach(function (params) {
var replace = params.replace || '',
flags = (params.flags !== undefined) ? params.flags : 'mg',
regex = (typeof params.regex === 'string') ? new RegExp(params.regex, flags) : params.regex;
output = output.replace(regex, replace);
});
done(null, new blob.constructor(output, blob));
};
},{}],10:[function(require,module,exports){
/*
* Copyright (c) 2011-2012, Yahoo! Inc. All rights reserved.
* Copyrights licensed under the New BSD License.
* See the accompanying LICENSE file for terms.
*/
/**
* Stamp blob with prefix or postfix string.
*
* @param options {Object} Task options.
* @param options.prefix {String} Prefix string.
* @param options.postfix {String} Postfix string.
* @param options.callback {Function} Callback can return text to injext between pre/postfix.
* @param blob {Object} Incoming blob.
* @param done {Function} Callback on task completion.
*/
exports.stamp = function(options, blob, done) {
options = options || {};
options.prefix = options.prefix || '';
options.postfix = options.postfix || '';
var result = options.callback ? options.callback(blob) : blob.result,
stamped = options.prefix + result + options.postfix;
done(null, new blob.constructor(stamped, blob));
};
},{}],11:[function(require,module,exports){
/*
* Copyright (c) 2011-2012, Yahoo! Inc. All rights reserved.
* Copyrights licensed under the New BSD License.
* See the accompanying LICENSE file for terms.
*/
var async = require('async');
/**
* Advanced flow execution.
*
* @param workflow {Object} Object of tasks to run.
* @param blobs {Object} Incoming blobs.
* @param done {Function} Callback on task completion.
*/
var tasks = exports.tasks = function tasks(workflow, blobs, done) {
var item,
task,
name,
requires,
fn,
auto = {},
self = this;
function runTask(name, options, requires) {
return function(callback, result) {
var new_blobs = requires.length ? [] : blobs;
result = result || [];
// Concat dependency blobs in order of requires array
requires.forEach(function(item) {
new_blobs = new_blobs.concat(result[item]);
});
self._dispatch(name, options, new_blobs, callback);
};
}
for (item in workflow) {
task = workflow[item].task;
if (task === undefined) {
task = ['noop'];
} else {
if (!Array.isArray(task)) {
task = [task];
}
}
requires = workflow[item].requires;
if (requires === undefined) {
requires = [];
} else {
if (!Array.isArray(requires)) {
requires = [requires];
}
}
fn = runTask(task[0], task[1], requires);
auto[item] = requires ? requires.concat(fn) : fn;
}
async.auto(auto, function(err, results) {
if (err) {
done(err);
return;
}
done(err, (results && results.join) ? results.join : []);
});
};
tasks.type = 'collect';
},{"async":14}],12:[function(require,module,exports){
/*
* Copyright (c) 2011-2012, Yahoo! Inc. All rights reserved.
* Copyrights licensed under the New BSD License.
* See the accompanying LICENSE file for terms.
*/
/**
* Write the blob to disk with an optional checksum in the filename.
*
* @param options {Object} Write options or filename.
* @param options.file {String} Filename to write.
* @param blob {Object} Incoming blob.
* @param done {Function} Callback on task completion.
*/
var write = exports.write = function write(options, blob, done) {
options = (typeof options === 'string') ? {name: options} : options;
var encoding = options.encoding || 'utf8';
blob.writeFile(options.name, blob, encoding, done);
};
write.type = 'slice';
},{}],13:[function(require,module,exports){
(function (process){
/*
* Util
* Provides methods to read JSON and JSON with comments (JSONC) files easily.
*/
/* jshint node: true, browser: true */
var readSync = process.browser ? readLocal : require('fs').readFileSync;
function readLocal(name) {
return localStorage[name];
}
function readJSON(filename) {
return JSON.parse(readSync(filename)+'');
}
function readJSONC(filename) {
return JSON.parse(JSONMinify(readSync(filename)+''));
}
// From https://github.com/getify/JSON.minify
function JSONMinify(json) {
var tokenizer = /"|(\/\*)|(\*\/)|(\/\/)|\n|\r/g,
in_string = false,
in_multiline_comment = false,
in_singleline_comment = false,
tmp, tmp2, new_str = [], ns = 0, from = 0, lc, rc;
tokenizer.lastIndex = 0;
/* jshint boss: true */
while (tmp = tokenizer.exec(json)) {
lc = RegExp.leftContext;
rc = RegExp.rightContext;
if (!in_multiline_comment && !in_singleline_comment) {
tmp2 = lc.substring(from);
if (!in_string) {
tmp2 = tmp2.replace(/(\n|\r|\s)*/g,"");
}
new_str[ns++] = tmp2;
}
from = tokenizer.lastIndex;
if (tmp[0] == "\"" && !in_multiline_comment && !in_singleline_comment) {
tmp2 = lc.match(/(\\)*$/);
if (!in_string || !tmp2 || (tmp2[0].length % 2) === 0) { // start of string with ", or unescaped " character found to end string
in_string = !in_string;
}
from--; // include " character in next catch
rc = json.substring(from);
}
else if (tmp[0] == "/*" && !in_string && !in_multiline_comment && !in_singleline_comment) {
in_multiline_comment = true;
}
else if (tmp[0] == "*/" && !in_string && in_multiline_comment && !in_singleline_comment) {
in_multiline_comment = false;
}
else if (tmp[0] == "//" && !in_string && !in_multiline_comment && !in_singleline_comment) {
in_singleline_comment = true;
}
else if ((tmp[0] == "\n" || tmp[0] == "\r") && !in_string && !in_multiline_comment && in_singleline_comment) {
in_singleline_comment = false;
}
else if (!in_multiline_comment && !in_singleline_comment && !(/\n|\r|\s/.test(tmp[0]))) {
new_str[ns++] = tmp[0];
}
}
new_str[ns++] = rc;
return new_str.join("");
}
module.exports = {
readJSON: readJSON,
readJSONC: readJSONC
};
}).call(this,require('_process'))
},{"_process":163,"fs":15}],14:[function(require,module,exports){
(function (process){
/*!
* async
* https://github.com/caolan/async
*
* Copyright 2010-2014 Caolan McMahon
* Released under the MIT license
*/
/*jshint onevar: false, indent:4 */
/*global setImmediate: false, setTimeout: false, console: false */
(function () {
var async = {};
// global on the server, window in the browser
var root, previous_async;
root = this;
if (root != null) {
previous_async = root.async;
}
async.noConflict = function () {
root.async = previous_async;
return async;
};
function only_once(fn) {
var called = false;
return function() {
if (called) throw new Error("Callback was already called.");
called = true;
fn.apply(root, arguments);
}
}
//// cross-browser compatiblity functions ////
var _toString = Object.prototype.toString;
var _isArray = Array.isArray || function (obj) {
return _toString.call(obj) === '[object Array]';
};
var _each = function (arr, iterator) {
if (arr.forEach) {
return arr.forEach(iterator);
}
for (var i = 0; i < arr.length; i += 1) {
iterator(arr[i], i, arr);
}
};
var _map = function (arr, iterator) {
if (arr.map) {
return arr.map(iterator);
}
var results = [];
_each(arr, function (x, i, a) {
results.push(iterator(x, i, a));
});
return results;
};
var _reduce = function (arr, iterator, memo) {
if (arr.reduce) {
return arr.reduce(iterator, memo);
}
_each(arr, function (x, i, a) {
memo = iterator(memo, x, i, a);
});
return memo;
};
var _keys = function (obj) {
if (Object.keys) {
return Object.keys(obj);
}
var keys = [];
for (var k in obj) {
if (obj.hasOwnProperty(k)) {
keys.push(k);
}
}
return keys;
};
//// exported async module functions ////
//// nextTick implementation with browser-compatible fallback ////
if (typeof process === 'undefined' || !(process.nextTick)) {
if (typeof setImmediate === 'function') {
async.nextTick = function (fn) {
// not a direct alias for IE10 compatibility
setImmediate(fn);
};
async.setImmediate = async.nextTick;
}
else {
async.nextTick = function (fn) {
setTimeout(fn, 0);
};
async.setImmediate = async.nextTick;
}
}
else {
async.nextTick = process.nextTick;
if (typeof setImmediate !== 'undefined') {
async.setImmediate = function (fn) {
// not a direct alias for IE10 compatibility
setImmediate(fn);
};
}
else {
async.setImmediate = async.nextTick;
}
}
async.each = function (arr, iterator, callback) {
callback = callback || function () {};
if (!arr.length) {
return callback();
}
var completed = 0;
_each(arr, function (x) {
iterator(x, only_once(done) );
});
function done(err) {
if (err) {
callback(err);
callback = function () {};
}
else {
completed += 1;
if (completed >= arr.length) {
callback();
}
}
}
};
async.forEach = async.each;
async.eachSeries = function (arr, iterator, callback) {
callback = callback || function () {};
if (!arr.length) {
return callback();
}
var completed = 0;
var iterate = function () {
iterator(arr[completed], function (err) {
if (err) {
callback(err);
callback = function () {};
}
else {
completed += 1;
if (completed >= arr.length) {
callback();
}
else {
iterate();
}
}
});
};
iterate();
};
async.forEachSeries = async.eachSeries;
async.eachLimit = function (arr, limit, iterator, callback) {
var fn = _eachLimit(limit);
fn.apply(null, [arr, iterator, callback]);
};
async.forEachLimit = async.eachLimit;
var _eachLimit = function (limit) {
return function (arr, iterator, callback) {
callback = callback || function () {};
if (!arr.length || limit <= 0) {
return callback();
}
var completed = 0;
var started = 0;
var running = 0;
(function replenish () {
if (completed >= arr.length) {
return callback();
}
while (running < limit && started < arr.length) {
started += 1;
running += 1;
iterator(arr[started - 1], function (err) {
if (err) {
callback(err);
callback = function () {};
}
else {
completed += 1;
running -= 1;
if (completed >= arr.length) {
callback();
}
else {
replenish();
}
}
});
}
})();
};
};
var doParallel = function (fn) {
return function () {
var args = Array.prototype.slice.call(arguments);
return fn.apply(null, [async.each].concat(args));
};
};
var doParallelLimit = function(limit, fn) {
return function () {
var args = Array.prototype.slice.call(arguments);
return fn.apply(null, [_eachLimit(limit)].concat(args));
};
};
var doSeries = function (fn) {
return function () {
var args = Array.prototype.slice.call(arguments);
return fn.apply(null, [async.eachSeries].concat(args));
};
};
var _asyncMap = function (eachfn, arr, iterator, callback) {
arr = _map(arr, function (x, i) {
return {index: i, value: x};
});
if (!callback) {
eachfn(arr, function (x, callback) {
iterator(x.value, function (err) {
callback(err);
});
});
} else {
var results = [];
eachfn(arr, function (x, callback) {
iterator(x.value, function (err, v) {
results[x.index] = v;
callback(err);
});
}, function (err) {
callback(err, results);
});
}
};
async.map = doParallel(_asyncMap);
async.mapSeries = doSeries(_asyncMap);
async.mapLimit = function (arr, limit, iterator, callback) {
return _mapLimit(limit)(arr, iterator, callback);
};
var _mapLimit = function(limit) {
return doParallelLimit(limit, _asyncMap);
};
// reduce only has a series version, as doing reduce in parallel won't
// work in many situations.
async.reduce = function (arr, memo, iterator, callback) {
async.eachSeries(arr, function (x, callback) {
iterator(memo, x, function (err, v) {
memo = v;
callback(err);
});
}, function (err) {
callback(err, memo);
});
};
// inject alias
async.inject = async.reduce;
// foldl alias
async.foldl = async.reduce;
async.reduceRight = function (arr, memo, iterator, callback) {
var reversed = _map(arr, function (x) {
return x;
}).reverse();
async.reduce(reversed, memo, iterator, callback);
};
// foldr alias
async.foldr = async.reduceRight;
var _filter = function (eachfn, arr, iterator, callback) {
var results = [];
arr = _map(arr, function (x, i) {
return {index: i, value: x};
});
eachfn(arr, function (x, callback) {
iterator(x.value, function (v) {
if (v) {
results.push(x);
}
callback();
});
}, function (err) {
callback(_map(results.sort(function (a, b) {
return a.index - b.index;
}), function (x) {
return x.value;
}));
});
};
async.filter = doParallel(_filter);
async.filterSeries = doSeries(_filter);
// select alias
async.select = async.filter;
async.selectSeries = async.filterSeries;
var _reject = function (eachfn, arr, iterator, callback) {
var results = [];
arr = _map(arr, function (x, i) {
return {index: i, value: x};
});
eachfn(arr, function (x, callback) {
iterator(x.value, function (v) {
if (!v) {
results.push(x);
}
callback();
});
}, function (err) {
callback(_map(results.sort(function (a, b) {
return a.index - b.index;
}), function (x) {
return x.value;
}));
});
};
async.reject = doParallel(_reject);
async.rejectSeries = doSeries(_reject);
var _detect = function (eachfn, arr, iterator, main_callback) {
eachfn(arr, function (x, callback) {
iterator(x, function (result) {
if (result) {
main_callback(x);
main_callback = function () {};
}
else {
callback();
}
});
}, function (err) {
main_callback();
});
};
async.detect = doParallel(_detect);
async.detectSeries = doSeries(_detect);
async.some = function (arr, iterator, main_callback) {
async.each(arr, function (x, callback) {
iterator(x, function (v) {
if (v) {
main_callback(true);
main_callback = function () {};
}
callback();
});
}, function (err) {
main_callback(false);
});
};
// any alias
async.any = async.some;
async.every = function (arr, iterator, main_callback) {
async.each(arr, function (x, callback) {
iterator(x, function (v) {
if (!v) {
main_callback(false);
main_callback = function () {};
}
callback();
});
}, function (err) {
main_callback(true);
});
};
// all alias
async.all = async.every;
async.sortBy = function (arr, iterator, callback) {
async.map(arr, function (x, callback) {
iterator(x, function (err, criteria) {
if (err) {
callback(err);
}
else {
callback(null, {value: x, criteria: criteria});
}
});
}, function (err, results) {
if (err) {
return callback(err);
}
else {
var fn = function (left, right) {
var a = left.criteria, b = right.criteria;
return a < b ? -1 : a > b ? 1 : 0;
};
callback(null, _map(results.sort(fn), function (x) {
return x.value;
}));
}
});
};
async.auto = function (tasks, callback) {
callback = callback || function () {};
var keys = _keys(tasks);
var remainingTasks = keys.length
if (!remainingTasks) {
return callback();
}
var results = {};
var listeners = [];
var addListener = function (fn) {
listeners.unshift(fn);
};
var removeListener = function (fn) {
for (var i = 0; i < listeners.length; i += 1) {
if (listeners[i] === fn) {
listeners.splice(i, 1);
return;
}
}
};
var taskComplete = function () {
remainingTasks--
_each(listeners.slice(0), function (fn) {
fn();
});
};
addListener(function () {
if (!remainingTasks) {
var theCallback = callback;
// prevent final callback from calling itself if it errors
callback = function () {};
theCallback(null, results);
}
});
_each(keys, function (k) {
var task = _isArray(tasks[k]) ? tasks[k]: [tasks[k]];
var taskCallback = function (err) {
var args = Array.prototype.slice.call(arguments, 1);
if (args.length <= 1) {
args = args[0];
}
if (err) {
var safeResults = {};
_each(_keys(results), function(rkey) {
safeResults[rkey] = results[rkey];
});
safeResults[k] = args;
callback(err, safeResults);
// stop subsequent errors hitting callback multiple times
callback = function () {};
}
else {
results[k] = args;
async.setImmediate(taskComplete);
}
};
var requires = task.slice(0, Math.abs(task.length - 1)) || [];
var ready = function () {
return _reduce(requires, function (a, x) {
return (a && results.hasOwnProperty(x));
}, true) && !results.hasOwnProperty(k);
};
if (ready()) {
task[task.length - 1](taskCallback, results);
}
else {
var listener = function () {
if (ready()) {
removeListener(listener);
task[task.length - 1](taskCallback, results);
}
};
addListener(listener);
}
});
};
async.retry = function(times, task, callback) {
var DEFAULT_TIMES = 5;
var attempts = [];
// Use defaults if times not passed
if (typeof times === 'function') {
callback = task;
task = times;
times = DEFAULT_TIMES;
}
// Make sure times is a number
times = parseInt(times, 10) || DEFAULT_TIMES;
var wrappedTask = function(wrappedCallback, wrappedResults) {
var retryAttempt = function(task, finalAttempt) {
return function(seriesCallback) {
task(function(err, result){
seriesCallback(!err || finalAttempt, {err: err, result: result});
}, wrappedResults);
};
};
while (times) {
attempts.push(retryAttempt(task, !(times-=1)));
}
async.series(attempts, function(done, data){
data = data[data.length - 1];
(wrappedCallback || callback)(data.err, data.result);
});
}
// If a callback is passed, run this as a controll flow
return callback ? wrappedTask() : wrappedTask
};
async.waterfall = function (tasks, callback) {
callback = callback || function () {};
if (!_isArray(tasks)) {
var err = new Error('First argument to waterfall must be an array of functions');
return callback(err);
}
if (!tasks.length) {
return callback();
}
var wrapIterator = function (iterator) {
return function (err) {
if (err) {
callback.apply(null, arguments);
callback = function () {};
}
else {
var args = Array.prototype.slice.call(arguments, 1);
var next = iterator.next();
if (next) {
args.push(wrapIterator(next));
}
else {
args.push(callback);
}
async.setImmediate(function () {
iterator.apply(null, args);
});
}
};
};
wrapIterator(async.iterator(tasks))();
};
var _parallel = function(eachfn, tasks, callback) {
callback = callback || function () {};
if (_isArray(tasks)) {
eachfn.map(tasks, function (fn, callback) {
if (fn) {
fn(function (err) {
var args = Array.prototype.slice.call(arguments, 1);
if (args.length <= 1) {
args = args[0];
}
callback.call(null, err, args);
});
}
}, callback);
}
else {
var results = {};
eachfn.each(_keys(tasks), function (k, callback) {
tasks[k](function (err) {
var args = Array.prototype.slice.call(arguments, 1);
if (args.length <= 1) {
args = args[0];
}
results[k] = args;
callback(err);
});
}, function (err) {
callback(err, results);
});
}
};
async.parallel = function (tasks, callback) {
_parallel({ map: async.map, each: async.each }, tasks, callback);
};
async.parallelLimit = function(tasks, limit, callback) {
_parallel({ map: _mapLimit(limit), each: _eachLimit(limit) }, tasks, callback);
};
async.series = function (tasks, callback) {
callback = callback || function () {};
if (_isArray(tasks)) {
async.mapSeries(tasks, function (fn, callback) {
if (fn) {
fn(function (err) {
var args = Array.prototype.slice.call(arguments, 1);
if (args.length <= 1) {
args = args[0];
}
callback.call(null, err, args);
});
}
}, callback);
}
else {
var results = {};
async.eachSeries(_keys(tasks), function (k, callback) {
tasks[k](function (err) {
var args = Array.prototype.slice.call(arguments, 1);
if (args.length <= 1) {
args = args[0];
}
results[k] = args;
callback(err);
});
}, function (err) {
callback(err, results);
});
}
};
async.iterator = function (tasks) {
var makeCallback = function (index) {
var fn = function () {
if (tasks.length) {
tasks[index].apply(null, arguments);
}
return fn.next();
};
fn.next = function () {
return (index < tasks.length - 1) ? makeCallback(index + 1): null;
};
return fn;
};
return makeCallback(0);
};
async.apply = function (fn) {
var args = Array.prototype.slice.call(arguments, 1);
return function () {
return fn.apply(
null, args.concat(Array.prototype.slice.call(arguments))
);
};
};
var _concat = function (eachfn, arr, fn, callback) {
var r = [];
eachfn(arr, function (x, cb) {
fn(x, function (err, y) {
r = r.concat(y || []);
cb(err);
});
}, function (err) {
callback(err, r);
});
};
async.concat = doParallel(_concat);
async.concatSeries = doSeries(_concat);
async.whilst = function (test, iterator, callback) {
if (test()) {
iterator(function (err) {
if (err) {
return callback(err);
}
async.whilst(test, iterator, callback);
});
}
else {
callback();
}
};
async.doWhilst = function (iterator, test, callback) {
iterator(function (err) {
if (err) {
return callback(err);
}
var args = Array.prototype.slice.call(arguments, 1);
if (test.apply(null, args)) {
async.doWhilst(iterator, test, callback);
}
else {
callback();
}
});
};
async.until = function (test, iterator, callback) {
if (!test()) {
iterator(function (err) {
if (err) {
return callback(err);
}
async.until(test, iterator, callback);
});
}
else {
callback();
}
};
async.doUntil = function (iterator, test, callback) {
iterator(function (err) {
if (err) {
return callback(err);
}
var args = Array.prototype.slice.call(arguments, 1);
if (!test.apply(null, args)) {
async.doUntil(iterator, test, callback);
}
else {
callback();
}
});
};
async.queue = function (worker, concurrency) {
if (concurrency === undefined) {
concurrency = 1;
}
function _insert(q, data, pos, callback) {
if (!q.started){
q.started = true;
}
if (!_isArray(data)) {
data = [data];
}
if(data.length == 0) {
// call drain immediately if there are no tasks
return async.setImmediate(function() {
if (q.drain) {
q.drain();
}
});
}
_each(data, function(task) {
var item = {
data: task,
callback: typeof callback === 'function' ? callback : null
};
if (pos) {
q.tasks.unshift(item);
} else {
q.tasks.push(item);
}
if (q.saturated && q.tasks.length === q.concurrency) {
q.saturated();
}
async.setImmediate(q.process);
});
}
var workers = 0;
var q = {
tasks: [],
concurrency: concurrency,
saturated: null,
empty: null,
drain: null,
started: false,
paused: false,
push: function (data, callback) {
_insert(q, data, false, callback);
},
kill: function () {
q.drain = null;
q.tasks = [];
},
unshift: function (data, callback) {
_insert(q, data, true, callback);
},
process: function () {
if (!q.paused && workers < q.concurrency && q.tasks.length) {
var task = q.tasks.shift();
if (q.empty && q.tasks.length === 0) {
q.empty();
}
workers += 1;
var next = function () {
workers -= 1;
if (task.callback) {
task.callback.apply(task, arguments);
}